From ce3aaaa0581ed698f7dd2b4af7c3dc18e0ffa0fc Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Tue, 15 Nov 2022 13:13:11 +0100 Subject: [PATCH 001/362] [issue-290] Add refactored file data structure Signed-off-by: Nicolaus Weidner --- src/model/__init__.py | 0 src/model/checksum.py | 42 +++++++++++++++++++ src/model/file.py | 73 +++++++++++++++++++++++++++++++++ src/model/license.py | 45 ++++++++++++++++++++ src/model/license_expression.py | 16 ++++++++ src/model/spdx_no_assertion.py | 24 +++++++++++ src/model/spdx_none.py | 24 +++++++++++ tests/model/__init__.py | 0 tests/model/test_license.py | 31 ++++++++++++++ 9 files changed, 255 insertions(+) create mode 100644 src/model/__init__.py create mode 100644 src/model/checksum.py create mode 100644 src/model/file.py create mode 100644 src/model/license.py create mode 100644 src/model/license_expression.py create mode 100644 src/model/spdx_no_assertion.py create mode 100644 src/model/spdx_none.py create mode 100644 tests/model/__init__.py create mode 100644 tests/model/test_license.py diff --git a/src/model/__init__.py b/src/model/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/model/checksum.py b/src/model/checksum.py new file mode 100644 index 000000000..1afd9a141 --- /dev/null +++ b/src/model/checksum.py @@ -0,0 +1,42 @@ +# 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 auto, Enum + + +class ChecksumAlgorithm(Enum): + SHA1 = auto() + SHA224 = auto() + SHA256 = auto() + SHA384 = auto() + SHA512 = auto() + SHA3_256 = auto() + SHA3_384 = auto() + SHA3_512 = auto() + BLAKE2B_256 = auto() + BLAKE2B_384 = auto() + BLAKE2B_512 = auto() + BLAKE3 = auto() + MD2 = auto() + MD4 = auto() + MD5 = auto() + MD6 = auto() + ADLER32 = auto() + + +class Checksum: + algorithm: ChecksumAlgorithm + value: str + + def __init__(self, algorithm: ChecksumAlgorithm, value: str): + self.algorithm = algorithm + self.value = value diff --git a/src/model/file.py b/src/model/file.py new file mode 100644 index 000000000..43e08803e --- /dev/null +++ b/src/model/file.py @@ -0,0 +1,73 @@ +# 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 typing import Optional, List, Union + +from src.model.checksum import Checksum +from src.model.license import License +from src.model.license_expression import LicenseExpression +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone + + +class FileType(Enum): + SOURCE = auto() + BINARY = auto() + ARCHIVE = auto() + APPLICATION = auto() + AUDIO = auto() + IMAGE = auto() + TEXT = auto() + VIDEO = auto() + DOCUMENTATION = auto() + SPDX = auto() + OTHER = auto() + + +class File: + name: str + spdx_id: str + file_type: List[FileType] + checksums: List[Checksum] + concluded_license: Optional[License, SpdxNoAssertion, SpdxNone] + license_info_in_file: Optional[List[LicenseExpression], SpdxNoAssertion, SpdxNone] + license_comment: Optional[str] + copyright_text: Optional[str, SpdxNoAssertion, SpdxNone] + comment: Optional[str] + notice: Optional[str] + contributors: List[str] + attribution_texts: List[str] + + # Deprecated properties that should be replaced during parsing: + # - file dependencies: replace by a DEPENDENCY_OF relationship (or one of the more precise versions) + # - artifact of (3 properties): replace by an external package reference and a GENERATED_FROM relationship + # between the file and this package + + def __init__(self, name: str, spdx_id: str, checksums: List[Checksum], file_type: List[FileType] = None, + comment: str = None, concluded_license: Optional[Union[License, SpdxNoAssertion, SpdxNone]] = None, + license_info_in_file: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, + license_comment: Optional[str] = None, + copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None, notice: Optional[str] = None, + contributors: List[str] = None, attribution_texts: List[str] = None): + self.name = name + self.spdx_id = spdx_id + self.file_type = [] if file_type is None else file_type + self.checksums = checksums + self.concluded_license = [] if concluded_license is None else concluded_license + self.license_info_in_file = [] if license_info_in_file is None else license_info_in_file + self.license_comment = license_comment + self.copyright_text = copyright_text + self.comment = comment + self.notice = notice + self.contributors = [] if contributors is None else contributors + self.attribution_texts = [] if attribution_texts is None else attribution_texts diff --git a/src/model/license.py b/src/model/license.py new file mode 100644 index 000000000..f0ea53ac1 --- /dev/null +++ b/src/model/license.py @@ -0,0 +1,45 @@ +# 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 spdx import config + + +def determine_full_name(identifier: str, full_name: str): + if full_name is not None: + return full_name + # Note: the license map contains both the ids and names of licenses as keys, with the name resp. id as value + if identifier in config.LICENSE_MAP: + return config.LICENSE_MAP[identifier] + return identifier + + +def determine_identifier(identifier: str, full_name: str): + if identifier is not None: + return identifier + # Note: the license map contains both the ids and names of licenses as keys, with the name resp. id as value + if full_name in config.LICENSE_MAP: + return config.LICENSE_MAP[full_name] + return full_name + + +class License: + identifier: str + full_name: str + + def __init__(self, identifier: str = None, full_name: str = None): + """Create a new license from identifier, full name or both. If only either identifier or full name is + provided, we try to retrieve the other value from the list of known licenses. If the license is unknown and + only one value is provided, both identifier and full name are set to this value.""" + if identifier is None and full_name is None: + raise ValueError("Must provide either identifier or full name for a license") + self.identifier = determine_identifier(identifier, full_name) + self.full_name = determine_full_name(identifier, full_name) diff --git a/src/model/license_expression.py b/src/model/license_expression.py new file mode 100644 index 000000000..3ae75f7f3 --- /dev/null +++ b/src/model/license_expression.py @@ -0,0 +1,16 @@ +# 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. + + +class LicenseExpression: + """So far, this just holds a string with the license expression. The ticket for adding license expression support + is https://github.com/spdx/tools-python/issues/10.""" + expression_string: str diff --git a/src/model/spdx_no_assertion.py b/src/model/spdx_no_assertion.py new file mode 100644 index 000000000..1546be326 --- /dev/null +++ b/src/model/spdx_no_assertion.py @@ -0,0 +1,24 @@ +# 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. + + +class SpdxNoAssertion: + """ + Represents the SPDX NOASSERTION value. + """ + + _string_value: str = "NOASSERTION" + + def __str__(self): + return self._string_value + + def __repr__(self): + return self._string_value diff --git a/src/model/spdx_none.py b/src/model/spdx_none.py new file mode 100644 index 000000000..7f9767cd8 --- /dev/null +++ b/src/model/spdx_none.py @@ -0,0 +1,24 @@ +# 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. + + +class SpdxNone: + """ + Represents the SPDX NONE value. + """ + + _string_value = "NONE" + + def __str__(self): + return self._string_value + + def __repr__(self): + return self._string_value diff --git a/tests/model/__init__.py b/tests/model/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/model/test_license.py b/tests/model/test_license.py new file mode 100644 index 000000000..210ad53a5 --- /dev/null +++ b/tests/model/test_license.py @@ -0,0 +1,31 @@ +# 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. + + +import pytest + +from src.model.license import determine_full_name, determine_identifier + + +@pytest.mark.parametrize("identifier,full_name,expected", + [("0BSD", "full_name", "full_name"), (None, "full_name", "full_name"), + (None, "BSD Zero Clause License", "BSD Zero Clause License"), + ("0BSD", None, "BSD Zero Clause License"), ("identifier", None, "identifier")]) +def test_determine_full_name(identifier, full_name, expected): + assert determine_full_name(identifier, full_name) == expected + + +@pytest.mark.parametrize("identifier,full_name,expected", + [("identifier", "BSD Zero Clause License", "identifier"), (None, "full_name", "full_name"), + (None, "BSD Zero Clause License", "0BSD"), ("0BSD", None, "0BSD"), + ("identifier", None, "identifier")]) +def test_determine_identifier(identifier, full_name, expected): + assert determine_identifier(identifier, full_name) == expected From f3651fbdcb83a9cef3b4c3ab5736e06c625c4a8f Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Tue, 15 Nov 2022 13:15:10 +0100 Subject: [PATCH 002/362] [issue-290] Add new annotation class Signed-off-by: Nicolaus Weidner --- src/model/annotation.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/model/annotation.py diff --git a/src/model/annotation.py b/src/model/annotation.py new file mode 100644 index 000000000..0ba8d0acb --- /dev/null +++ b/src/model/annotation.py @@ -0,0 +1,35 @@ +# 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 datetime import datetime +from enum import Enum, auto + + +class AnnotationType(Enum): + REVIEW = auto() + OTHER = auto() + + +class Annotation: + spdx_id: str + annotation_type: AnnotationType + annotator: str + annotation_date: datetime + annotation_comment: str + + def __init__(self, spdx_id: str, annotation_type: AnnotationType, annotator: str, annotation_date: datetime, + annotation_comment: str): + self.spdx_id = spdx_id + self.annotation_type = annotation_type + self.annotator = annotator + self.annotation_date = annotation_date + self.annotation_comment = annotation_comment From 66b8494e42b6c0ba532a8e2ac53dd52155bacbba Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Tue, 15 Nov 2022 15:58:47 +0100 Subject: [PATCH 003/362] [issue-290] Add new relationship class Signed-off-by: Nicolaus Weidner --- src/model/relationship.py | 76 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 src/model/relationship.py diff --git a/src/model/relationship.py b/src/model/relationship.py new file mode 100644 index 000000000..15c29a706 --- /dev/null +++ b/src/model/relationship.py @@ -0,0 +1,76 @@ +# 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 auto, Enum +from typing import Optional + + +class RelationshipType(Enum): + AMENDS = auto() + ANCESTOR_OF = auto() + BUILD_DEPENDENCY_OF = auto() + BUILD_TOOL_OF = auto() + CONTAINED_BY = auto() + CONTAINS = auto() + COPY_OF = auto() + DATA_FILE_OF = auto() + DEPENDENCY_MANIFEST_OF = auto() + DEPENDENCY_OF = auto() + DEPENDS_ON = auto() + DESCENDANT_OF = auto() + DESCRIBED_BY = auto() + DESCRIBES = auto() + DEV_DEPENDENCY_OF = auto() + DEV_TOOL_OF = auto() + DISTRIBUTION_ARTIFACT = auto() + DOCUMENTATION_OF = auto() + DYNAMIC_LINK = auto() + EXAMPLE_OF = auto() + EXPANDED_FROM_ARCHIVE = auto() + FILE_ADDED = auto() + FILE_DELETED = auto() + FILE_MODIFIED = auto() + GENERATED_FROM = auto() + GENERATES = auto() + HAS_PREREQUISITE = auto() + METAFILE_OF = auto() + OPTIONAL_COMPONENT_OF = auto() + OPTIONAL_DEPENDENCY_OF = auto() + OTHER = auto() + PACKAGE_OF = auto() + PATCH_APPLIED = auto() + PATCH_FOR = auto() + PREREQUISITE_FOR = auto() + PROVIDED_DEPENDENCY_OF = auto() + REQUIREMENT_DESCRIPTION_FOR = auto() + RUNTIME_DEPENDENCY_OF = auto() + SPECIFICATION_FOR = auto() + STATIC_LINK = auto() + TEST_CASE_OF = auto() + TEST_DEPENDENCY_OF = auto() + TEST_OF = auto() + TEST_TOOL_OF = auto() + VARIANT_OF = auto() + + +class Relationship: + spdx_element_id: str + relationship_type: RelationshipType + related_spdx_element_id: str + comment: Optional[str] + + def __init__(self, spdx_element_id: str, relationship_type: RelationshipType, related_spdx_element_id: str, + comment: Optional[str] = None): + self.spdx_element_id = spdx_element_id + self.relationship_type = relationship_type + self.related_spdx_element_id = related_spdx_element_id + self.comment = comment From 7a360704d4a4eb390ff6a9aa563dc0392ddf6c0c Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Tue, 15 Nov 2022 18:03:47 +0100 Subject: [PATCH 004/362] [issue-290] Add new package class Signed-off-by: Nicolaus Weidner --- src/model/actor.py | 31 ++++++++++ src/model/package.py | 140 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 src/model/actor.py create mode 100644 src/model/package.py diff --git a/src/model/actor.py b/src/model/actor.py new file mode 100644 index 000000000..de363e89f --- /dev/null +++ b/src/model/actor.py @@ -0,0 +1,31 @@ +# 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 typing import Optional + + +class ActorType(Enum): + PERSON = auto() + ORGANIZATION = auto() + TOOL = auto() + + +class Actor: + actor_type: ActorType + name: str + email: Optional[str] + + def __init__(self, actor_type: ActorType, name: str, email: Optional[str] = None): + self.actor_type = actor_type + self.name = name + self.email = email diff --git a/src/model/package.py b/src/model/package.py new file mode 100644 index 000000000..ea3ad6e65 --- /dev/null +++ b/src/model/package.py @@ -0,0 +1,140 @@ +# 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 datetime import datetime +from enum import Enum, auto +from typing import Optional, Union, List + +from src.model.actor import Actor +from src.model.checksum import Checksum +from src.model.license_expression import LicenseExpression +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone + + +class PackagePurpose(Enum): + APPLICATION = auto() + FRAMEWORK = auto() + LIBRARY = auto() + CONTAINER = auto() + OPERATING_SYSTEM = auto() + DEVICE = auto() + FIRMWARE = auto() + SOURCE = auto() + ARCHIVE = auto() + FILE = auto() + INSTALL = auto() + OTHER = auto() + + +class PackageVerificationCode: + value: str + excluded_files: List[str] + + def __init__(self, value: str, excluded_files: List[str] = None): + self.value = value + self.excluded_files = [] if excluded_files is None else excluded_files + + +class ExternalPackageReferenceCategory(Enum): + SECURITY = auto() + PACKAGE_MANAGER = auto() + PERSISTENT_ID = auto() + OTHER = auto() + + +class ExternalPackageReference: + category: ExternalPackageReferenceCategory + # In theory, once could refine the typing, + # see https://spdx.github.io/spdx-spec/v2.3/external-repository-identifiers/. But it's probably not worth the + # effort. + reference_type: str + locator: str + comment: Optional[str] + + def __init__(self, category: ExternalPackageReferenceCategory, reference_type: str, locator: str, + comment: Optional[str] = None): + self.category = category + self.reference_type = reference_type + self.locator = locator + self.comment = comment + + +class Package: + spdx_id: str + name: str + download_location: Union[str, SpdxNoAssertion, SpdxNone] + version: Optional[str] + file_name: Optional[str] + supplier: Optional[Actor, SpdxNoAssertion] + originator: Optional[Actor, SpdxNoAssertion] + files_analyzed: bool # defaults to True + verification_code: Optional[PackageVerificationCode] + checksums: List[Checksum] + homepage: Optional[str, SpdxNoAssertion, SpdxNone] + source_info: Optional[str] + license_concluded: Optional[LicenseExpression, SpdxNoAssertion, SpdxNone] + license_info_from_files: Optional[List[LicenseExpression], SpdxNoAssertion, SpdxNone] + license_declared: Optional[LicenseExpression, SpdxNoAssertion, SpdxNone] + license_comment: Optional[str] + copyright_text: Optional[str, SpdxNoAssertion, SpdxNone] + summary: Optional[str] + description: Optional[str] + comment: Optional[str] + external_references: List[ExternalPackageReference] + attribution_texts: List[str] + primary_package_purpose: Optional[PackagePurpose] + release_date: Optional[datetime] + built_date: Optional[datetime] + valid_until_date: Optional[datetime] + + def __init__(self, spdx_id: str, name: str, download_location: Union[str, SpdxNoAssertion, SpdxNone], + version: Optional[str] = None, file_name: Optional[str] = None, + supplier: Optional[Actor, SpdxNoAssertion] = None, originator: Optional[Actor, SpdxNoAssertion] = None, + files_analyzed: bool = True, verification_code: Optional[PackageVerificationCode] = None, + checksums: List[Checksum] = None, homepage: Optional[str, SpdxNoAssertion, SpdxNone] = None, + source_info: Optional[str] = None, + license_concluded: Optional[LicenseExpression, SpdxNoAssertion, SpdxNone] = None, + license_info_from_files: Optional[List[LicenseExpression], SpdxNoAssertion, SpdxNone] = None, + license_declared: Optional[LicenseExpression, SpdxNoAssertion, SpdxNone] = None, + license_comment: Optional[str] = None, + copyright_text: Optional[str, SpdxNoAssertion, SpdxNone] = None, + summary: Optional[str] = None, description: Optional[str] = None, comment: Optional[str] = None, + external_references: List[ExternalPackageReference] = None, attribution_texts: List[str] = None, + primary_package_purpose: Optional[PackagePurpose] = None, release_date: Optional[datetime] = None, + built_date: Optional[datetime] = None, valid_until_date: Optional[datetime] = None): + self.spdx_id = spdx_id + self.name = name + self.download_location = download_location + self.version = version + self.file_name = file_name + self.supplier = supplier + self.originator = originator + self.files_analyzed = files_analyzed + self.verification_code = verification_code + self.checksums = checksums + self.homepage = homepage + self.source_info = source_info + self.license_concluded = license_concluded + self.license_info_from_files = license_info_from_files + self.license_declared = license_declared + self.license_comment = license_comment + self.copyright_text = copyright_text + self.summary = summary + self.description = description + self.comment = comment + self.external_references = [] if external_references is None else external_references + self.attribution_texts = [] if attribution_texts is None else attribution_texts + self.primary_package_purpose = primary_package_purpose + self.release_date = release_date + self.built_date = built_date + self.valid_until_date = valid_until_date From bf384f8a39335d45f42116b8fe3cd01b8bf39840 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Wed, 16 Nov 2022 14:46:15 +0100 Subject: [PATCH 005/362] [issue-290] Add new snippet class Signed-off-by: Nicolaus Weidner --- src/model/snippet.py | 49 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/model/snippet.py diff --git a/src/model/snippet.py b/src/model/snippet.py new file mode 100644 index 000000000..acd972dbf --- /dev/null +++ b/src/model/snippet.py @@ -0,0 +1,49 @@ +# 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 typing import Tuple, Optional, List + +from src.model.license_expression import LicenseExpression +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone + + +class Snippet: + spdx_id: str + file_spdx_id: str + byte_range: Tuple[int, int] + line_range: Optional[Tuple[int, int]] + concluded_license: Optional[LicenseExpression, SpdxNoAssertion, SpdxNone] + license_info_in_snippet: Optional[List[LicenseExpression], SpdxNoAssertion, SpdxNone] + license_comment: Optional[str] + copyright_text: Optional[str] + comment: Optional[str] + name: Optional[str] + attribution_texts: List[str] + + def __init__(self, spdx_id: str, file_spdx_id: str, byte_range: Tuple[int, int], + line_range: Optional[Tuple[int, int]] = None, + concluded_license: Optional[LicenseExpression, SpdxNoAssertion, SpdxNone] = None, + license_info_in_snippet: Optional[List[LicenseExpression], SpdxNoAssertion, SpdxNone] = None, + license_comment: Optional[str] = None, copyright_text: Optional[str] = None, + comment: Optional[str] = None, name: Optional[str] = None, attribution_texts: List[str] = None): + self.spdx_id = spdx_id + self.file_spdx_id = file_spdx_id + self.byte_range = byte_range + self.line_range = line_range + self.concluded_license = concluded_license + self.license_info_in_snippet = license_info_in_snippet + self.license_comment = license_comment + self.copyright_text = copyright_text + self.comment = comment + self.name = name + self.attribution_texts = [] if attribution_texts is None else attribution_texts From 1720609949bf1749c6a943fd2f3dad69d6810c14 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Wed, 16 Nov 2022 15:16:10 +0100 Subject: [PATCH 006/362] [issue-290] Add new document class Signed-off-by: Nicolaus Weidner --- src/model/document.py | 76 +++++++++++++++++++++++++++ src/model/external_document_ref.py | 24 +++++++++ src/model/extracted_licensing_info.py | 30 +++++++++++ src/model/version.py | 47 +++++++++++++++++ tests/model/test_version.py | 30 +++++++++++ 5 files changed, 207 insertions(+) create mode 100644 src/model/document.py create mode 100644 src/model/external_document_ref.py create mode 100644 src/model/extracted_licensing_info.py create mode 100644 src/model/version.py create mode 100644 tests/model/test_version.py diff --git a/src/model/document.py b/src/model/document.py new file mode 100644 index 000000000..271897c64 --- /dev/null +++ b/src/model/document.py @@ -0,0 +1,76 @@ +# 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 datetime import datetime +from typing import List, Optional + +from src.model.actor import Actor +from src.model.annotation import Annotation +from src.model.external_document_ref import ExternalDocumentRef +from src.model.extracted_licensing_info import ExtractedLicensingInfo +from src.model.file import File +from src.model.package import Package +from src.model.relationship import Relationship +from src.model.snippet import Snippet +from src.model.version import Version + + +class CreationInfo: + creators: List[Actor] + created: datetime + comment: Optional[str] + license_list_version: Optional[Version] + + def __init__(self, creators: List[Actor], created: datetime, comment: Optional[str] = None, + license_list_version: Optional[Version] = None): + self.creators = creators + self.created = created + self.comment = comment + self.license_list_version = license_list_version + + +class Document: + data_license = "CC0-1.0" + spdx_version: str + spdx_id: str + name: str + document_namespace: str + creation_info: CreationInfo + external_document_references: List[ExternalDocumentRef] + comment: Optional[str] + + packages: List[Package] + files: List[File] + snippets: List[Snippet] + annotations: List[Annotation] + relationships: List[Relationship] + extracted_licensing_info: List[ExtractedLicensingInfo] + + def __init__(self, spdx_version: str, spdx_id: str, name: str, document_namespace: str, + creation_info: CreationInfo, external_document_references: List[ExternalDocumentRef] = None, + comment: Optional[str] = None, packages: List[Package] = None, files: List[File] = None, + snippets: List[Snippet] = None, annotations: List[Annotation] = None, + relationships: List[Relationship] = None, + extracted_licensing_info: List[ExtractedLicensingInfo] = None): + self.spdx_version = spdx_version + self.spdx_id = spdx_id + self.name = name + self.document_namespace = document_namespace + self.creation_info = creation_info + self.external_document_references = [] if external_document_references is None else external_document_references + self.comment = comment + self.packages = [] if packages is None else packages + self.files = [] if files is None else files + self.snippets = [] if snippets is None else snippets + self.annotations = [] if annotations is None else annotations + self.relationships = [] if relationships is None else relationships + self.extracted_licensing_info = [] if extracted_licensing_info is None else extracted_licensing_info diff --git a/src/model/external_document_ref.py b/src/model/external_document_ref.py new file mode 100644 index 000000000..51174be13 --- /dev/null +++ b/src/model/external_document_ref.py @@ -0,0 +1,24 @@ +# 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.model.checksum import Checksum + + +class ExternalDocumentRef: + document_uri: str + spdx_id: str + checksum: Checksum + + def __init__(self, document_uri: str, spdx_id: str, checksum: Checksum): + self.document_uri = document_uri + self.spdx_id = spdx_id + self.checksum = checksum diff --git a/src/model/extracted_licensing_info.py b/src/model/extracted_licensing_info.py new file mode 100644 index 000000000..11c8cafc0 --- /dev/null +++ b/src/model/extracted_licensing_info.py @@ -0,0 +1,30 @@ +# 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 typing import Optional, List + + +class ExtractedLicensingInfo: + license_id: Optional[str] + extracted_text: Optional[str] + license_name: Optional[str] + comment: Optional[str] + cross_references: List[str] + + def __init__(self, license_id: Optional[str] = None, extracted_text: Optional[str] = None, + license_name: Optional[str] = None, comment: Optional[str] = None, + cross_references: List[str] = None): + self.license_id = license_id + self.extracted_text = extracted_text + self.license_name = license_name + self.comment = comment + self.cross_references = [] if cross_references is None else cross_references diff --git a/src/model/version.py b/src/model/version.py new file mode 100644 index 000000000..94a586d62 --- /dev/null +++ b/src/model/version.py @@ -0,0 +1,47 @@ +# 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. + + +import re +from re import Pattern + + +class Version: + VERSION_REGEX: Pattern = re.compile(r"^(\d+)\.(\d+)$") + + major: int + minor: int + + @classmethod + def is_valid_version_string(cls, value: str) -> bool: + return cls.VERSION_REGEX.match(value) is not None + + # No type hint for Python reasons. + # See https://stackoverflow.com/questions/33533148/how-do-i-type-hint-a-method-with-the-type-of-the-enclosing-class + @classmethod + def from_string(cls, value: str): + if not Version.is_valid_version_string(value): + raise ValueError(f"{value} is not a valid version string") + + match = cls.VERSION_REGEX.match(value) + return cls(int(match.group(1)), int(match.group(2))) + + def __init__(self, major: int, minor: int): + self.major = major + self.minor = minor + + def __str__(self): + return f"{self.major}.{self.minor}" + + def __eq__(self, other): + if not isinstance(other, Version): + return False + return self.major == other.major and self.minor == other.minor diff --git a/tests/model/test_version.py b/tests/model/test_version.py new file mode 100644 index 000000000..997de23e2 --- /dev/null +++ b/tests/model/test_version.py @@ -0,0 +1,30 @@ +# 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. + + +import pytest + +from src.model.version import Version + + +@pytest.mark.parametrize("input_string,expected", [("1.2", Version(1, 2)), ("12.345", Version(12, 345))]) +def test_version_from_string(input_string, expected): + assert Version.is_valid_version_string(input_string) + version: Version = Version.from_string(input_string) + assert version == expected + + +@pytest.mark.parametrize("input_string", ["1", "1-2", "1.a", "a", "a.b", "a.1", "v1.2", "1.2v"]) +def test_invalid_version_string(input_string): + assert not Version.is_valid_version_string(input_string) + with pytest.raises(ValueError) as error: + Version.from_string(input_string) + assert str(error.value) == f"{input_string} is not a valid version string" From f88b51689677750e4c9cfa2047206624647b1fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 24 Nov 2022 13:23:32 +0100 Subject: [PATCH 007/362] refactor some code and shorten name of external_document_references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/model/document.py | 18 +++++++++--------- src/model/extracted_licensing_info.py | 2 +- src/model/file.py | 10 +++++----- src/model/package.py | 6 +++--- src/model/snippet.py | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/model/document.py b/src/model/document.py index 271897c64..78d96795f 100644 --- a/src/model/document.py +++ b/src/model/document.py @@ -45,7 +45,7 @@ class Document: name: str document_namespace: str creation_info: CreationInfo - external_document_references: List[ExternalDocumentRef] + external_document_refs: List[ExternalDocumentRef] comment: Optional[str] packages: List[Package] @@ -56,7 +56,7 @@ class Document: extracted_licensing_info: List[ExtractedLicensingInfo] def __init__(self, spdx_version: str, spdx_id: str, name: str, document_namespace: str, - creation_info: CreationInfo, external_document_references: List[ExternalDocumentRef] = None, + creation_info: CreationInfo, external_document_refs: List[ExternalDocumentRef] = None, comment: Optional[str] = None, packages: List[Package] = None, files: List[File] = None, snippets: List[Snippet] = None, annotations: List[Annotation] = None, relationships: List[Relationship] = None, @@ -66,11 +66,11 @@ def __init__(self, spdx_version: str, spdx_id: str, name: str, document_namespac self.name = name self.document_namespace = document_namespace self.creation_info = creation_info - self.external_document_references = [] if external_document_references is None else external_document_references + self.external_document_refs = external_document_refs or [] self.comment = comment - self.packages = [] if packages is None else packages - self.files = [] if files is None else files - self.snippets = [] if snippets is None else snippets - self.annotations = [] if annotations is None else annotations - self.relationships = [] if relationships is None else relationships - self.extracted_licensing_info = [] if extracted_licensing_info is None else extracted_licensing_info + self.packages = packages or [] + self.files = files or [] + self.snippets = snippets or [] + self.annotations = annotations or [] + self.relationships = relationships or [] + self.extracted_licensing_info = extracted_licensing_info or [] diff --git a/src/model/extracted_licensing_info.py b/src/model/extracted_licensing_info.py index 11c8cafc0..d0c9c6ed0 100644 --- a/src/model/extracted_licensing_info.py +++ b/src/model/extracted_licensing_info.py @@ -27,4 +27,4 @@ def __init__(self, license_id: Optional[str] = None, extracted_text: Optional[st self.extracted_text = extracted_text self.license_name = license_name self.comment = comment - self.cross_references = [] if cross_references is None else cross_references + self.cross_references = cross_references or [] diff --git a/src/model/file.py b/src/model/file.py index 43e08803e..189fff7da 100644 --- a/src/model/file.py +++ b/src/model/file.py @@ -61,13 +61,13 @@ def __init__(self, name: str, spdx_id: str, checksums: List[Checksum], file_type contributors: List[str] = None, attribution_texts: List[str] = None): self.name = name self.spdx_id = spdx_id - self.file_type = [] if file_type is None else file_type + self.file_type = file_type or [] self.checksums = checksums - self.concluded_license = [] if concluded_license is None else concluded_license - self.license_info_in_file = [] if license_info_in_file is None else license_info_in_file + self.concluded_license = concluded_license or [] + self.license_info_in_file = license_info_in_file or [] self.license_comment = license_comment self.copyright_text = copyright_text self.comment = comment self.notice = notice - self.contributors = [] if contributors is None else contributors - self.attribution_texts = [] if attribution_texts is None else attribution_texts + self.contributors = contributors or [] + self.attribution_texts = attribution_texts or [] diff --git a/src/model/package.py b/src/model/package.py index ea3ad6e65..a63f0ee9f 100644 --- a/src/model/package.py +++ b/src/model/package.py @@ -42,7 +42,7 @@ class PackageVerificationCode: def __init__(self, value: str, excluded_files: List[str] = None): self.value = value - self.excluded_files = [] if excluded_files is None else excluded_files + self.excluded_files = excluded_files or [] class ExternalPackageReferenceCategory(Enum): @@ -132,8 +132,8 @@ def __init__(self, spdx_id: str, name: str, download_location: Union[str, SpdxNo self.summary = summary self.description = description self.comment = comment - self.external_references = [] if external_references is None else external_references - self.attribution_texts = [] if attribution_texts is None else attribution_texts + self.external_references = external_references or [] + self.attribution_texts = attribution_texts or [] self.primary_package_purpose = primary_package_purpose self.release_date = release_date self.built_date = built_date diff --git a/src/model/snippet.py b/src/model/snippet.py index acd972dbf..7d1aae4aa 100644 --- a/src/model/snippet.py +++ b/src/model/snippet.py @@ -46,4 +46,4 @@ def __init__(self, spdx_id: str, file_spdx_id: str, byte_range: Tuple[int, int], self.copyright_text = copyright_text self.comment = comment self.name = name - self.attribution_texts = [] if attribution_texts is None else attribution_texts + self.attribution_texts = attribution_texts or [] From 35c3e8951a49826f5adc7a25758d61b0ea050beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Mon, 28 Nov 2022 15:53:44 +0100 Subject: [PATCH 008/362] [issue-321] introduce dataclass with properties and typeguard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- pyproject.toml | 2 +- src/model/actor.py | 15 +- src/model/annotation.py | 16 +- src/model/checksum.py | 10 +- src/model/dataclass_with_properties.py | 36 +++ src/model/document.py | 59 ++--- src/model/external_document_ref.py | 9 +- src/model/extracted_licensing_info.py | 25 +-- src/model/file.py | 44 ++-- src/model/license_expression.py | 3 + src/model/package.py | 112 +++------- src/model/relationship.py | 17 +- src/model/snippet.py | 41 ++-- src/model/spdx_no_assertion.py | 3 + src/model/spdx_none.py | 3 + tests/model/test_actor.py | 38 ++++ tests/model/test_annotation.py | 39 ++++ tests/model/test_checksum.py | 19 ++ tests/model/test_creation_info.py | 39 ++++ tests/model/test_document.py | 136 ++++++++++++ tests/model/test_external_document_ref.py | 30 +++ .../model/test_external_package_reference.py | 32 +++ tests/model/test_extracted_licensing_info.py | 37 ++++ tests/model/test_file.py | 115 ++++++++++ tests/model/test_package.py | 207 ++++++++++++++++++ tests/model/test_package_verification_code.py | 19 ++ tests/model/test_relationship.py | 31 +++ tests/model/test_snippet.py | 91 ++++++++ 28 files changed, 995 insertions(+), 233 deletions(-) create mode 100644 src/model/dataclass_with_properties.py create mode 100644 tests/model/test_actor.py create mode 100644 tests/model/test_annotation.py create mode 100644 tests/model/test_checksum.py create mode 100644 tests/model/test_creation_info.py create mode 100644 tests/model/test_document.py create mode 100644 tests/model/test_external_document_ref.py create mode 100644 tests/model/test_external_package_reference.py create mode 100644 tests/model/test_extracted_licensing_info.py create mode 100644 tests/model/test_file.py create mode 100644 tests/model/test_package.py create mode 100644 tests/model/test_package_verification_code.py create mode 100644 tests/model/test_relationship.py create mode 100644 tests/model/test_snippet.py diff --git a/pyproject.toml b/pyproject.toml index 6c44a244f..016e0cee6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ ] urls = {Homepage = "https://github.com/spdx/tools-python"} requires-python = ">=3.7" -dependencies = ["ply", "rdflib", "click", "pyyaml", "xmltodict"] +dependencies = ["ply", "rdflib", "click", "pyyaml", "xmltodict", "typeguard"] dynamic = ["version"] [project.optional-dependencies] diff --git a/src/model/actor.py b/src/model/actor.py index de363e89f..d5261aa5f 100644 --- a/src/model/actor.py +++ b/src/model/actor.py @@ -8,11 +8,14 @@ # 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 dataclasses import dataclass from enum import Enum, auto from typing import Optional +from typeguard import typechecked + +from src.model.dataclass_with_properties import dataclass_with_properties + class ActorType(Enum): PERSON = auto() @@ -20,12 +23,8 @@ class ActorType(Enum): TOOL = auto() +@dataclass_with_properties class Actor: actor_type: ActorType name: str - email: Optional[str] - - def __init__(self, actor_type: ActorType, name: str, email: Optional[str] = None): - self.actor_type = actor_type - self.name = name - self.email = email + email: Optional[str] = None diff --git a/src/model/annotation.py b/src/model/annotation.py index 0ba8d0acb..52fbbbdb5 100644 --- a/src/model/annotation.py +++ b/src/model/annotation.py @@ -8,28 +8,24 @@ # 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 dataclasses import dataclass from datetime import datetime from enum import Enum, auto +from typeguard import typechecked + +from src.model.dataclass_with_properties import dataclass_with_properties + class AnnotationType(Enum): REVIEW = auto() OTHER = auto() +@dataclass_with_properties class Annotation: spdx_id: str annotation_type: AnnotationType annotator: str annotation_date: datetime annotation_comment: str - - def __init__(self, spdx_id: str, annotation_type: AnnotationType, annotator: str, annotation_date: datetime, - annotation_comment: str): - self.spdx_id = spdx_id - self.annotation_type = annotation_type - self.annotator = annotator - self.annotation_date = annotation_date - self.annotation_comment = annotation_comment diff --git a/src/model/checksum.py b/src/model/checksum.py index 1afd9a141..d3f58d86d 100644 --- a/src/model/checksum.py +++ b/src/model/checksum.py @@ -8,9 +8,12 @@ # 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 dataclasses import dataclass +from enum import auto, Enum +from typeguard import typechecked -from enum import auto, Enum +from src.model.dataclass_with_properties import dataclass_with_properties class ChecksumAlgorithm(Enum): @@ -33,10 +36,7 @@ class ChecksumAlgorithm(Enum): ADLER32 = auto() +@dataclass_with_properties class Checksum: algorithm: ChecksumAlgorithm value: str - - def __init__(self, algorithm: ChecksumAlgorithm, value: str): - self.algorithm = algorithm - self.value = value diff --git a/src/model/dataclass_with_properties.py b/src/model/dataclass_with_properties.py new file mode 100644 index 000000000..6d1c81a21 --- /dev/null +++ b/src/model/dataclass_with_properties.py @@ -0,0 +1,36 @@ +from dataclasses import dataclass + +from typeguard import typechecked + + +def dataclass_with_properties(cls): + """Decorator to generate a dataclass with properties out of the class' value:type list. + Their getters and setters will be subjected to the @typechecked decorator to ensure type conformity.""" + data_cls = dataclass(cls) + for field_name, field_type in data_cls.__annotations__.items(): + set_field = make_setter(field_name, field_type) + get_field = make_getter(field_name, field_type) + + setattr(data_cls, field_name, property(get_field, set_field)) + + return data_cls + + +def make_setter(field_name, field_type): + """helper method to avoid late binding when generating functions in a for loop""" + + @typechecked + def set_field(self, value: field_type): + setattr(self, f"_{field_name}", value) + + return set_field + + +def make_getter(field_name, field_type): + """helper method to avoid late binding when generating functions in a for loop""" + + @typechecked + def get_field(self) -> field_type: + return getattr(self, f"_{field_name}") + + return get_field diff --git a/src/model/document.py b/src/model/document.py index 78d96795f..0eae8f1bf 100644 --- a/src/model/document.py +++ b/src/model/document.py @@ -8,11 +8,12 @@ # 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 dataclasses import dataclass, field from datetime import datetime from typing import List, Optional +from typeguard import typechecked + from src.model.actor import Actor from src.model.annotation import Annotation from src.model.external_document_ref import ExternalDocumentRef @@ -21,56 +22,32 @@ from src.model.package import Package from src.model.relationship import Relationship from src.model.snippet import Snippet +from src.model.dataclass_with_properties import dataclass_with_properties from src.model.version import Version +@dataclass_with_properties class CreationInfo: creators: List[Actor] created: datetime - comment: Optional[str] - license_list_version: Optional[Version] - - def __init__(self, creators: List[Actor], created: datetime, comment: Optional[str] = None, - license_list_version: Optional[Version] = None): - self.creators = creators - self.created = created - self.comment = comment - self.license_list_version = license_list_version + comment: Optional[str] = None + license_list_version: Optional[Version] = None +@dataclass_with_properties class Document: - data_license = "CC0-1.0" spdx_version: str spdx_id: str name: str document_namespace: str creation_info: CreationInfo - external_document_refs: List[ExternalDocumentRef] - comment: Optional[str] - - packages: List[Package] - files: List[File] - snippets: List[Snippet] - annotations: List[Annotation] - relationships: List[Relationship] - extracted_licensing_info: List[ExtractedLicensingInfo] - - def __init__(self, spdx_version: str, spdx_id: str, name: str, document_namespace: str, - creation_info: CreationInfo, external_document_refs: List[ExternalDocumentRef] = None, - comment: Optional[str] = None, packages: List[Package] = None, files: List[File] = None, - snippets: List[Snippet] = None, annotations: List[Annotation] = None, - relationships: List[Relationship] = None, - extracted_licensing_info: List[ExtractedLicensingInfo] = None): - self.spdx_version = spdx_version - self.spdx_id = spdx_id - self.name = name - self.document_namespace = document_namespace - self.creation_info = creation_info - self.external_document_refs = external_document_refs or [] - self.comment = comment - self.packages = packages or [] - self.files = files or [] - self.snippets = snippets or [] - self.annotations = annotations or [] - self.relationships = relationships or [] - self.extracted_licensing_info = extracted_licensing_info or [] + data_license: str = "CC0-1.0" + external_document_refs: List[ExternalDocumentRef] = field(default_factory=list) + comment: Optional[str] = None + + packages: List[Package] = field(default_factory=list) + files: List[File] = field(default_factory=list) + snippets: List[Snippet] = field(default_factory=list) + annotations: List[Annotation] = field(default_factory=list) + relationships: List[Relationship] = field(default_factory=list) + extracted_licensing_info: List[ExtractedLicensingInfo] = field(default_factory=list) diff --git a/src/model/external_document_ref.py b/src/model/external_document_ref.py index 51174be13..fbb517f3c 100644 --- a/src/model/external_document_ref.py +++ b/src/model/external_document_ref.py @@ -8,17 +8,16 @@ # 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 dataclasses import dataclass +from typeguard import typechecked from src.model.checksum import Checksum +from src.model.dataclass_with_properties import dataclass_with_properties +@dataclass_with_properties class ExternalDocumentRef: document_uri: str spdx_id: str checksum: Checksum - - def __init__(self, document_uri: str, spdx_id: str, checksum: Checksum): - self.document_uri = document_uri - self.spdx_id = spdx_id - self.checksum = checksum diff --git a/src/model/extracted_licensing_info.py b/src/model/extracted_licensing_info.py index d0c9c6ed0..46eb53f49 100644 --- a/src/model/extracted_licensing_info.py +++ b/src/model/extracted_licensing_info.py @@ -8,23 +8,18 @@ # 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 dataclasses import dataclass, field +from typing import Optional, List +from typeguard import typechecked -from typing import Optional, List +from src.model.dataclass_with_properties import dataclass_with_properties +@dataclass_with_properties class ExtractedLicensingInfo: - license_id: Optional[str] - extracted_text: Optional[str] - license_name: Optional[str] - comment: Optional[str] - cross_references: List[str] - - def __init__(self, license_id: Optional[str] = None, extracted_text: Optional[str] = None, - license_name: Optional[str] = None, comment: Optional[str] = None, - cross_references: List[str] = None): - self.license_id = license_id - self.extracted_text = extracted_text - self.license_name = license_name - self.comment = comment - self.cross_references = cross_references or [] + license_id: Optional[str] = None + extracted_text: Optional[str] = None + license_name: Optional[str] = None + comment: Optional[str] = None + cross_references: List[str] = field(default_factory=list) diff --git a/src/model/file.py b/src/model/file.py index 189fff7da..8003f1f6b 100644 --- a/src/model/file.py +++ b/src/model/file.py @@ -8,16 +8,18 @@ # 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 dataclasses import dataclass, field from enum import Enum, auto from typing import Optional, List, Union +from typeguard import typechecked + from src.model.checksum import Checksum from src.model.license import License from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone +from src.model.dataclass_with_properties import dataclass_with_properties class FileType(Enum): @@ -34,40 +36,22 @@ class FileType(Enum): OTHER = auto() +@dataclass_with_properties class File: name: str spdx_id: str - file_type: List[FileType] checksums: List[Checksum] - concluded_license: Optional[License, SpdxNoAssertion, SpdxNone] - license_info_in_file: Optional[List[LicenseExpression], SpdxNoAssertion, SpdxNone] - license_comment: Optional[str] - copyright_text: Optional[str, SpdxNoAssertion, SpdxNone] - comment: Optional[str] - notice: Optional[str] - contributors: List[str] - attribution_texts: List[str] + file_type: List[FileType] = field(default_factory=list) + concluded_license: Optional[Union[License, SpdxNoAssertion, SpdxNone]] = None + license_info_in_file: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = field(default_factory=list) + license_comment: Optional[str] = None + copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None + comment: Optional[str] = None + notice: Optional[str] = None + contributors: List[str] = field(default_factory=list) + attribution_texts: List[str] = field(default_factory=list) # Deprecated properties that should be replaced during parsing: # - file dependencies: replace by a DEPENDENCY_OF relationship (or one of the more precise versions) # - artifact of (3 properties): replace by an external package reference and a GENERATED_FROM relationship # between the file and this package - - def __init__(self, name: str, spdx_id: str, checksums: List[Checksum], file_type: List[FileType] = None, - comment: str = None, concluded_license: Optional[Union[License, SpdxNoAssertion, SpdxNone]] = None, - license_info_in_file: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, - license_comment: Optional[str] = None, - copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None, notice: Optional[str] = None, - contributors: List[str] = None, attribution_texts: List[str] = None): - self.name = name - self.spdx_id = spdx_id - self.file_type = file_type or [] - self.checksums = checksums - self.concluded_license = concluded_license or [] - self.license_info_in_file = license_info_in_file or [] - self.license_comment = license_comment - self.copyright_text = copyright_text - self.comment = comment - self.notice = notice - self.contributors = contributors or [] - self.attribution_texts = attribution_texts or [] diff --git a/src/model/license_expression.py b/src/model/license_expression.py index 3ae75f7f3..ec258ab4e 100644 --- a/src/model/license_expression.py +++ b/src/model/license_expression.py @@ -9,7 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from src.model.dataclass_with_properties import dataclass_with_properties + +@dataclass_with_properties class LicenseExpression: """So far, this just holds a string with the license expression. The ticket for adding license expression support is https://github.com/spdx/tools-python/issues/10.""" diff --git a/src/model/package.py b/src/model/package.py index a63f0ee9f..b27b69910 100644 --- a/src/model/package.py +++ b/src/model/package.py @@ -8,17 +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 dataclasses import dataclass, field from datetime import datetime from enum import Enum, auto from typing import Optional, Union, List +from typeguard import typechecked + from src.model.actor import Actor from src.model.checksum import Checksum from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone +from src.model.dataclass_with_properties import dataclass_with_properties class PackagePurpose(Enum): @@ -36,13 +38,10 @@ class PackagePurpose(Enum): OTHER = auto() +@dataclass_with_properties class PackageVerificationCode: value: str - excluded_files: List[str] - - def __init__(self, value: str, excluded_files: List[str] = None): - self.value = value - self.excluded_files = excluded_files or [] + excluded_files: List[str] = field(default_factory=list) class ExternalPackageReferenceCategory(Enum): @@ -52,6 +51,7 @@ class ExternalPackageReferenceCategory(Enum): OTHER = auto() +@dataclass_with_properties class ExternalPackageReference: category: ExternalPackageReferenceCategory # In theory, once could refine the typing, @@ -59,82 +59,34 @@ class ExternalPackageReference: # effort. reference_type: str locator: str - comment: Optional[str] - - def __init__(self, category: ExternalPackageReferenceCategory, reference_type: str, locator: str, - comment: Optional[str] = None): - self.category = category - self.reference_type = reference_type - self.locator = locator - self.comment = comment + comment: Optional[str] = None +@dataclass_with_properties class Package: spdx_id: str name: str download_location: Union[str, SpdxNoAssertion, SpdxNone] - version: Optional[str] - file_name: Optional[str] - supplier: Optional[Actor, SpdxNoAssertion] - originator: Optional[Actor, SpdxNoAssertion] - files_analyzed: bool # defaults to True - verification_code: Optional[PackageVerificationCode] - checksums: List[Checksum] - homepage: Optional[str, SpdxNoAssertion, SpdxNone] - source_info: Optional[str] - license_concluded: Optional[LicenseExpression, SpdxNoAssertion, SpdxNone] - license_info_from_files: Optional[List[LicenseExpression], SpdxNoAssertion, SpdxNone] - license_declared: Optional[LicenseExpression, SpdxNoAssertion, SpdxNone] - license_comment: Optional[str] - copyright_text: Optional[str, SpdxNoAssertion, SpdxNone] - summary: Optional[str] - description: Optional[str] - comment: Optional[str] - external_references: List[ExternalPackageReference] - attribution_texts: List[str] - primary_package_purpose: Optional[PackagePurpose] - release_date: Optional[datetime] - built_date: Optional[datetime] - valid_until_date: Optional[datetime] - - def __init__(self, spdx_id: str, name: str, download_location: Union[str, SpdxNoAssertion, SpdxNone], - version: Optional[str] = None, file_name: Optional[str] = None, - supplier: Optional[Actor, SpdxNoAssertion] = None, originator: Optional[Actor, SpdxNoAssertion] = None, - files_analyzed: bool = True, verification_code: Optional[PackageVerificationCode] = None, - checksums: List[Checksum] = None, homepage: Optional[str, SpdxNoAssertion, SpdxNone] = None, - source_info: Optional[str] = None, - license_concluded: Optional[LicenseExpression, SpdxNoAssertion, SpdxNone] = None, - license_info_from_files: Optional[List[LicenseExpression], SpdxNoAssertion, SpdxNone] = None, - license_declared: Optional[LicenseExpression, SpdxNoAssertion, SpdxNone] = None, - license_comment: Optional[str] = None, - copyright_text: Optional[str, SpdxNoAssertion, SpdxNone] = None, - summary: Optional[str] = None, description: Optional[str] = None, comment: Optional[str] = None, - external_references: List[ExternalPackageReference] = None, attribution_texts: List[str] = None, - primary_package_purpose: Optional[PackagePurpose] = None, release_date: Optional[datetime] = None, - built_date: Optional[datetime] = None, valid_until_date: Optional[datetime] = None): - self.spdx_id = spdx_id - self.name = name - self.download_location = download_location - self.version = version - self.file_name = file_name - self.supplier = supplier - self.originator = originator - self.files_analyzed = files_analyzed - self.verification_code = verification_code - self.checksums = checksums - self.homepage = homepage - self.source_info = source_info - self.license_concluded = license_concluded - self.license_info_from_files = license_info_from_files - self.license_declared = license_declared - self.license_comment = license_comment - self.copyright_text = copyright_text - self.summary = summary - self.description = description - self.comment = comment - self.external_references = external_references or [] - self.attribution_texts = attribution_texts or [] - self.primary_package_purpose = primary_package_purpose - self.release_date = release_date - self.built_date = built_date - self.valid_until_date = valid_until_date + version: Optional[str] = None + file_name: Optional[str] = None + supplier: Optional[Union[Actor, SpdxNoAssertion]] = None + originator: Optional[Union[Actor, SpdxNoAssertion]] = None + files_analyzed: bool = True + verification_code: Optional[PackageVerificationCode] = None + checksums: List[Checksum] = field(default_factory=list) + homepage: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None + source_info: Optional[str] = None + license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None + license_info_from_files: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = field(default_factory=list) + license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None + license_comment: Optional[str] = None + copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None + summary: Optional[str] = None + description: Optional[str] = None + comment: Optional[str] = None + external_references: List[ExternalPackageReference] = field(default_factory=list) + attribution_texts: List[str] = field(default_factory=list) + primary_package_purpose: Optional[PackagePurpose] = None + release_date: Optional[datetime] = None + built_date: Optional[datetime] = None + valid_until_date: Optional[datetime] = None diff --git a/src/model/relationship.py b/src/model/relationship.py index 15c29a706..da80c1216 100644 --- a/src/model/relationship.py +++ b/src/model/relationship.py @@ -8,11 +8,14 @@ # 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 dataclasses import dataclass from enum import auto, Enum from typing import Optional +from typeguard import typechecked + +from src.model.dataclass_with_properties import dataclass_with_properties + class RelationshipType(Enum): AMENDS = auto() @@ -62,15 +65,9 @@ class RelationshipType(Enum): VARIANT_OF = auto() +@dataclass_with_properties class Relationship: spdx_element_id: str relationship_type: RelationshipType related_spdx_element_id: str - comment: Optional[str] - - def __init__(self, spdx_element_id: str, relationship_type: RelationshipType, related_spdx_element_id: str, - comment: Optional[str] = None): - self.spdx_element_id = spdx_element_id - self.relationship_type = relationship_type - self.related_spdx_element_id = related_spdx_element_id - self.comment = comment + comment: Optional[str] = None diff --git a/src/model/snippet.py b/src/model/snippet.py index 7d1aae4aa..c3ad7da9d 100644 --- a/src/model/snippet.py +++ b/src/model/snippet.py @@ -8,42 +8,27 @@ # 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 dataclasses import dataclass, field +from typing import Tuple, Optional, List, Union - -from typing import Tuple, Optional, List +from typeguard import typechecked from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone +from src.model.dataclass_with_properties import dataclass_with_properties +@dataclass_with_properties class Snippet: spdx_id: str file_spdx_id: str byte_range: Tuple[int, int] - line_range: Optional[Tuple[int, int]] - concluded_license: Optional[LicenseExpression, SpdxNoAssertion, SpdxNone] - license_info_in_snippet: Optional[List[LicenseExpression], SpdxNoAssertion, SpdxNone] - license_comment: Optional[str] - copyright_text: Optional[str] - comment: Optional[str] - name: Optional[str] - attribution_texts: List[str] - - def __init__(self, spdx_id: str, file_spdx_id: str, byte_range: Tuple[int, int], - line_range: Optional[Tuple[int, int]] = None, - concluded_license: Optional[LicenseExpression, SpdxNoAssertion, SpdxNone] = None, - license_info_in_snippet: Optional[List[LicenseExpression], SpdxNoAssertion, SpdxNone] = None, - license_comment: Optional[str] = None, copyright_text: Optional[str] = None, - comment: Optional[str] = None, name: Optional[str] = None, attribution_texts: List[str] = None): - self.spdx_id = spdx_id - self.file_spdx_id = file_spdx_id - self.byte_range = byte_range - self.line_range = line_range - self.concluded_license = concluded_license - self.license_info_in_snippet = license_info_in_snippet - self.license_comment = license_comment - self.copyright_text = copyright_text - self.comment = comment - self.name = name - self.attribution_texts = attribution_texts or [] + line_range: Optional[Tuple[int, int]] = None + concluded_license: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None + license_info_in_snippet: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = None + license_comment: Optional[str] = None + copyright_text: Optional[str] = None + comment: Optional[str] = None + name: Optional[str] = None + attribution_texts: List[str] = field(default_factory=list) diff --git a/src/model/spdx_no_assertion.py b/src/model/spdx_no_assertion.py index 1546be326..60bec6fd6 100644 --- a/src/model/spdx_no_assertion.py +++ b/src/model/spdx_no_assertion.py @@ -22,3 +22,6 @@ def __str__(self): def __repr__(self): return self._string_value + + def __eq__(self, other): + return isinstance(other, SpdxNoAssertion) diff --git a/src/model/spdx_none.py b/src/model/spdx_none.py index 7f9767cd8..8e170ae4c 100644 --- a/src/model/spdx_none.py +++ b/src/model/spdx_none.py @@ -22,3 +22,6 @@ def __str__(self): def __repr__(self): return self._string_value + + def __eq__(self, other): + return isinstance(other, SpdxNone) diff --git a/tests/model/test_actor.py b/tests/model/test_actor.py new file mode 100644 index 000000000..f1eacf5b1 --- /dev/null +++ b/tests/model/test_actor.py @@ -0,0 +1,38 @@ +import pytest + +from src.model.actor import Actor, ActorType + + +def test_correct_initialization(): + actor = Actor(ActorType.TOOL, "tool_name", "mail") + assert actor.actor_type == ActorType.TOOL + assert actor.name == "tool_name" + assert actor.email == "mail" + + +def test_correct_initialization_with_optional_as_none(): + actor = Actor(ActorType.TOOL, "tool_name", None) + assert actor.actor_type == ActorType.TOOL + assert actor.name == "tool_name" + assert actor.email is None + + +def test_wrong_type_in_actor_type(): + with pytest.raises(TypeError): + Actor("PERSON", "name") + + +def test_wrong_type_in_name(): + with pytest.raises(TypeError): + Actor(ActorType.PERSON, 42) + + +def test_wrong_type_in_email(): + with pytest.raises(TypeError): + Actor(ActorType.PERSON, "name", []) + + +def test_wrong_type_in_email_after_initializing(): + with pytest.raises(TypeError): + actor = Actor(ActorType.PERSON, "name") + actor.email = [] diff --git a/tests/model/test_annotation.py b/tests/model/test_annotation.py new file mode 100644 index 000000000..819bd621b --- /dev/null +++ b/tests/model/test_annotation.py @@ -0,0 +1,39 @@ +from datetime import datetime + +import pytest + +from src.model.annotation import Annotation, AnnotationType + + +def test_correct_initialization(): + annotation = Annotation("id", AnnotationType.OTHER, "annotator", datetime(2022, 1, 1), "comment") + assert annotation.spdx_id == "id" + assert annotation.annotation_type == AnnotationType.OTHER + assert annotation.annotator == "annotator" + assert annotation.annotation_date == datetime(2022, 1, 1) + assert annotation.annotation_comment == "comment" + + +def test_wrong_type_in_spdx_id(): + with pytest.raises(TypeError): + Annotation(42, AnnotationType.OTHER, "annotator", datetime(2022, 1, 1), "comment") + + +def test_wrong_type_in_annotation_type(): + with pytest.raises(TypeError): + Annotation("id", 42, "annotator", datetime(2022, 1, 1), "comment") + + +def test_wrong_type_in_annotator(): + with pytest.raises(TypeError): + Annotation("id", AnnotationType.OTHER, 42, datetime(2022, 1, 1), "comment") + + +def test_wrong_type_in_annotation_date(): + with pytest.raises(TypeError): + Annotation("id", AnnotationType.OTHER, "annotator", 42, "comment") + + +def test_wrong_type_in_annotation_comment(): + with pytest.raises(TypeError): + Annotation("id", AnnotationType.OTHER, "annotator", datetime(2022, 1, 1), 42) diff --git a/tests/model/test_checksum.py b/tests/model/test_checksum.py new file mode 100644 index 000000000..41aeaf7b6 --- /dev/null +++ b/tests/model/test_checksum.py @@ -0,0 +1,19 @@ +import pytest + +from src.model.checksum import Checksum, ChecksumAlgorithm + + +def test_correct_initialization(): + checksum = Checksum(ChecksumAlgorithm.BLAKE2B_256, "value") + assert checksum.algorithm == ChecksumAlgorithm.BLAKE2B_256 + assert checksum.value == "value" + + +def test_wrong_type_in_algorithm(): + with pytest.raises(TypeError): + Checksum(42, "value") + + +def test_wrong_type_in_value(): + with pytest.raises(TypeError): + Checksum(ChecksumAlgorithm.BLAKE2B_256, 42) diff --git a/tests/model/test_creation_info.py b/tests/model/test_creation_info.py new file mode 100644 index 000000000..45dc6b47d --- /dev/null +++ b/tests/model/test_creation_info.py @@ -0,0 +1,39 @@ +from datetime import datetime +from unittest import mock + +import pytest + +from src.model.document import CreationInfo +from src.model.version import Version + + +@mock.patch('src.model.actor.Actor', autospec=True) +def test_correct_initialization(actor): + creation_info = CreationInfo([actor, actor], datetime(2022, 1, 1), "comment", Version(6, 3)) + assert creation_info.creators == [actor, actor] + assert creation_info.created == datetime(2022, 1, 1) + assert creation_info.comment == "comment" + assert creation_info.license_list_version == Version(6, 3) + + +def test_wrong_type_in_creators(): + with pytest.raises(TypeError): + CreationInfo(["person"], datetime(2022, 1, 1)) + + +@mock.patch('src.model.actor.Actor', autospec=True) +def test_wrong_type_in_created(actor): + with pytest.raises(TypeError): + CreationInfo([actor, actor], "2022-01-01") + + +@mock.patch('src.model.actor.Actor', autospec=True) +def test_wrong_type_in_comment(actor): + with pytest.raises(TypeError): + CreationInfo([actor, actor], datetime(2022, 1, 1), comment=["string"]) + + +@mock.patch('src.model.actor.Actor', autospec=True) +def test_wrong_type_in_license_list_version(actor): + with pytest.raises(TypeError): + CreationInfo([actor, actor], datetime(2022, 1, 1), license_list_version="6.4") diff --git a/tests/model/test_document.py b/tests/model/test_document.py new file mode 100644 index 000000000..56698858f --- /dev/null +++ b/tests/model/test_document.py @@ -0,0 +1,136 @@ +from unittest import mock + +import pytest + +from src.model.document import Document + + +@mock.patch('src.model.document.CreationInfo', autospec=True) +@mock.patch('src.model.external_document_ref.ExternalDocumentRef', autospec=True) +@mock.patch('src.model.package.Package', autospec=True) +@mock.patch('src.model.file.File', autospec=True) +@mock.patch('src.model.snippet.Snippet', autospec=True) +@mock.patch('src.model.annotation.Annotation', autospec=True) +@mock.patch('src.model.relationship.Relationship', autospec=True) +@mock.patch('src.model.extracted_licensing_info.ExtractedLicensingInfo', autospec=True) +def test_correct_initialization(creation_info, ext_ref, package, file, snippet, annotation, relationship, + extracted_lic): + document = Document("version", "id", "name", "namespace", creation_info, "data_license", [ext_ref, ext_ref], + "comment", [package, package], [file, file], [snippet, snippet], [annotation, annotation], + [relationship, relationship], [extracted_lic, extracted_lic]) + assert document.spdx_version == "version" + assert document.spdx_id == "id" + assert document.name == "name" + assert document.document_namespace == "namespace" + assert document.creation_info == creation_info + assert document.data_license == "data_license" + assert document.external_document_refs == [ext_ref, ext_ref] + assert document.comment == "comment" + assert document.packages == [package, package] + assert document.files == [file, file] + assert document.snippets == [snippet, snippet] + assert document.annotations == [annotation, annotation] + assert document.relationships == [relationship, relationship] + assert document.extracted_licensing_info == [extracted_lic, extracted_lic] + + +@mock.patch('src.model.document.CreationInfo', autospec=True) +def test_correct_initialization_with_default_values(creation_info): + document = Document("version", "id", "name", "namespace", creation_info, "data_license") + assert document.spdx_version == "version" + assert document.spdx_id == "id" + assert document.name == "name" + assert document.document_namespace == "namespace" + assert document.creation_info == creation_info + assert document.data_license == "data_license" + assert document.external_document_refs == [] + assert document.comment is None + assert document.packages == [] + assert document.files == [] + assert document.snippets == [] + assert document.annotations == [] + assert document.relationships == [] + assert document.extracted_licensing_info == [] + + +@mock.patch('src.model.document.CreationInfo', autospec=True) +def test_wrong_type_in_spdx_version(creation_info): + with pytest.raises(TypeError): + Document(2.3, "id", "name", "namespace", creation_info, "data_license") + + +@mock.patch('src.model.document.CreationInfo', autospec=True) +def test_wrong_type_in_spdx_id(creation_info): + with pytest.raises(TypeError): + Document("version", 42, "name", "namespace", creation_info, "data_license") + + +@mock.patch('src.model.document.CreationInfo', autospec=True) +def test_wrong_type_in_name(creation_info): + with pytest.raises(TypeError): + Document("version", "id", ["name"], "namespace", creation_info, "data_license") + + +@mock.patch('src.model.document.CreationInfo', autospec=True) +def test_wrong_type_in_document_namespace(creation_info): + with pytest.raises(TypeError): + Document("version", "id", "name", {}, creation_info, "data_license") + + +def test_wrong_type_in_creation_info(): + with pytest.raises(TypeError): + Document("version", "id", "name", "namespace", "string", "data_license") + + +@mock.patch('src.model.document.CreationInfo', autospec=True) +def test_wrong_type_in_data_license(creation_info): + with pytest.raises(TypeError): + Document("version", "id", "name", "namespace", creation_info, ["data_license"]) + + +@mock.patch('src.model.document.CreationInfo', autospec=True) +def test_wrong_type_in_external_document_refs(creation_info): + with pytest.raises(TypeError): + Document("version", "id", "name", "namespace", creation_info, "data_license", external_document_refs=["string"]) + + +@mock.patch('src.model.document.CreationInfo', autospec=True) +def test_wrong_type_in_comment(creation_info): + with pytest.raises(TypeError): + Document("version", "id", "name", "namespace", creation_info, "data_license", comment=42) + + +@mock.patch('src.model.document.CreationInfo', autospec=True) +def test_wrong_type_in_packages(creation_info): + with pytest.raises(TypeError): + Document("version", "id", "name", "namespace", creation_info, "data_license", packages=["string"]) + + +@mock.patch('src.model.document.CreationInfo', autospec=True) +def test_wrong_type_in_files(creation_info): + with pytest.raises(TypeError): + Document("version", "id", "name", "namespace", creation_info, "data_license", files={}) + + +@mock.patch('src.model.document.CreationInfo', autospec=True) +def test_wrong_type_in_snippets(creation_info): + with pytest.raises(TypeError): + Document("version", "id", "name", "namespace", creation_info, "data_license", snippets=()) + + +@mock.patch('src.model.document.CreationInfo', autospec=True) +def test_wrong_type_in_annotations(creation_info): + with pytest.raises(TypeError): + Document("version", "id", "name", "namespace", creation_info, "data_license", annotations=["string"]) + + +@mock.patch('src.model.document.CreationInfo', autospec=True) +def test_wrong_type_in_relationships(creation_info): + with pytest.raises(TypeError): + Document("version", "id", "name", "namespace", creation_info, "data_license", relationships="string") + + +@mock.patch('src.model.document.CreationInfo', autospec=True) +def test_wrong_type_in_extracted_licensing_info(creation_info): + with pytest.raises(TypeError): + Document("version", "id", "name", "namespace", creation_info, "data_license", extracted_licensing_info=42) diff --git a/tests/model/test_external_document_ref.py b/tests/model/test_external_document_ref.py new file mode 100644 index 000000000..902afc187 --- /dev/null +++ b/tests/model/test_external_document_ref.py @@ -0,0 +1,30 @@ +from unittest import mock + +import pytest + +from src.model.external_document_ref import ExternalDocumentRef + + +@mock.patch('src.model.checksum.Checksum', autospec=True) +def test_correct_initialization(checksum): + external_document_ref = ExternalDocumentRef("uri", "id", checksum) + assert external_document_ref.document_uri == "uri" + assert external_document_ref.spdx_id == "id" + assert external_document_ref.checksum == checksum + + +@mock.patch('src.model.checksum.Checksum', autospec=True) +def test_wrong_type_in_document_uri(checksum): + with pytest.raises(TypeError): + ExternalDocumentRef(42, "id", checksum) + + +@mock.patch('src.model.checksum.Checksum', autospec=True) +def test_wrong_type_in_spdx_id(checksum): + with pytest.raises(TypeError): + ExternalDocumentRef("uri", 42, checksum) + + +def test_wrong_type_in_checksum(): + with pytest.raises(TypeError): + ExternalDocumentRef("uri", "id", 42) diff --git a/tests/model/test_external_package_reference.py b/tests/model/test_external_package_reference.py new file mode 100644 index 000000000..307170b05 --- /dev/null +++ b/tests/model/test_external_package_reference.py @@ -0,0 +1,32 @@ +import pytest + +from src.model.package import ExternalPackageReference, ExternalPackageReferenceCategory + + +def test_correct_initialization(): + external_package_reference = ExternalPackageReference(ExternalPackageReferenceCategory.OTHER, "type", "locator", + "comment") + assert external_package_reference.category == ExternalPackageReferenceCategory.OTHER + assert external_package_reference.reference_type == "type" + assert external_package_reference.locator == "locator" + assert external_package_reference.comment == "comment" + + +def test_wrong_type_in_category(): + with pytest.raises(TypeError): + ExternalPackageReference([ExternalPackageReferenceCategory.OTHER], "type", "locator") + + +def test_wrong_type_in_reference_type(): + with pytest.raises(TypeError): + ExternalPackageReference(ExternalPackageReferenceCategory.OTHER, 42, "locator") + + +def test_wrong_type_in_locator(): + with pytest.raises(TypeError): + ExternalPackageReference(ExternalPackageReferenceCategory.OTHER, "type", 42) + + +def test_wrong_type_in_comment(): + with pytest.raises(TypeError): + ExternalPackageReference(ExternalPackageReferenceCategory.OTHER, "type", "locator", []) diff --git a/tests/model/test_extracted_licensing_info.py b/tests/model/test_extracted_licensing_info.py new file mode 100644 index 000000000..38a4aa600 --- /dev/null +++ b/tests/model/test_extracted_licensing_info.py @@ -0,0 +1,37 @@ +import pytest + +from src.model.extracted_licensing_info import ExtractedLicensingInfo + + +def test_correct_initialization(): + extracted_licensing_info = ExtractedLicensingInfo("id", "text", "name", "comment", ["reference"]) + assert extracted_licensing_info.license_id == "id" + assert extracted_licensing_info.extracted_text == "text" + assert extracted_licensing_info.license_name == "name" + assert extracted_licensing_info.comment == "comment" + assert extracted_licensing_info.cross_references == ["reference"] + + +def test_wrong_type_in_license_id(): + with pytest.raises(TypeError): + ExtractedLicensingInfo(license_id=42) + + +def test_wrong_type_in_extracted_text(): + with pytest.raises(TypeError): + ExtractedLicensingInfo(extracted_text=42) + + +def test_wrong_type_in_license_name(): + with pytest.raises(TypeError): + ExtractedLicensingInfo(license_name=42) + + +def test_wrong_type_in_comment(): + with pytest.raises(TypeError): + ExtractedLicensingInfo(comment=42) + + +def test_wrong_type_in_cross_references(): + with pytest.raises(TypeError): + ExtractedLicensingInfo(cross_references=["ref", 42]) diff --git a/tests/model/test_file.py b/tests/model/test_file.py new file mode 100644 index 000000000..0c8cd3513 --- /dev/null +++ b/tests/model/test_file.py @@ -0,0 +1,115 @@ +from unittest import mock + +import pytest + +from src.model.checksum import Checksum, ChecksumAlgorithm +from src.model.file import File, FileType +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone + + +@mock.patch('src.model.checksum.Checksum', autospec=True) +def test_correct_initialization(checksum): + file = File("name", "id", [checksum, checksum], [FileType.OTHER, FileType.SPDX], SpdxNone(), SpdxNoAssertion(), + "comment on license", "copyright", "comment", "notice", ["contributor"], ["attribution"]) + assert file.name == "name" + assert file.spdx_id == "id" + assert file.checksums == [checksum, checksum] + assert file.file_type == [FileType.OTHER, FileType.SPDX] + assert file.concluded_license == SpdxNone() + assert file.license_info_in_file == SpdxNoAssertion() + assert file.license_comment == "comment on license" + assert file.copyright_text == "copyright" + assert file.comment == "comment" + assert file.notice == "notice" + assert file.contributors == ["contributor"] + assert file.attribution_texts == ["attribution"] + + +@mock.patch('src.model.checksum.Checksum', autospec=True) +def test_correct_initialization_with_default_values(checksum): + file = File("name", "id", [checksum, checksum]) + assert file.name == "name" + assert file.spdx_id == "id" + assert file.checksums == [checksum, checksum] + assert file.file_type == [] + assert file.concluded_license is None + assert file.license_info_in_file == [] + assert file.license_comment is None + assert file.copyright_text is None + assert file.comment is None + assert file.notice is None + assert file.contributors == [] + assert file.attribution_texts == [] + + +@mock.patch('src.model.checksum.Checksum', autospec=True) +def test_wrong_type_in_name(checksum): + with pytest.raises(TypeError): + File(42, "id", [checksum]) + + +@mock.patch('src.model.checksum.Checksum', autospec=True) +def test_wrong_type_in_spdx_id(checksum): + with pytest.raises(TypeError): + File("name", 42, [checksum]) + + +def test_wrong_type_in_checksum(): + checksum = Checksum(ChecksumAlgorithm.BLAKE2B_256, "value") + with pytest.raises(TypeError): + File("name", "id", checksum) + + +@mock.patch('src.model.checksum.Checksum', autospec=True) +def test_wrong_type_in_file_type(checksum): + with pytest.raises(TypeError): + File("name", "id", [checksum], file_type=FileType.OTHER) + + +@mock.patch('src.model.checksum.Checksum', autospec=True) +def test_wrong_type_in_concluded_license(checksum): + with pytest.raises(TypeError): + File("name", "id", [checksum], concluded_license="NONE") + + +@mock.patch('src.model.checksum.Checksum', autospec=True) +def test_wrong_type_in_license_info_in_file(checksum): + with pytest.raises(TypeError): + File("name", "id", [checksum], license_info_in_file=[SpdxNone]) + + +@mock.patch('src.model.checksum.Checksum', autospec=True) +def test_wrong_type_in_license_comment(checksum): + with pytest.raises(TypeError): + File("name", "id", [checksum], license_comment=42) + + +@mock.patch('src.model.checksum.Checksum', autospec=True) +def test_wrong_type_in_copyright_text(checksum): + with pytest.raises(TypeError): + File("name", "id", [checksum], copyright_text=[SpdxNone()]) + + +@mock.patch('src.model.checksum.Checksum', autospec=True) +def test_wrong_type_in_comment(checksum): + with pytest.raises(TypeError): + File("name", "id", [checksum], comment=42) + + +@mock.patch('src.model.checksum.Checksum', autospec=True) +def test_wrong_type_in_notice(checksum): + with pytest.raises(TypeError): + File("name", "id", [checksum], notice=["notice"]) + + +@mock.patch('src.model.checksum.Checksum', autospec=True) +def test_wrong_type_in_contributors(checksum): + with pytest.raises(TypeError): + File("name", "id", [checksum], contributors="contributor") + + +@mock.patch('src.model.checksum.Checksum', autospec=True) +def test_wrong_type_in_attribution_texts(checksum): + with pytest.raises(TypeError): + File("name", "id", [checksum], attribution_texts=["attribution", 42]) diff --git a/tests/model/test_package.py b/tests/model/test_package.py new file mode 100644 index 000000000..2fc8ca494 --- /dev/null +++ b/tests/model/test_package.py @@ -0,0 +1,207 @@ +from datetime import datetime +from unittest import mock + +import pytest + +from src.model.checksum import Checksum, ChecksumAlgorithm +from src.model.license_expression import LicenseExpression +from src.model.package import Package, PackagePurpose +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone + + +@mock.patch('src.model.actor.Actor', autospec=True) +@mock.patch('src.model.package.PackageVerificationCode', autospec=True) +@mock.patch('src.model.checksum.Checksum', autospec=True) +@mock.patch('src.model.package.ExternalPackageReference', autospec=True) +def test_correct_initialization(actor, verif_code, checksum, ext_ref): + package = Package("id", "name", SpdxNoAssertion(), "version", "file_name", SpdxNoAssertion(), actor, True, + verif_code, [checksum], "homepage", "source_info", None, [LicenseExpression("expression")], + SpdxNone(), "comment on license", "copyright", "summary", "description", "comment", + [ext_ref, ext_ref], ["text"], PackagePurpose.OTHER, datetime(2022, 1, 1), None, None) + assert package.spdx_id == "id" + assert package.name == "name" + assert package.download_location == SpdxNoAssertion() + assert package.version == "version" + assert package.file_name == "file_name" + assert package.supplier == SpdxNoAssertion() + assert package.originator == actor + assert package.files_analyzed + assert package.verification_code == verif_code + assert package.checksums == [checksum] + assert package.homepage == "homepage" + assert package.source_info == "source_info" + assert package.license_concluded is None + assert package.license_info_from_files == [LicenseExpression("expression")] + assert package.license_declared == SpdxNone() + assert package.license_comment == "comment on license" + assert package.copyright_text == "copyright" + assert package.summary == "summary" + assert package.description == "description" + assert package.comment == "comment" + assert package.external_references == [ext_ref, ext_ref] + assert package.attribution_texts == ["text"] + assert package.primary_package_purpose == PackagePurpose.OTHER + assert package.release_date == datetime(2022, 1, 1) + assert package.built_date is None + assert package.valid_until_date is None + + +def test_correct_initialization_with_default_values(): + package = Package("id", "name", "location") + assert package.spdx_id == "id" + assert package.name == "name" + assert package.download_location == "location" + assert package.version is None + assert package.file_name is None + assert package.supplier is None + assert package.originator is None + assert package.files_analyzed + assert package.verification_code is None + assert package.checksums == [] + assert package.homepage is None + assert package.source_info is None + assert package.license_concluded is None + assert package.license_info_from_files == [] + assert package.license_declared is None + assert package.license_comment is None + assert package.copyright_text is None + assert package.summary is None + assert package.description is None + assert package.comment is None + assert package.external_references == [] + assert package.attribution_texts == [] + assert package.primary_package_purpose is None + assert package.release_date is None + assert package.built_date is None + assert package.valid_until_date is None + + +def test_wrong_type_in_spdx_id(): + with pytest.raises(TypeError): + Package(42, "name", "location") + + +def test_wrong_type_in_name(): + with pytest.raises(TypeError): + Package("id", 42, "location") + + +def test_wrong_type_in_download_location(): + with pytest.raises(TypeError): + Package("id", "name", 42) + + +def test_wrong_type_in_version(): + with pytest.raises(TypeError): + Package("id", "name", "location", version=42) + + +def test_wrong_type_in_file_name(): + with pytest.raises(TypeError): + Package("id", "name", "location", file_name=42) + + +def test_wrong_type_in_supplier(): + with pytest.raises(TypeError): + Package("id", "name", "location", supplier=SpdxNone()) + + +def test_wrong_type_in_originator(): + with pytest.raises(TypeError): + Package("id", "name", "location", originator=SpdxNone()) + + +def test_wrong_type_in_files_analyzed(): + with pytest.raises(TypeError): + Package("id", "name", "location", files_analyzed=None) + + +def test_wrong_type_in_verification_code(): + with pytest.raises(TypeError): + Package("id", "name", "location", verification_code=[]) + + +def test_wrong_type_in_checksums(): + with pytest.raises(TypeError): + Package("id", "name", "location", checksums=Checksum(ChecksumAlgorithm.MD2, "value")) + + +def test_wrong_type_in_homepage(): + with pytest.raises(TypeError): + Package("id", "name", "location", homepage=42) + + +def test_wrong_type_in_source_info(): + with pytest.raises(TypeError): + Package("id", "name", "location", source_info=42) + + +def test_wrong_type_in_license_concluded(): + with pytest.raises(TypeError): + Package("id", "name", "location", license_concluded=[]) + + +def test_wrong_type_in_license_info_from_files(): + with pytest.raises(TypeError): + Package("id", "name", "location", license_info_from_files=LicenseExpression("string")) + + +def test_wrong_type_in_license_declared(): + with pytest.raises(TypeError): + Package("id", "name", "location", license_declared=[]) + + +def test_wrong_type_in_license_comment(): + with pytest.raises(TypeError): + Package("id", "name", "location", license_comment=42) + + +def test_wrong_type_in_copyright_text(): + with pytest.raises(TypeError): + Package("id", "name", "location", copyright_text=42) + + +def test_wrong_type_in_summary(): + with pytest.raises(TypeError): + Package("id", "name", "location", summary=42) + + +def test_wrong_type_in_description(): + with pytest.raises(TypeError): + Package("id", "name", "location", description=42) + + +def test_wrong_type_in_comment(): + with pytest.raises(TypeError): + Package("id", "name", "location", comment=[]) + + +def test_wrong_type_in_external_references(): + with pytest.raises(TypeError): + Package("id", "name", "location", external_references=["external_ref"]) + + +def test_wrong_type_in_attribution_texts(): + with pytest.raises(TypeError): + Package("id", "name", "location", attribution_texts="text") + + +def test_wrong_type_in_primary_package_purpose(): + with pytest.raises(TypeError): + Package("id", "name", "location", primary_package_purpose=[]) + + +def test_wrong_type_in_release_date(): + with pytest.raises(TypeError): + Package("id", "name", "location", release_date=42) + + +def test_wrong_type_in_built_date(): + with pytest.raises(TypeError): + Package("id", "name", "location", built_date="2022-01-01") + + +def test_wrong_type_in_valid_until_date(): + with pytest.raises(TypeError): + Package("id", "name", "location", valid_until_date=SpdxNone()) diff --git a/tests/model/test_package_verification_code.py b/tests/model/test_package_verification_code.py new file mode 100644 index 000000000..266b7f157 --- /dev/null +++ b/tests/model/test_package_verification_code.py @@ -0,0 +1,19 @@ +import pytest + +from src.model.package import PackageVerificationCode + + +def test_correct_initialization(): + package_verification_code = PackageVerificationCode("value", ["file1", "file2"]) + assert package_verification_code.value == "value" + assert package_verification_code.excluded_files == ["file1", "file2"] + + +def test_wrong_type_in_value(): + with pytest.raises(TypeError): + PackageVerificationCode(42, ["file1", "file2"]) + + +def test_wrong_type_in_excluded_files(): + with pytest.raises(TypeError): + PackageVerificationCode("value", "file1") diff --git a/tests/model/test_relationship.py b/tests/model/test_relationship.py new file mode 100644 index 000000000..4d0a0a681 --- /dev/null +++ b/tests/model/test_relationship.py @@ -0,0 +1,31 @@ +import pytest + +from src.model.relationship import Relationship, RelationshipType + + +def test_correct_initialization(): + relationship = Relationship("id", RelationshipType.OTHER, "other_id", "comment") + assert relationship.spdx_element_id == "id" + assert relationship.relationship_type == RelationshipType.OTHER + assert relationship.related_spdx_element_id == "other_id" + assert relationship.comment == "comment" + + +def test_wrong_type_in_spdx_element_id(): + with pytest.raises(TypeError): + Relationship(42, RelationshipType.OTHER, "other_id") + + +def test_wrong_type_in_relationship_type(): + with pytest.raises(TypeError): + Relationship("id", 42, "other_id") + + +def test_wrong_type_in_related_spdx_element_id(): + with pytest.raises(TypeError): + Relationship("id", RelationshipType.OTHER, 42) + + +def test_wrong_type_in_comment(): + with pytest.raises(TypeError): + Relationship("id", RelationshipType.OTHER, "other_id", 42) diff --git a/tests/model/test_snippet.py b/tests/model/test_snippet.py new file mode 100644 index 000000000..0e7030507 --- /dev/null +++ b/tests/model/test_snippet.py @@ -0,0 +1,91 @@ +import pytest + +from src.model.snippet import Snippet +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone + + +def test_correct_initialization(): + snippet = Snippet("id", "file_id", (200, 400), (20, 40), SpdxNone(), SpdxNoAssertion(), "comment on license", + "copyright", "comment", "name", ["attribution"]) + assert snippet.spdx_id == "id" + assert snippet.file_spdx_id == "file_id" + assert snippet.byte_range == (200, 400) + assert snippet.line_range == (20, 40) + assert snippet.concluded_license == SpdxNone() + assert snippet.license_info_in_snippet == SpdxNoAssertion() + assert snippet.license_comment == "comment on license" + assert snippet.copyright_text == "copyright" + assert snippet.comment == "comment" + assert snippet.name == "name" + assert snippet.attribution_texts == ["attribution"] + + +def test_correct_initialization_with_default_values(): + snippet = Snippet("id", "file_id", (200, 400)) + assert snippet.spdx_id == "id" + assert snippet.file_spdx_id == "file_id" + assert snippet.byte_range == (200, 400) + assert snippet.line_range is None + assert snippet.concluded_license is None + assert snippet.license_info_in_snippet is None + assert snippet.license_comment is None + assert snippet.copyright_text is None + assert snippet.comment is None + assert snippet.name is None + assert snippet.attribution_texts == [] + + +def test_wrong_type_in_spdx_id(): + with pytest.raises(TypeError): + Snippet(42, "file_id", (200, 400)) + + +def test_wrong_type_in_file_spdx_id(): + with pytest.raises(TypeError): + Snippet("id", 42, (200, 400)) + + +def test_wrong_type_in_byte_range(): + with pytest.raises(TypeError): + Snippet("id", "file_id", (200, 300, 400)) + + +def test_wrong_type_in_line_range(): + with pytest.raises(TypeError): + Snippet("id", "file_id", (200, 400), line_range=(20, "40")) + + +def test_wrong_type_in_concluded_license(): + with pytest.raises(TypeError): + Snippet("id", "file_id", (200, 400), concluded_license="NONE") + + +def test_wrong_type_in_license_info_in_snippet(): + with pytest.raises(TypeError): + Snippet("id", "file_id", (200, 400), license_info_in_snippet=[SpdxNoAssertion()]) + + +def test_wrong_type_in_license_comment(): + with pytest.raises(TypeError): + Snippet("id", "file_id", (200, 400), license_comment=[]) + + +def test_wrong_type_in_copyright_text(): + with pytest.raises(TypeError): + Snippet("id", "file_id", (200, 400), copyright_text=["copyright"]) + + +def test_wrong_type_in_comment(): + with pytest.raises(TypeError): + Snippet("id", "file_id", (200, 400), comment=["comment"]) + + +def test_wrong_type_in_name(): + with pytest.raises(TypeError): + Snippet("id", "file_id", (200, 400), name=42) + + +def test_wrong_type_in_attribution_texts(): + with pytest.raises(TypeError): + Snippet("id", "file_id", (200, 400), attribution_texts="attribution") From 57ccd3ca837e31c7a819ece774e13a79c1b6c3f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Mon, 5 Dec 2022 14:42:08 +0100 Subject: [PATCH 009/362] [issue-306] refactor creation info (and shorten external package ref) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/model/document.py | 16 ++-- src/model/package.py | 8 +- tests/model/test_creation_info.py | 67 ++++++++++++++-- tests/model/test_document.py | 78 +++---------------- .../model/test_external_package_reference.py | 14 ++-- tests/model/test_package.py | 2 +- 6 files changed, 89 insertions(+), 96 deletions(-) diff --git a/src/model/document.py b/src/model/document.py index 0eae8f1bf..81f6d526f 100644 --- a/src/model/document.py +++ b/src/model/document.py @@ -28,22 +28,22 @@ @dataclass_with_properties class CreationInfo: + spdx_version: str + spdx_id: str + name: str + document_namespace: str creators: List[Actor] created: datetime - comment: Optional[str] = None + creator_comment: Optional[str] = None + data_license: str = "CC0-1.0" + external_document_refs: List[ExternalDocumentRef] = field(default_factory=list) license_list_version: Optional[Version] = None + document_comment: Optional[str] = None @dataclass_with_properties class Document: - spdx_version: str - spdx_id: str - name: str - document_namespace: str creation_info: CreationInfo - data_license: str = "CC0-1.0" - external_document_refs: List[ExternalDocumentRef] = field(default_factory=list) - comment: Optional[str] = None packages: List[Package] = field(default_factory=list) files: List[File] = field(default_factory=list) diff --git a/src/model/package.py b/src/model/package.py index b27b69910..32e7953c2 100644 --- a/src/model/package.py +++ b/src/model/package.py @@ -44,7 +44,7 @@ class PackageVerificationCode: excluded_files: List[str] = field(default_factory=list) -class ExternalPackageReferenceCategory(Enum): +class ExternalPackageRefCategory(Enum): SECURITY = auto() PACKAGE_MANAGER = auto() PERSISTENT_ID = auto() @@ -52,8 +52,8 @@ class ExternalPackageReferenceCategory(Enum): @dataclass_with_properties -class ExternalPackageReference: - category: ExternalPackageReferenceCategory +class ExternalPackageRef: + category: ExternalPackageRefCategory # In theory, once could refine the typing, # see https://spdx.github.io/spdx-spec/v2.3/external-repository-identifiers/. But it's probably not worth the # effort. @@ -84,7 +84,7 @@ class Package: summary: Optional[str] = None description: Optional[str] = None comment: Optional[str] = None - external_references: List[ExternalPackageReference] = field(default_factory=list) + external_references: List[ExternalPackageRef] = field(default_factory=list) attribution_texts: List[str] = field(default_factory=list) primary_package_purpose: Optional[PackagePurpose] = None release_date: Optional[datetime] = None diff --git a/tests/model/test_creation_info.py b/tests/model/test_creation_info.py index 45dc6b47d..b5650c865 100644 --- a/tests/model/test_creation_info.py +++ b/tests/model/test_creation_info.py @@ -8,32 +8,83 @@ @mock.patch('src.model.actor.Actor', autospec=True) -def test_correct_initialization(actor): - creation_info = CreationInfo([actor, actor], datetime(2022, 1, 1), "comment", Version(6, 3)) +@mock.patch('src.model.external_document_ref.ExternalDocumentRef', autospec=True) +def test_correct_initialization(actor, ext_ref): + creation_info = CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), + "creator_comment", "CC0-1.1", [ext_ref, ext_ref], Version(6, 3), "doc_comment") + assert creation_info.spdx_version == "version" + assert creation_info.spdx_id == "id" + assert creation_info.name == "name" + assert creation_info.document_namespace == "namespace" assert creation_info.creators == [actor, actor] assert creation_info.created == datetime(2022, 1, 1) - assert creation_info.comment == "comment" + assert creation_info.creator_comment == "creator_comment" + assert creation_info.data_license == "CC0-1.1" + assert creation_info.external_document_refs == [ext_ref, ext_ref] assert creation_info.license_list_version == Version(6, 3) + assert creation_info.document_comment == "doc_comment" + + +@mock.patch('src.model.actor.Actor', autospec=True) +def test_wrong_type_in_spdx_version(actor): + with pytest.raises(TypeError): + CreationInfo(42, "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1)) + + +@mock.patch('src.model.actor.Actor', autospec=True) +def test_wrong_type_in_spdx_id(actor): + with pytest.raises(TypeError): + CreationInfo("version", 42, "name", "namespace", [actor, actor], datetime(2022, 1, 1)) + + +@mock.patch('src.model.actor.Actor', autospec=True) +def test_wrong_type_in_name(actor): + with pytest.raises(TypeError): + CreationInfo("version", "id", 42, "namespace", [actor, actor], datetime(2022, 1, 1)) + + +@mock.patch('src.model.actor.Actor', autospec=True) +def test_wrong_type_in_document_namespace(actor): + with pytest.raises(TypeError): + CreationInfo("version", "id", "name", 42, [actor, actor], datetime(2022, 1, 1)) def test_wrong_type_in_creators(): with pytest.raises(TypeError): - CreationInfo(["person"], datetime(2022, 1, 1)) + CreationInfo("version", "id", "name", "namespace", ["person"], datetime(2022, 1, 1)) @mock.patch('src.model.actor.Actor', autospec=True) def test_wrong_type_in_created(actor): with pytest.raises(TypeError): - CreationInfo([actor, actor], "2022-01-01") + CreationInfo("version", "id", "name", "namespace", [actor, actor], "2022-01-01") @mock.patch('src.model.actor.Actor', autospec=True) -def test_wrong_type_in_comment(actor): +def test_wrong_type_in_creator_comment(actor): with pytest.raises(TypeError): - CreationInfo([actor, actor], datetime(2022, 1, 1), comment=["string"]) + CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), creator_comment=["string"]) + + +@mock.patch('src.model.actor.Actor', autospec=True) +def test_wrong_type_in_data_license(actor): + with pytest.raises(TypeError): + CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), data_license=42) + + +@mock.patch('src.model.actor.Actor', autospec=True) +def test_wrong_type_in_external_document_refs(actor): + with pytest.raises(TypeError): + CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), external_document_refs=()) @mock.patch('src.model.actor.Actor', autospec=True) def test_wrong_type_in_license_list_version(actor): with pytest.raises(TypeError): - CreationInfo([actor, actor], datetime(2022, 1, 1), license_list_version="6.4") + CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), license_list_version="6.4") + + +@mock.patch('src.model.actor.Actor', autospec=True) +def test_wrong_type_in_document_comment(actor): + with pytest.raises(TypeError): + CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), document_comment=["1"]) diff --git a/tests/model/test_document.py b/tests/model/test_document.py index 56698858f..1a0a92087 100644 --- a/tests/model/test_document.py +++ b/tests/model/test_document.py @@ -6,26 +6,17 @@ @mock.patch('src.model.document.CreationInfo', autospec=True) -@mock.patch('src.model.external_document_ref.ExternalDocumentRef', autospec=True) @mock.patch('src.model.package.Package', autospec=True) @mock.patch('src.model.file.File', autospec=True) @mock.patch('src.model.snippet.Snippet', autospec=True) @mock.patch('src.model.annotation.Annotation', autospec=True) @mock.patch('src.model.relationship.Relationship', autospec=True) @mock.patch('src.model.extracted_licensing_info.ExtractedLicensingInfo', autospec=True) -def test_correct_initialization(creation_info, ext_ref, package, file, snippet, annotation, relationship, +def test_correct_initialization(creation_info, package, file, snippet, annotation, relationship, extracted_lic): - document = Document("version", "id", "name", "namespace", creation_info, "data_license", [ext_ref, ext_ref], - "comment", [package, package], [file, file], [snippet, snippet], [annotation, annotation], + document = Document(creation_info, [package, package], [file, file], [snippet, snippet], [annotation, annotation], [relationship, relationship], [extracted_lic, extracted_lic]) - assert document.spdx_version == "version" - assert document.spdx_id == "id" - assert document.name == "name" - assert document.document_namespace == "namespace" assert document.creation_info == creation_info - assert document.data_license == "data_license" - assert document.external_document_refs == [ext_ref, ext_ref] - assert document.comment == "comment" assert document.packages == [package, package] assert document.files == [file, file] assert document.snippets == [snippet, snippet] @@ -36,15 +27,8 @@ def test_correct_initialization(creation_info, ext_ref, package, file, snippet, @mock.patch('src.model.document.CreationInfo', autospec=True) def test_correct_initialization_with_default_values(creation_info): - document = Document("version", "id", "name", "namespace", creation_info, "data_license") - assert document.spdx_version == "version" - assert document.spdx_id == "id" - assert document.name == "name" - assert document.document_namespace == "namespace" + document = Document(creation_info) assert document.creation_info == creation_info - assert document.data_license == "data_license" - assert document.external_document_refs == [] - assert document.comment is None assert document.packages == [] assert document.files == [] assert document.snippets == [] @@ -53,84 +37,42 @@ def test_correct_initialization_with_default_values(creation_info): assert document.extracted_licensing_info == [] -@mock.patch('src.model.document.CreationInfo', autospec=True) -def test_wrong_type_in_spdx_version(creation_info): - with pytest.raises(TypeError): - Document(2.3, "id", "name", "namespace", creation_info, "data_license") - - -@mock.patch('src.model.document.CreationInfo', autospec=True) -def test_wrong_type_in_spdx_id(creation_info): - with pytest.raises(TypeError): - Document("version", 42, "name", "namespace", creation_info, "data_license") - - -@mock.patch('src.model.document.CreationInfo', autospec=True) -def test_wrong_type_in_name(creation_info): - with pytest.raises(TypeError): - Document("version", "id", ["name"], "namespace", creation_info, "data_license") - - -@mock.patch('src.model.document.CreationInfo', autospec=True) -def test_wrong_type_in_document_namespace(creation_info): - with pytest.raises(TypeError): - Document("version", "id", "name", {}, creation_info, "data_license") - - def test_wrong_type_in_creation_info(): with pytest.raises(TypeError): - Document("version", "id", "name", "namespace", "string", "data_license") - - -@mock.patch('src.model.document.CreationInfo', autospec=True) -def test_wrong_type_in_data_license(creation_info): - with pytest.raises(TypeError): - Document("version", "id", "name", "namespace", creation_info, ["data_license"]) - - -@mock.patch('src.model.document.CreationInfo', autospec=True) -def test_wrong_type_in_external_document_refs(creation_info): - with pytest.raises(TypeError): - Document("version", "id", "name", "namespace", creation_info, "data_license", external_document_refs=["string"]) - - -@mock.patch('src.model.document.CreationInfo', autospec=True) -def test_wrong_type_in_comment(creation_info): - with pytest.raises(TypeError): - Document("version", "id", "name", "namespace", creation_info, "data_license", comment=42) + Document("string") @mock.patch('src.model.document.CreationInfo', autospec=True) def test_wrong_type_in_packages(creation_info): with pytest.raises(TypeError): - Document("version", "id", "name", "namespace", creation_info, "data_license", packages=["string"]) + Document(creation_info, packages=["string"]) @mock.patch('src.model.document.CreationInfo', autospec=True) def test_wrong_type_in_files(creation_info): with pytest.raises(TypeError): - Document("version", "id", "name", "namespace", creation_info, "data_license", files={}) + Document(creation_info, files={}) @mock.patch('src.model.document.CreationInfo', autospec=True) def test_wrong_type_in_snippets(creation_info): with pytest.raises(TypeError): - Document("version", "id", "name", "namespace", creation_info, "data_license", snippets=()) + Document(creation_info, snippets=()) @mock.patch('src.model.document.CreationInfo', autospec=True) def test_wrong_type_in_annotations(creation_info): with pytest.raises(TypeError): - Document("version", "id", "name", "namespace", creation_info, "data_license", annotations=["string"]) + Document(creation_info, annotations=["string"]) @mock.patch('src.model.document.CreationInfo', autospec=True) def test_wrong_type_in_relationships(creation_info): with pytest.raises(TypeError): - Document("version", "id", "name", "namespace", creation_info, "data_license", relationships="string") + Document(creation_info, relationships="string") @mock.patch('src.model.document.CreationInfo', autospec=True) def test_wrong_type_in_extracted_licensing_info(creation_info): with pytest.raises(TypeError): - Document("version", "id", "name", "namespace", creation_info, "data_license", extracted_licensing_info=42) + Document(creation_info, extracted_licensing_info=42) diff --git a/tests/model/test_external_package_reference.py b/tests/model/test_external_package_reference.py index 307170b05..52a2c2519 100644 --- a/tests/model/test_external_package_reference.py +++ b/tests/model/test_external_package_reference.py @@ -1,12 +1,12 @@ import pytest -from src.model.package import ExternalPackageReference, ExternalPackageReferenceCategory +from src.model.package import ExternalPackageRef, ExternalPackageRefCategory def test_correct_initialization(): - external_package_reference = ExternalPackageReference(ExternalPackageReferenceCategory.OTHER, "type", "locator", + external_package_reference = ExternalPackageRef(ExternalPackageRefCategory.OTHER, "type", "locator", "comment") - assert external_package_reference.category == ExternalPackageReferenceCategory.OTHER + assert external_package_reference.category == ExternalPackageRefCategory.OTHER assert external_package_reference.reference_type == "type" assert external_package_reference.locator == "locator" assert external_package_reference.comment == "comment" @@ -14,19 +14,19 @@ def test_correct_initialization(): def test_wrong_type_in_category(): with pytest.raises(TypeError): - ExternalPackageReference([ExternalPackageReferenceCategory.OTHER], "type", "locator") + ExternalPackageRef([ExternalPackageRefCategory.OTHER], "type", "locator") def test_wrong_type_in_reference_type(): with pytest.raises(TypeError): - ExternalPackageReference(ExternalPackageReferenceCategory.OTHER, 42, "locator") + ExternalPackageRef(ExternalPackageRefCategory.OTHER, 42, "locator") def test_wrong_type_in_locator(): with pytest.raises(TypeError): - ExternalPackageReference(ExternalPackageReferenceCategory.OTHER, "type", 42) + ExternalPackageRef(ExternalPackageRefCategory.OTHER, "type", 42) def test_wrong_type_in_comment(): with pytest.raises(TypeError): - ExternalPackageReference(ExternalPackageReferenceCategory.OTHER, "type", "locator", []) + ExternalPackageRef(ExternalPackageRefCategory.OTHER, "type", "locator", []) diff --git a/tests/model/test_package.py b/tests/model/test_package.py index 2fc8ca494..85f1d7920 100644 --- a/tests/model/test_package.py +++ b/tests/model/test_package.py @@ -13,7 +13,7 @@ @mock.patch('src.model.actor.Actor', autospec=True) @mock.patch('src.model.package.PackageVerificationCode', autospec=True) @mock.patch('src.model.checksum.Checksum', autospec=True) -@mock.patch('src.model.package.ExternalPackageReference', autospec=True) +@mock.patch('src.model.package.ExternalPackageRef', autospec=True) def test_correct_initialization(actor, verif_code, checksum, ext_ref): package = Package("id", "name", SpdxNoAssertion(), "version", "file_name", SpdxNoAssertion(), actor, True, verif_code, [checksum], "homepage", "source_info", None, [LicenseExpression("expression")], From 2ffc8bf50042e5ee78868f943c6d9c213eb3870a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 6 Dec 2022 14:38:31 +0100 Subject: [PATCH 010/362] change annotator type to Actor and clean imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/model/actor.py | 3 --- src/model/annotation.py | 6 ++---- src/model/checksum.py | 3 --- src/model/document.py | 6 ++---- src/model/external_document_ref.py | 3 --- src/model/extracted_licensing_info.py | 4 +--- src/model/file.py | 9 ++++---- src/model/package.py | 9 ++++---- src/model/relationship.py | 3 --- src/model/snippet.py | 6 ++---- tests/model/test_annotation.py | 31 ++++++++++++++++----------- 11 files changed, 34 insertions(+), 49 deletions(-) diff --git a/src/model/actor.py b/src/model/actor.py index d5261aa5f..d6ae1ece5 100644 --- a/src/model/actor.py +++ b/src/model/actor.py @@ -8,12 +8,9 @@ # 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 dataclasses import dataclass from enum import Enum, auto from typing import Optional -from typeguard import typechecked - from src.model.dataclass_with_properties import dataclass_with_properties diff --git a/src/model/annotation.py b/src/model/annotation.py index 52fbbbdb5..3f4a4893f 100644 --- a/src/model/annotation.py +++ b/src/model/annotation.py @@ -8,12 +8,10 @@ # 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 dataclasses import dataclass from datetime import datetime from enum import Enum, auto -from typeguard import typechecked - +from src.model.actor import Actor from src.model.dataclass_with_properties import dataclass_with_properties @@ -26,6 +24,6 @@ class AnnotationType(Enum): class Annotation: spdx_id: str annotation_type: AnnotationType - annotator: str + annotator: Actor annotation_date: datetime annotation_comment: str diff --git a/src/model/checksum.py b/src/model/checksum.py index d3f58d86d..bbcd71bbd 100644 --- a/src/model/checksum.py +++ b/src/model/checksum.py @@ -8,11 +8,8 @@ # 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 dataclasses import dataclass from enum import auto, Enum -from typeguard import typechecked - from src.model.dataclass_with_properties import dataclass_with_properties diff --git a/src/model/document.py b/src/model/document.py index 81f6d526f..7a6b7b274 100644 --- a/src/model/document.py +++ b/src/model/document.py @@ -8,21 +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 dataclasses import dataclass, field +from dataclasses import field from datetime import datetime from typing import List, Optional -from typeguard import typechecked - from src.model.actor import Actor from src.model.annotation import Annotation +from src.model.dataclass_with_properties import dataclass_with_properties from src.model.external_document_ref import ExternalDocumentRef from src.model.extracted_licensing_info import ExtractedLicensingInfo from src.model.file import File from src.model.package import Package from src.model.relationship import Relationship from src.model.snippet import Snippet -from src.model.dataclass_with_properties import dataclass_with_properties from src.model.version import Version diff --git a/src/model/external_document_ref.py b/src/model/external_document_ref.py index fbb517f3c..f268524d0 100644 --- a/src/model/external_document_ref.py +++ b/src/model/external_document_ref.py @@ -8,9 +8,6 @@ # 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 dataclasses import dataclass - -from typeguard import typechecked from src.model.checksum import Checksum from src.model.dataclass_with_properties import dataclass_with_properties diff --git a/src/model/extracted_licensing_info.py b/src/model/extracted_licensing_info.py index 46eb53f49..8f749bcc8 100644 --- a/src/model/extracted_licensing_info.py +++ b/src/model/extracted_licensing_info.py @@ -8,11 +8,9 @@ # 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 dataclasses import dataclass, field +from dataclasses import field from typing import Optional, List -from typeguard import typechecked - from src.model.dataclass_with_properties import dataclass_with_properties diff --git a/src/model/file.py b/src/model/file.py index 8003f1f6b..8f693e432 100644 --- a/src/model/file.py +++ b/src/model/file.py @@ -8,18 +8,16 @@ # 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 dataclasses import dataclass, field +from dataclasses import field from enum import Enum, auto from typing import Optional, List, Union -from typeguard import typechecked - from src.model.checksum import Checksum +from src.model.dataclass_with_properties import dataclass_with_properties from src.model.license import License from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone -from src.model.dataclass_with_properties import dataclass_with_properties class FileType(Enum): @@ -43,7 +41,8 @@ class File: checksums: List[Checksum] file_type: List[FileType] = field(default_factory=list) concluded_license: Optional[Union[License, SpdxNoAssertion, SpdxNone]] = None - license_info_in_file: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = field(default_factory=list) + license_info_in_file: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = field( + default_factory=list) license_comment: Optional[str] = None copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None comment: Optional[str] = None diff --git a/src/model/package.py b/src/model/package.py index 32e7953c2..1928d9ef4 100644 --- a/src/model/package.py +++ b/src/model/package.py @@ -8,19 +8,17 @@ # 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 dataclasses import dataclass, field +from dataclasses import field from datetime import datetime from enum import Enum, auto from typing import Optional, Union, List -from typeguard import typechecked - from src.model.actor import Actor from src.model.checksum import Checksum +from src.model.dataclass_with_properties import dataclass_with_properties from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone -from src.model.dataclass_with_properties import dataclass_with_properties class PackagePurpose(Enum): @@ -77,7 +75,8 @@ class Package: homepage: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None source_info: Optional[str] = None license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None - license_info_from_files: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = field(default_factory=list) + license_info_from_files: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = field( + default_factory=list) license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None license_comment: Optional[str] = None copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None diff --git a/src/model/relationship.py b/src/model/relationship.py index da80c1216..0a140bcbb 100644 --- a/src/model/relationship.py +++ b/src/model/relationship.py @@ -8,12 +8,9 @@ # 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 dataclasses import dataclass from enum import auto, Enum from typing import Optional -from typeguard import typechecked - from src.model.dataclass_with_properties import dataclass_with_properties diff --git a/src/model/snippet.py b/src/model/snippet.py index c3ad7da9d..49c3ffe1f 100644 --- a/src/model/snippet.py +++ b/src/model/snippet.py @@ -8,15 +8,13 @@ # 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 dataclasses import dataclass, field +from dataclasses import field from typing import Tuple, Optional, List, Union -from typeguard import typechecked - +from src.model.dataclass_with_properties import dataclass_with_properties from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone -from src.model.dataclass_with_properties import dataclass_with_properties @dataclass_with_properties diff --git a/tests/model/test_annotation.py b/tests/model/test_annotation.py index 819bd621b..5455569bc 100644 --- a/tests/model/test_annotation.py +++ b/tests/model/test_annotation.py @@ -1,39 +1,46 @@ from datetime import datetime +from unittest import mock import pytest from src.model.annotation import Annotation, AnnotationType -def test_correct_initialization(): - annotation = Annotation("id", AnnotationType.OTHER, "annotator", datetime(2022, 1, 1), "comment") +@mock.patch('src.model.actor.Actor', autospec=True) +def test_correct_initialization(actor): + annotation = Annotation("id", AnnotationType.OTHER, actor, datetime(2022, 1, 1), "comment") assert annotation.spdx_id == "id" assert annotation.annotation_type == AnnotationType.OTHER - assert annotation.annotator == "annotator" + assert annotation.annotator == actor assert annotation.annotation_date == datetime(2022, 1, 1) assert annotation.annotation_comment == "comment" -def test_wrong_type_in_spdx_id(): +@mock.patch('src.model.actor.Actor', autospec=True) +def test_wrong_type_in_spdx_id(actor): with pytest.raises(TypeError): - Annotation(42, AnnotationType.OTHER, "annotator", datetime(2022, 1, 1), "comment") + Annotation(42, AnnotationType.OTHER, actor, datetime(2022, 1, 1), "comment") -def test_wrong_type_in_annotation_type(): +@mock.patch('src.model.actor.Actor', autospec=True) +def test_wrong_type_in_annotation_type(actor): with pytest.raises(TypeError): - Annotation("id", 42, "annotator", datetime(2022, 1, 1), "comment") + Annotation("id", 42, actor, datetime(2022, 1, 1), "comment") -def test_wrong_type_in_annotator(): +@mock.patch('src.model.actor.Actor', autospec=True) +def test_wrong_type_in_annotator(actor): with pytest.raises(TypeError): Annotation("id", AnnotationType.OTHER, 42, datetime(2022, 1, 1), "comment") -def test_wrong_type_in_annotation_date(): +@mock.patch('src.model.actor.Actor', autospec=True) +def test_wrong_type_in_annotation_date(actor): with pytest.raises(TypeError): - Annotation("id", AnnotationType.OTHER, "annotator", 42, "comment") + Annotation("id", AnnotationType.OTHER, actor, 42, "comment") -def test_wrong_type_in_annotation_comment(): +@mock.patch('src.model.actor.Actor', autospec=True) +def test_wrong_type_in_annotation_comment(actor): with pytest.raises(TypeError): - Annotation("id", AnnotationType.OTHER, "annotator", datetime(2022, 1, 1), 42) + Annotation("id", AnnotationType.OTHER, actor, datetime(2022, 1, 1), 42) From 7fdf9a85046a79b427e76f3812ba2a42a6917e1f Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Tue, 6 Dec 2022 18:19:34 +0100 Subject: [PATCH 011/362] [issue-348] Add new constructor type check utility so all TypeErrors are returned in one go Signed-off-by: Nicolaus Weidner --- src/model/actor.py | 4 ++++ src/model/constructor_type_errors.py | 12 ++++++++++++ src/model/type_checks.py | 27 +++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 src/model/constructor_type_errors.py create mode 100644 src/model/type_checks.py diff --git a/src/model/actor.py b/src/model/actor.py index d6ae1ece5..0ac09fd56 100644 --- a/src/model/actor.py +++ b/src/model/actor.py @@ -12,6 +12,7 @@ from typing import Optional from src.model.dataclass_with_properties import dataclass_with_properties +from src.model.type_checks import check_types_and_set_values class ActorType(Enum): @@ -25,3 +26,6 @@ class Actor: actor_type: ActorType name: str email: Optional[str] = None + + def __init__(self, actor_type: ActorType, name: str, email: Optional[str] = None): + check_types_and_set_values(self, locals()) diff --git a/src/model/constructor_type_errors.py b/src/model/constructor_type_errors.py new file mode 100644 index 000000000..cd02944ba --- /dev/null +++ b/src/model/constructor_type_errors.py @@ -0,0 +1,12 @@ +from typing import List + + +class ConstructorTypeErrors(TypeError): + """ + Helper class that holds a list of error messages. Intended to capture all TypeErrors encountered during a + constructor call, instead of raising only the first one. + """ + messages: List[str] + + def __init__(self, messages: List[str]): + self.messages = messages diff --git a/src/model/type_checks.py b/src/model/type_checks.py new file mode 100644 index 000000000..5b79e1dac --- /dev/null +++ b/src/model/type_checks.py @@ -0,0 +1,27 @@ +from typing import Any, Dict + +from src.model.constructor_type_errors import ConstructorTypeErrors + + +def check_types_and_set_values(instance_under_construction: Any, local_variables: Dict) -> None: + """ + Helper method to accumulate all type errors encountered during a constructor call and return them in a + ConstructorTypeErrors instance. + Background: Our setters are enhanced with runtime typechecks using typeguard. However, this means that by + default, a TypeError is raised on the first type violation that is encountered. We consider it more helpful to + return all type violations in one go. + As an aside, defining constructors "manually" using this utility method helps avoid a nasty PyCharm bug: + https://youtrack.jetbrains.com/issue/PY-34569 + """ + errors = [] + for key, value_type in instance_under_construction.__annotations__.items(): + value = local_variables.get(key) + try: + setattr(instance_under_construction, key, value) + except TypeError as err: + error_message: str = err.args[0] + # As setters are created dynamically, their argument name is always "value". We replace it by the + # actual name so the error message is more helpful. + errors.append(error_message.replace("value", key, 1)) + if errors: + raise ConstructorTypeErrors(errors) From f086c34f07657f9ca6555d3e910e6fb42c8e6a80 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Tue, 6 Dec 2022 22:44:13 +0100 Subject: [PATCH 012/362] [issue-348] Add constructors with new style of type checks to remaining data classes Signed-off-by: Nicolaus Weidner --- src/model/annotation.py | 5 +++++ src/model/checksum.py | 4 ++++ src/model/document.py | 20 +++++++++++++++++ src/model/external_document_ref.py | 4 ++++ src/model/extracted_licensing_info.py | 7 ++++++ src/model/file.py | 17 +++++++++++++-- src/model/license_expression.py | 4 ++++ src/model/package.py | 31 +++++++++++++++++++++++++++ src/model/relationship.py | 5 +++++ src/model/snippet.py | 10 +++++++++ tests/model/test_creation_info.py | 2 +- tests/model/test_document.py | 4 ++-- 12 files changed, 108 insertions(+), 5 deletions(-) diff --git a/src/model/annotation.py b/src/model/annotation.py index 3f4a4893f..0cbcfefca 100644 --- a/src/model/annotation.py +++ b/src/model/annotation.py @@ -13,6 +13,7 @@ from src.model.actor import Actor from src.model.dataclass_with_properties import dataclass_with_properties +from src.model.type_checks import check_types_and_set_values class AnnotationType(Enum): @@ -27,3 +28,7 @@ class Annotation: annotator: Actor annotation_date: datetime annotation_comment: str + + def __init__(self, spdx_id: str, annotation_type: AnnotationType, annotator: str, annotation_date: datetime, + annotation_comment: str): + check_types_and_set_values(self, locals()) diff --git a/src/model/checksum.py b/src/model/checksum.py index bbcd71bbd..dcd597706 100644 --- a/src/model/checksum.py +++ b/src/model/checksum.py @@ -11,6 +11,7 @@ from enum import auto, Enum from src.model.dataclass_with_properties import dataclass_with_properties +from src.model.type_checks import check_types_and_set_values class ChecksumAlgorithm(Enum): @@ -37,3 +38,6 @@ class ChecksumAlgorithm(Enum): class Checksum: algorithm: ChecksumAlgorithm value: str + + def __init__(self, algorithm: ChecksumAlgorithm, value: str): + check_types_and_set_values(self, locals()) diff --git a/src/model/document.py b/src/model/document.py index 7a6b7b274..b7f0d05ad 100644 --- a/src/model/document.py +++ b/src/model/document.py @@ -21,6 +21,7 @@ from src.model.package import Package from src.model.relationship import Relationship from src.model.snippet import Snippet +from src.model.type_checks import check_types_and_set_values from src.model.version import Version @@ -38,6 +39,13 @@ class CreationInfo: license_list_version: Optional[Version] = None document_comment: Optional[str] = None + def __init__(self, spdx_version: str, spdx_id: str, name: str, document_namespace: str, creators: List[Actor], + created: datetime, creator_comment: Optional[str] = None, data_license: str = "CC0-1.0", + external_document_refs: List[ExternalDocumentRef] = None, + license_list_version: Optional[Version] = None, document_comment: Optional[str] = None): + external_document_refs = external_document_refs or [] + check_types_and_set_values(self, locals()) + @dataclass_with_properties class Document: @@ -49,3 +57,15 @@ class Document: annotations: List[Annotation] = field(default_factory=list) relationships: List[Relationship] = field(default_factory=list) extracted_licensing_info: List[ExtractedLicensingInfo] = field(default_factory=list) + + def __init__(self, creation_info: CreationInfo, packages: List[Package] = None, files: List[File] = None, + snippets: List[Snippet] = None, annotations: List[Annotation] = None, + relationships: List[Relationship] = None, + extracted_licensing_info: List[ExtractedLicensingInfo] = None): + packages = packages or [] + files = files or [] + snippets = snippets or [] + annotations = annotations or [] + relationships = relationships or [] + extracted_licensing_info = extracted_licensing_info or [] + check_types_and_set_values(self, locals()) diff --git a/src/model/external_document_ref.py b/src/model/external_document_ref.py index f268524d0..a38c2961a 100644 --- a/src/model/external_document_ref.py +++ b/src/model/external_document_ref.py @@ -11,6 +11,7 @@ from src.model.checksum import Checksum from src.model.dataclass_with_properties import dataclass_with_properties +from src.model.type_checks import check_types_and_set_values @dataclass_with_properties @@ -18,3 +19,6 @@ class ExternalDocumentRef: document_uri: str spdx_id: str checksum: Checksum + + def __init__(self, document_uri: str, spdx_id: str, checksum: Checksum): + check_types_and_set_values(self, locals()) diff --git a/src/model/extracted_licensing_info.py b/src/model/extracted_licensing_info.py index 8f749bcc8..78213efc2 100644 --- a/src/model/extracted_licensing_info.py +++ b/src/model/extracted_licensing_info.py @@ -12,6 +12,7 @@ from typing import Optional, List from src.model.dataclass_with_properties import dataclass_with_properties +from src.model.type_checks import check_types_and_set_values @dataclass_with_properties @@ -21,3 +22,9 @@ class ExtractedLicensingInfo: license_name: Optional[str] = None comment: Optional[str] = None cross_references: List[str] = field(default_factory=list) + + def __init__(self, license_id: Optional[str] = None, extracted_text: Optional[str] = None, + license_name: Optional[str] = None, comment: Optional[str] = None, + cross_references: List[str] = None): + cross_references = cross_references or [] + check_types_and_set_values(self, locals()) diff --git a/src/model/file.py b/src/model/file.py index 8f693e432..ee5cf9fb2 100644 --- a/src/model/file.py +++ b/src/model/file.py @@ -14,10 +14,10 @@ from src.model.checksum import Checksum from src.model.dataclass_with_properties import dataclass_with_properties -from src.model.license import License from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone +from src.model.type_checks import check_types_and_set_values class FileType(Enum): @@ -40,7 +40,7 @@ class File: spdx_id: str checksums: List[Checksum] file_type: List[FileType] = field(default_factory=list) - concluded_license: Optional[Union[License, SpdxNoAssertion, SpdxNone]] = None + concluded_license: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None license_info_in_file: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = field( default_factory=list) license_comment: Optional[str] = None @@ -54,3 +54,16 @@ class File: # - file dependencies: replace by a DEPENDENCY_OF relationship (or one of the more precise versions) # - artifact of (3 properties): replace by an external package reference and a GENERATED_FROM relationship # between the file and this package + + def __init__(self, name: str, spdx_id: str, checksums: List[Checksum], file_type: List[FileType] = None, + concluded_license: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, + license_info_in_file: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, + license_comment: Optional[str] = None, + copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None, + comment: str = None, notice: Optional[str] = None, + contributors: List[str] = None, attribution_texts: List[str] = None): + file_type = file_type or [] + license_info_in_file = license_info_in_file or [] + contributors = contributors or [] + attribution_texts = attribution_texts or [] + check_types_and_set_values(self, locals()) diff --git a/src/model/license_expression.py b/src/model/license_expression.py index ec258ab4e..7b24d3f85 100644 --- a/src/model/license_expression.py +++ b/src/model/license_expression.py @@ -10,6 +10,7 @@ # limitations under the License. from src.model.dataclass_with_properties import dataclass_with_properties +from src.model.type_checks import check_types_and_set_values @dataclass_with_properties @@ -17,3 +18,6 @@ class LicenseExpression: """So far, this just holds a string with the license expression. The ticket for adding license expression support is https://github.com/spdx/tools-python/issues/10.""" expression_string: str + + def __init__(self, expression_string: str): + check_types_and_set_values(self, locals()) diff --git a/src/model/package.py b/src/model/package.py index 1928d9ef4..1fec50a55 100644 --- a/src/model/package.py +++ b/src/model/package.py @@ -19,6 +19,7 @@ from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone +from src.model.type_checks import check_types_and_set_values class PackagePurpose(Enum): @@ -41,6 +42,10 @@ class PackageVerificationCode: value: str excluded_files: List[str] = field(default_factory=list) + def __init__(self, value: str, excluded_files: List[str] = None): + excluded_files = excluded_files or [] + check_types_and_set_values(self, locals()) + class ExternalPackageRefCategory(Enum): SECURITY = auto() @@ -59,6 +64,10 @@ class ExternalPackageRef: locator: str comment: Optional[str] = None + def __init__(self, category: ExternalPackageRefCategory, reference_type: str, locator: str, + comment: Optional[str] = None): + check_types_and_set_values(self, locals()) + @dataclass_with_properties class Package: @@ -89,3 +98,25 @@ class Package: release_date: Optional[datetime] = None built_date: Optional[datetime] = None valid_until_date: Optional[datetime] = None + + def __init__(self, spdx_id: str, name: str, download_location: Union[str, SpdxNoAssertion, SpdxNone], + version: Optional[str] = None, file_name: Optional[str] = None, + supplier: Optional[Union[Actor, SpdxNoAssertion]] = None, + originator: Optional[Union[Actor, SpdxNoAssertion]] = None, + files_analyzed: bool = True, verification_code: Optional[PackageVerificationCode] = None, + checksums: List[Checksum] = None, homepage: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None, + source_info: Optional[str] = None, + license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, + license_info_from_files: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = None, + license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, + license_comment: Optional[str] = None, + copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None, + summary: Optional[str] = None, description: Optional[str] = None, comment: Optional[str] = None, + external_references: List[ExternalPackageRef] = None, attribution_texts: List[str] = None, + primary_package_purpose: Optional[PackagePurpose] = None, release_date: Optional[datetime] = None, + built_date: Optional[datetime] = None, valid_until_date: Optional[datetime] = None): + checksums = checksums or [] + license_info_from_files = license_info_from_files or [] + external_references = external_references or [] + attribution_texts = attribution_texts or [] + check_types_and_set_values(self, locals()) diff --git a/src/model/relationship.py b/src/model/relationship.py index 0a140bcbb..f13b9a416 100644 --- a/src/model/relationship.py +++ b/src/model/relationship.py @@ -12,6 +12,7 @@ from typing import Optional from src.model.dataclass_with_properties import dataclass_with_properties +from src.model.type_checks import check_types_and_set_values class RelationshipType(Enum): @@ -68,3 +69,7 @@ class Relationship: relationship_type: RelationshipType related_spdx_element_id: str comment: Optional[str] = None + + def __init__(self, spdx_element_id: str, relationship_type: RelationshipType, related_spdx_element_id: str, + comment: Optional[str] = None): + check_types_and_set_values(self, locals()) diff --git a/src/model/snippet.py b/src/model/snippet.py index 49c3ffe1f..dd9966c59 100644 --- a/src/model/snippet.py +++ b/src/model/snippet.py @@ -15,6 +15,7 @@ from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone +from src.model.type_checks import check_types_and_set_values @dataclass_with_properties @@ -30,3 +31,12 @@ class Snippet: comment: Optional[str] = None name: Optional[str] = None attribution_texts: List[str] = field(default_factory=list) + + def __init__(self, spdx_id: str, file_spdx_id: str, byte_range: Tuple[int, int], + line_range: Optional[Tuple[int, int]] = None, + concluded_license: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, + license_info_in_snippet: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = None, + license_comment: Optional[str] = None, copyright_text: Optional[str] = None, + comment: Optional[str] = None, name: Optional[str] = None, attribution_texts: List[str] = None): + attribution_texts = attribution_texts or [] + check_types_and_set_values(self, locals()) diff --git a/tests/model/test_creation_info.py b/tests/model/test_creation_info.py index b5650c865..a95d40665 100644 --- a/tests/model/test_creation_info.py +++ b/tests/model/test_creation_info.py @@ -75,7 +75,7 @@ def test_wrong_type_in_data_license(actor): @mock.patch('src.model.actor.Actor', autospec=True) def test_wrong_type_in_external_document_refs(actor): with pytest.raises(TypeError): - CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), external_document_refs=()) + CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), external_document_refs=("ref",)) @mock.patch('src.model.actor.Actor', autospec=True) diff --git a/tests/model/test_document.py b/tests/model/test_document.py index 1a0a92087..dc123bc16 100644 --- a/tests/model/test_document.py +++ b/tests/model/test_document.py @@ -51,13 +51,13 @@ def test_wrong_type_in_packages(creation_info): @mock.patch('src.model.document.CreationInfo', autospec=True) def test_wrong_type_in_files(creation_info): with pytest.raises(TypeError): - Document(creation_info, files={}) + Document(creation_info, files={"fileName": "fileValue"}) @mock.patch('src.model.document.CreationInfo', autospec=True) def test_wrong_type_in_snippets(creation_info): with pytest.raises(TypeError): - Document(creation_info, snippets=()) + Document(creation_info, snippets=("snippet",)) @mock.patch('src.model.document.CreationInfo', autospec=True) From c303fadce0bd6fc167fc33f4e160da65008f1e50 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Tue, 6 Dec 2022 22:44:45 +0100 Subject: [PATCH 013/362] [refactor] Reformat test_creation_info.py Signed-off-by: Nicolaus Weidner --- tests/model/test_creation_info.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/tests/model/test_creation_info.py b/tests/model/test_creation_info.py index a95d40665..e60fdafe1 100644 --- a/tests/model/test_creation_info.py +++ b/tests/model/test_creation_info.py @@ -29,20 +29,20 @@ def test_correct_initialization(actor, ext_ref): def test_wrong_type_in_spdx_version(actor): with pytest.raises(TypeError): CreationInfo(42, "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1)) - - + + @mock.patch('src.model.actor.Actor', autospec=True) def test_wrong_type_in_spdx_id(actor): with pytest.raises(TypeError): CreationInfo("version", 42, "name", "namespace", [actor, actor], datetime(2022, 1, 1)) - - + + @mock.patch('src.model.actor.Actor', autospec=True) def test_wrong_type_in_name(actor): with pytest.raises(TypeError): CreationInfo("version", "id", 42, "namespace", [actor, actor], datetime(2022, 1, 1)) - - + + @mock.patch('src.model.actor.Actor', autospec=True) def test_wrong_type_in_document_namespace(actor): with pytest.raises(TypeError): @@ -63,27 +63,30 @@ def test_wrong_type_in_created(actor): @mock.patch('src.model.actor.Actor', autospec=True) def test_wrong_type_in_creator_comment(actor): with pytest.raises(TypeError): - CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), creator_comment=["string"]) + CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), + creator_comment=["string"]) @mock.patch('src.model.actor.Actor', autospec=True) def test_wrong_type_in_data_license(actor): with pytest.raises(TypeError): CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), data_license=42) - - + + @mock.patch('src.model.actor.Actor', autospec=True) def test_wrong_type_in_external_document_refs(actor): with pytest.raises(TypeError): - CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), external_document_refs=("ref",)) + CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), + external_document_refs=("ref",)) @mock.patch('src.model.actor.Actor', autospec=True) def test_wrong_type_in_license_list_version(actor): with pytest.raises(TypeError): - CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), license_list_version="6.4") - - + CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), + license_list_version="6.4") + + @mock.patch('src.model.actor.Actor', autospec=True) def test_wrong_type_in_document_comment(actor): with pytest.raises(TypeError): From cfeff0b1ce3c1ebea364cc624fcef27ee53c10bd Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Tue, 6 Dec 2022 22:53:52 +0100 Subject: [PATCH 014/362] [refactor] Move typing-related modules to typing package Signed-off-by: Nicolaus Weidner --- src/model/actor.py | 4 ++-- src/model/annotation.py | 4 ++-- src/model/checksum.py | 4 ++-- src/model/document.py | 4 ++-- src/model/external_document_ref.py | 4 ++-- src/model/extracted_licensing_info.py | 4 ++-- src/model/file.py | 4 ++-- src/model/license_expression.py | 4 ++-- src/model/package.py | 4 ++-- src/model/relationship.py | 4 ++-- src/model/snippet.py | 4 ++-- src/model/typing/__init__.py | 0 src/model/{ => typing}/constructor_type_errors.py | 0 src/model/{ => typing}/dataclass_with_properties.py | 0 src/model/{ => typing}/type_checks.py | 2 +- 15 files changed, 23 insertions(+), 23 deletions(-) create mode 100644 src/model/typing/__init__.py rename src/model/{ => typing}/constructor_type_errors.py (100%) rename src/model/{ => typing}/dataclass_with_properties.py (100%) rename src/model/{ => typing}/type_checks.py (94%) diff --git a/src/model/actor.py b/src/model/actor.py index 0ac09fd56..fff81c811 100644 --- a/src/model/actor.py +++ b/src/model/actor.py @@ -11,8 +11,8 @@ from enum import Enum, auto from typing import Optional -from src.model.dataclass_with_properties import dataclass_with_properties -from src.model.type_checks import check_types_and_set_values +from src.model.typing.dataclass_with_properties import dataclass_with_properties +from src.model.typing.type_checks import check_types_and_set_values class ActorType(Enum): diff --git a/src/model/annotation.py b/src/model/annotation.py index 0cbcfefca..dcbdd859a 100644 --- a/src/model/annotation.py +++ b/src/model/annotation.py @@ -12,8 +12,8 @@ from enum import Enum, auto from src.model.actor import Actor -from src.model.dataclass_with_properties import dataclass_with_properties -from src.model.type_checks import check_types_and_set_values +from src.model.typing.dataclass_with_properties import dataclass_with_properties +from src.model.typing.type_checks import check_types_and_set_values class AnnotationType(Enum): diff --git a/src/model/checksum.py b/src/model/checksum.py index dcd597706..3555fa45b 100644 --- a/src/model/checksum.py +++ b/src/model/checksum.py @@ -10,8 +10,8 @@ # limitations under the License. from enum import auto, Enum -from src.model.dataclass_with_properties import dataclass_with_properties -from src.model.type_checks import check_types_and_set_values +from src.model.typing.dataclass_with_properties import dataclass_with_properties +from src.model.typing.type_checks import check_types_and_set_values class ChecksumAlgorithm(Enum): diff --git a/src/model/document.py b/src/model/document.py index b7f0d05ad..f389d10fc 100644 --- a/src/model/document.py +++ b/src/model/document.py @@ -14,14 +14,14 @@ from src.model.actor import Actor from src.model.annotation import Annotation -from src.model.dataclass_with_properties import dataclass_with_properties +from src.model.typing.dataclass_with_properties import dataclass_with_properties from src.model.external_document_ref import ExternalDocumentRef from src.model.extracted_licensing_info import ExtractedLicensingInfo from src.model.file import File from src.model.package import Package from src.model.relationship import Relationship from src.model.snippet import Snippet -from src.model.type_checks import check_types_and_set_values +from src.model.typing.type_checks import check_types_and_set_values from src.model.version import Version diff --git a/src/model/external_document_ref.py b/src/model/external_document_ref.py index a38c2961a..76a8c1e7a 100644 --- a/src/model/external_document_ref.py +++ b/src/model/external_document_ref.py @@ -10,8 +10,8 @@ # limitations under the License. from src.model.checksum import Checksum -from src.model.dataclass_with_properties import dataclass_with_properties -from src.model.type_checks import check_types_and_set_values +from src.model.typing.dataclass_with_properties import dataclass_with_properties +from src.model.typing.type_checks import check_types_and_set_values @dataclass_with_properties diff --git a/src/model/extracted_licensing_info.py b/src/model/extracted_licensing_info.py index 78213efc2..831c06a22 100644 --- a/src/model/extracted_licensing_info.py +++ b/src/model/extracted_licensing_info.py @@ -11,8 +11,8 @@ from dataclasses import field from typing import Optional, List -from src.model.dataclass_with_properties import dataclass_with_properties -from src.model.type_checks import check_types_and_set_values +from src.model.typing.dataclass_with_properties import dataclass_with_properties +from src.model.typing.type_checks import check_types_and_set_values @dataclass_with_properties diff --git a/src/model/file.py b/src/model/file.py index ee5cf9fb2..d877b6a71 100644 --- a/src/model/file.py +++ b/src/model/file.py @@ -13,11 +13,11 @@ from typing import Optional, List, Union from src.model.checksum import Checksum -from src.model.dataclass_with_properties import dataclass_with_properties +from src.model.typing.dataclass_with_properties import dataclass_with_properties from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone -from src.model.type_checks import check_types_and_set_values +from src.model.typing.type_checks import check_types_and_set_values class FileType(Enum): diff --git a/src/model/license_expression.py b/src/model/license_expression.py index 7b24d3f85..1f73ffc33 100644 --- a/src/model/license_expression.py +++ b/src/model/license_expression.py @@ -9,8 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from src.model.dataclass_with_properties import dataclass_with_properties -from src.model.type_checks import check_types_and_set_values +from src.model.typing.dataclass_with_properties import dataclass_with_properties +from src.model.typing.type_checks import check_types_and_set_values @dataclass_with_properties diff --git a/src/model/package.py b/src/model/package.py index 1fec50a55..83c3c501e 100644 --- a/src/model/package.py +++ b/src/model/package.py @@ -15,11 +15,11 @@ from src.model.actor import Actor from src.model.checksum import Checksum -from src.model.dataclass_with_properties import dataclass_with_properties +from src.model.typing.dataclass_with_properties import dataclass_with_properties from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone -from src.model.type_checks import check_types_and_set_values +from src.model.typing.type_checks import check_types_and_set_values class PackagePurpose(Enum): diff --git a/src/model/relationship.py b/src/model/relationship.py index f13b9a416..1b34fe051 100644 --- a/src/model/relationship.py +++ b/src/model/relationship.py @@ -11,8 +11,8 @@ from enum import auto, Enum from typing import Optional -from src.model.dataclass_with_properties import dataclass_with_properties -from src.model.type_checks import check_types_and_set_values +from src.model.typing.dataclass_with_properties import dataclass_with_properties +from src.model.typing.type_checks import check_types_and_set_values class RelationshipType(Enum): diff --git a/src/model/snippet.py b/src/model/snippet.py index dd9966c59..09dfbdc4d 100644 --- a/src/model/snippet.py +++ b/src/model/snippet.py @@ -11,11 +11,11 @@ from dataclasses import field from typing import Tuple, Optional, List, Union -from src.model.dataclass_with_properties import dataclass_with_properties +from src.model.typing.dataclass_with_properties import dataclass_with_properties from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone -from src.model.type_checks import check_types_and_set_values +from src.model.typing.type_checks import check_types_and_set_values @dataclass_with_properties diff --git a/src/model/typing/__init__.py b/src/model/typing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/model/constructor_type_errors.py b/src/model/typing/constructor_type_errors.py similarity index 100% rename from src/model/constructor_type_errors.py rename to src/model/typing/constructor_type_errors.py diff --git a/src/model/dataclass_with_properties.py b/src/model/typing/dataclass_with_properties.py similarity index 100% rename from src/model/dataclass_with_properties.py rename to src/model/typing/dataclass_with_properties.py diff --git a/src/model/type_checks.py b/src/model/typing/type_checks.py similarity index 94% rename from src/model/type_checks.py rename to src/model/typing/type_checks.py index 5b79e1dac..d4bb2ecee 100644 --- a/src/model/type_checks.py +++ b/src/model/typing/type_checks.py @@ -1,6 +1,6 @@ from typing import Any, Dict -from src.model.constructor_type_errors import ConstructorTypeErrors +from src.model.typing.constructor_type_errors import ConstructorTypeErrors def check_types_and_set_values(instance_under_construction: Any, local_variables: Dict) -> None: From 69160b8327416e00c4bcaaa9cc6c77052d357b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 7 Dec 2022 10:18:50 +0100 Subject: [PATCH 015/362] [issue-348] refine error messages in getters and setters directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/model/typing/dataclass_with_properties.py | 23 +++++++++++++++++-- src/model/typing/type_checks.py | 4 +--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/model/typing/dataclass_with_properties.py b/src/model/typing/dataclass_with_properties.py index 6d1c81a21..4574434ad 100644 --- a/src/model/typing/dataclass_with_properties.py +++ b/src/model/typing/dataclass_with_properties.py @@ -23,7 +23,16 @@ def make_setter(field_name, field_type): def set_field(self, value: field_type): setattr(self, f"_{field_name}", value) - return set_field + def set_field_with_better_error_message(self, value: field_type): + try: + set_field(self, value) + except TypeError as err: + error_message: str = err.args[0] + # As setters are created dynamically, their argument name is always "value". We replace it by the + # actual name so the error message is more helpful. + raise TypeError(error_message.replace("value", field_name, 1) + f": {value}") + + return set_field_with_better_error_message def make_getter(field_name, field_type): @@ -33,4 +42,14 @@ def make_getter(field_name, field_type): def get_field(self) -> field_type: return getattr(self, f"_{field_name}") - return get_field + def get_field_with_better_error_message(self) -> field_type: + try: + return get_field(self) + except TypeError as err: + error_message: str = err.args[0] + # As getters are created dynamically, their argument name is always "the return value". We replace it by the + # actual name so the error message is more helpful. + raise TypeError( + error_message.replace("the return value", field_name, 1) + f': {getattr(self, f"_{field_name}")}') + + return get_field_with_better_error_message diff --git a/src/model/typing/type_checks.py b/src/model/typing/type_checks.py index d4bb2ecee..ab3b39fe4 100644 --- a/src/model/typing/type_checks.py +++ b/src/model/typing/type_checks.py @@ -20,8 +20,6 @@ def check_types_and_set_values(instance_under_construction: Any, local_variables setattr(instance_under_construction, key, value) except TypeError as err: error_message: str = err.args[0] - # As setters are created dynamically, their argument name is always "value". We replace it by the - # actual name so the error message is more helpful. - errors.append(error_message.replace("value", key, 1)) + errors.append(error_message) if errors: raise ConstructorTypeErrors(errors) From 65c06a026608bbf9ec139a56a844bb3347d9d860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 7 Dec 2022 14:48:28 +0100 Subject: [PATCH 016/362] [fix] change annotator type to actor in constructor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/model/annotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/annotation.py b/src/model/annotation.py index dcbdd859a..02e92aae5 100644 --- a/src/model/annotation.py +++ b/src/model/annotation.py @@ -29,6 +29,6 @@ class Annotation: annotation_date: datetime annotation_comment: str - def __init__(self, spdx_id: str, annotation_type: AnnotationType, annotator: str, annotation_date: datetime, + def __init__(self, spdx_id: str, annotation_type: AnnotationType, annotator: Actor, annotation_date: datetime, annotation_comment: str): check_types_and_set_values(self, locals()) From 7ce38ff8dd6b609f646450dc9f1a2a11496fe324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 7 Dec 2022 17:58:49 +0100 Subject: [PATCH 017/362] [fix] change constructor type of file.license_info_in_file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/model/file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/file.py b/src/model/file.py index d877b6a71..fc4627967 100644 --- a/src/model/file.py +++ b/src/model/file.py @@ -57,7 +57,7 @@ class File: def __init__(self, name: str, spdx_id: str, checksums: List[Checksum], file_type: List[FileType] = None, concluded_license: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, - license_info_in_file: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, + license_info_in_file: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = None, license_comment: Optional[str] = None, copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None, comment: str = None, notice: Optional[str] = None, From 1ff674c614d76086e3ea0a9c077ecee7a620909d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 8 Dec 2022 13:20:31 +0100 Subject: [PATCH 018/362] [issue-355] don't allow arbitrary falsey values for optional lists in constructors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit also revert a few tests to check for this Signed-off-by: Armin Tänzer --- src/model/document.py | 14 +++++++------- src/model/extracted_licensing_info.py | 2 +- src/model/file.py | 8 ++++---- src/model/package.py | 10 +++++----- src/model/snippet.py | 2 +- tests/model/test_creation_info.py | 2 +- tests/model/test_document.py | 4 ++-- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/model/document.py b/src/model/document.py index f389d10fc..a11f1c615 100644 --- a/src/model/document.py +++ b/src/model/document.py @@ -43,7 +43,7 @@ def __init__(self, spdx_version: str, spdx_id: str, name: str, document_namespac created: datetime, creator_comment: Optional[str] = None, data_license: str = "CC0-1.0", external_document_refs: List[ExternalDocumentRef] = None, license_list_version: Optional[Version] = None, document_comment: Optional[str] = None): - external_document_refs = external_document_refs or [] + external_document_refs = [] if external_document_refs is None else external_document_refs check_types_and_set_values(self, locals()) @@ -62,10 +62,10 @@ def __init__(self, creation_info: CreationInfo, packages: List[Package] = None, snippets: List[Snippet] = None, annotations: List[Annotation] = None, relationships: List[Relationship] = None, extracted_licensing_info: List[ExtractedLicensingInfo] = None): - packages = packages or [] - files = files or [] - snippets = snippets or [] - annotations = annotations or [] - relationships = relationships or [] - extracted_licensing_info = extracted_licensing_info or [] + packages = [] if packages is None else packages + files = [] if files is None else files + snippets = [] if snippets is None else snippets + annotations = [] if annotations is None else annotations + relationships = [] if relationships is None else relationships + extracted_licensing_info = [] if extracted_licensing_info is None else extracted_licensing_info check_types_and_set_values(self, locals()) diff --git a/src/model/extracted_licensing_info.py b/src/model/extracted_licensing_info.py index 831c06a22..df72fcfb7 100644 --- a/src/model/extracted_licensing_info.py +++ b/src/model/extracted_licensing_info.py @@ -26,5 +26,5 @@ class ExtractedLicensingInfo: def __init__(self, license_id: Optional[str] = None, extracted_text: Optional[str] = None, license_name: Optional[str] = None, comment: Optional[str] = None, cross_references: List[str] = None): - cross_references = cross_references or [] + cross_references = [] if cross_references is None else cross_references check_types_and_set_values(self, locals()) diff --git a/src/model/file.py b/src/model/file.py index fc4627967..6fd15ed4a 100644 --- a/src/model/file.py +++ b/src/model/file.py @@ -62,8 +62,8 @@ def __init__(self, name: str, spdx_id: str, checksums: List[Checksum], file_type copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None, comment: str = None, notice: Optional[str] = None, contributors: List[str] = None, attribution_texts: List[str] = None): - file_type = file_type or [] - license_info_in_file = license_info_in_file or [] - contributors = contributors or [] - attribution_texts = attribution_texts or [] + file_type = [] if file_type is None else file_type + license_info_in_file = [] if license_info_in_file is None else license_info_in_file + contributors = [] if contributors is None else contributors + attribution_texts = [] if attribution_texts is None else attribution_texts check_types_and_set_values(self, locals()) diff --git a/src/model/package.py b/src/model/package.py index 83c3c501e..b9ab7e0a0 100644 --- a/src/model/package.py +++ b/src/model/package.py @@ -43,7 +43,7 @@ class PackageVerificationCode: excluded_files: List[str] = field(default_factory=list) def __init__(self, value: str, excluded_files: List[str] = None): - excluded_files = excluded_files or [] + excluded_files = [] if excluded_files is None else excluded_files check_types_and_set_values(self, locals()) @@ -115,8 +115,8 @@ def __init__(self, spdx_id: str, name: str, download_location: Union[str, SpdxNo external_references: List[ExternalPackageRef] = None, attribution_texts: List[str] = None, primary_package_purpose: Optional[PackagePurpose] = None, release_date: Optional[datetime] = None, built_date: Optional[datetime] = None, valid_until_date: Optional[datetime] = None): - checksums = checksums or [] - license_info_from_files = license_info_from_files or [] - external_references = external_references or [] - attribution_texts = attribution_texts or [] + checksums = [] if checksums is None else checksums + license_info_from_files = [] if license_info_from_files is None else license_info_from_files + external_references = [] if external_references is None else external_references + attribution_texts = [] if attribution_texts is None else attribution_texts check_types_and_set_values(self, locals()) diff --git a/src/model/snippet.py b/src/model/snippet.py index 09dfbdc4d..329de5c25 100644 --- a/src/model/snippet.py +++ b/src/model/snippet.py @@ -38,5 +38,5 @@ def __init__(self, spdx_id: str, file_spdx_id: str, byte_range: Tuple[int, int], license_info_in_snippet: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = None, license_comment: Optional[str] = None, copyright_text: Optional[str] = None, comment: Optional[str] = None, name: Optional[str] = None, attribution_texts: List[str] = None): - attribution_texts = attribution_texts or [] + attribution_texts = [] if attribution_texts is None else attribution_texts check_types_and_set_values(self, locals()) diff --git a/tests/model/test_creation_info.py b/tests/model/test_creation_info.py index e60fdafe1..a0ba11b9e 100644 --- a/tests/model/test_creation_info.py +++ b/tests/model/test_creation_info.py @@ -77,7 +77,7 @@ def test_wrong_type_in_data_license(actor): def test_wrong_type_in_external_document_refs(actor): with pytest.raises(TypeError): CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), - external_document_refs=("ref",)) + external_document_refs=()) @mock.patch('src.model.actor.Actor', autospec=True) diff --git a/tests/model/test_document.py b/tests/model/test_document.py index dc123bc16..1a0a92087 100644 --- a/tests/model/test_document.py +++ b/tests/model/test_document.py @@ -51,13 +51,13 @@ def test_wrong_type_in_packages(creation_info): @mock.patch('src.model.document.CreationInfo', autospec=True) def test_wrong_type_in_files(creation_info): with pytest.raises(TypeError): - Document(creation_info, files={"fileName": "fileValue"}) + Document(creation_info, files={}) @mock.patch('src.model.document.CreationInfo', autospec=True) def test_wrong_type_in_snippets(creation_info): with pytest.raises(TypeError): - Document(creation_info, snippets=("snippet",)) + Document(creation_info, snippets=()) @mock.patch('src.model.document.CreationInfo', autospec=True) From 4a1f2478e54c17fafa7dc5ae0fc7e6158985d456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 9 Dec 2022 10:32:27 +0100 Subject: [PATCH 019/362] [issue-363] rework ExternalDocumentRef MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/model/external_document_ref.py | 4 ++-- tests/model/test_external_document_ref.py | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/model/external_document_ref.py b/src/model/external_document_ref.py index 76a8c1e7a..52010c938 100644 --- a/src/model/external_document_ref.py +++ b/src/model/external_document_ref.py @@ -16,9 +16,9 @@ @dataclass_with_properties class ExternalDocumentRef: + document_ref_id: str # of the form "DocumentRef-[idstring]" document_uri: str - spdx_id: str checksum: Checksum - def __init__(self, document_uri: str, spdx_id: str, checksum: Checksum): + def __init__(self, document_ref_id: str, document_uri: str, checksum: Checksum): check_types_and_set_values(self, locals()) diff --git a/tests/model/test_external_document_ref.py b/tests/model/test_external_document_ref.py index 902afc187..fecf4bb93 100644 --- a/tests/model/test_external_document_ref.py +++ b/tests/model/test_external_document_ref.py @@ -7,24 +7,24 @@ @mock.patch('src.model.checksum.Checksum', autospec=True) def test_correct_initialization(checksum): - external_document_ref = ExternalDocumentRef("uri", "id", checksum) + external_document_ref = ExternalDocumentRef("id", "uri", checksum) + assert external_document_ref.document_ref_id == "id" assert external_document_ref.document_uri == "uri" - assert external_document_ref.spdx_id == "id" assert external_document_ref.checksum == checksum @mock.patch('src.model.checksum.Checksum', autospec=True) -def test_wrong_type_in_document_uri(checksum): +def test_wrong_type_in_spdx_id(checksum): with pytest.raises(TypeError): - ExternalDocumentRef(42, "id", checksum) + ExternalDocumentRef(42, "uri", checksum) @mock.patch('src.model.checksum.Checksum', autospec=True) -def test_wrong_type_in_spdx_id(checksum): +def test_wrong_type_in_document_uri(checksum): with pytest.raises(TypeError): - ExternalDocumentRef("uri", 42, checksum) + ExternalDocumentRef("id", 42, checksum) def test_wrong_type_in_checksum(): with pytest.raises(TypeError): - ExternalDocumentRef("uri", "id", 42) + ExternalDocumentRef("id", "uri", 42) From 7bd496e3cabb7b6adc11392b8a5f99a2302608ad Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 9 Dec 2022 11:48:10 +0100 Subject: [PATCH 020/362] add class name to error messages from setter/ getter Signed-off-by: Meret Behrens --- src/model/typing/dataclass_with_properties.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model/typing/dataclass_with_properties.py b/src/model/typing/dataclass_with_properties.py index 4574434ad..b53d9eb37 100644 --- a/src/model/typing/dataclass_with_properties.py +++ b/src/model/typing/dataclass_with_properties.py @@ -27,7 +27,7 @@ def set_field_with_better_error_message(self, value: field_type): try: set_field(self, value) except TypeError as err: - error_message: str = err.args[0] + error_message: str = f"SetterError {self.__class__.__name__}: {err.args[0]}" # As setters are created dynamically, their argument name is always "value". We replace it by the # actual name so the error message is more helpful. raise TypeError(error_message.replace("value", field_name, 1) + f": {value}") @@ -46,7 +46,7 @@ def get_field_with_better_error_message(self) -> field_type: try: return get_field(self) except TypeError as err: - error_message: str = err.args[0] + error_message: str = f"GetterError {self.__class__.__name__}: {err.args[0]}" # As getters are created dynamically, their argument name is always "the return value". We replace it by the # actual name so the error message is more helpful. raise TypeError( From 8ccf536723012a53ba0cdfb54ecb79abc4d55d69 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 8 Dec 2022 23:44:43 +0100 Subject: [PATCH 021/362] [issue-358] Move CLI tools to new model, change packaging in pyproject.toml to include only the src package Signed-off-by: Nicolaus Weidner --- pyproject.toml | 6 ++-- spdx/cli_tools/__init__.py | 10 ------- src/__init__.py | 0 src/clitools/__init__.py | 0 {spdx/cli_tools => src/clitools}/convertor.py | 22 +++++++------- {spdx/cli_tools => src/clitools}/parser.py | 30 ++++++++----------- 6 files changed, 26 insertions(+), 42 deletions(-) delete mode 100644 spdx/cli_tools/__init__.py create mode 100644 src/__init__.py create mode 100644 src/clitools/__init__.py rename {spdx/cli_tools => src/clitools}/convertor.py (86%) rename {spdx/cli_tools => src/clitools}/parser.py (89%) diff --git a/pyproject.toml b/pyproject.toml index 016e0cee6..17d43ba00 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,15 +31,15 @@ dynamic = ["version"] test = ["pytest"] [project.scripts] -pyspdxtools_convertor = "spdx.cli_tools.convertor:main" -pyspdxtools_parser = "spdx.cli_tools.parser:main" +pyspdxtools_convertor = "src.clitools.convertor:main" +pyspdxtools_parser = "src.clitools.parser:main" [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] -include = ["spdx", "spdx.*"] +where = ["src"] [tool.setuptools_scm] git_describe_command = ["git", "describe", "--dirty", "--tags", "--long", "--match", "v[0-9]*"] # `python3.6` tag falsely matches to the default one, clrearly a bug in setuptools_scm diff --git a/spdx/cli_tools/__init__.py b/spdx/cli_tools/__init__.py deleted file mode 100644 index 1f63eb496..000000000 --- a/spdx/cli_tools/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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. diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/clitools/__init__.py b/src/clitools/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/spdx/cli_tools/convertor.py b/src/clitools/convertor.py similarity index 86% rename from spdx/cli_tools/convertor.py rename to src/clitools/convertor.py index 04e96af72..1f8e73e9f 100644 --- a/spdx/cli_tools/convertor.py +++ b/src/clitools/convertor.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2020 Yash Varshney # Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,16 +12,15 @@ # limitations under the License. import os -from spdx.parsers.builderexceptions import FileTypeError -from spdx.parsers.parse_anything import parse_file -from spdx.writers.write_anything import write_file 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: """ @@ -88,9 +87,12 @@ def main(infile, outfile, src, from_, to, force): """ 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 ' + To use : run 'pyspdxtools_convertor -f -t ' command on terminal + or use ' pyspdxtools_convertor --infile --outfile ' """ + raise NotImplementedError("Currently, conversion is not implemented") + try: infile, outfile = determine_infile_and_outfile(infile, outfile, src, from_, to) except ValueError as err: @@ -98,13 +100,11 @@ def main(infile, outfile, src, from_, to, force): print_help_msg(main) return - doc, errors = parse_file(infile) - if errors: - print("Errors while parsing: ", errors) - if not force: - return 1 + # Parse document from infile + # First one to implement is the Json parser: https://github.com/spdx/tools-python/issues/305 - write_file(doc, outfile) + # Write document to outfile + # First writer to implement is the Json writer: https://github.com/spdx/tools-python/issues/359 if __name__ == "__main__": diff --git a/spdx/cli_tools/parser.py b/src/clitools/parser.py similarity index 89% rename from spdx/cli_tools/parser.py rename to src/clitools/parser.py index aad96d1c2..5b7f7a9e3 100755 --- a/spdx/cli_tools/parser.py +++ b/src/clitools/parser.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2020 Yash Varshney # Licensed under the Apache License, Version 2.0 (the "License"); @@ -11,14 +11,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os - -from spdx import utils -from spdx.parsers.parse_anything import parse_file -import spdx.file as spdxfile - import click +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone + @click.command() @click.option("--file", prompt="File name", help="The file to be parsed") @@ -30,11 +27,10 @@ def main(file, force): To use : run `pyspdxtools_parser` using terminal or run `pyspdxtools_parser --file ` """ - doc, errors = parse_file(file) - if errors: - print("Errors while parsing: ", errors) - if not force: - return 1 + raise NotImplementedError("Currently, no parsers are implemented") + + # Parse document and set as doc here + # First one to implement is the Json parser: https://github.com/spdx/tools-python/issues/305 print("doc comment: {0}".format(doc.comment)) print("Creators:") @@ -90,7 +86,7 @@ def main(file, force): print( "\tFile license info in file: {0}".format( ",".join( - map(lambda l: l.identifier if not isinstance(l, (utils.SPDXNone, utils.NoAssert)) else l.to_value(), + map(lambda l: l.identifier if not isinstance(l, (SpdxNone, SpdxNoAssertion)) else l.to_value(), f.licenses_in_file)) ) ) @@ -105,7 +101,7 @@ def main(file, force): for lics in doc.extracted_licenses: print("\tIdentifier: {0}".format(lics.identifier)) print("\tName: {0}".format(lics.full_name)) - print("\License Text: {0}".format(lics.text)) + print("\tLicense Text: {0}".format(lics.text)) if doc.annotations: print("Annotations:") for an in doc.annotations: @@ -119,10 +115,8 @@ def main(file, force): print("Relationships: ") for relation in doc.relationships: print("\tRelationship: {0}".format(relation.relationship)) - try: - print("\tRelationship: {0}".format(relation.comment)) - except: - continue + if relation.comment: + print("\tRelationship Comment: {0}".format(relation.comment)) if __name__ == "__main__": From 300f737bebd59ef0e29422a65633ac83035f5d8f Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 8 Dec 2022 23:46:29 +0100 Subject: [PATCH 022/362] [issue-358] Remove legacy examples using the old data model Signed-off-by: Nicolaus Weidner --- examples/__init__.py | 10 ----- examples/pp_rdf.py | 23 ---------- examples/pp_tv.py | 32 -------------- examples/write_tv.py | 100 ------------------------------------------- 4 files changed, 165 deletions(-) delete mode 100644 examples/__init__.py delete mode 100755 examples/pp_rdf.py delete mode 100755 examples/pp_tv.py delete mode 100755 examples/write_tv.py diff --git a/examples/__init__.py b/examples/__init__.py deleted file mode 100644 index 1f63eb496..000000000 --- a/examples/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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. diff --git a/examples/pp_rdf.py b/examples/pp_rdf.py deleted file mode 100755 index 4545b0fc1..000000000 --- a/examples/pp_rdf.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -# Parses an RDF file and writes it out pretty-printed. -# Usage: pp_rdf - -if __name__ == "__main__": - import sys - from spdx.parsers.rdf import Parser - from spdx.parsers.loggers import StandardLogger - from spdx.parsers.rdfbuilders import Builder - from spdx.writers.rdf import write_document - - infile = sys.argv[1] - outfile = sys.argv[2] - p = Parser(Builder(), StandardLogger()) - with open(infile) as f: - doc, error = p.parse(f) - if not error: - with open(outfile, mode="wb") as out: - write_document(doc, out) - - else: - print("Errors while parsing") diff --git a/examples/pp_tv.py b/examples/pp_tv.py deleted file mode 100755 index e0de82159..000000000 --- a/examples/pp_tv.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python - -# Parses a tag/value file and writes it out pretty-printed. -# Usage: pp_tv -if __name__ == "__main__": - import sys - import codecs - from spdx.writers.tagvalue import write_document, InvalidDocumentError - from spdx.parsers.tagvalue import Parser - from spdx.parsers.loggers import StandardLogger - from spdx.parsers.loggers import ErrorMessages - from spdx.parsers.tagvaluebuilders import Builder - - source = sys.argv[1] - target = sys.argv[2] - p = Parser(Builder(), StandardLogger()) - p.build() - with open(source, "r") as f: - data = f.read() - document, error = p.parse(data) - if not error: - print("Parsing Successful") - with codecs.open(target, mode="w", encoding="utf-8") as out: - try: - write_document(document, out) - except InvalidDocumentError: - print("Document is Invalid") - messages = ErrorMessages() - document.validate(messages) - print("\n".join(messages.messages)) - else: - print("Errors encountered while parsing") diff --git a/examples/write_tv.py b/examples/write_tv.py deleted file mode 100755 index 0dec0f2f3..000000000 --- a/examples/write_tv.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env python -from spdx.relationship import Relationship - -# Writes a new tag/value file from scratch. -# Usage: write_tv -if __name__ == "__main__": - import sys - import codecs - from spdx.writers.tagvalue import write_document, InvalidDocumentError - from spdx.parsers.loggers import ErrorMessages - from spdx.document import Document - from spdx.license import License, LicenseConjunction, ExtractedLicense - from spdx.version import Version - from spdx.creationinfo import Person - from spdx.review import Review - from spdx.package import Package - from spdx.file import File, FileType - from spdx.checksum import Checksum - from spdx.utils import SPDXNone, NoAssert, UnKnown - - doc = Document() - doc.version = Version(1, 2) - doc.name = "Hello_SPDX" - doc.spdx_id = "Test#SPDXRef-DOCUMENT" - doc.comment = "Example Document" - doc.namespace = "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301" - doc.data_license = License.from_identifier("CC0-1.0") - doc.creation_info.add_creator(Person("Alice", "alice@example.com")) - doc.creation_info.set_created_now() - review = Review(Person("Joe", None)) - review.set_review_date_now() - review.comment = "Joe reviewed this document" - doc.add_review(review) - # File - testfile1 = File("TestFile1") - testfile1.type = FileType.BINARY - testfile1.spdx_id = "TestFilet#SPDXRef-FILE" - testfile1.comment = "This is a test file." - testfile1.chksum = Checksum("SHA1", "c537c5d99eca5333f23491d47ededd083fefb7ad") - testfile1.conc_lics = License.from_identifier("BSD-2-Clause") - testfile1.add_lics(License.from_identifier("BSD-2-Clause")) - testfile1.copyright = SPDXNone() - testfile1.add_artifact("name", "TagWriteTest") - testfile1.add_artifact("home", UnKnown()) - testfile1.add_artifact("uri", "http://tagwritetest.test") - - testfile2 = File("TestFile2") - testfile2.type = FileType.SOURCE - testfile2.spdx_id = "TestFile2#SPDXRef-FILE" - testfile2.comment = "This is a test file." - testfile2.chksum = Checksum("SHA1", "bb154f28d1cf0646ae21bb0bec6c669a2b90e113") - testfile2.conc_lics = License.from_identifier("Apache-2.0") - testfile2.add_lics(License.from_identifier("Apache-2.0")) - testfile2.copyright = NoAssert() - doc.add_file(testfile1) - doc.add_file(testfile2) - - # Package - package = Package() - package.name = "TagWriteTest" - package.version = "1.0" - package.file_name = "twt.jar" - package.spdx_id = 'TestPackage#SPDXRef-PACKAGE' - package.download_location = "http://www.tagwritetest.test/download" - package.checksum = Checksum("SHA1", "c537c5d99eca5333f23491d47ededd083fefb7ad") - package.homepage = SPDXNone() - package.verif_code = "4e3211c67a2d28fced849ee1bb76e7391b93feba" - license_set = LicenseConjunction( - License.from_identifier("Apache-2.0"), License.from_identifier("BSD-2-Clause") - ) - package.conc_lics = license_set - package.license_declared = license_set - package.add_lics_from_file(License.from_identifier("Apache-2.0")) - package.add_lics_from_file(License.from_identifier("BSD-2-Clause")) - package.cr_text = NoAssert() - package.summary = "Simple package." - package.description = "Really simple package." - - doc.package = package - relationship = Relationship("TestPackage#SPDXRef-PACKAGE CONTAINS TestFilet#SPDXRef-FILE") - doc.add_relationship(relationship) - relationship = Relationship("TestPackage#SPDXRef-PACKAGE CONTAINS TestFile2#SPDXRef-FILE") - doc.add_relationship(relationship) - - # An extracted license - - lic = ExtractedLicense("LicenseRef-1") - lic.text = "Some non legal legal text.." - doc.add_extr_lic(lic) - - file = sys.argv[1] - with codecs.open(file, mode="w", encoding="utf-8") as out: - try: - write_document(doc, out) - except InvalidDocumentError as e: - print("Document is Invalid:\n\t", end="") - print("\n\t".join(e.args[0])) - messages = ErrorMessages() - doc.validate(messages) - print("\n".join(messages.messages)) From cb6be81c9f0705120f3565989a7e7d5fc585e129 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 8 Dec 2022 23:46:58 +0100 Subject: [PATCH 023/362] [issue-358] Remove legacy data model, parsers and writers Signed-off-by: Nicolaus Weidner --- examples/tv_to_rdf.py | 62 - spdx/__init__.py | 1 - spdx/annotation.py | 104 - spdx/checksum.py | 98 - spdx/creationinfo.py | 185 - spdx/document.py | 298 - spdx/exceptions.json | 408 -- spdx/file.py | 249 - spdx/license.py | 207 - spdx/licenses.json | 4974 ----------------- spdx/package.py | 364 -- spdx/parsers/__init__.py | 10 - spdx/parsers/builderexceptions.py | 36 - spdx/parsers/jsonparser.py | 29 - spdx/parsers/jsonyamlxml.py | 1885 ------- spdx/parsers/jsonyamlxmlbuilders.py | 332 -- spdx/parsers/lexers/tagvalue.py | 252 - spdx/parsers/loggers.py | 60 - spdx/parsers/parse_anything.py | 51 - spdx/parsers/rdf.py | 1518 ----- spdx/parsers/rdfbuilders.py | 669 --- spdx/parsers/tagvalue.py | 1822 ------ spdx/parsers/tagvaluebuilders.py | 1717 ------ spdx/parsers/validations.py | 345 -- spdx/parsers/xmlparser.py | 81 - spdx/parsers/yamlparser.py | 29 - spdx/relationship.py | 111 - spdx/review.py | 81 - spdx/snippet.py | 115 - spdx/utils.py | 248 - spdx/version.py | 59 - spdx/writers/__init__.py | 0 spdx/writers/json.py | 37 - spdx/writers/jsonyamlxml.py | 627 --- spdx/writers/rdf.py | 1087 ---- spdx/writers/tagvalue.py | 475 -- spdx/writers/write_anything.py | 38 - spdx/writers/xml.py | 30 - spdx/writers/yaml.py | 30 - {spdx => src}/config.py | 3 +- src/exceptions.json | 408 ++ src/licenses.json | 4974 +++++++++++++++++ src/model/license.py | 2 +- .../lexers => tests/clitools}/__init__.py | 0 tests/{ => clitools}/test_cli_convertor.py | 3 +- tests/test_builder.py | 837 --- tests/test_config.py | 45 - tests/test_conversion.py | 274 - tests/test_creationinfo.py | 67 - tests/test_document.py | 618 -- tests/test_error_messages.py | 30 - tests/test_jsonyamlxml_parser.py | 72 - tests/test_jsonyamlxml_writer.py | 164 - tests/test_package.py | 36 - tests/test_parse_anything.py | 32 - tests/test_parsers_validation.py | 34 - tests/test_rdf_parser.py | 48 - tests/test_rdf_writer.py | 78 - tests/test_tag_value_parser.py | 411 -- tests/test_write_anything.py | 83 - tests/testing_utils.py | 3 + tests/utils_test.py | 556 -- 62 files changed, 5388 insertions(+), 22114 deletions(-) delete mode 100755 examples/tv_to_rdf.py delete mode 100644 spdx/__init__.py delete mode 100644 spdx/annotation.py delete mode 100644 spdx/checksum.py delete mode 100644 spdx/creationinfo.py delete mode 100644 spdx/document.py delete mode 100644 spdx/exceptions.json delete mode 100644 spdx/file.py delete mode 100644 spdx/license.py delete mode 100644 spdx/licenses.json delete mode 100644 spdx/package.py delete mode 100644 spdx/parsers/__init__.py delete mode 100644 spdx/parsers/builderexceptions.py delete mode 100644 spdx/parsers/jsonparser.py delete mode 100644 spdx/parsers/jsonyamlxml.py delete mode 100644 spdx/parsers/jsonyamlxmlbuilders.py delete mode 100644 spdx/parsers/lexers/tagvalue.py delete mode 100644 spdx/parsers/loggers.py delete mode 100644 spdx/parsers/parse_anything.py delete mode 100644 spdx/parsers/rdf.py delete mode 100644 spdx/parsers/rdfbuilders.py delete mode 100644 spdx/parsers/tagvalue.py delete mode 100644 spdx/parsers/tagvaluebuilders.py delete mode 100644 spdx/parsers/validations.py delete mode 100644 spdx/parsers/xmlparser.py delete mode 100644 spdx/parsers/yamlparser.py delete mode 100644 spdx/relationship.py delete mode 100644 spdx/review.py delete mode 100644 spdx/snippet.py delete mode 100644 spdx/utils.py delete mode 100644 spdx/version.py delete mode 100644 spdx/writers/__init__.py delete mode 100644 spdx/writers/json.py delete mode 100644 spdx/writers/jsonyamlxml.py delete mode 100644 spdx/writers/rdf.py delete mode 100644 spdx/writers/tagvalue.py delete mode 100644 spdx/writers/write_anything.py delete mode 100644 spdx/writers/xml.py delete mode 100644 spdx/writers/yaml.py rename {spdx => src}/config.py (98%) create mode 100644 src/exceptions.json create mode 100644 src/licenses.json rename {spdx/parsers/lexers => tests/clitools}/__init__.py (100%) rename tests/{ => clitools}/test_cli_convertor.py (97%) delete mode 100644 tests/test_builder.py delete mode 100644 tests/test_config.py delete mode 100644 tests/test_conversion.py delete mode 100644 tests/test_creationinfo.py delete mode 100644 tests/test_document.py delete mode 100644 tests/test_error_messages.py delete mode 100644 tests/test_jsonyamlxml_parser.py delete mode 100644 tests/test_jsonyamlxml_writer.py delete mode 100644 tests/test_package.py delete mode 100644 tests/test_parse_anything.py delete mode 100644 tests/test_parsers_validation.py delete mode 100644 tests/test_rdf_parser.py delete mode 100644 tests/test_rdf_writer.py delete mode 100644 tests/test_tag_value_parser.py delete mode 100644 tests/test_write_anything.py delete mode 100644 tests/utils_test.py diff --git a/examples/tv_to_rdf.py b/examples/tv_to_rdf.py deleted file mode 100755 index db890961e..000000000 --- a/examples/tv_to_rdf.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python -# Copyright (C) 2017 BMW AG -# Author: Thomas Hafner -# -# 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 spdx.parsers.loggers import StandardLogger -from spdx.writers.rdf import write_document -from spdx.parsers.tagvalue import Parser -from spdx.parsers.tagvaluebuilders import Builder -from spdx.parsers.loggers import ErrorMessages - - -def tv_to_rdf(infile_name, outfile_name): - """ - Convert a SPDX file from tag/value format to RDF format. - Return True on success, False otherwise. - """ - parser = Parser(Builder(), StandardLogger()) - parser.build() - with open(infile_name) as infile: - data = infile.read() - document, error = parser.parse(data) - if not error: - with open(outfile_name, mode="wb") as outfile: - write_document(document, outfile) - return True - else: - print("Errors encountered while parsing RDF file.") - messages = ErrorMessages() - document.validate(messages) - print("\n".join(messages.messages)) - return False - - -def main(): - args = sys.argv[1:] - if not args: - print( - "Usage: spdx-tv2rdf \n" - "Convert an SPDX tag/value document to RDF." - ) - sys.exit(1) - - tv_file = args[0] - rdf_file = args[1] - success = tv_to_rdf(tv_file, rdf_file) - sys.exit(0 if success else 1) - - -if __name__ == "__main__": - main() diff --git a/spdx/__init__.py b/spdx/__init__.py deleted file mode 100644 index 5284146eb..000000000 --- a/spdx/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__import__("pkg_resources").declare_namespace(__name__) diff --git a/spdx/annotation.py b/spdx/annotation.py deleted file mode 100644 index 51cff82d9..000000000 --- a/spdx/annotation.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2018 Yash M. Nisar -# 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 datetime import datetime -from functools import total_ordering - -from spdx.utils import datetime_iso_format - - -@total_ordering -class Annotation(object): - - """ - Document annotation information. - Fields: - - annotator: Person, Organization or tool that has commented on a file, - package, or the entire document. Conditional (Mandatory, one), if there - is an Annotation. - - annotation_date: To identify when the comment was made. Conditional - (Mandatory, one), if there is an Annotation. Type: datetime. - - comment: Annotation comment. Conditional (Mandatory, one), if there is - an Annotation. Type: str. - - annotation_type: Annotation type. Conditional (Mandatory, one), if there is an - Annotation. Type: str. - - spdx_id: Uniquely identify the element in an SPDX document which is being - referenced. Conditional (Mandatory, one), if there is an Annotation. - Type: str. - """ - - def __init__( - self, - annotator=None, - annotation_date=None, - comment=None, - annotation_type=None, - spdx_id=None, - ): - self.annotator = annotator - self.annotation_date = annotation_date - self.comment = comment - self.annotation_type = annotation_type - self.spdx_id = spdx_id - - def __eq__(self, other): - return ( - isinstance(other, Annotation) - and self.annotator == other.annotator - and self.annotation_date == other.annotation_date - and self.comment == other.comment - ) - - def __lt__(self, other): - return (self.annotator, self.annotation_date, self.comment) < ( - other.annotator, - other.annotation_date, - other.comment, - ) - - def set_annotation_date_now(self): - self.annotation_date = datetime.utcnow() - - @property - def annotation_date_iso_format(self): - return datetime_iso_format(self.annotation_date) - - @property - def has_comment(self): - return self.comment is not None - - def validate(self, messages): - """ - Check that all the fields are valid. - Appends any error messages to messages parameter shall be a ErrorMessages. - """ - self.validate_annotator(messages) - self.validate_annotation_date(messages) - self.validate_annotation_type(messages) - self.validate_spdx_id(messages) - - return messages - - def validate_annotator(self, messages): - if self.annotator is None: - messages.append("Annotation missing annotator.") - - def validate_annotation_date(self, messages): - if self.annotation_date is None: - messages.append("Annotation missing annotation date.") - - def validate_annotation_type(self, messages): - if self.annotation_type is None: - messages.append("Annotation missing annotation type.") - - def validate_spdx_id(self, messages): - if self.spdx_id is None: - messages.append("Annotation missing SPDX Identifier Reference.") diff --git a/spdx/checksum.py b/spdx/checksum.py deleted file mode 100644 index 315814269..000000000 --- a/spdx/checksum.py +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 re -from enum import Enum, auto - - -class ChecksumAlgorithm(Enum): - SHA1 = auto() - SHA224 = auto() - SHA256 = auto() - SHA384 = auto() - SHA512 = auto() - SHA3_256 = auto() - SHA3_384 = auto() - SHA3_512 = auto() - BLAKE2B_256 = auto() - BLAKE2B_384 = auto() - BLAKE2B_512 = auto() - BLAKE3 = auto() - MD2 = auto() - MD4 = auto() - MD5 = auto() - MD6 = auto() - ADLER32 = auto() - - def algorithm_to_rdf_representation(self) -> str: - if self.name.startswith("BLAKE2B"): - return "checksumAlgorithm_" + self.name.replace("_", "").lower() - else: - return "checksumAlgorithm_" + self.name.lower() - - @classmethod - def checksum_from_rdf(cls, identifier: str) -> 'ChecksumAlgorithm': - identifier = identifier.split('_', 1)[-1].upper() - blake_checksum = re.compile(r"^(BLAKE2B)(256|384|512)$", re.UNICODE) - match = blake_checksum.match(identifier) - if match: - identifier = match[1] + '_' + match[2] - if identifier not in ChecksumAlgorithm.__members__: - raise ValueError(f"Invalid algorithm for checksum: {identifier}") - return ChecksumAlgorithm[identifier] - - @classmethod - def checksum_algorithm_from_string(cls, identifier: str) -> 'ChecksumAlgorithm': - identifier = identifier.replace("-", "_").upper() - if identifier not in ChecksumAlgorithm.__members__: - raise ValueError(f"Invalid algorithm for checksum: {identifier}") - return ChecksumAlgorithm[identifier] - - -class Checksum(object): - """Generic checksum algorithm.""" - - def __init__(self, identifier: ChecksumAlgorithm, value: str): - self.identifier = identifier - self.value = value - - def __eq__(self, other) -> bool: - if not isinstance(other, Checksum): - return False - return self.identifier == other.identifier and self.value == other.value - - @classmethod - def checksum_from_string(cls, value: str) -> 'Checksum': - CHECKSUM_RE = re.compile("(ADLER32|BLAKE2b-256|BLAKE2b-384|BLAKE2b-512|BLAKE3|MD2|MD4|MD5|MD6|" \ - "SHA1|SHA224|SHA256|SHA384|SHA512|SHA3-256|SHA3-384|SHA3-512):\\s*([a-fA-F0-9]*)") - match = CHECKSUM_RE.match(value) - if match is None or match.group(1) is None or match.group(2) is None: - raise ValueError(f"Invalid checksum: {value}") - identifier = ChecksumAlgorithm.checksum_algorithm_from_string(match.group(1)) - return Checksum(identifier=identifier, value=match.group(2)) - - def to_tv(self) -> str: - algorithm_name: str = self.identifier.name - # Convert underscores to dashes, and other Blake2b-specific casing rules - if "_" in algorithm_name: - algorithm_name = CHECKSUM_ALGORITHM_TO_TV.get(algorithm_name) - if algorithm_name is None: - raise ValueError(f"Missing conversion rule for converting {self.identifier.name} to tag-value string") - return "{0}: {1}".format(algorithm_name, self.value) - - -CHECKSUM_ALGORITHM_TO_TV = { - ChecksumAlgorithm.BLAKE2B_256.name: "BLAKE2b-256", - ChecksumAlgorithm.BLAKE2B_384.name: "BLAKE2b-384", - ChecksumAlgorithm.BLAKE2B_512.name: "BLAKE2b-512", - ChecksumAlgorithm.SHA3_256.name: "SHA3-256", - ChecksumAlgorithm.SHA3_384.name: "SHA3-384", - ChecksumAlgorithm.SHA3_512.name: "SHA3-512" -} diff --git a/spdx/creationinfo.py b/spdx/creationinfo.py deleted file mode 100644 index eca9cbe37..000000000 --- a/spdx/creationinfo.py +++ /dev/null @@ -1,185 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 datetime import datetime -from functools import total_ordering - -from spdx import config -from spdx import utils -from spdx.version import Version - - -@total_ordering -class Creator(object): - """ - Creator entity. - Fields: - - name: creator's name/identifier - """ - - def __init__(self, name): - self.name = name - - # FIXME: do not override eq and not hash - def __eq__(self, other): - return isinstance(other, Creator) and self.name == other.name - - def __lt__(self, other): - return isinstance(other, Creator) and self.name < other.name - - -@total_ordering -class Organization(Creator): - """ - Organization entity. - Fields: - - name: Org's name/identifier. Mandatory. Type: str. - - email: Org's email address. Optional. Type: str. - """ - - def __init__(self, name, email=None): - super(Organization, self).__init__(name) - self.email = email - - # FIXME: do not override eq and not hash - def __eq__(self, other): - return isinstance(other, Organization) and (self.name, self.email) == ( - other.name, - other.email, - ) - - def __lt__(self, other): - return isinstance(other, Organization) and (self.name, self.email) < ( - other.name, - other.email, - ) - - def to_value(self): - if self.email: - return "Organization: {0} ({1})".format(self.name, self.email) - else: - return "Organization: {0}".format(self.name) - - def __str__(self): - return self.to_value() - - -@total_ordering -class Person(Creator): - """ - Person entity. - Fields: - - name: person's name/identifier. Mandatory. Type: str. - - email: person's email address. Optional. Type: str. - """ - - def __init__(self, name, email=None): - super(Person, self).__init__(name) - self.email = email - - # FIXME: do not override eq and not hash - def __eq__(self, other): - return isinstance(other, Person) and (self.name, self.email) == ( - other.name, - other.email, - ) - - def __lt__(self, other): - return isinstance(other, Person) and (self.name, self.email) < ( - other.name, - other.email, - ) - - def to_value(self): - if self.email is not None: - return "Person: {0} ({1})".format(self.name, self.email) - else: - return "Person: {0}".format(self.name) - - def __str__(self): - return self.to_value() - - -class Tool(Creator): - """ - Tool entity. - Fields: - - name: tool identifier, with version. Type: str. - """ - - def __init__(self, name): - super(Tool, self).__init__(name) - - def to_value(self): - return "Tool: {0}".format(self.name) - - def __str__(self): - return self.to_value() - - -class CreationInfo(object): - """ - Represent a document creation info. - Fields: - - creators: List of creators. At least one required. - Type: Creator. - - comment: Creation comment, optional. Type: str. - - license_list_version: version of SPDX license used in creation of SPDX - document. One, optional. Type: spdx.version.Version - - created: Creation date. Mandatory one. Type: datetime. - """ - - def __init__( - self, - created=None, - comment=None, - license_list_version=config.LICENSE_LIST_VERSION, - ): - self.creators = [] - self.created = created - self.comment = comment - self.license_list_version = license_list_version - - def add_creator(self, creator): - self.creators.append(creator) - - def remove_creator(self, creator): - self.creators.remove(creator) - - def set_created_now(self): - self.created = datetime.utcnow().replace(microsecond=0) - - def set_license_list_version(self, license_list_version): - self.license_list_version = Version.from_str(license_list_version) - - @property - def created_iso_format(self): - return utils.datetime_iso_format(self.created) - - @property - def has_comment(self): - return self.comment is not None - - def validate(self, messages): - """ - Check that all the fields are valid. - Appends any error messages to messages parameter shall be a ErrorMessages. - """ - self.validate_creators(messages) - self.validate_created(messages) - - def validate_creators(self, messages): - if len(self.creators) == 0: - messages.append("No creators defined, must have at least one.") - - def validate_created(self, messages): - if self.created is None: - messages.append("Creation info missing created date.") diff --git a/spdx/document.py b/spdx/document.py deleted file mode 100644 index 3175df40e..000000000 --- a/spdx/document.py +++ /dev/null @@ -1,298 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 typing import List, Optional, TYPE_CHECKING - -if TYPE_CHECKING: - from spdx.file import File -from spdx.license import ExtractedLicense -from spdx.parsers.loggers import ErrorMessages - -import warnings - -from functools import total_ordering -from spdx.relationship import Relationship - -from spdx.relationship import Relationship - - -@total_ordering -class ExternalDocumentRef(object): - """ - External Document References entity that contains the following fields : - - external_document_id: A unique string containing letters, numbers, '.', - '-' or '+'. - - spdx_document_uri: The unique ID of the SPDX document being referenced. - - check_sum: The checksum of the referenced SPDX document. - """ - - def __init__( - self, external_document_id=None, spdx_document_uri=None, checksum=None - ): - self.external_document_id = external_document_id - self.spdx_document_uri = spdx_document_uri - self.checksum = checksum - - def __eq__(self, other): - return ( - isinstance(other, ExternalDocumentRef) - and self.external_document_id == other.external_document_id - and self.spdx_document_uri == other.spdx_document_uri - and self.checksum == other.checksum - ) - - def __lt__(self, other): - return (self.external_document_id, self.spdx_document_uri, self.checksum) < ( - other.external_document_id, - other.spdx_document_uri, - other.checksum, - ) - - def validate(self, messages: ErrorMessages) -> ErrorMessages: - """ - Check that all the fields are valid. - Appends any error messages to messages parameter shall be a ErrorMessages. - """ - self.validate_ext_doc_id(messages) - self.validate_spdx_doc_uri(messages) - self.validate_checksum(messages) - return messages - - def validate_ext_doc_id(self, messages): - if not self.external_document_id: - messages.append("ExternalDocumentRef has no External Document ID.") - - def validate_spdx_doc_uri(self, messages): - if not self.spdx_document_uri: - messages.append("ExternalDocumentRef has no SPDX Document URI.") - - def validate_checksum(self, messages): - if not self.checksum: - messages.append("ExternalDocumentRef has no Checksum.") - - -class Document(object): - """ - Represent an SPDX document with these fields: - - version: Spec version. Mandatory, one - Type: Version. - - data_license: SPDX-Metadata license. Mandatory, one. Type: License. - - name: Name of the document. Mandatory, one. Type: str. - - spdx_id: SPDX Identifier for the document to refer to itself in - relationship to other elements. Mandatory, one. Type: str. - - ext_document_references: External SPDX documents referenced within the - given SPDX document. Optional, one or many. Type: ExternalDocumentRef - - comment: Comments on the SPDX file, optional one. Type: str - - documentNamespace: SPDX document specific namespace. Mandatory, one. Type: str - - creation_info: SPDX file creation info. Mandatory, one. Type: CreationInfo - - package: Package described by this document. Mandatory, one. Type: Package - - extracted_licenses: List of licenses extracted that are not part of the - SPDX license list. Optional, many. Type: ExtractedLicense. - - reviews: SPDX document review information, Optional zero or more. - Type: Review. - - annotations: SPDX document annotation information, Optional zero or more. - Type: Annotation. - - snippet: Snippet information. Optional zero or more. Type: Snippet. - - relationships: Relationship between two SPDX elements. Optional zero or more. - Type: Relationship. - """ - - def __init__( - self, - version=None, - data_license=None, - name=None, - spdx_id=None, - namespace=None, - comment=None, - package=None, - license_list_version=None, - ): - # avoid recursive import - from spdx.creationinfo import CreationInfo - - self.version = version - self.data_license = data_license - self.name = name - self.spdx_id = spdx_id - self.ext_document_references = [] - self.comment = comment - self.namespace = namespace - self.creation_info = CreationInfo() - self.files: List['File'] = [] - self.packages = [] - if package is not None: - self.packages.append(package) - self.extracted_licenses = [] - self.reviews = [] - self.annotations = [] - self.relationships: List[Relationship] = [] - self.snippet = [] - - # due to backwards compatibility write input argument for license list version to creation info - if license_list_version: - self.creation_info.set_license_list_version(license_list_version) - - def add_review(self, review): - self.reviews.append(review) - - def add_annotation(self, annotation): - self.annotations.append(annotation) - - def add_relationship(self, relationship): - self.relationships.append(relationship) - - def add_extr_lic(self, lic): - self.extracted_licenses.append(lic) - - def add_ext_document_reference(self, ext_doc_ref): - self.ext_document_references.append(ext_doc_ref) - - def add_snippet(self, snip): - self.snippet.append(snip) - - def add_package(self, package): - self.packages.append(package) - - def add_file(self, file: 'File') -> None: - self.files.append(file) - - # For backwards compatibility with older versions, we support a - # mode where the first package in a document may be referred to as - # the document's "package", and the files it contains may be - # referred to as the document's files. This usage is deprecated. - - @property - def package(self): - warnings.warn('document.package and document.files are deprecated; ' - 'use document.packages instead', - DeprecationWarning) - if len(self.packages) == 0: - return None - else: - return self.packages[0] - - @package.setter - def package(self, value): - warnings.warn('document.package and document.files are deprecated; ' - 'use document.packages instead', - DeprecationWarning) - if len(self.packages) == 0: - self.packages.append(value) - else: - self.packages[0] = value - - @property - def has_comment(self): - return self.comment is not None - - def validate(self, messages=None): - """ - Validate all fields of the document and update the - messages list with user friendly error messages for display. - """ - if isinstance(messages, list): - raise TypeError("messages should be None or an instance of ErrorMessages") - if messages is None: - messages = ErrorMessages() - - messages.push_context(self.name) - self.validate_version(messages) - self.validate_data_lics(messages) - self.validate_name(messages) - self.validate_spdx_id(messages) - self.validate_namespace(messages) - self.validate_ext_document_references(messages) - self.validate_creation_info(messages) - self.validate_files(messages) - self.validate_packages(messages) - self.validate_extracted_licenses(messages) - self.validate_reviews(messages) - self.validate_snippet(messages) - self.validate_annotations(messages) - self.validate_relationships(messages) - messages.pop_context() - return messages - - def validate_version(self, messages): - if self.version is None: - messages.append("Document has no version.") - - def validate_data_lics(self, messages): - if self.data_license is None: - messages.append("Document has no data license.") - else: - # FIXME: REALLY? what if someone wants to use something else? - if self.data_license.identifier != "CC0-1.0": - messages.append("Document data license must be CC0-1.0.") - - def validate_name(self, messages): - if self.name is None: - messages.append("Document has no name.") - - def validate_namespace(self, messages): - if self.namespace is None: - messages.append("Document has no namespace.") - - def validate_spdx_id(self, messages): - if self.spdx_id is None: - messages.append("Document has no SPDX Identifier.") - else: - if not self.spdx_id.endswith("SPDXRef-DOCUMENT"): - messages.append("Invalid Document SPDX Identifier value.") - - def validate_ext_document_references(self, messages): - for doc in self.ext_document_references: - if isinstance(doc, ExternalDocumentRef): - messages = doc.validate(messages) - else: - messages = list(messages) + [ - "External document references must be of the type " - "spdx.document.ExternalDocumentRef and not " + str(type(doc)) - ] - - def validate_reviews(self, messages): - for review in self.reviews: - messages = review.validate(messages) - - def validate_files(self, messages: ErrorMessages) -> None: - for file in self.files: - messages = file.validate(messages) - - def validate_annotations(self, messages): - for annotation in self.annotations: - messages = annotation.validate(messages) - - def validate_relationships(self, messages): - for relationship in self.relationships: - messages = relationship.validate(messages) - - def validate_snippet(self, messages=None): - for snippet in self.snippet: - snippet.validate(messages) - - def validate_creation_info(self, messages): - if self.creation_info is not None: - self.creation_info.validate(messages) - else: - messages.append("Document has no creation information.") - - def validate_packages(self, messages): - for package in self.packages: - messages = package.validate(messages) - - def validate_extracted_licenses(self, messages): - for lic in self.extracted_licenses: - if isinstance(lic, ExtractedLicense): - messages = lic.validate(messages) - else: - messages.append( - "Document extracted licenses must be of type " - "spdx.document.ExtractedLicense and not " + type(lic) - ) diff --git a/spdx/exceptions.json b/spdx/exceptions.json deleted file mode 100644 index 0f77cd372..000000000 --- a/spdx/exceptions.json +++ /dev/null @@ -1,408 +0,0 @@ -{ - "licenseListVersion": "3.6", - "releaseDate": "2019-07-10", - "exceptions": [ - { - "reference": "./Libtool-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Libtool-exception.json", - "referenceNumber": "1", - "name": "Libtool Exception", - "seeAlso": [ - "http://git.savannah.gnu.org/cgit/libtool.git/tree/m4/libtool.m4" - ], - "licenseExceptionId": "Libtool-exception" - }, - { - "reference": "./Linux-syscall-note.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Linux-syscall-note.json", - "referenceNumber": "2", - "name": "Linux Syscall Note", - "seeAlso": [ - "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/COPYING" - ], - "licenseExceptionId": "Linux-syscall-note" - }, - { - "reference": "./Autoconf-exception-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Autoconf-exception-3.0.json", - "referenceNumber": "3", - "name": "Autoconf exception 3.0", - "seeAlso": [ - "http://www.gnu.org/licenses/autoconf-exception-3.0.html" - ], - "licenseExceptionId": "Autoconf-exception-3.0" - }, - { - "reference": "./OCCT-exception-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OCCT-exception-1.0.json", - "referenceNumber": "4", - "name": "Open CASCADE Exception 1.0", - "seeAlso": [ - "http://www.opencascade.com/content/licensing" - ], - "licenseExceptionId": "OCCT-exception-1.0" - }, - { - "reference": "./openvpn-openssl-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/openvpn-openssl-exception.json", - "referenceNumber": "5", - "name": "OpenVPN OpenSSL Exception", - "seeAlso": [ - "http://openvpn.net/index.php/license.html" - ], - "licenseExceptionId": "openvpn-openssl-exception" - }, - { - "reference": "./gnu-javamail-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/gnu-javamail-exception.json", - "referenceNumber": "6", - "name": "GNU JavaMail exception", - "seeAlso": [ - "http://www.gnu.org/software/classpathx/javamail/javamail.html" - ], - "licenseExceptionId": "gnu-javamail-exception" - }, - { - "reference": "./OpenJDK-assembly-exception-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OpenJDK-assembly-exception-1.0.json", - "referenceNumber": "7", - "name": "OpenJDK Assembly exception 1.0", - "seeAlso": [ - "http://openjdk.java.net/legal/assembly-exception.html" - ], - "licenseExceptionId": "OpenJDK-assembly-exception-1.0" - }, - { - "reference": "./Bison-exception-2.2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Bison-exception-2.2.json", - "referenceNumber": "8", - "name": "Bison exception 2.2", - "seeAlso": [ - "http://git.savannah.gnu.org/cgit/bison.git/tree/data/yacc.c?id\u003d193d7c7054ba7197b0789e14965b739162319b5e#n141" - ], - "licenseExceptionId": "Bison-exception-2.2" - }, - { - "reference": "./i2p-gpl-java-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/i2p-gpl-java-exception.json", - "referenceNumber": "9", - "name": "i2p GPL+Java Exception", - "seeAlso": [ - "http://geti2p.net/en/get-involved/develop/licenses#java_exception" - ], - "licenseExceptionId": "i2p-gpl-java-exception" - }, - { - "reference": "./Universal-FOSS-exception-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Universal-FOSS-exception-1.0.json", - "referenceNumber": "10", - "name": "Universal FOSS Exception, Version 1.0", - "seeAlso": [ - "https://oss.oracle.com/licenses/universal-foss-exception/" - ], - "licenseExceptionId": "Universal-FOSS-exception-1.0" - }, - { - "reference": "./Qt-LGPL-exception-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Qt-LGPL-exception-1.1.json", - "referenceNumber": "11", - "name": "Qt LGPL exception 1.1", - "seeAlso": [ - "http://code.qt.io/cgit/qt/qtbase.git/tree/LGPL_EXCEPTION.txt" - ], - "licenseExceptionId": "Qt-LGPL-exception-1.1" - }, - { - "reference": "./389-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/389-exception.json", - "referenceNumber": "12", - "name": "389 Directory Server Exception", - "seeAlso": [ - "http://directory.fedoraproject.org/wiki/GPL_Exception_License_Text" - ], - "licenseExceptionId": "389-exception" - }, - { - "reference": "./Classpath-exception-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Classpath-exception-2.0.json", - "referenceNumber": "13", - "name": "Classpath exception 2.0", - "seeAlso": [ - "http://www.gnu.org/software/classpath/license.html", - "https://fedoraproject.org/wiki/Licensing/GPL_Classpath_Exception" - ], - "licenseExceptionId": "Classpath-exception-2.0" - }, - { - "reference": "./Fawkes-Runtime-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Fawkes-Runtime-exception.json", - "referenceNumber": "14", - "name": "Fawkes Runtime Exception", - "seeAlso": [ - "http://www.fawkesrobotics.org/about/license/" - ], - "licenseExceptionId": "Fawkes-Runtime-exception" - }, - { - "reference": "./PS-or-PDF-font-exception-20170817.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/PS-or-PDF-font-exception-20170817.json", - "referenceNumber": "15", - "name": "PS/PDF font exception (2017-08-17)", - "seeAlso": [ - "https://github.com/ArtifexSoftware/urw-base35-fonts/blob/65962e27febc3883a17e651cdb23e783668c996f/LICENSE" - ], - "licenseExceptionId": "PS-or-PDF-font-exception-20170817" - }, - { - "reference": "./Qt-GPL-exception-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Qt-GPL-exception-1.0.json", - "referenceNumber": "16", - "name": "Qt GPL exception 1.0", - "seeAlso": [ - "http://code.qt.io/cgit/qt/qtbase.git/tree/LICENSE.GPL3-EXCEPT" - ], - "licenseExceptionId": "Qt-GPL-exception-1.0" - }, - { - "reference": "./LZMA-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LZMA-exception.json", - "referenceNumber": "17", - "name": "LZMA exception", - "seeAlso": [ - "http://nsis.sourceforge.net/Docs/AppendixI.html#I.6" - ], - "licenseExceptionId": "LZMA-exception" - }, - { - "reference": "./freertos-exception-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/freertos-exception-2.0.json", - "referenceNumber": "18", - "name": "FreeRTOS Exception 2.0", - "seeAlso": [ - "https://web.archive.org/web/20060809182744/http://www.freertos.org/a00114.html" - ], - "licenseExceptionId": "freertos-exception-2.0" - }, - { - "reference": "./Qwt-exception-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Qwt-exception-1.0.json", - "referenceNumber": "19", - "name": "Qwt exception 1.0", - "seeAlso": [ - "http://qwt.sourceforge.net/qwtlicense.html" - ], - "licenseExceptionId": "Qwt-exception-1.0" - }, - { - "reference": "./CLISP-exception-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CLISP-exception-2.0.json", - "referenceNumber": "20", - "name": "CLISP exception 2.0", - "seeAlso": [ - "http://sourceforge.net/p/clisp/clisp/ci/default/tree/COPYRIGHT" - ], - "licenseExceptionId": "CLISP-exception-2.0" - }, - { - "reference": "./FLTK-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/FLTK-exception.json", - "referenceNumber": "21", - "name": "FLTK exception", - "seeAlso": [ - "http://www.fltk.org/COPYING.php" - ], - "licenseExceptionId": "FLTK-exception" - }, - { - "reference": "./Bootloader-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Bootloader-exception.json", - "referenceNumber": "22", - "name": "Bootloader Distribution Exception", - "seeAlso": [ - "https://github.com/pyinstaller/pyinstaller/blob/develop/COPYING.txt" - ], - "licenseExceptionId": "Bootloader-exception" - }, - { - "reference": "./Nokia-Qt-exception-1.1.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/Nokia-Qt-exception-1.1.json", - "referenceNumber": "23", - "name": "Nokia Qt LGPL exception 1.1", - "seeAlso": [ - "https://www.keepassx.org/dev/projects/keepassx/repository/revisions/b8dfb9cc4d5133e0f09cd7533d15a4f1c19a40f2/entry/LICENSE.NOKIA-LGPL-EXCEPTION" - ], - "licenseExceptionId": "Nokia-Qt-exception-1.1" - }, - { - "reference": "./LLVM-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LLVM-exception.json", - "referenceNumber": "24", - "name": "LLVM Exception", - "seeAlso": [ - "http://llvm.org/foundation/relicensing/LICENSE.txt" - ], - "licenseExceptionId": "LLVM-exception" - }, - { - "reference": "./WxWindows-exception-3.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/WxWindows-exception-3.1.json", - "referenceNumber": "25", - "name": "WxWindows Library Exception 3.1", - "seeAlso": [ - "http://www.opensource.org/licenses/WXwindows" - ], - "licenseExceptionId": "WxWindows-exception-3.1" - }, - { - "reference": "./DigiRule-FOSS-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/DigiRule-FOSS-exception.json", - "referenceNumber": "26", - "name": "DigiRule FOSS License Exception", - "seeAlso": [ - "http://www.digirulesolutions.com/drupal/foss" - ], - "licenseExceptionId": "DigiRule-FOSS-exception" - }, - { - "reference": "./Swift-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Swift-exception.json", - "referenceNumber": "27", - "name": "Swift Exception", - "seeAlso": [ - "https://swift.org/LICENSE.txt", - "https://github.com/apple/swift-package-manager/blob/7ab2275f447a5eb37497ed63a9340f8a6d1e488b/LICENSE.txt#L205" - ], - "licenseExceptionId": "Swift-exception" - }, - { - "reference": "./GCC-exception-3.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/GCC-exception-3.1.json", - "referenceNumber": "28", - "name": "GCC Runtime Library exception 3.1", - "seeAlso": [ - "http://www.gnu.org/licenses/gcc-exception-3.1.html" - ], - "licenseExceptionId": "GCC-exception-3.1" - }, - { - "reference": "./eCos-exception-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/eCos-exception-2.0.json", - "referenceNumber": "29", - "name": "eCos exception 2.0", - "seeAlso": [ - "http://ecos.sourceware.org/license-overview.html" - ], - "licenseExceptionId": "eCos-exception-2.0" - }, - { - "reference": "./Autoconf-exception-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Autoconf-exception-2.0.json", - "referenceNumber": "30", - "name": "Autoconf exception 2.0", - "seeAlso": [ - "http://ac-archive.sourceforge.net/doc/copyright.html", - "http://ftp.gnu.org/gnu/autoconf/autoconf-2.59.tar.gz" - ], - "licenseExceptionId": "Autoconf-exception-2.0" - }, - { - "reference": "./GPL-CC-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/GPL-CC-1.0.json", - "referenceNumber": "31", - "name": "GPL Cooperation Commitment 1.0", - "seeAlso": [ - "https://github.com/gplcc/gplcc/blob/master/Project/COMMITMENT", - "https://gplcc.github.io/gplcc/Project/README-PROJECT.html" - ], - "licenseExceptionId": "GPL-CC-1.0" - }, - { - "reference": "./Font-exception-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Font-exception-2.0.json", - "referenceNumber": "32", - "name": "Font exception 2.0", - "seeAlso": [ - "http://www.gnu.org/licenses/gpl-faq.html#FontException" - ], - "licenseExceptionId": "Font-exception-2.0" - }, - { - "reference": "./u-boot-exception-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/u-boot-exception-2.0.json", - "referenceNumber": "33", - "name": "U-Boot exception 2.0", - "seeAlso": [ - "http://git.denx.de/?p\u003du-boot.git;a\u003dblob;f\u003dLicenses/Exceptions" - ], - "licenseExceptionId": "u-boot-exception-2.0" - }, - { - "reference": "./GCC-exception-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/GCC-exception-2.0.json", - "referenceNumber": "34", - "name": "GCC Runtime Library exception 2.0", - "seeAlso": [ - "https://gcc.gnu.org/git/?p\u003dgcc.git;a\u003dblob;f\u003dgcc/libgcc1.c;h\u003d762f5143fc6eed57b6797c82710f3538aa52b40b;hb\u003dcb143a3ce4fb417c68f5fa2691a1b1b1053dfba9#l10" - ], - "licenseExceptionId": "GCC-exception-2.0" - }, - { - "reference": "./mif-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/mif-exception.json", - "referenceNumber": "35", - "name": "Macros and Inline Functions Exception", - "seeAlso": [ - "http://www.scs.stanford.edu/histar/src/lib/cppsup/exception", - "http://dev.bertos.org/doxygen/", - "https://www.threadingbuildingblocks.org/licensing" - ], - "licenseExceptionId": "mif-exception" - }, - { - "reference": "./OCaml-LGPL-linking-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OCaml-LGPL-linking-exception.json", - "referenceNumber": "36", - "name": "OCaml LGPL Linking Exception", - "seeAlso": [ - "https://caml.inria.fr/ocaml/license.en.html" - ], - "licenseExceptionId": "OCaml-LGPL-linking-exception" - } - ] -} \ No newline at end of file diff --git a/spdx/file.py b/spdx/file.py deleted file mode 100644 index bab50e2ad..000000000 --- a/spdx/file.py +++ /dev/null @@ -1,249 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 hashlib -import warnings -from enum import Enum, auto -from functools import total_ordering -from typing import Optional - -from spdx import utils -from spdx.checksum import Checksum, ChecksumAlgorithm -from spdx.license import License -from spdx.parsers.builderexceptions import SPDXValueError -from spdx.parsers.loggers import ErrorMessages - - -class FileType(Enum): - SOURCE = auto() - BINARY = auto() - ARCHIVE = auto() - OTHER = auto() - APPLICATION = auto() - AUDIO = auto() - IMAGE = auto() - TEXT = auto() - DOCUMENTATION = auto() - SPDX = auto() - VIDEO = auto() - - -def file_type_from_rdf(rdf_file_type: str) -> FileType: - """e.g. convert fileType_source to FileType.SOURCE""" - file_type_str = rdf_file_type.split("_")[1].upper() - - if file_type_str not in FileType.__members__: - raise SPDXValueError("File:FileType") - - return FileType[file_type_str] - - -def file_type_to_rdf(file_type: FileType) -> str: - """e.g. convert SOURCE to fileType_source""" - return f"fileType_{file_type.name.lower()}" - - -@total_ordering -class File(object): - """ - Represent an SPDX file. - Fields: - - name: File name, str mandatory one. - - spdx_id: Uniquely identify any element in an SPDX document which may be - referenced by other elements. Mandatory, one. Type: str. - - comment: File comment str, Optional zero or one. - - file_types: list of file types. Cardinality 0..* - - checksums: Dict with checksum.ChecksumAlgorithm as key and checksum.Checksum as value, - there must be a SHA1 hash, at least. - - conc_lics: Mandatory one. license.License or utils.NoAssert or utils.SPDXNone. - - licenses_in_file: list of licenses found in file, mandatory one or more. - document.License or utils.SPDXNone or utils.NoAssert. - - document.license or utils.NoAssert or utils.SPDXNone. - - license_comment: Optional. - - copyright: Copyright text, Mandatory one. utils.NoAssert or utils.SPDXNone or str. - - notice: optional One, str. - - contributors: List of strings. - - dependencies: list of file locations. - - artifact_of_project_name: list of project names, possibly empty. - - artifact_of_project_home: list of project home page, possibly empty. - - artifact_of_project_uri: list of project uris, possibly empty. - -attribution_text: optional string. - """ - - def __init__(self, name, spdx_id=None): - self.name = name - self.spdx_id = spdx_id - self.comment = None - self.file_types = [] - self.checksums = {} - self.conc_lics = None - self.licenses_in_file = [] - self.license_comment = None - self.copyright = None - self.notice = None - self.attribution_text = None - self.contributors = [] - self.dependencies = [] - self.artifact_of_project_name = [] - self.artifact_of_project_home = [] - self.artifact_of_project_uri = [] - - def __eq__(self, other): - return isinstance(other, File) and self.name == other.name - - def __lt__(self, other): - return self.name < other.name - - @property - def checksum(self): - """ - Backwards compatibility, return SHA1 checksum. - """ - warnings.warn("This property is deprecated. Use get_checksum instead.") - return self.get_checksum(ChecksumAlgorithm.SHA1) - - @checksum.setter - def checksum(self, value): - """ - Backwards compatibility, set checksum. - """ - warnings.warn("This property is deprecated. Use set_checksum instead.") - if isinstance(value, str): - self.set_checksum(Checksum("SHA1", value)) - elif isinstance(value, Checksum): - self.set_checksum(value) - - def add_lics(self, lics): - self.licenses_in_file.append(lics) - - def add_contrib(self, contrib): - self.contributors.append(contrib) - - def add_depend(self, depend): - self.dependencies.append(depend) - - def add_artifact(self, symbol, value): - """ - Add value as artifact_of_project{symbol}. - """ - symbol = "artifact_of_project_{}".format(symbol) - artifact = getattr(self, symbol) - artifact.append(value) - - def validate(self, messages): - """ - Check that all the fields are valid. - Appends any error messages to messages parameter shall be a ErrorMessages. - """ - messages.push_context(self.name) - self.validate_concluded_license(messages) - self.validate_file_types(messages) - self.validate_checksums(messages) - self.validate_licenses_in_file(messages) - self.validate_copyright(messages) - self.validate_artifacts(messages) - self.validate_spdx_id(messages) - messages.pop_context() - return messages - - def validate_spdx_id(self, messages): - if self.spdx_id is None: - messages.append("File has no SPDX Identifier.") - - return messages - - def validate_copyright(self, messages): - if self.copyright and not isinstance( - self.copyright, - (str, utils.NoAssert, utils.SPDXNone), - ): - messages.append( - "File copyright must be str or unicode or " - "spdx.utils.NoAssert or spdx.utils.SPDXNone" - ) - - return messages - - def validate_artifacts(self, messages): - if len(self.artifact_of_project_home) < max( - len(self.artifact_of_project_uri), len(self.artifact_of_project_name) - ): - messages.append( - "File must have as much artifact of project as uri or homepage" - ) - - return messages - - def validate_licenses_in_file(self, messages): - for license_in_file in self.licenses_in_file: - if not isinstance( - license_in_file, (utils.SPDXNone, utils.NoAssert, License) - ): - messages.append( - "License in file must be instance of " - "spdx.utils.SPDXNone or spdx.utils.NoAssert or " - "spdx.license.License" - ) - - return messages - - def validate_concluded_license(self, messages): - if self.conc_lics and not isinstance( - self.conc_lics, (utils.SPDXNone, utils.NoAssert, License) - ): - messages.append( - "File concluded license must be instance of " - "spdx.utils.SPDXNone or spdx.utils.NoAssert or " - "spdx.license.License" - ) - - return messages - - def validate_file_types(self, messages): - for file_type in self.file_types: - if not isinstance(file_type, FileType): - messages.append(f"{file_type} is not of type FileType.") - return messages - - def validate_checksums(self, messages: ErrorMessages): - for checksum in self.checksums.values(): - if not isinstance(checksum, Checksum): - messages.append("File checksum must be instance of spdx.checksum.Checksum.") - - if self.get_checksum(ChecksumAlgorithm.SHA1) is None: - messages.append("At least one file checksum algorithm must be SHA1") - - def calculate_checksum(self, hash_algorithm='SHA1'): - if hash_algorithm not in ChecksumAlgorithm.__members__: - raise ValueError - BUFFER_SIZE = 65536 - - file_hash = hashlib.new(hash_algorithm.lower()) - with open(self.name, "rb") as file_handle: - while True: - data = file_handle.read(BUFFER_SIZE) - if not data: - break - file_hash.update(data) - - return file_hash.hexdigest() - - def get_checksum(self, hash_algorithm: ChecksumAlgorithm = ChecksumAlgorithm.SHA1) -> Optional[Checksum]: - return self.checksums.get(hash_algorithm) - - def set_checksum(self, new_checksum: Checksum): - if not isinstance(new_checksum, Checksum): - raise SPDXValueError - - self.checksums[new_checksum.identifier] = new_checksum - - def has_optional_field(self, field): - return bool(getattr(self, field, None)) diff --git a/spdx/license.py b/spdx/license.py deleted file mode 100644 index ea973f5fd..000000000 --- a/spdx/license.py +++ /dev/null @@ -1,207 +0,0 @@ -# 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 functools import total_ordering - -from spdx import config - - -@total_ordering -class License(object): - def __init__(self, full_name, identifier): - """if one of the argument is None, we try to map as much as possible - """ - self._full_name = None - self._identifier = None - self.set_full_name(full_name) - self.set_identifier(identifier) - - @classmethod - def from_identifier(cls, identifier): - """If identifier exists in config.LICENSE_MAP - the full_name is retrieved from it. Otherwise - the full_name is the same as the identifier. - """ - return cls(None, identifier) - - @classmethod - def from_full_name(cls, full_name): - """ - Return a new License for a full_name. If the full_name exists in - config.LICENSE_MAP the identifier is retrieved from it. - Otherwise the identifier is the same as the full_name. - """ - return cls(full_name, None) - - @property - def url(self): - return "http://spdx.org/licenses/{0}".format(self.identifier) - - @property - def full_name(self): - return self._full_name - - @full_name.setter - def full_name(self, value): - self.set_full_name(value) - - def set_full_name(self, value): - - if value is None: - return - if self._identifier is None: - if value in config.LICENSE_MAP: - self._identifier = config.LICENSE_MAP[value] - else: - self._identifier = value - self._full_name = value - - @property - def identifier(self): - return self._identifier - - @identifier.setter - def identifier(self, value): - self.set_identifier(value) - - def set_identifier(self, value): - if value is None: - return - if self._full_name is None: - if value in config.LICENSE_MAP: - self._full_name = config.LICENSE_MAP[value] - else: - self._full_name = value - - self._identifier = value - - - def __eq__(self, other): - return ( - isinstance(other, License) - and self.identifier == other.identifier - and self.full_name == other.full_name - ) - - def __lt__(self, other): - return isinstance(other, License) and self.identifier < other.identifier - - def __str__(self): - return self.identifier - - def __hash__(self): - return self.identifier.__hash__() - - -class LicenseConjunction(License): - """ - A conjunction of two licenses. - """ - - def __init__(self, license_1, license_2): - self.license_1 = license_1 - self.license_2 = license_2 - super(LicenseConjunction, self).__init__(self.full_name, self.identifier) - - @property - def full_name(self): - license_1_complex = type(self.license_1) == LicenseDisjunction - license_2_complex = type(self.license_2) == LicenseDisjunction - - return "{0} AND {1}".format( - _add_parens(license_1_complex, self.license_1.full_name), - _add_parens(license_2_complex, self.license_2.full_name), - ) - - @property - def identifier(self): - license_1_complex = type(self.license_1) == LicenseDisjunction - license_2_complex = type(self.license_2) == LicenseDisjunction - - return "{0} AND {1}".format( - _add_parens(license_1_complex, self.license_1.identifier), - _add_parens(license_2_complex, self.license_2.identifier), - ) - - -class LicenseDisjunction(License): - """ - A disjunction of two licenses. - """ - - def __init__(self, license_1, license_2): - self.license_1 = license_1 - self.license_2 = license_2 - super(LicenseDisjunction, self).__init__(self.full_name, self.identifier) - - @property - def full_name(self): - license_1_complex = type(self.license_1) == LicenseConjunction - license_2_complex = type(self.license_2) == LicenseConjunction - - return "{0} OR {1}".format( - _add_parens(license_1_complex, self.license_1.full_name), - _add_parens(license_2_complex, self.license_2.full_name), - ) - - @property - def identifier(self): - license_1_complex = type(self.license_1) == LicenseConjunction - license_2_complex = type(self.license_2) == LicenseConjunction - - return "{0} OR {1}".format( - _add_parens(license_1_complex, self.license_1.identifier), - _add_parens(license_2_complex, self.license_2.identifier), - ) - - -@total_ordering -class ExtractedLicense(License): - """ - Represent an ExtractedLicense with its additional attributes: - - text: Extracted text, str. Mandatory. - - cross_ref: list of cross references. - - comment: license comment, str. - - full_name: license name. str or utils.NoAssert. - """ - - def __init__(self, identifier): - super(ExtractedLicense, self).__init__(None, identifier) - self.text = None - self.cross_ref = [] - self.comment = None - - def __eq__(self, other): - return ( - isinstance(other, ExtractedLicense) - and self.identifier == other.identifier - and self.full_name == other.full_name - ) - - def __lt__(self, other): - return ( - isinstance(other, ExtractedLicense) and self.identifier < other.identifier - ) - - def add_xref(self, ref): - self.cross_ref.append(ref) - - def validate(self, messages): - if self.text is None: - messages.append("ExtractedLicense text can not be None") - - -def _add_parens(required, text): - """ - Add parens around a license expression if `required` is True, otherwise - return `text` unmodified. - """ - return "({})".format(text) if required else text diff --git a/spdx/licenses.json b/spdx/licenses.json deleted file mode 100644 index 10550cbd4..000000000 --- a/spdx/licenses.json +++ /dev/null @@ -1,4974 +0,0 @@ -{ - "licenseListVersion": "3.6", - "licenses": [ - { - "reference": "./0BSD.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/0BSD.json", - "referenceNumber": "319", - "name": "BSD Zero Clause License", - "licenseId": "0BSD", - "seeAlso": [ - "http://landley.net/toybox/license.html" - ], - "isOsiApproved": true - }, - { - "reference": "./AAL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/AAL.json", - "referenceNumber": "21", - "name": "Attribution Assurance License", - "licenseId": "AAL", - "seeAlso": [ - "https://opensource.org/licenses/attribution" - ], - "isOsiApproved": true - }, - { - "reference": "./ADSL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/ADSL.json", - "referenceNumber": "19", - "name": "Amazon Digital Services License", - "licenseId": "ADSL", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/AmazonDigitalServicesLicense" - ], - "isOsiApproved": false - }, - { - "reference": "./AFL-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AFL-1.1.json", - "referenceNumber": "118", - "name": "Academic Free License v1.1", - "licenseId": "AFL-1.1", - "seeAlso": [ - "http://opensource.linux-mirror.org/licenses/afl-1.1.txt", - "http://wayback.archive.org/web/20021004124254/http://www.opensource.org/licenses/academic.php" - ], - "isOsiApproved": true - }, - { - "reference": "./AFL-1.2.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AFL-1.2.json", - "referenceNumber": "136", - "name": "Academic Free License v1.2", - "licenseId": "AFL-1.2", - "seeAlso": [ - "http://opensource.linux-mirror.org/licenses/afl-1.2.txt", - "http://wayback.archive.org/web/20021204204652/http://www.opensource.org/licenses/academic.php" - ], - "isOsiApproved": true - }, - { - "reference": "./AFL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AFL-2.0.json", - "referenceNumber": "115", - "name": "Academic Free License v2.0", - "licenseId": "AFL-2.0", - "seeAlso": [ - "http://wayback.archive.org/web/20060924134533/http://www.opensource.org/licenses/afl-2.0.txt" - ], - "isOsiApproved": true - }, - { - "reference": "./AFL-2.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AFL-2.1.json", - "referenceNumber": "251", - "name": "Academic Free License v2.1", - "licenseId": "AFL-2.1", - "seeAlso": [ - "http://opensource.linux-mirror.org/licenses/afl-2.1.txt" - ], - "isOsiApproved": true - }, - { - "reference": "./AFL-3.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AFL-3.0.json", - "referenceNumber": "216", - "name": "Academic Free License v3.0", - "licenseId": "AFL-3.0", - "seeAlso": [ - "http://www.rosenlaw.com/AFL3.0.htm", - "https://opensource.org/licenses/afl-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./AGPL-1.0.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AGPL-1.0.json", - "referenceNumber": "335", - "name": "Affero General Public License v1.0", - "licenseId": "AGPL-1.0", - "seeAlso": [ - "http://www.affero.org/oagpl.html" - ], - "isOsiApproved": false - }, - { - "reference": "./AGPL-1.0-only.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/AGPL-1.0-only.json", - "referenceNumber": "384", - "name": "Affero General Public License v1.0 only", - "licenseId": "AGPL-1.0-only", - "seeAlso": [ - "http://www.affero.org/oagpl.html" - ], - "isOsiApproved": false - }, - { - "reference": "./AGPL-1.0-or-later.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/AGPL-1.0-or-later.json", - "referenceNumber": "332", - "name": "Affero General Public License v1.0 or later", - "licenseId": "AGPL-1.0-or-later", - "seeAlso": [ - "http://www.affero.org/oagpl.html" - ], - "isOsiApproved": false - }, - { - "reference": "./AGPL-3.0.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AGPL-3.0.json", - "referenceNumber": "229", - "name": "GNU Affero General Public License v3.0", - "licenseId": "AGPL-3.0", - "seeAlso": [ - "https://www.gnu.org/licenses/agpl.txt", - "https://opensource.org/licenses/AGPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./AGPL-3.0-only.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AGPL-3.0-only.json", - "referenceNumber": "95", - "name": "GNU Affero General Public License v3.0 only", - "licenseId": "AGPL-3.0-only", - "seeAlso": [ - "https://www.gnu.org/licenses/agpl.txt", - "https://opensource.org/licenses/AGPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./AGPL-3.0-or-later.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AGPL-3.0-or-later.json", - "referenceNumber": "155", - "name": "GNU Affero General Public License v3.0 or later", - "licenseId": "AGPL-3.0-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/agpl.txt", - "https://opensource.org/licenses/AGPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./AMDPLPA.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/AMDPLPA.json", - "referenceNumber": "33", - "name": "AMD\u0027s plpa_map.c License", - "licenseId": "AMDPLPA", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/AMD_plpa_map_License" - ], - "isOsiApproved": false - }, - { - "reference": "./AML.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/AML.json", - "referenceNumber": "148", - "name": "Apple MIT License", - "licenseId": "AML", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Apple_MIT_License" - ], - "isOsiApproved": false - }, - { - "reference": "./AMPAS.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/AMPAS.json", - "referenceNumber": "191", - "name": "Academy of Motion Picture Arts and Sciences BSD", - "licenseId": "AMPAS", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/BSD#AMPASBSD" - ], - "isOsiApproved": false - }, - { - "reference": "./ANTLR-PD.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/ANTLR-PD.json", - "referenceNumber": "395", - "name": "ANTLR Software Rights Notice", - "licenseId": "ANTLR-PD", - "seeAlso": [ - "http://www.antlr2.org/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./APAFML.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/APAFML.json", - "referenceNumber": "195", - "name": "Adobe Postscript AFM License", - "licenseId": "APAFML", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/AdobePostscriptAFM" - ], - "isOsiApproved": false - }, - { - "reference": "./APL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/APL-1.0.json", - "referenceNumber": "252", - "name": "Adaptive Public License 1.0", - "licenseId": "APL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/APL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./APSL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/APSL-1.0.json", - "referenceNumber": "354", - "name": "Apple Public Source License 1.0", - "licenseId": "APSL-1.0", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Apple_Public_Source_License_1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./APSL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/APSL-1.1.json", - "referenceNumber": "324", - "name": "Apple Public Source License 1.1", - "licenseId": "APSL-1.1", - "seeAlso": [ - "http://www.opensource.apple.com/source/IOSerialFamily/IOSerialFamily-7/APPLE_LICENSE" - ], - "isOsiApproved": true - }, - { - "reference": "./APSL-1.2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/APSL-1.2.json", - "referenceNumber": "34", - "name": "Apple Public Source License 1.2", - "licenseId": "APSL-1.2", - "seeAlso": [ - "http://www.samurajdata.se/opensource/mirror/licenses/apsl.php" - ], - "isOsiApproved": true - }, - { - "reference": "./APSL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/APSL-2.0.json", - "referenceNumber": "109", - "name": "Apple Public Source License 2.0", - "licenseId": "APSL-2.0", - "seeAlso": [ - "http://www.opensource.apple.com/license/apsl/" - ], - "isOsiApproved": true - }, - { - "reference": "./Abstyles.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Abstyles.json", - "referenceNumber": "80", - "name": "Abstyles License", - "licenseId": "Abstyles", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Abstyles" - ], - "isOsiApproved": false - }, - { - "reference": "./Adobe-2006.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Adobe-2006.json", - "referenceNumber": "285", - "name": "Adobe Systems Incorporated Source Code License Agreement", - "licenseId": "Adobe-2006", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/AdobeLicense" - ], - "isOsiApproved": false - }, - { - "reference": "./Adobe-Glyph.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Adobe-Glyph.json", - "referenceNumber": "107", - "name": "Adobe Glyph List License", - "licenseId": "Adobe-Glyph", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/MIT#AdobeGlyph" - ], - "isOsiApproved": false - }, - { - "reference": "./Afmparse.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Afmparse.json", - "referenceNumber": "42", - "name": "Afmparse License", - "licenseId": "Afmparse", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Afmparse" - ], - "isOsiApproved": false - }, - { - "reference": "./Aladdin.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Aladdin.json", - "referenceNumber": "258", - "name": "Aladdin Free Public License", - "licenseId": "Aladdin", - "seeAlso": [ - "http://pages.cs.wisc.edu/~ghost/doc/AFPL/6.01/Public.htm" - ], - "isOsiApproved": false - }, - { - "reference": "./Apache-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Apache-1.0.json", - "referenceNumber": "237", - "name": "Apache License 1.0", - "licenseId": "Apache-1.0", - "seeAlso": [ - "http://www.apache.org/licenses/LICENSE-1.0" - ], - "isOsiApproved": false - }, - { - "reference": "./Apache-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Apache-1.1.json", - "referenceNumber": "84", - "name": "Apache License 1.1", - "licenseId": "Apache-1.1", - "seeAlso": [ - "http://apache.org/licenses/LICENSE-1.1", - "https://opensource.org/licenses/Apache-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./Apache-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Apache-2.0.json", - "referenceNumber": "26", - "name": "Apache License 2.0", - "licenseId": "Apache-2.0", - "seeAlso": [ - "http://www.apache.org/licenses/LICENSE-2.0", - "https://opensource.org/licenses/Apache-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Artistic-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Artistic-1.0.json", - "referenceNumber": "165", - "name": "Artistic License 1.0", - "licenseId": "Artistic-1.0", - "seeAlso": [ - "https://opensource.org/licenses/Artistic-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Artistic-1.0-Perl.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Artistic-1.0-Perl.json", - "referenceNumber": "377", - "name": "Artistic License 1.0 (Perl)", - "licenseId": "Artistic-1.0-Perl", - "seeAlso": [ - "http://dev.perl.org/licenses/artistic.html" - ], - "isOsiApproved": true - }, - { - "reference": "./Artistic-1.0-cl8.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Artistic-1.0-cl8.json", - "referenceNumber": "13", - "name": "Artistic License 1.0 w/clause 8", - "licenseId": "Artistic-1.0-cl8", - "seeAlso": [ - "https://opensource.org/licenses/Artistic-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Artistic-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Artistic-2.0.json", - "referenceNumber": "189", - "name": "Artistic License 2.0", - "licenseId": "Artistic-2.0", - "seeAlso": [ - "http://www.perlfoundation.org/artistic_license_2_0", - "https://opensource.org/licenses/artistic-license-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./BSD-1-Clause.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-1-Clause.json", - "referenceNumber": "358", - "name": "BSD 1-Clause License", - "licenseId": "BSD-1-Clause", - "seeAlso": [ - "https://svnweb.freebsd.org/base/head/include/ifaddrs.h?revision\u003d326823" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-2-Clause.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-2-Clause.json", - "referenceNumber": "325", - "name": "BSD 2-Clause \"Simplified\" License", - "licenseId": "BSD-2-Clause", - "seeAlso": [ - "https://opensource.org/licenses/BSD-2-Clause" - ], - "isOsiApproved": true - }, - { - "reference": "./BSD-2-Clause-FreeBSD.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/BSD-2-Clause-FreeBSD.json", - "referenceNumber": "121", - "name": "BSD 2-Clause FreeBSD License", - "licenseId": "BSD-2-Clause-FreeBSD", - "seeAlso": [ - "http://www.freebsd.org/copyright/freebsd-license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-2-Clause-NetBSD.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-2-Clause-NetBSD.json", - "referenceNumber": "381", - "name": "BSD 2-Clause NetBSD License", - "licenseId": "BSD-2-Clause-NetBSD", - "seeAlso": [ - "http://www.netbsd.org/about/redistribution.html#default" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-2-Clause-Patent.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-2-Clause-Patent.json", - "referenceNumber": "169", - "name": "BSD-2-Clause Plus Patent License", - "licenseId": "BSD-2-Clause-Patent", - "seeAlso": [ - "https://opensource.org/licenses/BSDplusPatent" - ], - "isOsiApproved": true - }, - { - "reference": "./BSD-3-Clause.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause.json", - "referenceNumber": "270", - "name": "BSD 3-Clause \"New\" or \"Revised\" License", - "licenseId": "BSD-3-Clause", - "seeAlso": [ - "https://opensource.org/licenses/BSD-3-Clause" - ], - "isOsiApproved": true - }, - { - "reference": "./BSD-3-Clause-Attribution.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-Attribution.json", - "referenceNumber": "39", - "name": "BSD with attribution", - "licenseId": "BSD-3-Clause-Attribution", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/BSD_with_Attribution" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-3-Clause-Clear.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-Clear.json", - "referenceNumber": "212", - "name": "BSD 3-Clause Clear License", - "licenseId": "BSD-3-Clause-Clear", - "seeAlso": [ - "http://labs.metacarta.com/license-explanation.html#license" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-3-Clause-LBNL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-LBNL.json", - "referenceNumber": "337", - "name": "Lawrence Berkeley National Labs BSD variant license", - "licenseId": "BSD-3-Clause-LBNL", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/LBNLBSD" - ], - "isOsiApproved": true - }, - { - "reference": "./BSD-3-Clause-No-Nuclear-License.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.json", - "referenceNumber": "12", - "name": "BSD 3-Clause No Nuclear License", - "licenseId": "BSD-3-Clause-No-Nuclear-License", - "seeAlso": [ - "http://download.oracle.com/otn-pub/java/licenses/bsd.txt?AuthParam\u003d1467140197_43d516ce1776bd08a58235a7785be1cc" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-3-Clause-No-Nuclear-License-2014.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.json", - "referenceNumber": "137", - "name": "BSD 3-Clause No Nuclear License 2014", - "licenseId": "BSD-3-Clause-No-Nuclear-License-2014", - "seeAlso": [ - "https://java.net/projects/javaeetutorial/pages/BerkeleyLicense" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-3-Clause-No-Nuclear-Warranty.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.json", - "referenceNumber": "44", - "name": "BSD 3-Clause No Nuclear Warranty", - "licenseId": "BSD-3-Clause-No-Nuclear-Warranty", - "seeAlso": [ - "https://jogamp.org/git/?p\u003dgluegen.git;a\u003dblob_plain;f\u003dLICENSE.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-3-Clause-Open-MPI.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-Open-MPI.json", - "referenceNumber": "349", - "name": "BSD 3-Clause Open MPI variant", - "licenseId": "BSD-3-Clause-Open-MPI", - "seeAlso": [ - "https://www.open-mpi.org/community/license.php", - "http://www.netlib.org/lapack/LICENSE.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-4-Clause.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/BSD-4-Clause.json", - "referenceNumber": "162", - "name": "BSD 4-Clause \"Original\" or \"Old\" License", - "licenseId": "BSD-4-Clause", - "seeAlso": [ - "http://directory.fsf.org/wiki/License:BSD_4Clause" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-4-Clause-UC.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-4-Clause-UC.json", - "referenceNumber": "203", - "name": "BSD-4-Clause (University of California-Specific)", - "licenseId": "BSD-4-Clause-UC", - "seeAlso": [ - "http://www.freebsd.org/copyright/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-Protection.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-Protection.json", - "referenceNumber": "119", - "name": "BSD Protection License", - "licenseId": "BSD-Protection", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/BSD_Protection_License" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-Source-Code.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-Source-Code.json", - "referenceNumber": "308", - "name": "BSD Source Code Attribution", - "licenseId": "BSD-Source-Code", - "seeAlso": [ - "https://github.com/robbiehanson/CocoaHTTPServer/blob/master/LICENSE.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./BSL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/BSL-1.0.json", - "referenceNumber": "224", - "name": "Boost Software License 1.0", - "licenseId": "BSL-1.0", - "seeAlso": [ - "http://www.boost.org/LICENSE_1_0.txt", - "https://opensource.org/licenses/BSL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Bahyph.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Bahyph.json", - "referenceNumber": "366", - "name": "Bahyph License", - "licenseId": "Bahyph", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Bahyph" - ], - "isOsiApproved": false - }, - { - "reference": "./Barr.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Barr.json", - "referenceNumber": "333", - "name": "Barr License", - "licenseId": "Barr", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Barr" - ], - "isOsiApproved": false - }, - { - "reference": "./Beerware.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Beerware.json", - "referenceNumber": "17", - "name": "Beerware License", - "licenseId": "Beerware", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Beerware", - "https://people.freebsd.org/~phk/" - ], - "isOsiApproved": false - }, - { - "reference": "./BitTorrent-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BitTorrent-1.0.json", - "referenceNumber": "218", - "name": "BitTorrent Open Source License v1.0", - "licenseId": "BitTorrent-1.0", - "seeAlso": [ - "http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/licenses/BitTorrent?r1\u003d1.1\u0026r2\u003d1.1.1.1\u0026diff_format\u003ds" - ], - "isOsiApproved": false - }, - { - "reference": "./BitTorrent-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/BitTorrent-1.1.json", - "referenceNumber": "179", - "name": "BitTorrent Open Source License v1.1", - "licenseId": "BitTorrent-1.1", - "seeAlso": [ - "http://directory.fsf.org/wiki/License:BitTorrentOSL1.1" - ], - "isOsiApproved": false - }, - { - "reference": "./BlueOak-1.0.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BlueOak-1.0.0.json", - "referenceNumber": "23", - "name": "Blue Oak Model License 1.0.0", - "licenseId": "BlueOak-1.0.0", - "seeAlso": [ - "https://blueoakcouncil.org/license/1.0.0" - ], - "isOsiApproved": false - }, - { - "reference": "./Borceux.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Borceux.json", - "referenceNumber": "311", - "name": "Borceux license", - "licenseId": "Borceux", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Borceux" - ], - "isOsiApproved": false - }, - { - "reference": "./CATOSL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CATOSL-1.1.json", - "referenceNumber": "262", - "name": "Computer Associates Trusted Open Source License 1.1", - "licenseId": "CATOSL-1.1", - "seeAlso": [ - "https://opensource.org/licenses/CATOSL-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./CC-BY-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-1.0.json", - "referenceNumber": "128", - "name": "Creative Commons Attribution 1.0 Generic", - "licenseId": "CC-BY-1.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by/1.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-2.0.json", - "referenceNumber": "232", - "name": "Creative Commons Attribution 2.0 Generic", - "licenseId": "CC-BY-2.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by/2.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-2.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-2.5.json", - "referenceNumber": "129", - "name": "Creative Commons Attribution 2.5 Generic", - "licenseId": "CC-BY-2.5", - "seeAlso": [ - "https://creativecommons.org/licenses/by/2.5/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-3.0.json", - "referenceNumber": "256", - "name": "Creative Commons Attribution 3.0 Unported", - "licenseId": "CC-BY-3.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by/3.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-4.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CC-BY-4.0.json", - "referenceNumber": "330", - "name": "Creative Commons Attribution 4.0 International", - "licenseId": "CC-BY-4.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by/4.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-1.0.json", - "referenceNumber": "130", - "name": "Creative Commons Attribution Non Commercial 1.0 Generic", - "licenseId": "CC-BY-NC-1.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc/1.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-2.0.json", - "referenceNumber": "244", - "name": "Creative Commons Attribution Non Commercial 2.0 Generic", - "licenseId": "CC-BY-NC-2.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc/2.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-2.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-2.5.json", - "referenceNumber": "1", - "name": "Creative Commons Attribution Non Commercial 2.5 Generic", - "licenseId": "CC-BY-NC-2.5", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc/2.5/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-3.0.json", - "referenceNumber": "255", - "name": "Creative Commons Attribution Non Commercial 3.0 Unported", - "licenseId": "CC-BY-NC-3.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc/3.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-4.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-4.0.json", - "referenceNumber": "186", - "name": "Creative Commons Attribution Non Commercial 4.0 International", - "licenseId": "CC-BY-NC-4.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc/4.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-ND-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-1.0.json", - "referenceNumber": "59", - "name": "Creative Commons Attribution Non Commercial No Derivatives 1.0 Generic", - "licenseId": "CC-BY-NC-ND-1.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nd-nc/1.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-ND-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-2.0.json", - "referenceNumber": "36", - "name": "Creative Commons Attribution Non Commercial No Derivatives 2.0 Generic", - "licenseId": "CC-BY-NC-ND-2.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-nd/2.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-ND-2.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-2.5.json", - "referenceNumber": "158", - "name": "Creative Commons Attribution Non Commercial No Derivatives 2.5 Generic", - "licenseId": "CC-BY-NC-ND-2.5", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-nd/2.5/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-ND-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-3.0.json", - "referenceNumber": "48", - "name": "Creative Commons Attribution Non Commercial No Derivatives 3.0 Unported", - "licenseId": "CC-BY-NC-ND-3.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-nd/3.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-ND-4.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-4.0.json", - "referenceNumber": "281", - "name": "Creative Commons Attribution Non Commercial No Derivatives 4.0 International", - "licenseId": "CC-BY-NC-ND-4.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-SA-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-1.0.json", - "referenceNumber": "178", - "name": "Creative Commons Attribution Non Commercial Share Alike 1.0 Generic", - "licenseId": "CC-BY-NC-SA-1.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-sa/1.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-SA-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-2.0.json", - "referenceNumber": "81", - "name": "Creative Commons Attribution Non Commercial Share Alike 2.0 Generic", - "licenseId": "CC-BY-NC-SA-2.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-sa/2.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-SA-2.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-2.5.json", - "referenceNumber": "62", - "name": "Creative Commons Attribution Non Commercial Share Alike 2.5 Generic", - "licenseId": "CC-BY-NC-SA-2.5", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-sa/2.5/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-SA-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-3.0.json", - "referenceNumber": "22", - "name": "Creative Commons Attribution Non Commercial Share Alike 3.0 Unported", - "licenseId": "CC-BY-NC-SA-3.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-sa/3.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-SA-4.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-4.0.json", - "referenceNumber": "47", - "name": "Creative Commons Attribution Non Commercial Share Alike 4.0 International", - "licenseId": "CC-BY-NC-SA-4.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-ND-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-1.0.json", - "referenceNumber": "50", - "name": "Creative Commons Attribution No Derivatives 1.0 Generic", - "licenseId": "CC-BY-ND-1.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nd/1.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-ND-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-2.0.json", - "referenceNumber": "287", - "name": "Creative Commons Attribution No Derivatives 2.0 Generic", - "licenseId": "CC-BY-ND-2.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nd/2.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-ND-2.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-2.5.json", - "referenceNumber": "68", - "name": "Creative Commons Attribution No Derivatives 2.5 Generic", - "licenseId": "CC-BY-ND-2.5", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nd/2.5/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-ND-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-3.0.json", - "referenceNumber": "393", - "name": "Creative Commons Attribution No Derivatives 3.0 Unported", - "licenseId": "CC-BY-ND-3.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nd/3.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-ND-4.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-4.0.json", - "referenceNumber": "132", - "name": "Creative Commons Attribution No Derivatives 4.0 International", - "licenseId": "CC-BY-ND-4.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nd/4.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-SA-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-1.0.json", - "referenceNumber": "322", - "name": "Creative Commons Attribution Share Alike 1.0 Generic", - "licenseId": "CC-BY-SA-1.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-sa/1.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-SA-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-2.0.json", - "referenceNumber": "142", - "name": "Creative Commons Attribution Share Alike 2.0 Generic", - "licenseId": "CC-BY-SA-2.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-sa/2.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-SA-2.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-2.5.json", - "referenceNumber": "306", - "name": "Creative Commons Attribution Share Alike 2.5 Generic", - "licenseId": "CC-BY-SA-2.5", - "seeAlso": [ - "https://creativecommons.org/licenses/by-sa/2.5/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-SA-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-3.0.json", - "referenceNumber": "394", - "name": "Creative Commons Attribution Share Alike 3.0 Unported", - "licenseId": "CC-BY-SA-3.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-sa/3.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-SA-4.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-4.0.json", - "referenceNumber": "32", - "name": "Creative Commons Attribution Share Alike 4.0 International", - "licenseId": "CC-BY-SA-4.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-sa/4.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-PDDC.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-PDDC.json", - "referenceNumber": "371", - "name": "Creative Commons Public Domain Dedication and Certification", - "licenseId": "CC-PDDC", - "seeAlso": [ - "https://creativecommons.org/licenses/publicdomain/" - ], - "isOsiApproved": false - }, - { - "reference": "./CC0-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CC0-1.0.json", - "referenceNumber": "213", - "name": "Creative Commons Zero v1.0 Universal", - "licenseId": "CC0-1.0", - "seeAlso": [ - "https://creativecommons.org/publicdomain/zero/1.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CDDL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CDDL-1.0.json", - "referenceNumber": "138", - "name": "Common Development and Distribution License 1.0", - "licenseId": "CDDL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/cddl1" - ], - "isOsiApproved": true - }, - { - "reference": "./CDDL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CDDL-1.1.json", - "referenceNumber": "376", - "name": "Common Development and Distribution License 1.1", - "licenseId": "CDDL-1.1", - "seeAlso": [ - "http://glassfish.java.net/public/CDDL+GPL_1_1.html", - "https://javaee.github.io/glassfish/LICENSE" - ], - "isOsiApproved": false - }, - { - "reference": "./CDLA-Permissive-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CDLA-Permissive-1.0.json", - "referenceNumber": "250", - "name": "Community Data License Agreement Permissive 1.0", - "licenseId": "CDLA-Permissive-1.0", - "seeAlso": [ - "https://cdla.io/permissive-1-0" - ], - "isOsiApproved": false - }, - { - "reference": "./CDLA-Sharing-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CDLA-Sharing-1.0.json", - "referenceNumber": "310", - "name": "Community Data License Agreement Sharing 1.0", - "licenseId": "CDLA-Sharing-1.0", - "seeAlso": [ - "https://cdla.io/sharing-1-0" - ], - "isOsiApproved": false - }, - { - "reference": "./CECILL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CECILL-1.0.json", - "referenceNumber": "223", - "name": "CeCILL Free Software License Agreement v1.0", - "licenseId": "CECILL-1.0", - "seeAlso": [ - "http://www.cecill.info/licences/Licence_CeCILL_V1-fr.html" - ], - "isOsiApproved": false - }, - { - "reference": "./CECILL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CECILL-1.1.json", - "referenceNumber": "300", - "name": "CeCILL Free Software License Agreement v1.1", - "licenseId": "CECILL-1.1", - "seeAlso": [ - "http://www.cecill.info/licences/Licence_CeCILL_V1.1-US.html" - ], - "isOsiApproved": false - }, - { - "reference": "./CECILL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CECILL-2.0.json", - "referenceNumber": "352", - "name": "CeCILL Free Software License Agreement v2.0", - "licenseId": "CECILL-2.0", - "seeAlso": [ - "http://www.cecill.info/licences/Licence_CeCILL_V2-en.html" - ], - "isOsiApproved": false - }, - { - "reference": "./CECILL-2.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CECILL-2.1.json", - "referenceNumber": "120", - "name": "CeCILL Free Software License Agreement v2.1", - "licenseId": "CECILL-2.1", - "seeAlso": [ - "http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.html" - ], - "isOsiApproved": true - }, - { - "reference": "./CECILL-B.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CECILL-B.json", - "referenceNumber": "340", - "name": "CeCILL-B Free Software License Agreement", - "licenseId": "CECILL-B", - "seeAlso": [ - "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html" - ], - "isOsiApproved": false - }, - { - "reference": "./CECILL-C.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CECILL-C.json", - "referenceNumber": "77", - "name": "CeCILL-C Free Software License Agreement", - "licenseId": "CECILL-C", - "seeAlso": [ - "http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html" - ], - "isOsiApproved": false - }, - { - "reference": "./CERN-OHL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CERN-OHL-1.1.json", - "referenceNumber": "341", - "name": "CERN Open Hardware License v1.1", - "licenseId": "CERN-OHL-1.1", - "seeAlso": [ - "https://www.ohwr.org/project/licenses/wikis/cern-ohl-v1.1" - ], - "isOsiApproved": false - }, - { - "reference": "./CERN-OHL-1.2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CERN-OHL-1.2.json", - "referenceNumber": "3", - "name": "CERN Open Hardware Licence v1.2", - "licenseId": "CERN-OHL-1.2", - "seeAlso": [ - "https://www.ohwr.org/project/licenses/wikis/cern-ohl-v1.2" - ], - "isOsiApproved": false - }, - { - "reference": "./CNRI-Jython.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CNRI-Jython.json", - "referenceNumber": "94", - "name": "CNRI Jython License", - "licenseId": "CNRI-Jython", - "seeAlso": [ - "http://www.jython.org/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./CNRI-Python.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CNRI-Python.json", - "referenceNumber": "45", - "name": "CNRI Python License", - "licenseId": "CNRI-Python", - "seeAlso": [ - "https://opensource.org/licenses/CNRI-Python" - ], - "isOsiApproved": true - }, - { - "reference": "./CNRI-Python-GPL-Compatible.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CNRI-Python-GPL-Compatible.json", - "referenceNumber": "202", - "name": "CNRI Python Open Source GPL Compatible License Agreement", - "licenseId": "CNRI-Python-GPL-Compatible", - "seeAlso": [ - "http://www.python.org/download/releases/1.6.1/download_win/" - ], - "isOsiApproved": false - }, - { - "reference": "./CPAL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CPAL-1.0.json", - "referenceNumber": "170", - "name": "Common Public Attribution License 1.0", - "licenseId": "CPAL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/CPAL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./CPL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CPL-1.0.json", - "referenceNumber": "172", - "name": "Common Public License 1.0", - "licenseId": "CPL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/CPL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./CPOL-1.02.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CPOL-1.02.json", - "referenceNumber": "28", - "name": "Code Project Open License 1.02", - "licenseId": "CPOL-1.02", - "seeAlso": [ - "http://www.codeproject.com/info/cpol10.aspx" - ], - "isOsiApproved": false - }, - { - "reference": "./CUA-OPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CUA-OPL-1.0.json", - "referenceNumber": "365", - "name": "CUA Office Public License v1.0", - "licenseId": "CUA-OPL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/CUA-OPL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Caldera.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Caldera.json", - "referenceNumber": "108", - "name": "Caldera License", - "licenseId": "Caldera", - "seeAlso": [ - "http://www.lemis.com/grog/UNIX/ancient-source-all.pdf" - ], - "isOsiApproved": false - }, - { - "reference": "./ClArtistic.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/ClArtistic.json", - "referenceNumber": "271", - "name": "Clarified Artistic License", - "licenseId": "ClArtistic", - "seeAlso": [ - "http://gianluca.dellavedova.org/2011/01/03/clarified-artistic-license/", - "http://www.ncftp.com/ncftp/doc/LICENSE.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./Condor-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Condor-1.1.json", - "referenceNumber": "307", - "name": "Condor Public License v1.1", - "licenseId": "Condor-1.1", - "seeAlso": [ - "http://research.cs.wisc.edu/condor/license.html#condor", - "http://web.archive.org/web/20111123062036/http://research.cs.wisc.edu/condor/license.html#condor" - ], - "isOsiApproved": false - }, - { - "reference": "./Crossword.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Crossword.json", - "referenceNumber": "363", - "name": "Crossword License", - "licenseId": "Crossword", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Crossword" - ], - "isOsiApproved": false - }, - { - "reference": "./CrystalStacker.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CrystalStacker.json", - "referenceNumber": "168", - "name": "CrystalStacker License", - "licenseId": "CrystalStacker", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing:CrystalStacker?rd\u003dLicensing/CrystalStacker" - ], - "isOsiApproved": false - }, - { - "reference": "./Cube.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Cube.json", - "referenceNumber": "370", - "name": "Cube License", - "licenseId": "Cube", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Cube" - ], - "isOsiApproved": false - }, - { - "reference": "./D-FSL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/D-FSL-1.0.json", - "referenceNumber": "182", - "name": "Deutsche Freie Software Lizenz", - "licenseId": "D-FSL-1.0", - "seeAlso": [ - "http://www.dipp.nrw.de/d-fsl/lizenzen/", - "http://www.dipp.nrw.de/d-fsl/index_html/lizenzen/de/D-FSL-1_0_de.txt", - "http://www.dipp.nrw.de/d-fsl/index_html/lizenzen/en/D-FSL-1_0_en.txt", - "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl", - "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/deutsche-freie-software-lizenz", - "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/german-free-software-license", - "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/D-FSL-1_0_de.txt/at_download/file", - "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/D-FSL-1_0_en.txt/at_download/file" - ], - "isOsiApproved": false - }, - { - "reference": "./DOC.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/DOC.json", - "referenceNumber": "160", - "name": "DOC License", - "licenseId": "DOC", - "seeAlso": [ - "http://www.cs.wustl.edu/~schmidt/ACE-copying.html" - ], - "isOsiApproved": false - }, - { - "reference": "./DSDP.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/DSDP.json", - "referenceNumber": "141", - "name": "DSDP License", - "licenseId": "DSDP", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/DSDP" - ], - "isOsiApproved": false - }, - { - "reference": "./Dotseqn.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Dotseqn.json", - "referenceNumber": "390", - "name": "Dotseqn License", - "licenseId": "Dotseqn", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Dotseqn" - ], - "isOsiApproved": false - }, - { - "reference": "./ECL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/ECL-1.0.json", - "referenceNumber": "396", - "name": "Educational Community License v1.0", - "licenseId": "ECL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/ECL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./ECL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/ECL-2.0.json", - "referenceNumber": "298", - "name": "Educational Community License v2.0", - "licenseId": "ECL-2.0", - "seeAlso": [ - "https://opensource.org/licenses/ECL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./EFL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/EFL-1.0.json", - "referenceNumber": "150", - "name": "Eiffel Forum License v1.0", - "licenseId": "EFL-1.0", - "seeAlso": [ - "http://www.eiffel-nice.org/license/forum.txt", - "https://opensource.org/licenses/EFL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./EFL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/EFL-2.0.json", - "referenceNumber": "161", - "name": "Eiffel Forum License v2.0", - "licenseId": "EFL-2.0", - "seeAlso": [ - "http://www.eiffel-nice.org/license/eiffel-forum-license-2.html", - "https://opensource.org/licenses/EFL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./EPL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/EPL-1.0.json", - "referenceNumber": "214", - "name": "Eclipse Public License 1.0", - "licenseId": "EPL-1.0", - "seeAlso": [ - "http://www.eclipse.org/legal/epl-v10.html", - "https://opensource.org/licenses/EPL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./EPL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/EPL-2.0.json", - "referenceNumber": "134", - "name": "Eclipse Public License 2.0", - "licenseId": "EPL-2.0", - "seeAlso": [ - "https://www.eclipse.org/legal/epl-2.0", - "https://www.opensource.org/licenses/EPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./EUDatagrid.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/EUDatagrid.json", - "referenceNumber": "192", - "name": "EU DataGrid Software License", - "licenseId": "EUDatagrid", - "seeAlso": [ - "http://eu-datagrid.web.cern.ch/eu-datagrid/license.html", - "https://opensource.org/licenses/EUDatagrid" - ], - "isOsiApproved": true - }, - { - "reference": "./EUPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/EUPL-1.0.json", - "referenceNumber": "173", - "name": "European Union Public License 1.0", - "licenseId": "EUPL-1.0", - "seeAlso": [ - "http://ec.europa.eu/idabc/en/document/7330.html", - "http://ec.europa.eu/idabc/servlets/Doc027f.pdf?id\u003d31096" - ], - "isOsiApproved": false - }, - { - "reference": "./EUPL-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/EUPL-1.1.json", - "referenceNumber": "92", - "name": "European Union Public License 1.1", - "licenseId": "EUPL-1.1", - "seeAlso": [ - "https://joinup.ec.europa.eu/software/page/eupl/licence-eupl", - "https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl1.1.-licence-en_0.pdf", - "https://opensource.org/licenses/EUPL-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./EUPL-1.2.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/EUPL-1.2.json", - "referenceNumber": "387", - "name": "European Union Public License 1.2", - "licenseId": "EUPL-1.2", - "seeAlso": [ - "https://joinup.ec.europa.eu/page/eupl-text-11-12", - "https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl_v1.2_en.pdf", - "https://joinup.ec.europa.eu/sites/default/files/inline-files/EUPL%20v1_2%20EN(1).txt", - "http://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri\u003dCELEX:32017D0863", - "https://opensource.org/licenses/EUPL-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./Entessa.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Entessa.json", - "referenceNumber": "99", - "name": "Entessa Public License v1.0", - "licenseId": "Entessa", - "seeAlso": [ - "https://opensource.org/licenses/Entessa" - ], - "isOsiApproved": true - }, - { - "reference": "./ErlPL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/ErlPL-1.1.json", - "referenceNumber": "157", - "name": "Erlang Public License v1.1", - "licenseId": "ErlPL-1.1", - "seeAlso": [ - "http://www.erlang.org/EPLICENSE" - ], - "isOsiApproved": false - }, - { - "reference": "./Eurosym.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Eurosym.json", - "referenceNumber": "113", - "name": "Eurosym License", - "licenseId": "Eurosym", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Eurosym" - ], - "isOsiApproved": false - }, - { - "reference": "./FSFAP.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/FSFAP.json", - "referenceNumber": "114", - "name": "FSF All Permissive License", - "licenseId": "FSFAP", - "seeAlso": [ - "https://www.gnu.org/prep/maintain/html_node/License-Notices-for-Other-Files.html" - ], - "isOsiApproved": false - }, - { - "reference": "./FSFUL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/FSFUL.json", - "referenceNumber": "193", - "name": "FSF Unlimited License", - "licenseId": "FSFUL", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/FSF_Unlimited_License" - ], - "isOsiApproved": false - }, - { - "reference": "./FSFULLR.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/FSFULLR.json", - "referenceNumber": "43", - "name": "FSF Unlimited License (with License Retention)", - "licenseId": "FSFULLR", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/FSF_Unlimited_License#License_Retention_Variant" - ], - "isOsiApproved": false - }, - { - "reference": "./FTL.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/FTL.json", - "referenceNumber": "240", - "name": "Freetype Project License", - "licenseId": "FTL", - "seeAlso": [ - "http://freetype.fis.uniroma2.it/FTL.TXT", - "http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT" - ], - "isOsiApproved": false - }, - { - "reference": "./Fair.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Fair.json", - "referenceNumber": "297", - "name": "Fair License", - "licenseId": "Fair", - "seeAlso": [ - "http://fairlicense.org/", - "https://opensource.org/licenses/Fair" - ], - "isOsiApproved": true - }, - { - "reference": "./Frameworx-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Frameworx-1.0.json", - "referenceNumber": "389", - "name": "Frameworx Open License 1.0", - "licenseId": "Frameworx-1.0", - "seeAlso": [ - "https://opensource.org/licenses/Frameworx-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./FreeImage.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/FreeImage.json", - "referenceNumber": "277", - "name": "FreeImage Public License v1.0", - "licenseId": "FreeImage", - "seeAlso": [ - "http://freeimage.sourceforge.net/freeimage-license.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.1.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.1.json", - "referenceNumber": "98", - "name": "GNU Free Documentation License v1.1", - "licenseId": "GFDL-1.1", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.1-only.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.1-only.json", - "referenceNumber": "102", - "name": "GNU Free Documentation License v1.1 only", - "licenseId": "GFDL-1.1-only", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.1-or-later.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.1-or-later.json", - "referenceNumber": "348", - "name": "GNU Free Documentation License v1.1 or later", - "licenseId": "GFDL-1.1-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.2.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.2.json", - "referenceNumber": "197", - "name": "GNU Free Documentation License v1.2", - "licenseId": "GFDL-1.2", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.2-only.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.2-only.json", - "referenceNumber": "236", - "name": "GNU Free Documentation License v1.2 only", - "licenseId": "GFDL-1.2-only", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.2-or-later.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.2-or-later.json", - "referenceNumber": "215", - "name": "GNU Free Documentation License v1.2 or later", - "licenseId": "GFDL-1.2-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.3.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.3.json", - "referenceNumber": "112", - "name": "GNU Free Documentation License v1.3", - "licenseId": "GFDL-1.3", - "seeAlso": [ - "https://www.gnu.org/licenses/fdl-1.3.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.3-only.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.3-only.json", - "referenceNumber": "69", - "name": "GNU Free Documentation License v1.3 only", - "licenseId": "GFDL-1.3-only", - "seeAlso": [ - "https://www.gnu.org/licenses/fdl-1.3.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.3-or-later.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.3-or-later.json", - "referenceNumber": "4", - "name": "GNU Free Documentation License v1.3 or later", - "licenseId": "GFDL-1.3-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/fdl-1.3.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GL2PS.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/GL2PS.json", - "referenceNumber": "124", - "name": "GL2PS License", - "licenseId": "GL2PS", - "seeAlso": [ - "http://www.geuz.org/gl2ps/COPYING.GL2PS" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-1.0.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-1.0.json", - "referenceNumber": "79", - "name": "GNU General Public License v1.0 only", - "licenseId": "GPL-1.0", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-1.0+.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-1.0+.json", - "referenceNumber": "175", - "name": "GNU General Public License v1.0 or later", - "licenseId": "GPL-1.0+", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-1.0-only.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/GPL-1.0-only.json", - "referenceNumber": "15", - "name": "GNU General Public License v1.0 only", - "licenseId": "GPL-1.0-only", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-1.0-or-later.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/GPL-1.0-or-later.json", - "referenceNumber": "357", - "name": "GNU General Public License v1.0 or later", - "licenseId": "GPL-1.0-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-2.0.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0.json", - "referenceNumber": "147", - "name": "GNU General Public License v2.0 only", - "licenseId": "GPL-2.0", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", - "https://opensource.org/licenses/GPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-2.0+.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0+.json", - "referenceNumber": "75", - "name": "GNU General Public License v2.0 or later", - "licenseId": "GPL-2.0+", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", - "https://opensource.org/licenses/GPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-2.0-only.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0-only.json", - "referenceNumber": "233", - "name": "GNU General Public License v2.0 only", - "licenseId": "GPL-2.0-only", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", - "https://opensource.org/licenses/GPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-2.0-or-later.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0-or-later.json", - "referenceNumber": "56", - "name": "GNU General Public License v2.0 or later", - "licenseId": "GPL-2.0-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", - "https://opensource.org/licenses/GPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-2.0-with-GCC-exception.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-GCC-exception.json", - "referenceNumber": "117", - "name": "GNU General Public License v2.0 w/GCC Runtime Library exception", - "licenseId": "GPL-2.0-with-GCC-exception", - "seeAlso": [ - "https://gcc.gnu.org/git/?p\u003dgcc.git;a\u003dblob;f\u003dgcc/libgcc1.c;h\u003d762f5143fc6eed57b6797c82710f3538aa52b40b;hb\u003dcb143a3ce4fb417c68f5fa2691a1b1b1053dfba9#l10" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-2.0-with-autoconf-exception.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-autoconf-exception.json", - "referenceNumber": "355", - "name": "GNU General Public License v2.0 w/Autoconf exception", - "licenseId": "GPL-2.0-with-autoconf-exception", - "seeAlso": [ - "http://ac-archive.sourceforge.net/doc/copyright.html" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-2.0-with-bison-exception.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-bison-exception.json", - "referenceNumber": "378", - "name": "GNU General Public License v2.0 w/Bison exception", - "licenseId": "GPL-2.0-with-bison-exception", - "seeAlso": [ - "http://git.savannah.gnu.org/cgit/bison.git/tree/data/yacc.c?id\u003d193d7c7054ba7197b0789e14965b739162319b5e#n141" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-2.0-with-classpath-exception.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-classpath-exception.json", - "referenceNumber": "60", - "name": "GNU General Public License v2.0 w/Classpath exception", - "licenseId": "GPL-2.0-with-classpath-exception", - "seeAlso": [ - "https://www.gnu.org/software/classpath/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-2.0-with-font-exception.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-font-exception.json", - "referenceNumber": "375", - "name": "GNU General Public License v2.0 w/Font exception", - "licenseId": "GPL-2.0-with-font-exception", - "seeAlso": [ - "https://www.gnu.org/licenses/gpl-faq.html#FontException" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-3.0.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GPL-3.0.json", - "referenceNumber": "242", - "name": "GNU General Public License v3.0 only", - "licenseId": "GPL-3.0", - "seeAlso": [ - "https://www.gnu.org/licenses/gpl-3.0-standalone.html", - "https://opensource.org/licenses/GPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-3.0+.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GPL-3.0+.json", - "referenceNumber": "73", - "name": "GNU General Public License v3.0 or later", - "licenseId": "GPL-3.0+", - "seeAlso": [ - "https://www.gnu.org/licenses/gpl-3.0-standalone.html", - "https://opensource.org/licenses/GPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-3.0-only.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GPL-3.0-only.json", - "referenceNumber": "206", - "name": "GNU General Public License v3.0 only", - "licenseId": "GPL-3.0-only", - "seeAlso": [ - "https://www.gnu.org/licenses/gpl-3.0-standalone.html", - "https://opensource.org/licenses/GPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-3.0-or-later.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GPL-3.0-or-later.json", - "referenceNumber": "196", - "name": "GNU General Public License v3.0 or later", - "licenseId": "GPL-3.0-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/gpl-3.0-standalone.html", - "https://opensource.org/licenses/GPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-3.0-with-GCC-exception.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-3.0-with-GCC-exception.json", - "referenceNumber": "221", - "name": "GNU General Public License v3.0 w/GCC Runtime Library exception", - "licenseId": "GPL-3.0-with-GCC-exception", - "seeAlso": [ - "https://www.gnu.org/licenses/gcc-exception-3.1.html" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-3.0-with-autoconf-exception.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-3.0-with-autoconf-exception.json", - "referenceNumber": "235", - "name": "GNU General Public License v3.0 w/Autoconf exception", - "licenseId": "GPL-3.0-with-autoconf-exception", - "seeAlso": [ - "https://www.gnu.org/licenses/autoconf-exception-3.0.html" - ], - "isOsiApproved": false - }, - { - "reference": "./Giftware.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Giftware.json", - "referenceNumber": "369", - "name": "Giftware License", - "licenseId": "Giftware", - "seeAlso": [ - "http://liballeg.org/license.html#allegro-4-the-giftware-license" - ], - "isOsiApproved": false - }, - { - "reference": "./Glide.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Glide.json", - "referenceNumber": "374", - "name": "3dfx Glide License", - "licenseId": "Glide", - "seeAlso": [ - "http://www.users.on.net/~triforce/glidexp/COPYING.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./Glulxe.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Glulxe.json", - "referenceNumber": "93", - "name": "Glulxe License", - "licenseId": "Glulxe", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Glulxe" - ], - "isOsiApproved": false - }, - { - "reference": "./HPND.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/HPND.json", - "referenceNumber": "264", - "name": "Historical Permission Notice and Disclaimer", - "licenseId": "HPND", - "seeAlso": [ - "https://opensource.org/licenses/HPND" - ], - "isOsiApproved": true - }, - { - "reference": "./HPND-sell-variant.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/HPND-sell-variant.json", - "referenceNumber": "145", - "name": "Historical Permission Notice and Disclaimer - sell variant", - "licenseId": "HPND-sell-variant", - "seeAlso": [ - "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/sunrpc/auth_gss/gss_generic_token.c?h\u003dv4.19" - ], - "isOsiApproved": false - }, - { - "reference": "./HaskellReport.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/HaskellReport.json", - "referenceNumber": "122", - "name": "Haskell Language Report License", - "licenseId": "HaskellReport", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Haskell_Language_Report_License" - ], - "isOsiApproved": false - }, - { - "reference": "./IBM-pibs.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/IBM-pibs.json", - "referenceNumber": "207", - "name": "IBM PowerPC Initialization and Boot Software", - "licenseId": "IBM-pibs", - "seeAlso": [ - "http://git.denx.de/?p\u003du-boot.git;a\u003dblob;f\u003darch/powerpc/cpu/ppc4xx/miiphy.c;h\u003d297155fdafa064b955e53e9832de93bfb0cfb85b;hb\u003d9fab4bf4cc077c21e43941866f3f2c196f28670d" - ], - "isOsiApproved": false - }, - { - "reference": "./ICU.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/ICU.json", - "referenceNumber": "194", - "name": "ICU License", - "licenseId": "ICU", - "seeAlso": [ - "http://source.icu-project.org/repos/icu/icu/trunk/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./IJG.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/IJG.json", - "referenceNumber": "55", - "name": "Independent JPEG Group License", - "licenseId": "IJG", - "seeAlso": [ - "http://dev.w3.org/cvsweb/Amaya/libjpeg/Attic/README?rev\u003d1.2" - ], - "isOsiApproved": false - }, - { - "reference": "./IPA.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/IPA.json", - "referenceNumber": "312", - "name": "IPA Font License", - "licenseId": "IPA", - "seeAlso": [ - "https://opensource.org/licenses/IPA" - ], - "isOsiApproved": true - }, - { - "reference": "./IPL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/IPL-1.0.json", - "referenceNumber": "31", - "name": "IBM Public License v1.0", - "licenseId": "IPL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/IPL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./ISC.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/ISC.json", - "referenceNumber": "110", - "name": "ISC License", - "licenseId": "ISC", - "seeAlso": [ - "https://www.isc.org/downloads/software-support-policy/isc-license/", - "https://opensource.org/licenses/ISC" - ], - "isOsiApproved": true - }, - { - "reference": "./ImageMagick.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/ImageMagick.json", - "referenceNumber": "231", - "name": "ImageMagick License", - "licenseId": "ImageMagick", - "seeAlso": [ - "http://www.imagemagick.org/script/license.php" - ], - "isOsiApproved": false - }, - { - "reference": "./Imlib2.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Imlib2.json", - "referenceNumber": "257", - "name": "Imlib2 License", - "licenseId": "Imlib2", - "seeAlso": [ - "http://trac.enlightenment.org/e/browser/trunk/imlib2/COPYING", - "https://git.enlightenment.org/legacy/imlib2.git/tree/COPYING" - ], - "isOsiApproved": false - }, - { - "reference": "./Info-ZIP.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Info-ZIP.json", - "referenceNumber": "104", - "name": "Info-ZIP License", - "licenseId": "Info-ZIP", - "seeAlso": [ - "http://www.info-zip.org/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./Intel.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Intel.json", - "referenceNumber": "167", - "name": "Intel Open Source License", - "licenseId": "Intel", - "seeAlso": [ - "https://opensource.org/licenses/Intel" - ], - "isOsiApproved": true - }, - { - "reference": "./Intel-ACPI.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Intel-ACPI.json", - "referenceNumber": "88", - "name": "Intel ACPI Software License Agreement", - "licenseId": "Intel-ACPI", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Intel_ACPI_Software_License_Agreement" - ], - "isOsiApproved": false - }, - { - "reference": "./Interbase-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Interbase-1.0.json", - "referenceNumber": "83", - "name": "Interbase Public License v1.0", - "licenseId": "Interbase-1.0", - "seeAlso": [ - "https://web.archive.org/web/20060319014854/http://info.borland.com/devsupport/interbase/opensource/IPL.html" - ], - "isOsiApproved": false - }, - { - "reference": "./JPNIC.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/JPNIC.json", - "referenceNumber": "105", - "name": "Japan Network Information Center License", - "licenseId": "JPNIC", - "seeAlso": [ - "https://gitlab.isc.org/isc-projects/bind9/blob/master/COPYRIGHT#L366" - ], - "isOsiApproved": false - }, - { - "reference": "./JSON.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/JSON.json", - "referenceNumber": "372", - "name": "JSON License", - "licenseId": "JSON", - "seeAlso": [ - "http://www.json.org/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./JasPer-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/JasPer-2.0.json", - "referenceNumber": "239", - "name": "JasPer License", - "licenseId": "JasPer-2.0", - "seeAlso": [ - "http://www.ece.uvic.ca/~mdadams/jasper/LICENSE" - ], - "isOsiApproved": false - }, - { - "reference": "./LAL-1.2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LAL-1.2.json", - "referenceNumber": "380", - "name": "Licence Art Libre 1.2", - "licenseId": "LAL-1.2", - "seeAlso": [ - "http://artlibre.org/licence/lal/licence-art-libre-12/" - ], - "isOsiApproved": false - }, - { - "reference": "./LAL-1.3.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LAL-1.3.json", - "referenceNumber": "156", - "name": "Licence Art Libre 1.3", - "licenseId": "LAL-1.3", - "seeAlso": [ - "http://artlibre.org/" - ], - "isOsiApproved": false - }, - { - "reference": "./LGPL-2.0.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-2.0.json", - "referenceNumber": "268", - "name": "GNU Library General Public License v2 only", - "licenseId": "LGPL-2.0", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-2.0+.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-2.0+.json", - "referenceNumber": "52", - "name": "GNU Library General Public License v2 or later", - "licenseId": "LGPL-2.0+", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-2.0-only.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LGPL-2.0-only.json", - "referenceNumber": "276", - "name": "GNU Library General Public License v2 only", - "licenseId": "LGPL-2.0-only", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-2.0-or-later.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LGPL-2.0-or-later.json", - "referenceNumber": "217", - "name": "GNU Library General Public License v2 or later", - "licenseId": "LGPL-2.0-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-2.1.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-2.1.json", - "referenceNumber": "166", - "name": "GNU Lesser General Public License v2.1 only", - "licenseId": "LGPL-2.1", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", - "https://opensource.org/licenses/LGPL-2.1" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-2.1+.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-2.1+.json", - "referenceNumber": "64", - "name": "GNU Library General Public License v2.1 or later", - "licenseId": "LGPL-2.1+", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", - "https://opensource.org/licenses/LGPL-2.1" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-2.1-only.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-2.1-only.json", - "referenceNumber": "2", - "name": "GNU Lesser General Public License v2.1 only", - "licenseId": "LGPL-2.1-only", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", - "https://opensource.org/licenses/LGPL-2.1" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-2.1-or-later.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-2.1-or-later.json", - "referenceNumber": "338", - "name": "GNU Lesser General Public License v2.1 or later", - "licenseId": "LGPL-2.1-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", - "https://opensource.org/licenses/LGPL-2.1" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-3.0.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-3.0.json", - "referenceNumber": "210", - "name": "GNU Lesser General Public License v3.0 only", - "licenseId": "LGPL-3.0", - "seeAlso": [ - "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", - "https://opensource.org/licenses/LGPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-3.0+.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-3.0+.json", - "referenceNumber": "152", - "name": "GNU Lesser General Public License v3.0 or later", - "licenseId": "LGPL-3.0+", - "seeAlso": [ - "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", - "https://opensource.org/licenses/LGPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-3.0-only.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-3.0-only.json", - "referenceNumber": "254", - "name": "GNU Lesser General Public License v3.0 only", - "licenseId": "LGPL-3.0-only", - "seeAlso": [ - "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", - "https://opensource.org/licenses/LGPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-3.0-or-later.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-3.0-or-later.json", - "referenceNumber": "301", - "name": "GNU Lesser General Public License v3.0 or later", - "licenseId": "LGPL-3.0-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", - "https://opensource.org/licenses/LGPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPLLR.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LGPLLR.json", - "referenceNumber": "103", - "name": "Lesser General Public License For Linguistic Resources", - "licenseId": "LGPLLR", - "seeAlso": [ - "http://www-igm.univ-mlv.fr/~unitex/lgpllr.html" - ], - "isOsiApproved": false - }, - { - "reference": "./LPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LPL-1.0.json", - "referenceNumber": "89", - "name": "Lucent Public License Version 1.0", - "licenseId": "LPL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/LPL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./LPL-1.02.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LPL-1.02.json", - "referenceNumber": "131", - "name": "Lucent Public License v1.02", - "licenseId": "LPL-1.02", - "seeAlso": [ - "http://plan9.bell-labs.com/plan9/license.html", - "https://opensource.org/licenses/LPL-1.02" - ], - "isOsiApproved": true - }, - { - "reference": "./LPPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LPPL-1.0.json", - "referenceNumber": "259", - "name": "LaTeX Project Public License v1.0", - "licenseId": "LPPL-1.0", - "seeAlso": [ - "http://www.latex-project.org/lppl/lppl-1-0.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./LPPL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LPPL-1.1.json", - "referenceNumber": "309", - "name": "LaTeX Project Public License v1.1", - "licenseId": "LPPL-1.1", - "seeAlso": [ - "http://www.latex-project.org/lppl/lppl-1-1.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./LPPL-1.2.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LPPL-1.2.json", - "referenceNumber": "392", - "name": "LaTeX Project Public License v1.2", - "licenseId": "LPPL-1.2", - "seeAlso": [ - "http://www.latex-project.org/lppl/lppl-1-2.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./LPPL-1.3a.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LPPL-1.3a.json", - "referenceNumber": "305", - "name": "LaTeX Project Public License v1.3a", - "licenseId": "LPPL-1.3a", - "seeAlso": [ - "http://www.latex-project.org/lppl/lppl-1-3a.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./LPPL-1.3c.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LPPL-1.3c.json", - "referenceNumber": "326", - "name": "LaTeX Project Public License v1.3c", - "licenseId": "LPPL-1.3c", - "seeAlso": [ - "http://www.latex-project.org/lppl/lppl-1-3c.txt", - "https://opensource.org/licenses/LPPL-1.3c" - ], - "isOsiApproved": true - }, - { - "reference": "./Latex2e.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Latex2e.json", - "referenceNumber": "283", - "name": "Latex2e License", - "licenseId": "Latex2e", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Latex2e" - ], - "isOsiApproved": false - }, - { - "reference": "./Leptonica.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Leptonica.json", - "referenceNumber": "159", - "name": "Leptonica License", - "licenseId": "Leptonica", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Leptonica" - ], - "isOsiApproved": false - }, - { - "reference": "./LiLiQ-P-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LiLiQ-P-1.1.json", - "referenceNumber": "379", - "name": "Licence Libre du Québec – Permissive version 1.1", - "licenseId": "LiLiQ-P-1.1", - "seeAlso": [ - "https://forge.gouv.qc.ca/licence/fr/liliq-v1-1/", - "http://opensource.org/licenses/LiLiQ-P-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./LiLiQ-R-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LiLiQ-R-1.1.json", - "referenceNumber": "286", - "name": "Licence Libre du Québec – Réciprocité version 1.1", - "licenseId": "LiLiQ-R-1.1", - "seeAlso": [ - "https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-en-francais/licence-libre-du-quebec-reciprocite-liliq-r-v1-1/", - "http://opensource.org/licenses/LiLiQ-R-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./LiLiQ-Rplus-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LiLiQ-Rplus-1.1.json", - "referenceNumber": "139", - "name": "Licence Libre du Québec – Réciprocité forte version 1.1", - "licenseId": "LiLiQ-Rplus-1.1", - "seeAlso": [ - "https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-en-francais/licence-libre-du-quebec-reciprocite-forte-liliq-r-v1-1/", - "http://opensource.org/licenses/LiLiQ-Rplus-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./Libpng.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Libpng.json", - "referenceNumber": "101", - "name": "libpng License", - "licenseId": "Libpng", - "seeAlso": [ - "http://www.libpng.org/pub/png/src/libpng-LICENSE.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./Linux-OpenIB.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Linux-OpenIB.json", - "referenceNumber": "5", - "name": "Linux Kernel Variant of OpenIB.org license", - "licenseId": "Linux-OpenIB", - "seeAlso": [ - "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/sa.h" - ], - "isOsiApproved": false - }, - { - "reference": "./MIT.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/MIT.json", - "referenceNumber": "201", - "name": "MIT License", - "licenseId": "MIT", - "seeAlso": [ - "https://opensource.org/licenses/MIT" - ], - "isOsiApproved": true - }, - { - "reference": "./MIT-0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MIT-0.json", - "referenceNumber": "6", - "name": "MIT No Attribution", - "licenseId": "MIT-0", - "seeAlso": [ - "https://github.com/aws/mit-0", - "https://romanrm.net/mit-zero", - "https://github.com/awsdocs/aws-cloud9-user-guide/blob/master/LICENSE-SAMPLECODE" - ], - "isOsiApproved": true - }, - { - "reference": "./MIT-CMU.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MIT-CMU.json", - "referenceNumber": "9", - "name": "CMU License", - "licenseId": "MIT-CMU", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing:MIT?rd\u003dLicensing/MIT#CMU_Style", - "https://github.com/python-pillow/Pillow/blob/fffb426092c8db24a5f4b6df243a8a3c01fb63cd/LICENSE" - ], - "isOsiApproved": false - }, - { - "reference": "./MIT-advertising.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MIT-advertising.json", - "referenceNumber": "8", - "name": "Enlightenment License (e16)", - "licenseId": "MIT-advertising", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/MIT_With_Advertising" - ], - "isOsiApproved": false - }, - { - "reference": "./MIT-enna.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MIT-enna.json", - "referenceNumber": "25", - "name": "enna License", - "licenseId": "MIT-enna", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/MIT#enna" - ], - "isOsiApproved": false - }, - { - "reference": "./MIT-feh.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MIT-feh.json", - "referenceNumber": "38", - "name": "feh License", - "licenseId": "MIT-feh", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/MIT#feh" - ], - "isOsiApproved": false - }, - { - "reference": "./MITNFA.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MITNFA.json", - "referenceNumber": "294", - "name": "MIT +no-false-attribs license", - "licenseId": "MITNFA", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/MITNFA" - ], - "isOsiApproved": false - }, - { - "reference": "./MPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MPL-1.0.json", - "referenceNumber": "49", - "name": "Mozilla Public License 1.0", - "licenseId": "MPL-1.0", - "seeAlso": [ - "http://www.mozilla.org/MPL/MPL-1.0.html", - "https://opensource.org/licenses/MPL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./MPL-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/MPL-1.1.json", - "referenceNumber": "304", - "name": "Mozilla Public License 1.1", - "licenseId": "MPL-1.1", - "seeAlso": [ - "http://www.mozilla.org/MPL/MPL-1.1.html", - "https://opensource.org/licenses/MPL-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./MPL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/MPL-2.0.json", - "referenceNumber": "234", - "name": "Mozilla Public License 2.0", - "licenseId": "MPL-2.0", - "seeAlso": [ - "http://www.mozilla.org/MPL/2.0/", - "https://opensource.org/licenses/MPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./MPL-2.0-no-copyleft-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MPL-2.0-no-copyleft-exception.json", - "referenceNumber": "303", - "name": "Mozilla Public License 2.0 (no copyleft exception)", - "licenseId": "MPL-2.0-no-copyleft-exception", - "seeAlso": [ - "http://www.mozilla.org/MPL/2.0/", - "https://opensource.org/licenses/MPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./MS-PL.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/MS-PL.json", - "referenceNumber": "336", - "name": "Microsoft Public License", - "licenseId": "MS-PL", - "seeAlso": [ - "http://www.microsoft.com/opensource/licenses.mspx", - "https://opensource.org/licenses/MS-PL" - ], - "isOsiApproved": true - }, - { - "reference": "./MS-RL.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/MS-RL.json", - "referenceNumber": "280", - "name": "Microsoft Reciprocal License", - "licenseId": "MS-RL", - "seeAlso": [ - "http://www.microsoft.com/opensource/licenses.mspx", - "https://opensource.org/licenses/MS-RL" - ], - "isOsiApproved": true - }, - { - "reference": "./MTLL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MTLL.json", - "referenceNumber": "181", - "name": "Matrix Template Library License", - "licenseId": "MTLL", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Matrix_Template_Library_License" - ], - "isOsiApproved": false - }, - { - "reference": "./MakeIndex.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MakeIndex.json", - "referenceNumber": "187", - "name": "MakeIndex License", - "licenseId": "MakeIndex", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/MakeIndex" - ], - "isOsiApproved": false - }, - { - "reference": "./MirOS.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MirOS.json", - "referenceNumber": "299", - "name": "MirOS License", - "licenseId": "MirOS", - "seeAlso": [ - "https://opensource.org/licenses/MirOS" - ], - "isOsiApproved": true - }, - { - "reference": "./Motosoto.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Motosoto.json", - "referenceNumber": "317", - "name": "Motosoto License", - "licenseId": "Motosoto", - "seeAlso": [ - "https://opensource.org/licenses/Motosoto" - ], - "isOsiApproved": true - }, - { - "reference": "./Multics.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Multics.json", - "referenceNumber": "63", - "name": "Multics License", - "licenseId": "Multics", - "seeAlso": [ - "https://opensource.org/licenses/Multics" - ], - "isOsiApproved": true - }, - { - "reference": "./Mup.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Mup.json", - "referenceNumber": "353", - "name": "Mup License", - "licenseId": "Mup", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Mup" - ], - "isOsiApproved": false - }, - { - "reference": "./NASA-1.3.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NASA-1.3.json", - "referenceNumber": "87", - "name": "NASA Open Source Agreement 1.3", - "licenseId": "NASA-1.3", - "seeAlso": [ - "http://ti.arc.nasa.gov/opensource/nosa/", - "https://opensource.org/licenses/NASA-1.3" - ], - "isOsiApproved": true - }, - { - "reference": "./NBPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NBPL-1.0.json", - "referenceNumber": "361", - "name": "Net Boolean Public License v1", - "licenseId": "NBPL-1.0", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d37b4b3f6cc4bf34e1d3dec61e69914b9819d8894" - ], - "isOsiApproved": false - }, - { - "reference": "./NCSA.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/NCSA.json", - "referenceNumber": "58", - "name": "University of Illinois/NCSA Open Source License", - "licenseId": "NCSA", - "seeAlso": [ - "http://otm.illinois.edu/uiuc_openSource", - "https://opensource.org/licenses/NCSA" - ], - "isOsiApproved": true - }, - { - "reference": "./NGPL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NGPL.json", - "referenceNumber": "71", - "name": "Nethack General Public License", - "licenseId": "NGPL", - "seeAlso": [ - "https://opensource.org/licenses/NGPL" - ], - "isOsiApproved": true - }, - { - "reference": "./NLOD-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NLOD-1.0.json", - "referenceNumber": "209", - "name": "Norwegian Licence for Open Government Data", - "licenseId": "NLOD-1.0", - "seeAlso": [ - "http://data.norge.no/nlod/en/1.0" - ], - "isOsiApproved": false - }, - { - "reference": "./NLPL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NLPL.json", - "referenceNumber": "344", - "name": "No Limit Public License", - "licenseId": "NLPL", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/NLPL" - ], - "isOsiApproved": false - }, - { - "reference": "./NOSL.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/NOSL.json", - "referenceNumber": "383", - "name": "Netizen Open Source License", - "licenseId": "NOSL", - "seeAlso": [ - "http://bits.netizen.com.au/licenses/NOSL/nosl.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./NPL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/NPL-1.0.json", - "referenceNumber": "328", - "name": "Netscape Public License v1.0", - "licenseId": "NPL-1.0", - "seeAlso": [ - "http://www.mozilla.org/MPL/NPL/1.0/" - ], - "isOsiApproved": false - }, - { - "reference": "./NPL-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/NPL-1.1.json", - "referenceNumber": "185", - "name": "Netscape Public License v1.1", - "licenseId": "NPL-1.1", - "seeAlso": [ - "http://www.mozilla.org/MPL/NPL/1.1/" - ], - "isOsiApproved": false - }, - { - "reference": "./NPOSL-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NPOSL-3.0.json", - "referenceNumber": "222", - "name": "Non-Profit Open Software License 3.0", - "licenseId": "NPOSL-3.0", - "seeAlso": [ - "https://opensource.org/licenses/NOSL3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./NRL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NRL.json", - "referenceNumber": "53", - "name": "NRL License", - "licenseId": "NRL", - "seeAlso": [ - "http://web.mit.edu/network/isakmp/nrllicense.html" - ], - "isOsiApproved": false - }, - { - "reference": "./NTP.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NTP.json", - "referenceNumber": "261", - "name": "NTP License", - "licenseId": "NTP", - "seeAlso": [ - "https://opensource.org/licenses/NTP" - ], - "isOsiApproved": true - }, - { - "reference": "./Naumen.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Naumen.json", - "referenceNumber": "278", - "name": "Naumen Public License", - "licenseId": "Naumen", - "seeAlso": [ - "https://opensource.org/licenses/Naumen" - ], - "isOsiApproved": true - }, - { - "reference": "./Net-SNMP.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Net-SNMP.json", - "referenceNumber": "284", - "name": "Net-SNMP License", - "licenseId": "Net-SNMP", - "seeAlso": [ - "http://net-snmp.sourceforge.net/about/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./NetCDF.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NetCDF.json", - "referenceNumber": "46", - "name": "NetCDF license", - "licenseId": "NetCDF", - "seeAlso": [ - "http://www.unidata.ucar.edu/software/netcdf/copyright.html" - ], - "isOsiApproved": false - }, - { - "reference": "./Newsletr.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Newsletr.json", - "referenceNumber": "279", - "name": "Newsletr License", - "licenseId": "Newsletr", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Newsletr" - ], - "isOsiApproved": false - }, - { - "reference": "./Nokia.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Nokia.json", - "referenceNumber": "327", - "name": "Nokia Open Source License", - "licenseId": "Nokia", - "seeAlso": [ - "https://opensource.org/licenses/nokia" - ], - "isOsiApproved": true - }, - { - "reference": "./Noweb.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Noweb.json", - "referenceNumber": "364", - "name": "Noweb License", - "licenseId": "Noweb", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Noweb" - ], - "isOsiApproved": false - }, - { - "reference": "./Nunit.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Nunit.json", - "referenceNumber": "288", - "name": "Nunit License", - "licenseId": "Nunit", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Nunit" - ], - "isOsiApproved": false - }, - { - "reference": "./OCCT-PL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OCCT-PL.json", - "referenceNumber": "282", - "name": "Open CASCADE Technology Public License", - "licenseId": "OCCT-PL", - "seeAlso": [ - "http://www.opencascade.com/content/occt-public-license" - ], - "isOsiApproved": false - }, - { - "reference": "./OCLC-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OCLC-2.0.json", - "referenceNumber": "111", - "name": "OCLC Research Public License 2.0", - "licenseId": "OCLC-2.0", - "seeAlso": [ - "http://www.oclc.org/research/activities/software/license/v2final.htm", - "https://opensource.org/licenses/OCLC-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./ODC-By-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/ODC-By-1.0.json", - "referenceNumber": "144", - "name": "Open Data Commons Attribution License v1.0", - "licenseId": "ODC-By-1.0", - "seeAlso": [ - "https://opendatacommons.org/licenses/by/1.0/" - ], - "isOsiApproved": false - }, - { - "reference": "./ODbL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/ODbL-1.0.json", - "referenceNumber": "246", - "name": "ODC Open Database License v1.0", - "licenseId": "ODbL-1.0", - "seeAlso": [ - "http://www.opendatacommons.org/licenses/odbl/1.0/" - ], - "isOsiApproved": false - }, - { - "reference": "./OFL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OFL-1.0.json", - "referenceNumber": "153", - "name": "SIL Open Font License 1.0", - "licenseId": "OFL-1.0", - "seeAlso": [ - "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL10_web" - ], - "isOsiApproved": false - }, - { - "reference": "./OFL-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OFL-1.1.json", - "referenceNumber": "315", - "name": "SIL Open Font License 1.1", - "licenseId": "OFL-1.1", - "seeAlso": [ - "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL_web", - "https://opensource.org/licenses/OFL-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./OGL-UK-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OGL-UK-1.0.json", - "referenceNumber": "116", - "name": "Open Government Licence v1.0", - "licenseId": "OGL-UK-1.0", - "seeAlso": [ - "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/1/" - ], - "isOsiApproved": false - }, - { - "reference": "./OGL-UK-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OGL-UK-2.0.json", - "referenceNumber": "289", - "name": "Open Government Licence v2.0", - "licenseId": "OGL-UK-2.0", - "seeAlso": [ - "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/2/" - ], - "isOsiApproved": false - }, - { - "reference": "./OGL-UK-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OGL-UK-3.0.json", - "referenceNumber": "226", - "name": "Open Government Licence v3.0", - "licenseId": "OGL-UK-3.0", - "seeAlso": [ - "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/" - ], - "isOsiApproved": false - }, - { - "reference": "./OGTSL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OGTSL.json", - "referenceNumber": "125", - "name": "Open Group Test Suite License", - "licenseId": "OGTSL", - "seeAlso": [ - "http://www.opengroup.org/testing/downloads/The_Open_Group_TSL.txt", - "https://opensource.org/licenses/OGTSL" - ], - "isOsiApproved": true - }, - { - "reference": "./OLDAP-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-1.1.json", - "referenceNumber": "97", - "name": "Open LDAP Public License v1.1", - "licenseId": "OLDAP-1.1", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d806557a5ad59804ef3a44d5abfbe91d706b0791f" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-1.2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-1.2.json", - "referenceNumber": "190", - "name": "Open LDAP Public License v1.2", - "licenseId": "OLDAP-1.2", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d42b0383c50c299977b5893ee695cf4e486fb0dc7" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-1.3.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-1.3.json", - "referenceNumber": "106", - "name": "Open LDAP Public License v1.3", - "licenseId": "OLDAP-1.3", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003de5f8117f0ce088d0bd7a8e18ddf37eaa40eb09b1" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-1.4.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-1.4.json", - "referenceNumber": "30", - "name": "Open LDAP Public License v1.4", - "licenseId": "OLDAP-1.4", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dc9f95c2f3f2ffb5e0ae55fe7388af75547660941" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.0.json", - "referenceNumber": "266", - "name": "Open LDAP Public License v2.0 (or possibly 2.0A and 2.0B)", - "licenseId": "OLDAP-2.0", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dcbf50f4e1185a21abd4c0a54d3f4341fe28f36ea" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.0.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.0.1.json", - "referenceNumber": "350", - "name": "Open LDAP Public License v2.0.1", - "licenseId": "OLDAP-2.0.1", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003db6d68acd14e51ca3aab4428bf26522aa74873f0e" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.1.json", - "referenceNumber": "154", - "name": "Open LDAP Public License v2.1", - "licenseId": "OLDAP-2.1", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003db0d176738e96a0d3b9f85cb51e140a86f21be715" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.2.json", - "referenceNumber": "362", - "name": "Open LDAP Public License v2.2", - "licenseId": "OLDAP-2.2", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d470b0c18ec67621c85881b2733057fecf4a1acc3" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.2.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.2.1.json", - "referenceNumber": "339", - "name": "Open LDAP Public License v2.2.1", - "licenseId": "OLDAP-2.2.1", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d4bc786f34b50aa301be6f5600f58a980070f481e" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.2.2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.2.2.json", - "referenceNumber": "199", - "name": "Open LDAP Public License 2.2.2", - "licenseId": "OLDAP-2.2.2", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003ddf2cc1e21eb7c160695f5b7cffd6296c151ba188" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.3.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.3.json", - "referenceNumber": "164", - "name": "Open LDAP Public License v2.3", - "licenseId": "OLDAP-2.3", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dd32cf54a32d581ab475d23c810b0a7fbaf8d63c3" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.4.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.4.json", - "referenceNumber": "66", - "name": "Open LDAP Public License v2.4", - "licenseId": "OLDAP-2.4", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dcd1284c4a91a8a380d904eee68d1583f989ed386" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.5.json", - "referenceNumber": "183", - "name": "Open LDAP Public License v2.5", - "licenseId": "OLDAP-2.5", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d6852b9d90022e8593c98205413380536b1b5a7cf" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.6.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.6.json", - "referenceNumber": "61", - "name": "Open LDAP Public License v2.6", - "licenseId": "OLDAP-2.6", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d1cae062821881f41b73012ba816434897abf4205" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.7.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.7.json", - "referenceNumber": "123", - "name": "Open LDAP Public License v2.7", - "licenseId": "OLDAP-2.7", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d47c2415c1df81556eeb39be6cad458ef87c534a2" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.8.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.8.json", - "referenceNumber": "37", - "name": "Open LDAP Public License v2.8", - "licenseId": "OLDAP-2.8", - "seeAlso": [ - "http://www.openldap.org/software/release/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./OML.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OML.json", - "referenceNumber": "65", - "name": "Open Market License", - "licenseId": "OML", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Open_Market_License" - ], - "isOsiApproved": false - }, - { - "reference": "./OPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OPL-1.0.json", - "referenceNumber": "343", - "name": "Open Public License v1.0", - "licenseId": "OPL-1.0", - "seeAlso": [ - "http://old.koalateam.com/jackaroo/OPL_1_0.TXT", - "https://fedoraproject.org/wiki/Licensing/Open_Public_License" - ], - "isOsiApproved": false - }, - { - "reference": "./OSET-PL-2.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OSET-PL-2.1.json", - "referenceNumber": "291", - "name": "OSET Public License version 2.1", - "licenseId": "OSET-PL-2.1", - "seeAlso": [ - "http://www.osetfoundation.org/public-license", - "https://opensource.org/licenses/OPL-2.1" - ], - "isOsiApproved": true - }, - { - "reference": "./OSL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OSL-1.0.json", - "referenceNumber": "85", - "name": "Open Software License 1.0", - "licenseId": "OSL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/OSL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./OSL-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OSL-1.1.json", - "referenceNumber": "334", - "name": "Open Software License 1.1", - "licenseId": "OSL-1.1", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/OSL1.1" - ], - "isOsiApproved": false - }, - { - "reference": "./OSL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OSL-2.0.json", - "referenceNumber": "20", - "name": "Open Software License 2.0", - "licenseId": "OSL-2.0", - "seeAlso": [ - "http://web.archive.org/web/20041020171434/http://www.rosenlaw.com/osl2.0.html" - ], - "isOsiApproved": true - }, - { - "reference": "./OSL-2.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OSL-2.1.json", - "referenceNumber": "24", - "name": "Open Software License 2.1", - "licenseId": "OSL-2.1", - "seeAlso": [ - "http://web.archive.org/web/20050212003940/http://www.rosenlaw.com/osl21.htm", - "https://opensource.org/licenses/OSL-2.1" - ], - "isOsiApproved": true - }, - { - "reference": "./OSL-3.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OSL-3.0.json", - "referenceNumber": "100", - "name": "Open Software License 3.0", - "licenseId": "OSL-3.0", - "seeAlso": [ - "https://web.archive.org/web/20120101081418/http://rosenlaw.com:80/OSL3.0.htm", - "https://opensource.org/licenses/OSL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./OpenSSL.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OpenSSL.json", - "referenceNumber": "249", - "name": "OpenSSL License", - "licenseId": "OpenSSL", - "seeAlso": [ - "http://www.openssl.org/source/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./PDDL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/PDDL-1.0.json", - "referenceNumber": "14", - "name": "ODC Public Domain Dedication \u0026 License 1.0", - "licenseId": "PDDL-1.0", - "seeAlso": [ - "http://opendatacommons.org/licenses/pddl/1.0/" - ], - "isOsiApproved": false - }, - { - "reference": "./PHP-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/PHP-3.0.json", - "referenceNumber": "385", - "name": "PHP License v3.0", - "licenseId": "PHP-3.0", - "seeAlso": [ - "http://www.php.net/license/3_0.txt", - "https://opensource.org/licenses/PHP-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./PHP-3.01.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/PHP-3.01.json", - "referenceNumber": "316", - "name": "PHP License v3.01", - "licenseId": "PHP-3.01", - "seeAlso": [ - "http://www.php.net/license/3_01.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./Parity-6.0.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Parity-6.0.0.json", - "referenceNumber": "91", - "name": "The Parity Public License 6.0.0", - "licenseId": "Parity-6.0.0", - "seeAlso": [ - "https://paritylicense.com/versions/6.0.0.html" - ], - "isOsiApproved": false - }, - { - "reference": "./Plexus.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Plexus.json", - "referenceNumber": "225", - "name": "Plexus Classworlds License", - "licenseId": "Plexus", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Plexus_Classworlds_License" - ], - "isOsiApproved": false - }, - { - "reference": "./PostgreSQL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/PostgreSQL.json", - "referenceNumber": "247", - "name": "PostgreSQL License", - "licenseId": "PostgreSQL", - "seeAlso": [ - "http://www.postgresql.org/about/licence", - "https://opensource.org/licenses/PostgreSQL" - ], - "isOsiApproved": true - }, - { - "reference": "./Python-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Python-2.0.json", - "referenceNumber": "35", - "name": "Python License 2.0", - "licenseId": "Python-2.0", - "seeAlso": [ - "https://opensource.org/licenses/Python-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./QPL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/QPL-1.0.json", - "referenceNumber": "27", - "name": "Q Public License 1.0", - "licenseId": "QPL-1.0", - "seeAlso": [ - "http://doc.qt.nokia.com/3.3/license.html", - "https://opensource.org/licenses/QPL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Qhull.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Qhull.json", - "referenceNumber": "67", - "name": "Qhull License", - "licenseId": "Qhull", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Qhull" - ], - "isOsiApproved": false - }, - { - "reference": "./RHeCos-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/RHeCos-1.1.json", - "referenceNumber": "149", - "name": "Red Hat eCos Public License v1.1", - "licenseId": "RHeCos-1.1", - "seeAlso": [ - "http://ecos.sourceware.org/old-license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./RPL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/RPL-1.1.json", - "referenceNumber": "269", - "name": "Reciprocal Public License 1.1", - "licenseId": "RPL-1.1", - "seeAlso": [ - "https://opensource.org/licenses/RPL-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./RPL-1.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/RPL-1.5.json", - "referenceNumber": "227", - "name": "Reciprocal Public License 1.5", - "licenseId": "RPL-1.5", - "seeAlso": [ - "https://opensource.org/licenses/RPL-1.5" - ], - "isOsiApproved": true - }, - { - "reference": "./RPSL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/RPSL-1.0.json", - "referenceNumber": "273", - "name": "RealNetworks Public Source License v1.0", - "licenseId": "RPSL-1.0", - "seeAlso": [ - "https://helixcommunity.org/content/rpsl", - "https://opensource.org/licenses/RPSL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./RSA-MD.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/RSA-MD.json", - "referenceNumber": "82", - "name": "RSA Message-Digest License ", - "licenseId": "RSA-MD", - "seeAlso": [ - "http://www.faqs.org/rfcs/rfc1321.html" - ], - "isOsiApproved": false - }, - { - "reference": "./RSCPL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/RSCPL.json", - "referenceNumber": "211", - "name": "Ricoh Source Code Public License", - "licenseId": "RSCPL", - "seeAlso": [ - "http://wayback.archive.org/web/20060715140826/http://www.risource.org/RPL/RPL-1.0A.shtml", - "https://opensource.org/licenses/RSCPL" - ], - "isOsiApproved": true - }, - { - "reference": "./Rdisc.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Rdisc.json", - "referenceNumber": "295", - "name": "Rdisc License", - "licenseId": "Rdisc", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Rdisc_License" - ], - "isOsiApproved": false - }, - { - "reference": "./Ruby.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Ruby.json", - "referenceNumber": "263", - "name": "Ruby License", - "licenseId": "Ruby", - "seeAlso": [ - "http://www.ruby-lang.org/en/LICENSE.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./SAX-PD.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SAX-PD.json", - "referenceNumber": "140", - "name": "Sax Public Domain Notice", - "licenseId": "SAX-PD", - "seeAlso": [ - "http://www.saxproject.org/copying.html" - ], - "isOsiApproved": false - }, - { - "reference": "./SCEA.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SCEA.json", - "referenceNumber": "16", - "name": "SCEA Shared Source License", - "licenseId": "SCEA", - "seeAlso": [ - "http://research.scea.com/scea_shared_source_license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./SGI-B-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SGI-B-1.0.json", - "referenceNumber": "90", - "name": "SGI Free Software License B v1.0", - "licenseId": "SGI-B-1.0", - "seeAlso": [ - "http://oss.sgi.com/projects/FreeB/SGIFreeSWLicB.1.0.html" - ], - "isOsiApproved": false - }, - { - "reference": "./SGI-B-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SGI-B-1.1.json", - "referenceNumber": "241", - "name": "SGI Free Software License B v1.1", - "licenseId": "SGI-B-1.1", - "seeAlso": [ - "http://oss.sgi.com/projects/FreeB/" - ], - "isOsiApproved": false - }, - { - "reference": "./SGI-B-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/SGI-B-2.0.json", - "referenceNumber": "272", - "name": "SGI Free Software License B v2.0", - "licenseId": "SGI-B-2.0", - "seeAlso": [ - "http://oss.sgi.com/projects/FreeB/SGIFreeSWLicB.2.0.pdf" - ], - "isOsiApproved": false - }, - { - "reference": "./SHL-0.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SHL-0.5.json", - "referenceNumber": "72", - "name": "Solderpad Hardware License v0.5", - "licenseId": "SHL-0.5", - "seeAlso": [ - "https://solderpad.org/licenses/SHL-0.5/" - ], - "isOsiApproved": false - }, - { - "reference": "./SHL-0.51.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SHL-0.51.json", - "referenceNumber": "314", - "name": "Solderpad Hardware License, Version 0.51", - "licenseId": "SHL-0.51", - "seeAlso": [ - "https://solderpad.org/licenses/SHL-0.51/" - ], - "isOsiApproved": false - }, - { - "reference": "./SISSL.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/SISSL.json", - "referenceNumber": "74", - "name": "Sun Industry Standards Source License v1.1", - "licenseId": "SISSL", - "seeAlso": [ - "http://www.openoffice.org/licenses/sissl_license.html", - "https://opensource.org/licenses/SISSL" - ], - "isOsiApproved": true - }, - { - "reference": "./SISSL-1.2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SISSL-1.2.json", - "referenceNumber": "7", - "name": "Sun Industry Standards Source License v1.2", - "licenseId": "SISSL-1.2", - "seeAlso": [ - "http://gridscheduler.sourceforge.net/Gridengine_SISSL_license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./SMLNJ.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/SMLNJ.json", - "referenceNumber": "296", - "name": "Standard ML of New Jersey License", - "licenseId": "SMLNJ", - "seeAlso": [ - "https://www.smlnj.org/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./SMPPL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SMPPL.json", - "referenceNumber": "127", - "name": "Secure Messaging Protocol Public License", - "licenseId": "SMPPL", - "seeAlso": [ - "https://github.com/dcblake/SMP/blob/master/Documentation/License.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./SNIA.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SNIA.json", - "referenceNumber": "230", - "name": "SNIA Public License 1.1", - "licenseId": "SNIA", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/SNIA_Public_License" - ], - "isOsiApproved": false - }, - { - "reference": "./SPL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/SPL-1.0.json", - "referenceNumber": "54", - "name": "Sun Public License v1.0", - "licenseId": "SPL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/SPL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./SSPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SSPL-1.0.json", - "referenceNumber": "356", - "name": "Server Side Public License, v 1", - "licenseId": "SSPL-1.0", - "seeAlso": [ - "https://www.mongodb.com/licensing/server-side-public-license" - ], - "isOsiApproved": false - }, - { - "reference": "./SWL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SWL.json", - "referenceNumber": "208", - "name": "Scheme Widget Library (SWL) Software License Agreement", - "licenseId": "SWL", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/SWL" - ], - "isOsiApproved": false - }, - { - "reference": "./Saxpath.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Saxpath.json", - "referenceNumber": "18", - "name": "Saxpath License", - "licenseId": "Saxpath", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Saxpath_License" - ], - "isOsiApproved": false - }, - { - "reference": "./Sendmail.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Sendmail.json", - "referenceNumber": "151", - "name": "Sendmail License", - "licenseId": "Sendmail", - "seeAlso": [ - "http://www.sendmail.com/pdfs/open_source/sendmail_license.pdf", - "https://web.archive.org/web/20160322142305/https://www.sendmail.com/pdfs/open_source/sendmail_license.pdf" - ], - "isOsiApproved": false - }, - { - "reference": "./Sendmail-8.23.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Sendmail-8.23.json", - "referenceNumber": "41", - "name": "Sendmail License 8.23", - "licenseId": "Sendmail-8.23", - "seeAlso": [ - "https://www.proofpoint.com/sites/default/files/sendmail-license.pdf", - "https://web.archive.org/web/20181003101040/https://www.proofpoint.com/sites/default/files/sendmail-license.pdf" - ], - "isOsiApproved": false - }, - { - "reference": "./SimPL-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SimPL-2.0.json", - "referenceNumber": "184", - "name": "Simple Public License 2.0", - "licenseId": "SimPL-2.0", - "seeAlso": [ - "https://opensource.org/licenses/SimPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Sleepycat.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Sleepycat.json", - "referenceNumber": "290", - "name": "Sleepycat License", - "licenseId": "Sleepycat", - "seeAlso": [ - "https://opensource.org/licenses/Sleepycat" - ], - "isOsiApproved": true - }, - { - "reference": "./Spencer-86.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Spencer-86.json", - "referenceNumber": "313", - "name": "Spencer License 86", - "licenseId": "Spencer-86", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Henry_Spencer_Reg-Ex_Library_License" - ], - "isOsiApproved": false - }, - { - "reference": "./Spencer-94.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Spencer-94.json", - "referenceNumber": "29", - "name": "Spencer License 94", - "licenseId": "Spencer-94", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Henry_Spencer_Reg-Ex_Library_License" - ], - "isOsiApproved": false - }, - { - "reference": "./Spencer-99.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Spencer-99.json", - "referenceNumber": "386", - "name": "Spencer License 99", - "licenseId": "Spencer-99", - "seeAlso": [ - "http://www.opensource.apple.com/source/tcl/tcl-5/tcl/generic/regfronts.c" - ], - "isOsiApproved": false - }, - { - "reference": "./StandardML-NJ.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/StandardML-NJ.json", - "referenceNumber": "219", - "name": "Standard ML of New Jersey License", - "licenseId": "StandardML-NJ", - "seeAlso": [ - "http://www.smlnj.org//license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./SugarCRM-1.1.3.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SugarCRM-1.1.3.json", - "referenceNumber": "292", - "name": "SugarCRM Public License v1.1.3", - "licenseId": "SugarCRM-1.1.3", - "seeAlso": [ - "http://www.sugarcrm.com/crm/SPL" - ], - "isOsiApproved": false - }, - { - "reference": "./TAPR-OHL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/TAPR-OHL-1.0.json", - "referenceNumber": "267", - "name": "TAPR Open Hardware License v1.0", - "licenseId": "TAPR-OHL-1.0", - "seeAlso": [ - "https://www.tapr.org/OHL" - ], - "isOsiApproved": false - }, - { - "reference": "./TCL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/TCL.json", - "referenceNumber": "265", - "name": "TCL/TK License", - "licenseId": "TCL", - "seeAlso": [ - "http://www.tcl.tk/software/tcltk/license.html", - "https://fedoraproject.org/wiki/Licensing/TCL" - ], - "isOsiApproved": false - }, - { - "reference": "./TCP-wrappers.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/TCP-wrappers.json", - "referenceNumber": "274", - "name": "TCP Wrappers License", - "licenseId": "TCP-wrappers", - "seeAlso": [ - "http://rc.quest.com/topics/openssh/license.php#tcpwrappers" - ], - "isOsiApproved": false - }, - { - "reference": "./TMate.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/TMate.json", - "referenceNumber": "253", - "name": "TMate Open Source License", - "licenseId": "TMate", - "seeAlso": [ - "http://svnkit.com/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./TORQUE-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/TORQUE-1.1.json", - "referenceNumber": "171", - "name": "TORQUE v2.5+ Software License v1.1", - "licenseId": "TORQUE-1.1", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/TORQUEv1.1" - ], - "isOsiApproved": false - }, - { - "reference": "./TOSL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/TOSL.json", - "referenceNumber": "360", - "name": "Trusster Open Source License", - "licenseId": "TOSL", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/TOSL" - ], - "isOsiApproved": false - }, - { - "reference": "./TU-Berlin-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/TU-Berlin-1.0.json", - "referenceNumber": "373", - "name": "Technische Universitaet Berlin License 1.0", - "licenseId": "TU-Berlin-1.0", - "seeAlso": [ - "https://github.com/swh/ladspa/blob/7bf6f3799fdba70fda297c2d8fd9f526803d9680/gsm/COPYRIGHT" - ], - "isOsiApproved": false - }, - { - "reference": "./TU-Berlin-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/TU-Berlin-2.0.json", - "referenceNumber": "391", - "name": "Technische Universitaet Berlin License 2.0", - "licenseId": "TU-Berlin-2.0", - "seeAlso": [ - "https://github.com/CorsixTH/deps/blob/fd339a9f526d1d9c9f01ccf39e438a015da50035/licences/libgsm.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./UPL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/UPL-1.0.json", - "referenceNumber": "205", - "name": "Universal Permissive License v1.0", - "licenseId": "UPL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/UPL" - ], - "isOsiApproved": true - }, - { - "reference": "./Unicode-DFS-2015.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Unicode-DFS-2015.json", - "referenceNumber": "11", - "name": "Unicode License Agreement - Data Files and Software (2015)", - "licenseId": "Unicode-DFS-2015", - "seeAlso": [ - "https://web.archive.org/web/20151224134844/http://unicode.org/copyright.html" - ], - "isOsiApproved": false - }, - { - "reference": "./Unicode-DFS-2016.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Unicode-DFS-2016.json", - "referenceNumber": "382", - "name": "Unicode License Agreement - Data Files and Software (2016)", - "licenseId": "Unicode-DFS-2016", - "seeAlso": [ - "http://www.unicode.org/copyright.html" - ], - "isOsiApproved": false - }, - { - "reference": "./Unicode-TOU.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Unicode-TOU.json", - "referenceNumber": "70", - "name": "Unicode Terms of Use", - "licenseId": "Unicode-TOU", - "seeAlso": [ - "http://www.unicode.org/copyright.html" - ], - "isOsiApproved": false - }, - { - "reference": "./Unlicense.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Unlicense.json", - "referenceNumber": "293", - "name": "The Unlicense", - "licenseId": "Unlicense", - "seeAlso": [ - "http://unlicense.org/" - ], - "isOsiApproved": false - }, - { - "reference": "./VOSTROM.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/VOSTROM.json", - "referenceNumber": "228", - "name": "VOSTROM Public License for Open Source", - "licenseId": "VOSTROM", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/VOSTROM" - ], - "isOsiApproved": false - }, - { - "reference": "./VSL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/VSL-1.0.json", - "referenceNumber": "180", - "name": "Vovida Software License v1.0", - "licenseId": "VSL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/VSL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Vim.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Vim.json", - "referenceNumber": "133", - "name": "Vim License", - "licenseId": "Vim", - "seeAlso": [ - "http://vimdoc.sourceforge.net/htmldoc/uganda.html" - ], - "isOsiApproved": false - }, - { - "reference": "./W3C.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/W3C.json", - "referenceNumber": "351", - "name": "W3C Software Notice and License (2002-12-31)", - "licenseId": "W3C", - "seeAlso": [ - "http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231.html", - "https://opensource.org/licenses/W3C" - ], - "isOsiApproved": true - }, - { - "reference": "./W3C-19980720.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/W3C-19980720.json", - "referenceNumber": "323", - "name": "W3C Software Notice and License (1998-07-20)", - "licenseId": "W3C-19980720", - "seeAlso": [ - "http://www.w3.org/Consortium/Legal/copyright-software-19980720.html" - ], - "isOsiApproved": false - }, - { - "reference": "./W3C-20150513.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/W3C-20150513.json", - "referenceNumber": "51", - "name": "W3C Software Notice and Document License (2015-05-13)", - "licenseId": "W3C-20150513", - "seeAlso": [ - "https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document" - ], - "isOsiApproved": false - }, - { - "reference": "./WTFPL.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/WTFPL.json", - "referenceNumber": "368", - "name": "Do What The F*ck You Want To Public License", - "licenseId": "WTFPL", - "seeAlso": [ - "http://sam.zoy.org/wtfpl/COPYING" - ], - "isOsiApproved": false - }, - { - "reference": "./Watcom-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Watcom-1.0.json", - "referenceNumber": "177", - "name": "Sybase Open Watcom Public License 1.0", - "licenseId": "Watcom-1.0", - "seeAlso": [ - "https://opensource.org/licenses/Watcom-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Wsuipa.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Wsuipa.json", - "referenceNumber": "135", - "name": "Wsuipa License", - "licenseId": "Wsuipa", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Wsuipa" - ], - "isOsiApproved": false - }, - { - "reference": "./X11.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/X11.json", - "referenceNumber": "188", - "name": "X11 License", - "licenseId": "X11", - "seeAlso": [ - "http://www.xfree86.org/3.3.6/COPYRIGHT2.html#3" - ], - "isOsiApproved": false - }, - { - "reference": "./XFree86-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/XFree86-1.1.json", - "referenceNumber": "243", - "name": "XFree86 License 1.1", - "licenseId": "XFree86-1.1", - "seeAlso": [ - "http://www.xfree86.org/current/LICENSE4.html" - ], - "isOsiApproved": false - }, - { - "reference": "./XSkat.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/XSkat.json", - "referenceNumber": "96", - "name": "XSkat License", - "licenseId": "XSkat", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/XSkat_License" - ], - "isOsiApproved": false - }, - { - "reference": "./Xerox.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Xerox.json", - "referenceNumber": "163", - "name": "Xerox License", - "licenseId": "Xerox", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Xerox" - ], - "isOsiApproved": false - }, - { - "reference": "./Xnet.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Xnet.json", - "referenceNumber": "388", - "name": "X.Net License", - "licenseId": "Xnet", - "seeAlso": [ - "https://opensource.org/licenses/Xnet" - ], - "isOsiApproved": true - }, - { - "reference": "./YPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/YPL-1.0.json", - "referenceNumber": "174", - "name": "Yahoo! Public License v1.0", - "licenseId": "YPL-1.0", - "seeAlso": [ - "http://www.zimbra.com/license/yahoo_public_license_1.0.html" - ], - "isOsiApproved": false - }, - { - "reference": "./YPL-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/YPL-1.1.json", - "referenceNumber": "57", - "name": "Yahoo! Public License v1.1", - "licenseId": "YPL-1.1", - "seeAlso": [ - "http://www.zimbra.com/license/yahoo_public_license_1.1.html" - ], - "isOsiApproved": false - }, - { - "reference": "./ZPL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/ZPL-1.1.json", - "referenceNumber": "359", - "name": "Zope Public License 1.1", - "licenseId": "ZPL-1.1", - "seeAlso": [ - "http://old.zope.org/Resources/License/ZPL-1.1" - ], - "isOsiApproved": false - }, - { - "reference": "./ZPL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/ZPL-2.0.json", - "referenceNumber": "78", - "name": "Zope Public License 2.0", - "licenseId": "ZPL-2.0", - "seeAlso": [ - "http://old.zope.org/Resources/License/ZPL-2.0", - "https://opensource.org/licenses/ZPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./ZPL-2.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/ZPL-2.1.json", - "referenceNumber": "345", - "name": "Zope Public License 2.1", - "licenseId": "ZPL-2.1", - "seeAlso": [ - "http://old.zope.org/Resources/ZPL/" - ], - "isOsiApproved": false - }, - { - "reference": "./Zed.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Zed.json", - "referenceNumber": "248", - "name": "Zed License", - "licenseId": "Zed", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Zed" - ], - "isOsiApproved": false - }, - { - "reference": "./Zend-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Zend-2.0.json", - "referenceNumber": "198", - "name": "Zend License v2.0", - "licenseId": "Zend-2.0", - "seeAlso": [ - "https://web.archive.org/web/20130517195954/http://www.zend.com/license/2_00.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./Zimbra-1.3.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Zimbra-1.3.json", - "referenceNumber": "40", - "name": "Zimbra Public License v1.3", - "licenseId": "Zimbra-1.3", - "seeAlso": [ - "http://web.archive.org/web/20100302225219/http://www.zimbra.com/license/zimbra-public-license-1-3.html" - ], - "isOsiApproved": false - }, - { - "reference": "./Zimbra-1.4.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Zimbra-1.4.json", - "referenceNumber": "238", - "name": "Zimbra Public License v1.4", - "licenseId": "Zimbra-1.4", - "seeAlso": [ - "http://www.zimbra.com/legal/zimbra-public-license-1-4" - ], - "isOsiApproved": false - }, - { - "reference": "./Zlib.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Zlib.json", - "referenceNumber": "320", - "name": "zlib License", - "licenseId": "Zlib", - "seeAlso": [ - "http://www.zlib.net/zlib_license.html", - "https://opensource.org/licenses/Zlib" - ], - "isOsiApproved": true - }, - { - "reference": "./blessing.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/blessing.json", - "referenceNumber": "331", - "name": "SQLite Blessing", - "licenseId": "blessing", - "seeAlso": [ - "https://www.sqlite.org/src/artifact/e33a4df7e32d742a?ln\u003d4-9", - "https://sqlite.org/src/artifact/df5091916dbb40e6" - ], - "isOsiApproved": false - }, - { - "reference": "./bzip2-1.0.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/bzip2-1.0.5.json", - "referenceNumber": "200", - "name": "bzip2 and libbzip2 License v1.0.5", - "licenseId": "bzip2-1.0.5", - "seeAlso": [ - "http://bzip.org/1.0.5/bzip2-manual-1.0.5.html" - ], - "isOsiApproved": false - }, - { - "reference": "./bzip2-1.0.6.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/bzip2-1.0.6.json", - "referenceNumber": "302", - "name": "bzip2 and libbzip2 License v1.0.6", - "licenseId": "bzip2-1.0.6", - "seeAlso": [ - "https://github.com/asimonov-im/bzip2/blob/master/LICENSE" - ], - "isOsiApproved": false - }, - { - "reference": "./copyleft-next-0.3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/copyleft-next-0.3.0.json", - "referenceNumber": "176", - "name": "copyleft-next 0.3.0", - "licenseId": "copyleft-next-0.3.0", - "seeAlso": [ - "https://github.com/copyleft-next/copyleft-next/blob/master/Releases/copyleft-next-0.3.0" - ], - "isOsiApproved": false - }, - { - "reference": "./copyleft-next-0.3.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/copyleft-next-0.3.1.json", - "referenceNumber": "347", - "name": "copyleft-next 0.3.1", - "licenseId": "copyleft-next-0.3.1", - "seeAlso": [ - "https://github.com/copyleft-next/copyleft-next/blob/master/Releases/copyleft-next-0.3.1" - ], - "isOsiApproved": false - }, - { - "reference": "./curl.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/curl.json", - "referenceNumber": "260", - "name": "curl License", - "licenseId": "curl", - "seeAlso": [ - "https://github.com/bagder/curl/blob/master/COPYING" - ], - "isOsiApproved": false - }, - { - "reference": "./diffmark.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/diffmark.json", - "referenceNumber": "367", - "name": "diffmark license", - "licenseId": "diffmark", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/diffmark" - ], - "isOsiApproved": false - }, - { - "reference": "./dvipdfm.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/dvipdfm.json", - "referenceNumber": "143", - "name": "dvipdfm License", - "licenseId": "dvipdfm", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/dvipdfm" - ], - "isOsiApproved": false - }, - { - "reference": "./eCos-2.0.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/eCos-2.0.json", - "referenceNumber": "329", - "name": "eCos license version 2.0", - "licenseId": "eCos-2.0", - "seeAlso": [ - "https://www.gnu.org/licenses/ecos-license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./eGenix.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/eGenix.json", - "referenceNumber": "204", - "name": "eGenix.com Public License 1.1.0", - "licenseId": "eGenix", - "seeAlso": [ - "http://www.egenix.com/products/eGenix.com-Public-License-1.1.0.pdf", - "https://fedoraproject.org/wiki/Licensing/eGenix.com_Public_License_1.1.0" - ], - "isOsiApproved": false - }, - { - "reference": "./gSOAP-1.3b.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/gSOAP-1.3b.json", - "referenceNumber": "346", - "name": "gSOAP Public License v1.3b", - "licenseId": "gSOAP-1.3b", - "seeAlso": [ - "http://www.cs.fsu.edu/~engelen/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./gnuplot.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/gnuplot.json", - "referenceNumber": "10", - "name": "gnuplot License", - "licenseId": "gnuplot", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Gnuplot" - ], - "isOsiApproved": false - }, - { - "reference": "./iMatix.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/iMatix.json", - "referenceNumber": "342", - "name": "iMatix Standard Function Library Agreement", - "licenseId": "iMatix", - "seeAlso": [ - "http://legacy.imatix.com/html/sfl/sfl4.htm#license" - ], - "isOsiApproved": false - }, - { - "reference": "./libpng-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/libpng-2.0.json", - "referenceNumber": "76", - "name": "PNG Reference Library version 2", - "licenseId": "libpng-2.0", - "seeAlso": [ - "http://www.libpng.org/pub/png/src/libpng-LICENSE.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./libtiff.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/libtiff.json", - "referenceNumber": "220", - "name": "libtiff License", - "licenseId": "libtiff", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/libtiff" - ], - "isOsiApproved": false - }, - { - "reference": "./mpich2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/mpich2.json", - "referenceNumber": "318", - "name": "mpich2 License", - "licenseId": "mpich2", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/MIT" - ], - "isOsiApproved": false - }, - { - "reference": "./psfrag.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/psfrag.json", - "referenceNumber": "245", - "name": "psfrag License", - "licenseId": "psfrag", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/psfrag" - ], - "isOsiApproved": false - }, - { - "reference": "./psutils.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/psutils.json", - "referenceNumber": "126", - "name": "psutils License", - "licenseId": "psutils", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/psutils" - ], - "isOsiApproved": false - }, - { - "reference": "./wxWindows.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/wxWindows.json", - "referenceNumber": "86", - "name": "wxWindows Library License", - "licenseId": "wxWindows", - "seeAlso": [ - "https://opensource.org/licenses/WXwindows" - ], - "isOsiApproved": false - }, - { - "reference": "./xinetd.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/xinetd.json", - "referenceNumber": "146", - "name": "xinetd License", - "licenseId": "xinetd", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Xinetd_License" - ], - "isOsiApproved": false - }, - { - "reference": "./xpp.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/xpp.json", - "referenceNumber": "275", - "name": "XPP License", - "licenseId": "xpp", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/xpp" - ], - "isOsiApproved": false - }, - { - "reference": "./zlib-acknowledgement.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/zlib-acknowledgement.json", - "referenceNumber": "321", - "name": "zlib/libpng License with Acknowledgement", - "licenseId": "zlib-acknowledgement", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/ZlibWithAcknowledgement" - ], - "isOsiApproved": false - } - ], - "releaseDate": "2019-07-10" -} \ No newline at end of file diff --git a/spdx/package.py b/spdx/package.py deleted file mode 100644 index 75c50dbe6..000000000 --- a/spdx/package.py +++ /dev/null @@ -1,364 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 warnings -from datetime import datetime -from enum import Enum -from functools import reduce -from typing import Optional - -from spdx import creationinfo -from spdx import license -from spdx import utils -from spdx.checksum import Checksum, ChecksumAlgorithm -from spdx.parsers.builderexceptions import SPDXValueError -from spdx.parsers.loggers import ErrorMessages - - -class PackagePurpose(Enum): - APPLICATION = 1 - FRAMEWORK = 2 - LIBRARY = 3 - CONTAINER = 4 - OPERATING_SYSTEM = 5 - DEVICE = 6 - FIRMWARE = 7 - SOURCE = 8 - ARCHIVE = 9 - FILE = 10 - INSTALL = 11 - OTHER = 12 - - -class Package(object): - """ - Represent an analyzed Package. - Fields: - - name: Mandatory, string. - - spdx_id: Uniquely identify any element in an SPDX document which may be - referenced by other elements. Mandatory, one. Type: str. - - version: Optional, string. - - file_name: Optional, string. - - supplier: Optional, Organization or Person or NO_ASSERTION. - - originator: Optional, Organization or Person. - - download_location: Mandatory, URL as string. - - files_analyzed: Indicates whether the file content of this package has - been available for or subjected to analysis when creating the SPDX - document. If "false" indicates packages that represent metadata or URI - references to a project, product, artifact, distribution or a component. - If set to "false", the package must not contain any files. - Optional, boolean. - - homepage: Optional, URL as string or NONE or NO_ASSERTION. - - verif_code: string. According to the specification, this is Mandatory - whenever files_analyzed is True or None (omitted) and Must be None (omitted) - if files_analyzed is False. However, as a convenience within this library, - we allow this to be Optional even when files_analyzed is True/None. - - checksums: Optional, Dict with checksum.ChecksumAlgorithm as key and checksum.Checksum as value. - - source_info: Optional string. - - conc_lics: Mandatory license.License or utils.SPDXNone or - utils.NoAssert. - - license_declared: Mandatory license.License or utils.SPDXNone or - utils.NoAssert. - - license_comment: optional string. - - licenses_from_files: list of license.License or utils.SPDXNone or - utils.NoAssert. - - cr_text: Copyright text, string , utils.NoAssert or utils.SPDXNone. Mandatory. - - summary: Optional str. - - description: Optional str. - - comment: Comments about the package being described, optional one. - Type: str - - verif_exc_files: list of file names excluded from verification code or None. - - ext_pkg_refs: External references referenced within the given package. - Optional, one or many. Type: ExternalPackageRef - - attribution_text: optional string. - - primary_package_purpose: Optional one. Type: PackagePurpose - """ - - def __init__( - self, - name=None, - spdx_id=None, - download_location=None, - version=None, - file_name=None, - supplier=None, - originator=None, - ): - self.name = name - self.spdx_id = spdx_id - self.version = version - self.file_name = file_name - self.supplier = supplier - self.originator = originator - self.download_location = download_location - self.files_analyzed = None - self.homepage = None - self.verif_code = None - self.checksums = {} - self.source_info = None - self.conc_lics = None - self.license_declared = None - self.license_comment = None - self.licenses_from_files = [] - self.cr_text = None - self.summary = None - self.description = None - self.comment = None - self.attribution_text = None - self.verif_exc_files = [] - self.pkg_ext_refs = [] - self.primary_package_purpose: Optional[PackagePurpose] = None - self.release_date: Optional[datetime] = None - self.built_date: Optional[datetime] = None - self.valid_until_date: Optional[datetime] = None - - @property - def checksum(self): - """ - Backwards compatibility, return SHA1 checksum. - """ - warnings.warn("This property is deprecated. Use get_checksum instead.") - return self.get_checksum(ChecksumAlgorithm.SHA1) - - @checksum.setter - def checksum(self, value): - """ - Backwards compatibility, set SHA1 checksum. - """ - warnings.warn("This property is deprecated. Use set_checksum instead.") - if isinstance(value, str): - self.set_checksum(Checksum("SHA1", value)) - elif isinstance(value, Checksum): - self.set_checksum(value) - - @property - def are_files_analyzed(self): - return self.files_analyzed is not False - # as default None Value is False, previous line is simplification of - # return self.files_analyzed or self.files_analyzed is None - - def add_lics_from_file(self, lics): - self.licenses_from_files.append(lics) - - def add_exc_file(self, filename): - self.verif_exc_files.append(filename) - - def add_pkg_ext_refs(self, pkg_ext_ref): - self.pkg_ext_refs.append(pkg_ext_ref) - - def validate(self, messages): - """ - Validate the package fields. - Append user friendly error messages to the `messages` list. - """ - messages.push_context(self.name) - self.validate_files_analyzed(messages) - self.validate_checksums(messages) - self.validate_optional_str_fields(messages) - self.validate_mandatory_str_fields(messages) - self.validate_pkg_ext_refs(messages) - self.validate_optional_fields(messages) - messages.pop_context() - - return messages - - def validate_files_analyzed(self, messages): - if self.files_analyzed not in [True, False, None]: - messages.append( - 'Package files_analyzed must be True or False or None (omitted)' - ) - if not self.are_files_analyzed and self.verif_code is not None: - messages.append( - 'Package verif_code must be None (omitted) when files_analyzed is False' - ) - - return messages - - def validate_primary_package_purposes(self, messages: ErrorMessages) -> ErrorMessages: - if self.primary_package_purpose not in PackagePurpose: - messages.append("Primary package purpose has a value that is not allowed!") - return messages - - def validate_optional_fields(self, messages): - if self.originator and not isinstance( - self.originator, (utils.NoAssert, creationinfo.Creator) - ): - messages.append( - "Package originator must be instance of " - "spdx.utils.NoAssert or spdx.creationinfo.Creator" - ) - - if self.supplier and not isinstance( - self.supplier, (utils.NoAssert, creationinfo.Creator) - ): - messages.append( - "Package supplier must be instance of " - "spdx.utils.NoAssert or spdx.creationinfo.Creator" - ) - - if self.conc_lics and not isinstance( - self.conc_lics, (utils.SPDXNone, utils.NoAssert, license.License) - ): - messages.append( - "Package concluded license must be instance of " - "spdx.utils.SPDXNone or spdx.utils.NoAssert or " - "spdx.license.License" - ) - - if self.license_declared and not isinstance( - self.license_declared, (utils.SPDXNone, utils.NoAssert, license.License) - ): - messages.append( - "Package declared license must be instance of " - "spdx.utils.SPDXNone or spdx.utils.NoAssert or " - "spdx.license.License" - ) - - license_from_file_check = lambda prev, el: prev and isinstance( - el, (license.License, utils.SPDXNone, utils.NoAssert) - ) - if not reduce(license_from_file_check, self.licenses_from_files, True): - messages.append( - "Each element in licenses_from_files must be instance of " - "spdx.utils.SPDXNone or spdx.utils.NoAssert or " - "spdx.license.License" - ) - - return messages - - def validate_pkg_ext_refs(self, messages): - for ref in self.pkg_ext_refs: - if isinstance(ref, ExternalPackageRef): - messages = ref.validate(messages) - else: - messages.append( - "External package references must be of the type " - "spdx.package.ExternalPackageRef and not " + str(type(ref)) - ) - - return messages - - def validate_optional_str_fields(self, messages): - """Fields marked as optional and of type string in class - docstring must be of a type that provides __str__ method. - """ - FIELDS = [ - "file_name", - "version", - "homepage", - "source_info", - "summary", - "description", - "attribution_text", - "comment", - "cr_text" - ] - self.validate_str_fields(FIELDS, True, messages) - - return messages - - def validate_mandatory_str_fields(self, messages): - """Fields marked as Mandatory and of type string in class - docstring must be of a type that provides __str__ method. - """ - FIELDS = ["name", "spdx_id", "download_location"] - self.validate_str_fields(FIELDS, False, messages) - - return messages - - def validate_str_fields(self, fields, optional, messages): - """Helper for validate_mandatory_str_field and - validate_optional_str_fields""" - for field_str in fields: - field = getattr(self, field_str) - if field is not None: - # FIXME: this does not make sense??? - attr = getattr(field, "__str__", None) - if not callable(attr): - messages.append( - "{0} must provide __str__ method.".format(field) - ) - # Continue checking. - elif not optional: - messages.append("Package {0} can not be None.".format(field_str)) - - return messages - - def validate_checksums(self, messages: ErrorMessages): - if not self.checksums: - return - for checksum in self.checksums.values(): - if not isinstance(checksum, Checksum): - messages.append("Package checksum must be instance of spdx.checksum.Checksum") - - def get_checksum(self, hash_algorithm: ChecksumAlgorithm = ChecksumAlgorithm.SHA1) -> Optional[Checksum]: - return self.checksums.get(hash_algorithm) - - def set_checksum(self, new_checksum: Checksum): - if not isinstance(new_checksum, Checksum): - raise SPDXValueError("Package::Checksum") - - self.checksums[new_checksum.identifier] = new_checksum - - def has_optional_field(self, field): - return bool(getattr(self, field, None)) - - -class ExternalPackageRef(object): - """ - An External Reference allows a Package to reference an external source of - additional information, metadata, enumerations, asset identifiers, or - downloadable content believed to be relevant to the Package. - Fields: - - category: "SECURITY" or "PACKAGE-MANAGER" or "OTHER". - - pkg_ext_ref_type: A unique string containing letters, numbers, ".","-". - - locator: A unique string with no spaces necessary to access the - package-specific information, metadata, or content within the target - location. - - comment: To provide information about the purpose and target of the - reference. - """ - - def __init__( - self, category=None, pkg_ext_ref_type=None, locator=None, comment=None - ): - self.category = category - self.pkg_ext_ref_type = pkg_ext_ref_type - self.locator = locator - self.comment = comment - - def validate(self, messages): - """ - Check that all the fields are valid. - Appends any error messages to messages parameter shall be a ErrorMessages. - """ - self.validate_category(messages) - self.validate_pkg_ext_ref_type(messages) - self.validate_locator(messages) - - return messages - - def validate_category(self, messages): - if self.category is None: - messages.append("ExternalPackageRef has no category.") - - return messages - - def validate_pkg_ext_ref_type(self, messages): - if self.pkg_ext_ref_type is None: - messages.append("ExternalPackageRef has no type.") - - return messages - - def validate_locator(self, messages): - if self.locator is None: - messages.append("ExternalPackageRef has no locator.") - - return messages diff --git a/spdx/parsers/__init__.py b/spdx/parsers/__init__.py deleted file mode 100644 index 588b404ec..000000000 --- a/spdx/parsers/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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. diff --git a/spdx/parsers/builderexceptions.py b/spdx/parsers/builderexceptions.py deleted file mode 100644 index fe03f8d51..000000000 --- a/spdx/parsers/builderexceptions.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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. - - -class BuilderException(Exception): - """Builder exception base class.""" - - pass - - -class CardinalityError(BuilderException): - def __init__(self, msg): - self.msg = msg - - -class SPDXValueError(BuilderException): - def __init__(self, msg): - self.msg = msg - - -class OrderError(BuilderException): - def __init__(self, msg): - self.msg = msg - - -class FileTypeError(BuilderException): - def __init__(self, msg): - self.msg = msg diff --git a/spdx/parsers/jsonparser.py b/spdx/parsers/jsonparser.py deleted file mode 100644 index 5436a6798..000000000 --- a/spdx/parsers/jsonparser.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) Xavier Figueroa -# 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 json - -from spdx.parsers import jsonyamlxml - - -class Parser(jsonyamlxml.Parser): - """ - Wrapper class for jsonyamlxml.Parser to provide an interface similar to - RDF and TV Parser classes (i.e., spdx.parsers..Parser) for JSON parser. - It also avoids to repeat jsonyamlxml.Parser.parse code for JSON, YAML and XML parsers - """ - - def __init__(self, builder, logger): - super(Parser, self).__init__(builder, logger) - - def parse(self, file): - self.json_yaml_set_document(json.load(file)) - return super(Parser, self).parse() diff --git a/spdx/parsers/jsonyamlxml.py b/spdx/parsers/jsonyamlxml.py deleted file mode 100644 index 5469376b0..000000000 --- a/spdx/parsers/jsonyamlxml.py +++ /dev/null @@ -1,1885 +0,0 @@ -# Copyright (c) Xavier Figueroa -# 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 datetime import datetime -from enum import Enum, auto -from typing import List, Dict, Tuple, Callable, Optional - -from spdx import document -from spdx import utils -from spdx.license import LicenseConjunction, LicenseDisjunction -from spdx.package import ExternalPackageRef, PackagePurpose, Package -from spdx.parsers import rdf -from spdx.parsers.builderexceptions import SPDXValueError, CardinalityError, OrderError -from spdx.parsers.loggers import ErrorMessages -from spdx.snippet import Snippet -from spdx.utils import UnKnown - -ERROR_MESSAGES = rdf.ERROR_MESSAGES - - -class BaseParser(object): - def __init__(self, builder, logger): - self.builder = builder - self.logger = logger - - def order_error(self, first_tag, second_tag): - """ - Helper method for logging an OrderError raised. - - first_tag: field to be added - - second_tag: required field - """ - self.error = True - msg = "{0} Can not appear before {1}".format(first_tag, second_tag) - self.logger.log(msg) - - def more_than_one_error(self, field): - """ - Helper method for logging an CardinalityError raised. - - field: field/property that has been already defined. - """ - msg = "More than one {0} defined.".format(field) - self.logger.log(msg) - self.error = True - - def value_error(self, key, bad_value): - """ - Helper method for logging an SPDXValueError raised. - It reports a value error using ERROR_MESSAGES dict. - - key: key to use for ERROR_MESSAGES. If not present, a default message is logged - - bad_value: malformed value - """ - msg = ERROR_MESSAGES.get(key) - if msg: - self.logger.log(msg.format(bad_value)) - else: - msg = "'{0}' is not a valid value for {1}".format(bad_value, key) - self.logger.log(msg) - self.error = True - - -class CreationInfoParser(BaseParser): - def __init__(self, builder, logger): - super(CreationInfoParser, self).__init__(builder, logger) - - def parse_creation_info(self, creation_info): - """ - Parse Creation Information fields - - creation_info: Python dict with Creation Information fields in it - """ - if isinstance(creation_info, dict): - self.parse_creation_info_comment(creation_info.get("comment")) - self.parse_creation_info_lic_list_version( - creation_info.get("licenseListVersion") - ) - self.parse_creation_info_created(creation_info.get("created")) - self.parse_creation_info_creators(creation_info.get("creators")) - else: - self.value_error("CREATION_INFO_SECTION", creation_info) - - def parse_creation_info_comment(self, comment): - """ - Parse CreationInfo comment - - comment: Python str/unicode - """ - if isinstance(comment, str): - try: - return self.builder.set_creation_comment(self.document, comment) - except CardinalityError: - self.more_than_one_error("CreationInfo comment") - elif comment is not None: - self.value_error("CREATION_COMMENT", comment) - - def parse_creation_info_lic_list_version(self, license_list_version): - """ - Parse CreationInfo license list version - - license_list_version: Python str/unicode - """ - if isinstance(license_list_version, str): - try: - return self.builder.set_lics_list_ver( - self.document, license_list_version - ) - except SPDXValueError: - raise - self.value_error("LL_VALUE", license_list_version) - except CardinalityError: - self.more_than_one_error("CreationInfo licenseListVersion") - elif license_list_version is not None: - self.value_error("LL_VALUE", license_list_version) - - def parse_creation_info_created(self, created): - """ - Parse CreationInfo creation date - - created: Python str/unicode (ISO-8601 representation of datetime) - """ - if isinstance(created, str): - try: - return self.builder.set_created_date(self.document, created) - except SPDXValueError: - self.value_error("CREATED_VALUE", created) - except CardinalityError: - self.more_than_one_error("CreationInfo created") - else: - self.value_error("CREATED_VALUE", created) - - def parse_creation_info_creators(self, creators): - """ - Parse CreationInfo creators - - creators: Python list of creators (str/unicode) - """ - if isinstance(creators, list): - for creator in creators: - if isinstance(creator, str): - entity = self.builder.create_entity(self.document, creator) - try: - self.builder.add_creator(self.document, entity) - except SPDXValueError: - self.value_error("CREATOR_VALUE", creator) - else: - self.value_error("CREATOR_VALUE", creator) - else: - self.value_error("CREATORS_SECTION", creators) - - -class ExternalDocumentRefsParser(BaseParser): - def __init__(self, builder, logger): - super(ExternalDocumentRefsParser, self).__init__(builder, logger) - - def parse_external_document_refs(self, external_document_refs): - """ - Parse External Document References fields - - external_document_refs: Python list with External Document References dicts in it - """ - if isinstance(external_document_refs, list): - for external_document_ref in external_document_refs: - if isinstance(external_document_ref, dict): - self.parse_ext_doc_ref_id( - external_document_ref.get("externalDocumentId") - ) - self.parse_ext_doc_ref_namespace( - external_document_ref.get("spdxDocument") - ) - self.parse_ext_doc_ref_chksum(external_document_ref.get("checksum")) - else: - self.value_error("EXT_DOC_REF", external_document_ref) - elif external_document_refs is not None: - self.value_error("EXT_DOC_REFS_SECTION", external_document_refs) - - def parse_ext_doc_ref_id(self, ext_doc_ref_id): - """ - Parse ExternalDocumentReference id - ext_doc_ref_id: Python str/unicode - """ - if isinstance(ext_doc_ref_id, str): - return self.builder.set_ext_doc_id(self.document, ext_doc_ref_id) - self.value_error("EXT_DOC_REF_ID", ext_doc_ref_id) - return self.builder.set_ext_doc_id(self.document, "dummy_ext_doc_ref") - # ext_doc_ref_id is set even if it is None or not string. If weren't, the other attributes - # would be added to the ex_doc_ref previously added. - # Another approach is to skip the whole ex_doc_ref itself - - def parse_ext_doc_ref_namespace(self, namespace): - """ - Parse ExternalDocumentReference namespace - namespace: Python str/unicode - """ - if isinstance(namespace, str): - try: - return self.builder.set_spdx_doc_uri(self.document, namespace) - except SPDXValueError: - self.value_error("EXT_DOC_REF_VALUE", namespace) - else: - self.value_error("EXT_DOC_REF_VALUE", namespace) - - def parse_ext_doc_ref_chksum(self, chksum): - """ - Parse ExternalDocumentReference checksum - chksum: Python dict('algorithm':str/unicode, 'value':str/unicode) - """ - if isinstance(chksum, dict): - value = chksum.get("checksumValue") - if isinstance(value, str): - try: - return self.builder.set_chksum(self.document, value) - except SPDXValueError: - self.value_error("CHECKSUM_VALUE", value) - else: - self.value_error("CHECKSUM_VALUE", value) - else: - self.value_error("CHECKSUM_FIELD", chksum) - - -class LicenseParser(BaseParser): - def __init__(self, builder, logger): - super(LicenseParser, self).__init__(builder, logger) - - def parse_extracted_license_info(self, extracted_license_info): - """ - Parse Extracted Lisence Information fields - - extracted_license_info: Python list with Extracted Lisence Information dicts in it - """ - if isinstance(extracted_license_info, list): - for extracted_license in extracted_license_info: - if isinstance(extracted_license, dict): - if self.parse_ext_lic_id(extracted_license.get("licenseId")): - self.parse_ext_lic_name(extracted_license.get("name")) - self.parse_ext_lic_comment(extracted_license.get("comment")) - self.parse_ext_lic_text(extracted_license.get("extractedText")) - self.parse_ext_lic_cross_refs(extracted_license.get("seeAlsos")) - else: - self.value_error("EXTR_LIC", extracted_license) - - def parse_ext_lic_id(self, ext_lic_id): - """ - Parse ExtractedLicenseInformation id - ext_lic_id: Python str/unicode - """ - if isinstance(ext_lic_id, str): - try: - return self.builder.set_lic_id(self.document, ext_lic_id) - except SPDXValueError: - self.value_error("EXTR_LIC_ID", ext_lic_id) - else: - self.value_error("EXTR_LIC_ID", ext_lic_id) - - def parse_ext_lic_name(self, ext_lic_name): - """ - Parse ExtractedLicenseInformation name - ext_lic_name: Python str/unicode - """ - try: - return self.builder.set_lic_name(self.document, ext_lic_name) - except SPDXValueError: - self.value_error("EXTR_LIC_NAME", ext_lic_name) - except CardinalityError: - self.more_than_one_error("ExtractedLicense name") - except OrderError: - self.order_error("ExtractedLicense name", "ExtractedLicense id") - - def parse_ext_lic_comment(self, ext_lic_comment): - """ - Parse ExtractedLicenseInformation comment - ext_lic_comment: Python str/unicode - """ - if isinstance(ext_lic_comment, str): - try: - return self.builder.set_lic_comment(self.document, ext_lic_comment) - except CardinalityError: - self.more_than_one_error("ExtractedLicense comment") - except OrderError: - self.order_error("ExtractedLicense comment", "ExtractedLicense id") - elif ext_lic_comment is not None: - self.value_error("EXTR_LIC_COMMENT", ext_lic_comment) - - def parse_ext_lic_text(self, ext_lic_text): - """ - Parse ExtractedLicenseInformation text - ext_lic_text: Python str/unicode - """ - if isinstance(ext_lic_text, str): - try: - return self.builder.set_lic_text(self.document, ext_lic_text) - except CardinalityError: - self.more_than_one_error("ExtractedLicense text") - except OrderError: - self.order_error("ExtractedLicense text", "ExtractedLicense id") - else: - self.value_error("EXTR_LIC_TXT", ext_lic_text) - - def parse_ext_lic_cross_refs(self, cross_refs): - """ - Parse ExtractedLicenseInformation cross references - cross_refs: Python list of cross references (str/unicode) - """ - if isinstance(cross_refs, list): - for cross_ref in cross_refs: - if isinstance(cross_ref, str): - try: - self.builder.add_lic_xref(self.document, cross_ref) - except OrderError: - self.order_error( - "ExtractedLicense cross references", "ExtractedLicense id" - ) - else: - self.value_error("CROSS_REF", cross_ref) - - def replace_license(self, license_object): - if isinstance(license_object, LicenseConjunction): - return LicenseConjunction( - self.replace_license(license_object.license_1), - self.replace_license(license_object.license_2), - ) - elif isinstance(license_object, LicenseDisjunction): - return LicenseDisjunction( - self.replace_license(license_object.license_1), - self.replace_license(license_object.license_2), - ) - else: - license_objects = list( - filter( - lambda lic: lic.identifier == license_object.identifier, - self.document.extracted_licenses, - ) - ) - return license_objects[-1] if license_objects else license_object - - -class AnnotationParser(BaseParser): - def __init__(self, builder, logger): - super(AnnotationParser, self).__init__(builder, logger) - - def parse_annotations(self, annotations, spdx_id: str = None): - """ - Parse Annotation Information fields - - annotations: Python list with Annotation Information dicts in it - """ - if isinstance(annotations, list): - for annotation in annotations: - if isinstance(annotation, dict): - if self.parse_annotation_annotator(annotation.get("annotator")): - self.parse_annotation_date(annotation.get("annotationDate")) - self.parse_annotation_comment(annotation.get("comment")) - self.parse_annotation_type(annotation.get("annotationType")) - if annotation.get("SPDXID"): - self.parse_annotation_id(annotation.get("SPDXID")) - else: - self.parse_annotation_id(spdx_id) - else: - self.value_error("ANNOTATION", annotation) - - def parse_annotation_annotator(self, annotator): - """ - Parse Annotation annotator - - annotator: Python str/unicode - """ - if isinstance(annotator, str): - entity = self.builder.create_entity(self.document, annotator) - try: - return self.builder.add_annotator(self.document, entity) - except SPDXValueError: - self.value_error("ANNOTATOR_VALUE", annotator) - else: - self.value_error("ANNOTATOR_VALUE", annotator) - - def parse_annotation_date(self, date): - """ - Parse Annotation date - - date: Python str/unicode (ISO-8601 representation of datetime) - """ - if isinstance(date, str): - try: - return self.builder.add_annotation_date(self.document, date) - except SPDXValueError: - self.value_error("ANNOTATION_DATE", date) - except CardinalityError: - self.more_than_one_error("Annotation date") - except OrderError: - self.order_error("ANNOTATION_DATE", "ANNOTATOR_VALUE") - else: - self.value_error("ANNOTATION_DATE", date) - - def parse_annotation_comment(self, comment): - """ - Parse Annotation comment - - comment: Python str/unicode - """ - if isinstance(comment, str): - try: - return self.builder.add_annotation_comment(self.document, comment) - except CardinalityError: - self.more_than_one_error("Annotation comment") - except OrderError: - self.order_error("ANNOTATION_COMMENT", "ANNOTATOR_VALUE") - else: - self.value_error("ANNOTATION_COMMENT", comment) - - def parse_annotation_type(self, annotation_type): - """ - Parse Annotation type - - annotation_type: Python str/unicode (REVIEW or OTHER) - """ - if isinstance(annotation_type, str): - try: - return self.builder.add_annotation_type(self.document, annotation_type) - except SPDXValueError: - self.value_error("ANNOTATION_TYPE", annotation_type) - except CardinalityError: - self.more_than_one_error("ANNOTATION_TYPE") - except OrderError: - self.order_error("ANNOTATION_TYPE", "ANNOTATOR_VALUE") - else: - self.value_error("ANNOTATION_TYPE", annotation_type) - - def parse_annotation_id(self, annotation_id): - """ - Parse Annotation id - - annotation_id: Python str/unicode - """ - if isinstance(annotation_id, str): - try: - return self.builder.set_annotation_spdx_id(self.document, annotation_id) - except CardinalityError: - self.more_than_one_error("ANNOTATION_ID") - except OrderError: - self.order_error("ANNOTATION_ID", "ANNOTATOR_VALUE") - else: - self.value_error("ANNOTATION_ID", annotation_id) - - -class RelationshipParser(BaseParser): - def __init__(self, builder, logger): - super(RelationshipParser, self).__init__(builder, logger) - - def parse_relationships(self, relationships): - """ - Parse Relationship Information fields - - relationships: Python list with Relationship Information dicts in it - """ - if isinstance(relationships, list): - for relationship in relationships: - if isinstance(relationship, dict): - if self.parse_relationship( - relationship.get("spdxElementId"), - relationship.get("relationshipType"), - relationship.get("relatedSpdxElement"), - ): - self.parse_relationship_comment(relationship.get("comment")) - else: - self.value_error("RELATIONSHIP", relationship) - - def parse_relationship(self, spdxelementid, relationshiptype, relatedspdxelement): - """ - Parse Relationshiptype, spdxElementId and relatedSpdxElement - - relationship: Python str/unicode - """ - if not isinstance(relationshiptype, str): - self.value_error("RELATIONSHIP_VALUE", relationshiptype) - return - if not isinstance(spdxelementid, str): - self.value_error("SPDXELEMENTID", spdxelementid) - return - if not isinstance(relatedspdxelement, str): - self.value_error("RELATEDSPDXELEMENT", relatedspdxelement) - return - relate = spdxelementid + " " + relationshiptype + " " + relatedspdxelement - try: - return self.builder.add_relationship(self.document, relate) - except SPDXValueError: - self.value_error("RELATIONSHIP_VALUE", relate) - - - def parse_relationship_comment(self, relationship_comment): - """ - Parse relationship comment - - relationship_comment: Python str/unicode - """ - if isinstance(relationship_comment, str): - try: - return self.builder.add_relationship_comment( - self.document, relationship_comment - ) - except CardinalityError: - self.more_than_one_error("RELATIONSHIP_COMMENT") - except OrderError: - self.order_error("RELATIONSHIP_COMMENT", "RELATIONSHIP") - elif relationship_comment is not None: - self.value_error("RELATIONSHIP_COMMENT", relationship_comment) - - -class RangeType(Enum): - BYTE = auto() - LINE = auto() - - -class SnippetParser(BaseParser): - def __init__(self, builder, logger): - super(SnippetParser, self).__init__(builder, logger) - - @property - def snippet(self) -> Snippet: - return self.document.snippet[-1] - - def parse_snippets(self, snippets): - """ - Parse Snippet Information fields - - snippets: Python list with Snippet Information dicts in it - """ - if isinstance(snippets, list): - for snippet in snippets: - if isinstance(snippet, dict): - if self.parse_snippet_id(snippet.get("SPDXID")): - self.parse_snippet_name(snippet.get("name")) - self.parse_snippet_comment(snippet.get("comment")) - self.parse_snippet_copyright(snippet.get("copyrightText")) - self.parse_snippet_license_comment( - snippet.get("licenseComments") - ) - self.parse_snippet_file_spdxid(snippet.get("snippetFromFile")) - self.parse_snippet_concluded_license( - snippet.get("licenseConcluded") - ) - self.parse_snippet_attribution_text( - snippet.get("attributionTexts") - ) - self.parse_snippet_license_info_from_snippet( - snippet.get("licenseInfoInSnippets") - ) - self.parse_annotations(snippet.get("annotations"), spdx_id=snippet.get("SPDXID")) - self.parse_snippet_ranges(snippet.get("ranges")) - else: - self.value_error("SNIPPET", snippet) - - def parse_snippet_id(self, snippet_id): - """ - Parse Snippet id - - snippet_id: Python str/unicode - """ - if isinstance(snippet_id, str): - try: - return self.builder.create_snippet(self.document, snippet_id) - except SPDXValueError: - self.value_error("SNIPPET_SPDX_ID_VALUE", snippet_id) - else: - self.value_error("SNIPPET_SPDX_ID_VALUE", snippet_id) - - def parse_snippet_name(self, snippet_name): - """ - Parse Snippet name - - snippet_name: Python str/unicode - """ - if isinstance(snippet_name, str): - try: - return self.builder.set_snippet_name(self.document, snippet_name) - except CardinalityError: - self.more_than_one_error("SNIPPET_NAME") - elif snippet_name is not None: - self.value_error("SNIPPET_NAME", snippet_name) - - def parse_snippet_comment(self, snippet_comment): - """ - Parse Snippet comment - - snippet_comment: Python str/unicode - """ - if isinstance(snippet_comment, str): - try: - return self.builder.set_snippet_comment(self.document, snippet_comment) - except CardinalityError: - self.more_than_one_error("SNIPPET_COMMENT") - elif snippet_comment is not None: - self.value_error("SNIPPET_COMMENT", snippet_comment) - - def parse_snippet_attribution_text(self, snippet_attribution_texts): - """ - Parse Snippet attribution texts - - snippet_attribution_texts: list in yaml, json and string in xml format - """ - if isinstance(snippet_attribution_texts, list) or isinstance( - snippet_attribution_texts, str - ): - for snippet_attribution_text in snippet_attribution_texts: - try: - return self.builder.set_snippet_attribution_text( - self.document, snippet_attribution_texts - ) - except CardinalityError: - self.more_than_one_error("SNIPPET_ATTRIBUTION_TEXT") - except OrderError: - self.order_error("SNIPPET_ATTRIBUTION_TEXT", "SNIPPET_NAME") - else: - self.value_error("SNIPPET_ATTRIBUTION_TEXT", snippet_attribution_texts) - - def parse_snippet_copyright(self, copyright_text): - """ - Parse Snippet copyright text - - copyright_text: Python str/unicode - """ - if isinstance(copyright_text, str): - try: - return self.builder.set_snippet_copyright(self.document, copyright_text) - except CardinalityError: - self.more_than_one_error("SNIPPET_COPYRIGHT") - elif copyright_text is not None: - self.value_error("SNIPPET_COPYRIGHT", copyright_text) - - def parse_snippet_license_comment(self, license_comment): - """ - Parse Snippet license comment - - license_comment: Python str/unicode - """ - if isinstance(license_comment, str): - try: - return self.builder.set_snippet_lic_comment( - self.document, license_comment - ) - except CardinalityError: - self.more_than_one_error("SNIPPET_LIC_COMMENTS") - elif license_comment is not None: - self.value_error("SNIPPET_LIC_COMMENTS", license_comment) - - def parse_snippet_file_spdxid(self, file_spdxid): - """ - Parse Snippet file id - - file_spdxid: Python str/unicode - """ - if isinstance(file_spdxid, str): - try: - return self.builder.set_snip_from_file_spdxid( - self.document, file_spdxid - ) - except SPDXValueError: - self.value_error("SNIPPET_FILE_ID", file_spdxid) - except CardinalityError: - self.more_than_one_error("SNIPPET_FILE_ID") - else: - self.value_error("SNIPPET_FILE_ID", file_spdxid) - - def parse_snippet_concluded_license(self, concluded_license): - """ - Parse Snippet concluded license - - concluded_license: Python str/unicode - """ - if isinstance(concluded_license, str): - lic_parser = utils.LicenseListParser() - lic_parser.build(write_tables=0, debug=0) - license_object = self.replace_license(lic_parser.parse(concluded_license)) - try: - return self.builder.set_snip_concluded_license( - self.document, license_object - ) - except SPDXValueError: - self.value_error("SNIPPET_CONCLUDED_LICENSE", concluded_license) - except CardinalityError: - self.more_than_one_error("SNIPPET_CONCLUDED_LICENSE") - elif concluded_license is not None: - self.value_error("SNIPPET_CONCLUDED_LICENSE", concluded_license) - - def parse_snippet_license_info_from_snippet(self, license_info_from_snippet): - """ - Parse Snippet license information from snippet - - license_info_from_snippet: Python list of licenses information from snippet (str/unicode) - """ - if isinstance(license_info_from_snippet, list): - for lic_in_snippet in license_info_from_snippet: - if isinstance(lic_in_snippet, str): - lic_parser = utils.LicenseListParser() - lic_parser.build(write_tables=0, debug=0) - license_object = self.replace_license( - lic_parser.parse(lic_in_snippet) - ) - try: - self.builder.set_snippet_lics_info( - self.document, license_object - ) - except SPDXValueError: - self.value_error("SNIPPET_LIC_INFO", lic_in_snippet) - else: - self.value_error("SNIPPET_LIC_INFO", lic_in_snippet) - elif license_info_from_snippet is not None: - self.value_error("SNIPPET_LIC_INFO_FIELD", license_info_from_snippet) - - def parse_snippet_ranges(self, ranges_from_snippet: List[Dict]) -> None: - """ - Parse ranges (byte range and optional line range) from snippet - - ranges_from_snippet; Python list of dict - """ - if not isinstance(ranges_from_snippet, list): - self.value_error("SNIPPET_RANGES", ranges_from_snippet) - return - - for range_dict in ranges_from_snippet: - try: - range_type = self.validate_range_and_get_type(range_dict) - start_end_tuple: Tuple[int, int] = SnippetParser.get_start_end_tuple(range_dict, range_type) - except SPDXValueError: - self.value_error("SNIPPET_RANGE", range_dict) - return - - if range_type == RangeType.BYTE: - self.snippet.byte_range = start_end_tuple - elif range_type == RangeType.LINE: - self.snippet.line_range = start_end_tuple - - @staticmethod - def get_start_end_tuple(range_dict: Dict, range_type: RangeType) -> Tuple[int, int]: - end_pointer = range_dict["endPointer"] - start_pointer = range_dict["startPointer"] - if range_type == RangeType.BYTE: - start = int(start_pointer["offset"]) - end = int(end_pointer["offset"]) - else: - start = int(start_pointer["lineNumber"]) - end = int(end_pointer["lineNumber"]) - if start > end: - raise SPDXValueError("Snippet::ranges") - - return start, end - - def validate_range_and_get_type(self, range_dict: Dict) -> RangeType: - if ("startPointer" not in range_dict) or ("endPointer" not in range_dict): - raise SPDXValueError("Snippet::ranges") - start_pointer_type = self.validate_pointer_and_get_type(range_dict["startPointer"]) - end_pointer_type = self.validate_pointer_and_get_type(range_dict["endPointer"]) - if start_pointer_type != end_pointer_type: - raise SPDXValueError("Snippet::ranges") - return start_pointer_type - - def validate_pointer_and_get_type(self, pointer: Dict) -> RangeType: - if self.snippet.snip_from_file_spdxid != pointer["reference"]: - raise SPDXValueError("Snippet::ranges") - if ("offset" in pointer and "lineNumber" in pointer) or ( - "offset" not in pointer and "lineNumber" not in pointer): - raise SPDXValueError("Snippet::ranges") - - return RangeType.BYTE if "offset" in pointer else RangeType.LINE - - -class ReviewParser(BaseParser): - def __init__(self, builder, logger): - super(ReviewParser, self).__init__(builder, logger) - - def parse_reviews(self, reviews): - """ - Parse Review Information fields - - reviews: Python list with Review Information dicts in it - """ - if isinstance(reviews, list): - for review in reviews: - if isinstance(review, dict): - if self.parse_review_reviewer(review.get("reviewer")): - self.parse_review_date(review.get("reviewDate")) - self.parse_review_comment(review.get("comment")) - else: - self.value_error("REVIEW", review) - - def parse_review_reviewer(self, reviewer): - """ - Parse Review reviewer - - reviewer: Python str/unicode - """ - if isinstance(reviewer, str): - entity = self.builder.create_entity(self.document, reviewer) - try: - return self.builder.add_reviewer(self.document, entity) - except SPDXValueError: - self.value_error("REVIEWER_VALUE", reviewer) - else: - self.value_error("REVIEWER_VALUE", reviewer) - - def parse_review_date(self, review_date): - """ - Parse Review date - - review_date: Python str/unicode (ISO-8601 representation of datetime) - """ - if isinstance(review_date, str): - try: - return self.builder.add_review_date(self.document, review_date) - except SPDXValueError: - self.value_error("REVIEW_DATE", review_date) - except CardinalityError: - self.more_than_one_error("REVIEW_DATE") - except OrderError: - self.order_error("REVIEW_DATE", "REVIEWER_VALUE") - else: - self.value_error("REVIEW_DATE", review_date) - - def parse_review_comment(self, review_comment): - """ - Parse Review comment - - review_comment: Python str/unicode - """ - if isinstance(review_comment, str): - try: - return self.builder.add_review_comment(self.document, review_comment) - except CardinalityError: - self.more_than_one_error("REVIEW_COMMENT") - except OrderError: - self.order_error("REVIEW_COMMENT", "REVIEWER_VALUE") - elif review_comment is not None: - self.value_error("REVIEW_COMMENT", review_comment) - - -class FileParser(BaseParser): - def __init__(self, builder, logger): - super(FileParser, self).__init__(builder, logger) - - def parse_file(self, file): - """ - Parse File Information fields - - file: Python dict with File Information fields in it - """ - if isinstance(file, dict): - self.parse_file_name(file.get("fileName")) - self.parse_file_id(file.get("SPDXID")) - self.parse_file_types(file.get("fileTypes")) - self.parse_file_concluded_license(file.get("licenseConcluded")) - self.parse_file_license_info_in_files(file.get("licenseInfoInFiles")) - self.parse_file_license_comments(file.get("licenseComments")) - self.parse_file_copyright_text(file.get("copyrightText")) - self.parse_file_artifacts(file.get("artifactOf")) - self.parse_file_comment(file.get("comment")) - self.parse_file_notice_text(file.get("noticeText")) - self.parse_file_contributors(file.get("fileContributors")) - self.parse_file_attribution_text(file.get("attributionTexts")) - self.parse_file_dependencies(file.get("fileDependencies")) - self.parse_annotations(file.get("annotations"), spdx_id=file.get("SPDXID")) - self.parse_file_checksums(file.get("checksums")) - else: - self.value_error("FILE", file) - - def parse_file_name(self, file_name): - """ - Parse File name - - file_name: Python str/unicode - """ - if isinstance(file_name, str): - return self.builder.set_file_name(self.document, file_name) - self.value_error("FILE_NAME", file_name) - return self.builder.set_file_name(self.document, "dummy_file") - # file_name is set even if it is None or not string. If weren't, the other attributes - # would be added to the file previously added. - # Another approach is to skip the whole file itself - - def parse_file_id(self, file_id): - """ - Parse File id - - file_id: Python str/unicode - """ - if isinstance(file_id, str): - try: - return self.builder.set_file_spdx_id(self.document, file_id) - except SPDXValueError: - self.value_error("FILE_ID", file_id) - except CardinalityError: - self.more_than_one_error("FILE_ID") - except OrderError: - self.order_error("FILE_ID", "FILE_NAME") - else: - self.value_error("FILE_ID", file_id) - - def parse_file_types(self, file_types): - """ - Parse File types - - file_types: Python list of file types (str/unicode: fileType_archive, fileType_binary, fileType_source or - fileType_other) - """ - if isinstance(file_types, list): # file_types is an array in JSON examples... - for file_type in file_types: - self.parse_file_type(file_type) - # ...but file.File allows only one type at the moment. - elif isinstance(file_types, str): - return self.parse_file_type(file_types) - elif file_types is not None: - self.value_error("FILE_TYPES", file_types) - - def parse_file_type(self, file_type): - """ - Parse File type - - file_type: Python str/unicode (fileType_archive, fileType_binary, fileType_source or fileType_other) - """ - if isinstance(file_type, str): - try: - return self.builder.set_file_type(self.document, file_type) - except SPDXValueError: - self.value_error("FILE_TYPE", file_type) - except OrderError: - self.order_error("FILE_TYPE", "FILE_NAME") - else: - self.value_error("FILE_TYPE", file_type) - - def parse_file_concluded_license(self, concluded_license): - """ - Parse File concluded license - - concluded_license: Python str/unicode - """ - if isinstance(concluded_license, str): - lic_parser = utils.LicenseListParser() - lic_parser.build(write_tables=0, debug=0) - license_object = self.replace_license(lic_parser.parse(concluded_license)) - try: - return self.builder.set_concluded_license(self.document, license_object) - except SPDXValueError: - self.value_error("FILE_SINGLE_LICS", concluded_license) - except CardinalityError: - self.more_than_one_error("FILE_SINGLE_LICS") - except OrderError: - self.order_error("FILE_SINGLE_LICS", "FILE_NAME") - elif concluded_license is not None: - self.value_error("FILE_SINGLE_LICS", concluded_license) - - def parse_file_license_info_in_files(self, license_info_in_files): - """ - Parse File license information from files - - license_info_from_files: Python list of licenses information from files (str/unicode) - """ - if isinstance(license_info_in_files, list): - for license_info_in_file in license_info_in_files: - if isinstance(license_info_in_file, str): - lic_parser = utils.LicenseListParser() - lic_parser.build(write_tables=0, debug=0) - license_object = self.replace_license( - lic_parser.parse(license_info_in_file) - ) - try: - self.builder.set_file_license_in_file( - self.document, license_object - ) - except SPDXValueError: - self.value_error("FILE_LIC_IN_FILES", license_info_in_file) - except OrderError: - self.order_error("FILE_LIC_IN_FILES", "FILE_NAME") - else: - self.value_error("FILE_LIC_IN_FILES", license_info_in_file) - elif license_info_in_files is not None: - self.value_error("FILE_LIC_IN_FILES_FIELD", license_info_in_files) - - def parse_file_license_comments(self, license_comments): - """ - Parse File license comments - - license_comments: Python str/unicode - """ - if isinstance(license_comments, str): - try: - return self.builder.set_file_license_comment( - self.document, license_comments - ) - except CardinalityError: - self.more_than_one_error("FILE_LIC_COMMENTS") - except OrderError: - self.order_error("FILE_LIC_COMMENTS", "FILE_NAME") - elif license_comments is not None: - self.value_error("FILE_LIC_COMMENTS", license_comments) - - def parse_file_attribution_text(self, file_attribution_texts): - """ - Parse File attribution texts - - file_attribution_texts: list in yaml, json and string in xml format - """ - if isinstance(file_attribution_texts, list): - for file_attribution_text in file_attribution_texts: - try: - return self.builder.set_file_attribution_text( - self.document, file_attribution_text - ) - except CardinalityError: - self.more_than_one_error("FILE_ATTRIBUTION_TEXT") - except OrderError: - self.order_error("FILE_ATTRIBUTION_TEXT", "FILE_NAME") - else: - self.value_error("FILE_ATTRIBUTION_TEXT", file_attribution_texts) - elif isinstance(file_attribution_texts, str): - try: - return self.builder.set_file_attribution_text( - self.document, file_attribution_texts - ) - except CardinalityError: - self.more_than_one_error("FILE_ATTRIBUTION_TEXT") - except OrderError: - self.order_error("FILE_ATTRIBUTION_TEXT", "FILE_NAME") - - def parse_file_copyright_text(self, copyright_text): - """ - Parse File copyright text - - copyright_text: Python str/unicode - """ - if isinstance(copyright_text, str): - try: - return self.builder.set_file_copyright(self.document, copyright_text) - except CardinalityError: - self.more_than_one_error("FILE_COPYRIGHT_TEXT") - except OrderError: - self.order_error("FILE_COPYRIGHT_TEXT", "FILE_NAME") - elif copyright_text is not None: - self.value_error("FILE_COPYRIGHT_TEXT", copyright_text) - - def parse_file_artifacts(self, file_artifacts): - """ - Parse File artifacts - - file_artifacts: Python list of dict('name':str/unicode, 'homePage':str/unicode, 'projectUri':str/unicode) - """ - if isinstance(file_artifacts, list): - for artifact in file_artifacts: - if isinstance(artifact, dict): - self.builder.set_file_atrificat_of_project( - self.document, "name", artifact.get("name", UnKnown()) - ) - self.builder.set_file_atrificat_of_project( - self.document, "home", artifact.get("homePage", UnKnown()) - ) - self.builder.set_file_atrificat_of_project( - self.document, "uri", artifact.get("projectUri", UnKnown()) - ) - return True - else: - self.value_error("ARTIFACT_OF_VALUE", artifact) - elif file_artifacts is not None: - self.value_error("ARTIFACT_OF_FIELD", file_artifacts) - - def parse_file_comment(self, file_comment): - """ - Parse File comment - - file_comment: Python str/unicode - """ - if isinstance(file_comment, str): - try: - return self.builder.set_file_comment(self.document, file_comment) - except CardinalityError: - self.more_than_one_error("FILE_COMMENT") - except OrderError: - self.order_error("FILE_COMMENT", "FILE_NAME") - elif file_comment is not None: - self.value_error("FILE_COMMENT", file_comment) - - def parse_file_notice_text(self, notice_text): - """ - Parse File notice text - - notice_text: Python str/unicode - """ - if isinstance(notice_text, str): - try: - return self.builder.set_file_notice(self.document, notice_text) - except CardinalityError: - self.more_than_one_error("FILE_NOTICE_TEXT") - except OrderError: - self.order_error("FILE_NOTICE_TEXT", "FILE_NAME") - elif notice_text is not None: - self.value_error("FILE_NOTICE_TEXT", notice_text) - - def parse_file_contributors(self, file_contributors): - """ - Parse File contributors - - file_contributors: Python list of contributors (str/unicode) - """ - if isinstance(file_contributors, list): - for contributor in file_contributors: - if isinstance(contributor, str): - try: - self.builder.add_file_contribution(self.document, contributor) - except OrderError: - self.order_error("FILE_CONTRIBUTOR", "FILE_NAME") - else: - self.value_error("FILE_CONTRIBUTOR", contributor) - elif file_contributors is not None: - self.value_error("FILE_CONTRIBUTORS", file_contributors) - - def parse_file_dependencies(self, file_dependencies): - """ - Parse File dependencies - - file_dependencies: Python list of dependencies (str/unicode or file dict as in FileParser.parse_file) - """ - if isinstance(file_dependencies, list): - for dependency in file_dependencies: - dependency = self._handle_file_dependency(dependency) - if isinstance(dependency, str): - try: - self.builder.add_file_dep(self.document, dependency) - except OrderError: - self.order_error("FILE_DEPENDENCY", "FILE_NAME") - else: - self.value_error("FILE_DEPENDENCY", dependency) - elif file_dependencies is not None: - self.value_error("FILE_DEPENDENCIES", file_dependencies) - - def _handle_file_dependency(self, file_dependency): - """ - Helper method that handles file-like dependency - - file_dependency: Python dict as in FileParser.parse_file - return: file name (str/unicode) or None - """ - if isinstance(file_dependency, dict): - filelike_dependency = file_dependency.get("File") - if isinstance(filelike_dependency, dict): - return filelike_dependency.get("name") - return None - return None - - def parse_file_checksums(self, file_checksums: List[Dict]) -> Optional[bool]: - """ - Parse File checksums - - file_checksums: Python List - """ - if isinstance(file_checksums, list): - for checksum in file_checksums: - self.builder.set_file_checksum(self.document, checksum) - return True - if isinstance(file_checksums, str): - # kept for backwards compatibility - try: - return self.builder.set_file_checksum(self.document, file_checksums) - except CardinalityError: - self.more_than_one_error("FILE_CHECKSUM") - except OrderError: - self.order_error("FILE_CHECKSUM", "FILE_NAME") - else: - self.value_error("FILE_CHECKSUM", file_checksums) - - def parse_files(self, files: List[Dict]) -> None: - if files is None: - return - if isinstance(files, list): - for file in files: - self.parse_file(file) - else: - self.value_error("FILES", files) - - - -class PackageParser(BaseParser): - def __init__(self, builder, logger): - super(PackageParser, self).__init__(builder, logger) - - @property - def package(self): - # current package being parsed is the last one - return self.document.packages[-1] - - def parse_package(self, package: Package, method_to_parse_relationship: Callable): - """ - Parse Package Information fields - - package: Python dict with Package Information fields in it - """ - if isinstance(package, dict): - # The builder has the notion of current package, here, we force to start a new one - self.builder.reset_package() - self.parse_pkg_name(package.get("name")) - self.parse_pkg_id(package.get("SPDXID")) - self.parse_pkg_files_analyzed(package.get("filesAnalyzed")) - self.parse_pkg_version(package.get("versionInfo")) - self.parse_pkg_file_name(package.get("packageFileName")) - self.parse_pkg_supplier(package.get("supplier")) - self.parse_pkg_originator(package.get("originator")) - self.parse_pkg_down_location(package.get("downloadLocation")) - self.parse_pkg_verif_code_field(package.get("packageVerificationCode")) - self.parse_pkg_homepage(package.get("homepage")) - self.parse_pkg_source_info(package.get("sourceInfo")) - self.parse_pkg_concluded_license(package.get("licenseConcluded")) - self.parse_pkg_license_info_from_files(package.get("licenseInfoFromFiles")) - self.parse_pkg_declared_license(package.get("licenseDeclared")) - self.parse_pkg_license_comment(package.get("licenseComments")) - self.parse_pkg_copyright_text(package.get("copyrightText")) - self.parse_pkg_summary(package.get("summary")) - self.parse_pkg_comment(package.get("comment")) - self.parse_pkg_description(package.get("description")) - self.parse_annotations(package.get("annotations"), spdx_id=package.get("SPDXID")) - self.parse_pkg_attribution_text(package.get("attributionTexts")) - self.parse_pkg_files(package.get("hasFiles"), method_to_parse_relationship) - self.parse_pkg_checksums(package.get("checksums")) - self.parse_package_external_refs(package.get("externalRefs")) - self.parse_primary_package_purpose(package.get("primaryPackagePurpose")) - self.parse_release_date(package.get("releaseDate")) - self.parse_built_date(package.get("builtDate")) - self.parse_valid_until_date(package.get("validUntilDate")) - else: - self.value_error("PACKAGE", package) - - def parse_pkg_name(self, pkg_name): - """ - Parse Package name - - pkg_name: Python str/unicode - """ - if isinstance(pkg_name, str): - return self.builder.create_package(self.document, pkg_name) - self.value_error("PKG_NAME", pkg_name) - return self.builder.create_package(self.document, "dummy_package") - # pkg_name is set even if it is None or not string. If weren't, the other attributes - # would be added to the package previously added. - # Another approach is to skip the whole package itself - - def parse_pkg_id(self, pkg_id): - """ - Parse Package id - - pkg_id: Python str/unicode - """ - if isinstance(pkg_id, str): - try: - return self.builder.set_pkg_spdx_id(self.document, pkg_id) - except SPDXValueError: - self.value_error("PKG_ID", pkg_id) - except CardinalityError: - self.more_than_one_error("PKG_ID") - else: - self.value_error("PKG_ID", pkg_id) - - def parse_pkg_version(self, pkg_version): - """ - Parse Package version - - pkg_name: Python str/unicode - """ - if isinstance(pkg_version, str): - try: - return self.builder.set_pkg_vers(self.document, pkg_version) - except CardinalityError: - self.more_than_one_error("PKG_VERSION") - except OrderError: - self.order_error("PKG_VERSION", "PKG_NAME") - elif pkg_version is not None: - self.value_error("PKG_VERSION", pkg_version) - - def parse_pkg_file_name(self, pkg_file_name): - """ - Parse Package file name - - pkg_file_name: Python str/unicode - """ - if isinstance(pkg_file_name, str): - try: - return self.builder.set_pkg_file_name(self.document, pkg_file_name) - except CardinalityError: - self.more_than_one_error("PKG_FILE_NAME") - except OrderError: - self.order_error("PKG_FILE_NAME", "PKG_NAME") - elif pkg_file_name is not None: - self.value_error("PKG_FILE_NAME", pkg_file_name) - - def parse_pkg_supplier(self, pkg_supplier): - """ - Parse Package supplier - - pkg_supplier: Python str/unicode - """ - if isinstance(pkg_supplier, str): - entity = self.builder.create_entity(self.document, pkg_supplier) - try: - return self.builder.set_pkg_supplier(self.document, entity) - except SPDXValueError: - self.value_error("PKG_SUPPL_VALUE", pkg_supplier) - except CardinalityError: - self.more_than_one_error("PKG_SUPPL_VALUE") - except OrderError: - self.order_error("PKG_SUPPL_VALUE", "PKG_NAME") - elif pkg_supplier is not None: - self.value_error("PKG_SUPPL_VALUE", pkg_supplier) - - def parse_pkg_originator(self, pkg_originator): - """ - Parse Package originator - - pkg_originator: Python str/unicode - """ - if isinstance(pkg_originator, str): - entity = self.builder.create_entity(self.document, pkg_originator) - try: - return self.builder.set_pkg_originator(self.document, entity) - except SPDXValueError: - self.value_error("PKG_ORIGINATOR_VALUE", pkg_originator) - except CardinalityError: - self.more_than_one_error("PKG_ORIGINATOR_VALUE") - except OrderError: - self.order_error("PKG_ORIGINATOR_VALUE", "PKG_NAME") - elif pkg_originator is not None: - self.value_error("PKG_ORIGINATOR_VALUE", pkg_originator) - - def parse_pkg_down_location(self, pkg_down_location): - """ - Parse Package download location - - pkg_down_location: Python str/unicode - """ - if isinstance(pkg_down_location, str): - try: - return self.builder.set_pkg_down_location( - self.document, pkg_down_location - ) - except CardinalityError: - self.more_than_one_error("PKG_DOWN_LOC") - except OrderError: - self.order_error("PKG_DOWN_LOC", "PKG_NAME") - else: - self.value_error("PKG_DOWN_LOC", pkg_down_location) - - def parse_pkg_files_analyzed(self, pkg_files_analyzed): - """ - Parse Package files analyzed - - pkg_files_analyzed: Python boolean - """ - # Files Analyzed optional - if pkg_files_analyzed is None: - return - - # For XML, this is a string, not a boolean. - # xmltodict doesn't do this translation for us, so we do it here. - if isinstance(pkg_files_analyzed, str): - if pkg_files_analyzed.lower() in ['true', '1']: - pkg_files_analyzed = True - elif pkg_files_analyzed.lower() in ['false', '0']: - pkg_files_analyzed = False - - if isinstance(pkg_files_analyzed, bool): - try: - return self.builder.set_pkg_files_analyzed( - self.document, pkg_files_analyzed - ) - except CardinalityError: - self.more_than_one_error("PKG_FILES_ANALYZED") - else: - self.value_error("PKG_FILES_ANALYZED", pkg_files_analyzed) - - def parse_pkg_verif_code_field(self, pkg_verif_code_field): - """ - Parse Package verification code dict - - pkg_verif_code_field: Python dict('value':str/unicode, 'excludedFilesNames':list) - """ - if not self.package.are_files_analyzed: - if pkg_verif_code_field is not None: - self.value_error("PKG_VERIF_CODE_FIELD", pkg_verif_code_field) - return - - if isinstance(pkg_verif_code_field, dict): - self.parse_pkg_verif_exc_files( - pkg_verif_code_field.get("packageVerificationCodeExcludedFiles") - ) - return self.parse_pkg_verif_code(pkg_verif_code_field.get("packageVerificationCodeValue")) - elif pkg_verif_code_field is not None: - self.value_error("PKG_VERIF_CODE_FIELD", pkg_verif_code_field) - - def parse_pkg_verif_code(self, pkg_verif_code): - """ - Parse Package verification code value - - pkg_verif_code: Python str/unicode - """ - if not self.package.are_files_analyzed: - if pkg_verif_code is not None: - self.value_error("PKG_VERIF_CODE", pkg_verif_code) - return - - if isinstance(pkg_verif_code, str): - try: - return self.builder.set_pkg_verif_code(self.document, pkg_verif_code) - except CardinalityError: - self.more_than_one_error("PKG_VERIF_CODE") - except OrderError: - self.order_error("PKG_VERIF_CODE", "PKG_NAME") - else: - self.value_error("PKG_VERIF_CODE", pkg_verif_code) - - def parse_pkg_verif_exc_files(self, pkg_verif_exc_files): - """ - Parse Package files excluded from verification code - - pkg_verif_exc_files: Python list of files excluded (str/unicode) - """ - if isinstance(pkg_verif_exc_files, list): - for pkg_verif_exc_file in pkg_verif_exc_files: - if isinstance(pkg_verif_exc_file, str): - try: - self.builder.set_pkg_excl_file( - self.document, pkg_verif_exc_file - ) - except OrderError: - self.order_error("PKG_VERIF_EXC_FILE", "PKG_NAME") - else: - self.value_error("PKG_VERIF_EXC_FILE", pkg_verif_exc_file) - elif pkg_verif_exc_files is not None: - self.value_error("PKG_VERIF_EXC_FILE_FIELD", pkg_verif_exc_files) - - def parse_pkg_homepage(self, pkg_homepage): - """ - Parse Package homepage - - pkg_homepage: Python str/unicode - """ - if isinstance(pkg_homepage, str): - try: - return self.builder.set_pkg_home(self.document, pkg_homepage) - except SPDXValueError: - self.value_error("PKG_HOMEPAGE", pkg_homepage) - except CardinalityError: - self.more_than_one_error("PKG_HOMEPAGE") - except OrderError: - self.order_error("PKG_HOMEPAGE", "PKG_NAME") - elif pkg_homepage is not None: - self.value_error("PKG_HOMEPAGE", pkg_homepage) - - def parse_pkg_source_info(self, pkg_source_info): - """ - Parse Package source information - - pkg_source_info: Python str/unicode - """ - if isinstance(pkg_source_info, str): - try: - return self.builder.set_pkg_source_info(self.document, pkg_source_info) - except CardinalityError: - self.more_than_one_error("PKG_SRC_INFO") - except OrderError: - self.order_error("PKG_SRC_INFO", "PKG_NAME") - elif pkg_source_info is not None: - self.value_error("PKG_SRC_INFO", pkg_source_info) - - def parse_pkg_concluded_license(self, pkg_concluded_license): - """ - Parse Package concluded license - - pkg_concluded_license: Python str/unicode - """ - if isinstance(pkg_concluded_license, str): - lic_parser = utils.LicenseListParser() - lic_parser.build(write_tables=0, debug=0) - license_object = self.replace_license( - lic_parser.parse(pkg_concluded_license) - ) - try: - return self.builder.set_pkg_licenses_concluded( - self.document, license_object - ) - except SPDXValueError: - self.value_error("PKG_SINGLE_LICS", pkg_concluded_license) - except CardinalityError: - self.more_than_one_error("PKG_SINGLE_LICS") - except OrderError: - self.order_error("PKG_SINGLE_LICS", "PKG_NAME") - elif pkg_concluded_license is not None: - self.value_error("PKG_SINGLE_LICS", pkg_concluded_license) - - def parse_pkg_license_info_from_files(self, license_info_from_files): - """ - Parse Package license information from files - - license_info_from_files: Python list of licenses information from files (str/unicode) - """ - if not self.package.are_files_analyzed: - if license_info_from_files is not None: - self.value_error("PKG_LIC_FRM_FILES", license_info_from_files) - return - if isinstance(license_info_from_files, list): - for license_info_from_file in license_info_from_files: - if isinstance(license_info_from_file, str): - lic_parser = utils.LicenseListParser() - lic_parser.build(write_tables=0, debug=0) - license_object = self.replace_license( - lic_parser.parse(license_info_from_file) - ) - try: - self.builder.set_pkg_license_from_file( - self.document, license_object - ) - except SPDXValueError: - self.value_error("PKG_LIC_FRM_FILES", license_info_from_file) - except OrderError: - self.order_error("PKG_LIC_FRM_FILES", "PKG_NAME") - else: - self.value_error("PKG_LIC_FRM_FILES", license_info_from_file) - elif license_info_from_files is not None: - self.value_error("PKG_LIC_FRM_FILES_FIELD", license_info_from_files) - - def parse_pkg_attribution_text(self, pkg_attribution_texts): - """ - Parse Package attribution texts - - pkg_attribution_texts: list in yaml, json and string in xml format - """ - if isinstance(pkg_attribution_texts, list) or isinstance( - pkg_attribution_texts, str - ): - for pkg_attribution_text in pkg_attribution_texts: - try: - return self.builder.set_pkg_attribution_text( - self.document, pkg_attribution_text - ) - except CardinalityError: - self.more_than_one_error("PKG_ATTRIBUTION_TEXT") - except OrderError: - self.order_error("PKG_ATTRIBUTION_TEXT", "PKG_NAME") - else: - self.value_error("PKG_ATTRIBUTION_TEXT", pkg_attribution_texts) - - def parse_pkg_declared_license(self, pkg_declared_license): - """ - Parse Package license declared - - pkg_declared_license: Python str/unicode - """ - if isinstance(pkg_declared_license, str): - lic_parser = utils.LicenseListParser() - lic_parser.build(write_tables=0, debug=0) - license_object = self.replace_license( - lic_parser.parse(pkg_declared_license) - ) - try: - return self.builder.set_pkg_license_declared( - self.document, license_object - ) - except SPDXValueError: - self.value_error("PKG_DECL_LIC", pkg_declared_license) - except CardinalityError: - self.more_than_one_error("PKG_DECL_LIC") - except OrderError: - self.order_error("PKG_DECL_LIC", "PKG_NAME") - elif pkg_declared_license is not None: - self.value_error("PKG_DECL_LIC", pkg_declared_license) - - def parse_pkg_license_comment(self, pkg_license_comment): - """ - Parse Package license comment - - pkg_license_comment: Python str/unicode - """ - if isinstance(pkg_license_comment, str): - try: - return self.builder.set_pkg_license_comment( - self.document, pkg_license_comment - ) - except CardinalityError: - self.more_than_one_error("PKG_LIC_COMMENT") - except OrderError: - self.order_error("PKG_LIC_COMMENT", "PKG_NAME") - elif pkg_license_comment is not None: - self.value_error("PKG_LIC_COMMENT", pkg_license_comment) - - def parse_pkg_copyright_text(self, pkg_copyright_text): - """ - Parse Package copyright text - - pkg_copyright_text: Python str/unicode - """ - if isinstance(pkg_copyright_text, str): - try: - return self.builder.set_pkg_cr_text(self.document, pkg_copyright_text) - except CardinalityError: - self.more_than_one_error("PKG_COPYRIGHT_TEXT") - except OrderError: - self.order_error("PKG_COPYRIGHT_TEXT", "PKG_NAME") - elif pkg_copyright_text is not None: - self.value_error("PKG_COPYRIGHT_TEXT", pkg_copyright_text) - - def parse_pkg_summary(self, pkg_summary): - """ - Parse Package summary - - pkg_summary: Python str/unicode - """ - if isinstance(pkg_summary, str): - try: - return self.builder.set_pkg_summary(self.document, pkg_summary) - except CardinalityError: - self.more_than_one_error("PKG_SUMMARY") - except OrderError: - self.order_error("PKG_SUMMARY", "PKG_NAME") - elif pkg_summary is not None: - self.value_error("PKG_SUMMARY", pkg_summary) - - def parse_pkg_comment(self, pkg_comment): - """ - Parse Package comment - - pkg_comment: Python str/unicode - """ - if isinstance(pkg_comment, str): - try: - return self.builder.set_pkg_comment(self.document, pkg_comment) - except CardinalityError: - self.more_than_one_error("PKG_COMMENT") - except OrderError: - self.order_error("PKG_COMMENT", "PKG_NAME") - elif pkg_comment is not None: - self.value_error("PKG_COMMENT", pkg_comment) - - def parse_pkg_description(self, pkg_description): - """ - Parse Package description - - pkg_description: Python str/unicode - """ - if isinstance(pkg_description, str): - try: - return self.builder.set_pkg_desc(self.document, pkg_description) - except CardinalityError: - self.more_than_one_error("PKG_DESCRIPTION") - except OrderError: - self.order_error("PKG_DESCRIPTION", "PKG_NAME") - elif pkg_description is not None: - self.value_error("PKG_DESCRIPTION", pkg_description) - - def parse_pkg_files(self, pkg_has_files: List[str], method_to_parse_relationship: Callable) -> None: - """ - Parse Package files - - pkg_has_files: Python list of spdx_ids - """ - if not self.package.are_files_analyzed: - if pkg_has_files is not None: - self.value_error("PKG_FILES", pkg_has_files) - return - - if isinstance(pkg_has_files, list): - for pkg_file_spdx_id in pkg_has_files: - if isinstance(pkg_file_spdx_id, str): - method_to_parse_relationship(self.package.spdx_id, "CONTAINS", pkg_file_spdx_id) - else: - self.value_error("PKG_FILE", pkg_file_spdx_id) - elif pkg_has_files is not None: - self.value_error("PKG_HAS_FILES", pkg_has_files) - - def parse_pkg_checksums(self, pkg_checksums: List[Dict]) -> Optional[bool]: - """ - Parse Package checksums - - pkg_chksums: Python List - """ - if isinstance(pkg_checksums, list): - for checksum in pkg_checksums: - self.builder.set_pkg_checksum(self.document, checksum) - return True - if isinstance(pkg_checksums, str): - # kept for backwards compatibility - try: - return self.builder.set_pkg_checksum(self.document, pkg_checksums) - except CardinalityError: - self.more_than_one_error("PKG_CHECKSUM") - except OrderError: - self.order_error("PKG_CHECKSUM", "PKG_NAME") - elif pkg_checksums is not None: - self.value_error("PKG_CHECKSUM", pkg_checksums) - - def parse_package_external_refs(self, external_refs: List[Dict]): - if external_refs is None: - return - if not isinstance(external_refs, list): - self.value_error("PACKAGE_EXTERNAL_REFS", external_refs) - return - - for external_ref_dict in external_refs: - external_ref = ExternalPackageRef(category=external_ref_dict["referenceCategory"], - pkg_ext_ref_type=external_ref_dict["referenceType"], - locator=external_ref_dict["referenceLocator"]) - if "comment" in external_ref_dict: - external_ref.comment = external_ref_dict["comment"] - self.package.add_pkg_ext_refs(external_ref) - - def parse_primary_package_purpose(self, primary_package_purpose: str): - if primary_package_purpose is None: - return - primary_package_purpose = primary_package_purpose.replace("-", "_") # OPERATING-SYSTEM -> OPERATING_SYSTEM - if primary_package_purpose not in [purpose.name for purpose in PackagePurpose]: - self.value_error("PRIMARY_PACKAGE_PURPOSE", primary_package_purpose) - return - - purpose_enum = PackagePurpose[primary_package_purpose] - self.package.primary_package_purpose = purpose_enum - - def parse_release_date(self, release_date: str): - if release_date is None: - return - - parsed_date: datetime = utils.datetime_from_iso_format(release_date) - if parsed_date is not None: - self.package.release_date = parsed_date - else: - self.value_error("RELEASE_DATE", release_date) - - def parse_built_date(self, built_date: str): - if built_date is None: - return - - parsed_date: datetime = utils.datetime_from_iso_format(built_date) - if parsed_date is not None: - self.package.built_date = parsed_date - else: - self.value_error("BUILT_DATE", built_date) - - def parse_valid_until_date(self, valid_until_date: str): - if valid_until_date is None: - return - - parsed_date: datetime = utils.datetime_from_iso_format(valid_until_date) - if parsed_date is not None: - self.package.valid_until_date = parsed_date - else: - self.value_error("VALID_UNTIL_DATE", valid_until_date) - -def flatten_document(document): - """ - Flatten document to match current data model. File objects are nested within packages according to hasFiles-tag. - """ - files_by_id = {} - if "files" in document: - for f in document.get("files"): - for checksum in f["checksums"]: - if checksum["algorithm"] == "SHA1" or "sha1" in checksum["algorithm"]: - f["sha1"] = checksum["checksumValue"] - break - files_by_id[f["SPDXID"]] = f - if "packages" in document: - packages = document.get("packages") - for package in packages: - if "hasFiles" in package: - package["files"] = [{ - "File": files_by_id[spdxid.split("#")[-1]]} for spdxid in package["hasFiles"] - ] - for checksum in package.get("checksums", []): - if checksum["algorithm"] == "SHA1" or "sha1" in checksum["algorithm"]: - package["sha1"] = checksum["checksumValue"] - break - - return document - - -class Parser( - CreationInfoParser, - ExternalDocumentRefsParser, - LicenseParser, - AnnotationParser, - RelationshipParser, - SnippetParser, - ReviewParser, - FileParser, - PackageParser, -): - def __init__(self, builder, logger): - super(Parser, self).__init__(builder, logger) - - def json_yaml_set_document(self, data): - # we could verify that the spdxVersion >= 2.2, but we try to be resilient in parsing - if data.get("spdxVersion"): - self.document_object = data - return - self.document_object = data.get("Document") - - def parse(self): - """ - Parse Document Information fields - """ - self.error = False - self.document = document.Document() - self.document_object = flatten_document(self.document_object) - if not isinstance(self.document_object, dict): - self.logger.log("Empty or not valid SPDX Document") - self.error = True - return self.document, self.error - - self.parse_doc_version(self.document_object.get("spdxVersion")) - self.parse_doc_data_license(self.document_object.get("dataLicense")) - self.parse_doc_id(self.document_object.get("SPDXID")) - self.parse_doc_name(self.document_object.get("name")) - self.parse_doc_namespace(self.document_object.get("documentNamespace")) - self.parse_doc_comment(self.document_object.get("comment")) - self.parse_creation_info(self.document_object.get("creationInfo")) - self.parse_external_document_refs( - self.document_object.get("externalDocumentRefs") - ) - self.parse_extracted_license_info( - self.document_object.get("hasExtractedLicensingInfos") - ) - self.parse_annotations(self.document_object.get("annotations"), spdx_id=self.document_object.get("SPDXID")) - self.parse_relationships(self.document_object.get("relationships")) - self.parse_reviews(self.document_object.get("reviewers")) - self.parse_snippets(self.document_object.get("snippets")) - - self.parse_packages(self.document_object.get("packages")) - self.parse_files(self.document_object.get("files")) - - if self.document_object.get("documentDescribes"): - self.parse_doc_described_objects(self.document_object.get("documentDescribes")) - - validation_messages = ErrorMessages() - # Report extra errors if self.error is False otherwise there will be - # redundant messages - validation_messages = self.document.validate(validation_messages) - if not self.error: - if validation_messages: - for msg in validation_messages: - self.logger.log(msg) - self.error = True - - return self.document, self.error - - def parse_doc_version(self, doc_version): - """ - Parse Document version - - doc_version: Python str/unicode - """ - if isinstance(doc_version, str): - try: - return self.builder.set_doc_version(self.document, doc_version) - except SPDXValueError: - self.value_error("DOC_VERS_VALUE", doc_version) - except CardinalityError: - self.more_than_one_error("DOC_VERS_VALUE") - else: - self.value_error("DOC_VERS_VALUE", doc_version) - - def parse_doc_data_license(self, doc_data_license): - """ - Parse Document data license - - doc_data_license: Python str/unicode - """ - try: - return self.builder.set_doc_data_lics(self.document, doc_data_license) - except SPDXValueError: - self.value_error("DOC_D_LICS", doc_data_license) - except CardinalityError: - self.more_than_one_error("DOC_D_LICS") - - def parse_doc_id(self, doc_id): - """ - Parse Document SPDX id - - doc_id: Python str/unicode - """ - if isinstance(doc_id, str): - try: - return self.builder.set_doc_spdx_id(self.document, doc_id) - except SPDXValueError: - self.value_error("DOC_SPDX_ID_VALUE", doc_id) - except CardinalityError: - self.more_than_one_error("DOC_SPDX_ID_VALUE") - else: - self.value_error("DOC_SPDX_ID_VALUE", doc_id) - - def parse_doc_name(self, doc_name): - """ - Parse Document name - - doc_name: Python str/unicode - """ - if isinstance(doc_name, str): - try: - return self.builder.set_doc_name(self.document, doc_name) - except CardinalityError: - self.more_than_one_error("DOC_NAME_VALUE") - else: - self.value_error("DOC_NAME_VALUE", doc_name) - - def parse_doc_namespace(self, doc_namespace): - """ - Parse Document namespace - - doc_namespace: Python str/unicode - """ - if isinstance(doc_namespace, str): - try: - return self.builder.set_doc_namespace(self.document, doc_namespace) - except SPDXValueError: - self.value_error("DOC_NAMESPACE_VALUE", doc_namespace) - except CardinalityError: - self.more_than_one_error("DOC_NAMESPACE_VALUE") - else: - self.value_error("DOC_NAMESPACE_VALUE", doc_namespace) - - def parse_doc_comment(self, doc_comment): - """ - Parse Document comment - - doc_comment: Python str/unicode - """ - if isinstance(doc_comment, str): - try: - return self.builder.set_doc_comment(self.document, doc_comment) - except CardinalityError: - self.more_than_one_error("DOC_COMMENT_VALUE") - elif doc_comment is not None: - self.value_error("DOC_COMMENT_VALUE", doc_comment) - - def parse_doc_described_objects(self, doc_described_objects): - """ - Parse Document documentDescribes (SPDXIDs) - - doc_described_objects: Python list of strings - """ - if isinstance(doc_described_objects, list): - described_spdxids = filter( - lambda described: isinstance(described, str), doc_described_objects - ) - for spdxid in described_spdxids: - self.parse_relationship(self.document.spdx_id, "DESCRIBES", spdxid) - - return True - else: - self.value_error("DOC_DESCRIBES", doc_described_objects) - - def parse_packages(self, packages): - """ - Parse SPDXLite packages list - """ - if packages is None: - return - if isinstance(packages, list): - for package in packages: - self.parse_package(package, self.parse_relationship) - return True - else: - self.value_error("PACKAGES", packages) diff --git a/spdx/parsers/jsonyamlxmlbuilders.py b/spdx/parsers/jsonyamlxmlbuilders.py deleted file mode 100644 index 94d464edc..000000000 --- a/spdx/parsers/jsonyamlxmlbuilders.py +++ /dev/null @@ -1,332 +0,0 @@ -# Copyright (c) Xavier Figueroa -# 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 typing import Dict, Union - -from spdx.document import Document -from spdx.parsers import rdfbuilders -from spdx.parsers import tagvaluebuilders -from spdx.parsers import validations -from spdx.checksum import Checksum, ChecksumAlgorithm -from spdx.parsers.builderexceptions import SPDXValueError -from spdx.parsers.builderexceptions import CardinalityError -from spdx.parsers.builderexceptions import OrderError - - -class CreationInfoBuilder(rdfbuilders.CreationInfoBuilder): - def __init__(self): - super(CreationInfoBuilder, self).__init__() - - -class ExternalDocumentRefsBuilder(rdfbuilders.ExternalDocumentRefBuilder): - def __init__(self): - super(ExternalDocumentRefsBuilder, self).__init__() - - -class EntityBuilder(rdfbuilders.EntityBuilder): - def __init__(self): - super(EntityBuilder, self).__init__() - - -class SnippetBuilder(rdfbuilders.SnippetBuilder): - def __init__(self): - super(SnippetBuilder, self).__init__() - - -class ReviewBuilder(rdfbuilders.ReviewBuilder): - def __init__(self): - super(ReviewBuilder, self).__init__() - - -class PackageBuilder(rdfbuilders.PackageBuilder): - def __init__(self): - super(PackageBuilder, self).__init__() - - -class DocBuilder(tagvaluebuilders.DocBuilder): - def __init__(self): - super(DocBuilder, self).__init__() - - def set_doc_spdx_id(self, doc, doc_spdx_id_line): - """ - Set the document SPDX Identifier. - Raise SPDXValueError if malformed value, CardinalityError - if already defined. - """ - if not self.doc_spdx_id_set: - if ( - doc_spdx_id_line == "SPDXRef-DOCUMENT" - or validations.validate_doc_spdx_id(doc_spdx_id_line) - ): - doc.spdx_id = doc_spdx_id_line - self.doc_spdx_id_set = True - return True - else: - raise SPDXValueError("Document::SPDXID") - else: - raise CardinalityError("Document::SPDXID") - - def set_doc_comment(self, doc, comment): - """ - Set document comment. - Raise CardinalityError if comment already set. - """ - if not self.doc_comment_set: - self.doc_comment_set = True - doc.comment = comment - else: - raise CardinalityError("Document::Comment") - - def set_doc_namespace(self, doc, namespace): - """ - Set the document namespace. - Raise SPDXValueError if malformed value. - Raise CardinalityError if already defined. - """ - if not self.doc_namespace_set: - self.doc_namespace_set = True - if validations.validate_doc_namespace(namespace): - doc.namespace = namespace - return True - else: - raise SPDXValueError("Document::Namespace") - else: - raise CardinalityError("Document::Comment") - - -class LicenseBuilder(tagvaluebuilders.LicenseBuilder): - def __init__(self): - super(LicenseBuilder, self).__init__() - - def set_lic_name(self, doc, name): - """ - Set license name. - Raise SPDXValueError if name is not str or utils.NoAssert - Raise CardinalityError if it is already set - Raise OrderError if no license id defined. - """ - if self.has_extr_lic(doc): - if not self.extr_lic_name_set: - self.extr_lic_name_set = True - if validations.validate_extr_lic_name(name, True): - self.extr_lic(doc).full_name = name - return True - else: - raise SPDXValueError("ExtractedLicense::Name") - else: - raise CardinalityError("ExtractedLicense::Name") - else: - raise OrderError("ExtractedLicense::Name") - - def set_lic_text(self, doc, text): - """ - Set license name. - Raise CardinalityError if it is already set. - Raise OrderError if no license id defined. - """ - if self.has_extr_lic(doc): - if not self.extr_text_set: - self.extr_text_set = True - self.extr_lic(doc).text = text - return True - else: - raise CardinalityError("ExtractedLicense::text") - else: - raise OrderError("ExtractedLicense::text") - - def set_lic_comment(self, doc, comment): - """ - Set license comment. - Raise CardinalityError if it is already set. - Raise OrderError if no license ID defined. - """ - if self.has_extr_lic(doc): - if not self.extr_lic_comment_set: - self.extr_lic_comment_set = True - self.extr_lic(doc).comment = comment - return True - else: - raise CardinalityError("ExtractedLicense::comment") - else: - raise OrderError("ExtractedLicense::comment") - - -class FileBuilder(rdfbuilders.FileBuilder): - def __init__(self): - super(FileBuilder, self).__init__() - - def set_file_checksum(self, doc: Document, checksum: Union[Dict, Checksum, str]) -> bool: - """ - Set the file checksum. - checksum - A string - raise OrderError if no file defined. - """ - if not self.has_file(doc): - raise OrderError("No file for checksum defined.") - - if isinstance(checksum, dict): - algo = checksum.get('algorithm') or 'SHA1' - identifier = ChecksumAlgorithm.checksum_algorithm_from_string(algo) - self.file(doc).set_checksum(Checksum(identifier, checksum.get('checksumValue'))) - elif isinstance(checksum, Checksum): - self.file(doc).set_checksum(checksum) - elif isinstance(checksum, str): - self.file(doc).set_checksum(Checksum(ChecksumAlgorithm.SHA1, checksum)) - return True - - def set_file_notice(self, doc, text): - """ - Set file notice - Raise OrderError if no file defined. - Raise CardinalityError if more than one. - """ - if not self.has_file(doc): - raise OrderError("File::Notice") - - self.file_notice_set = True - self.file(doc).notice = text - return True - - def set_file_type(self, doc, type_value): - """ - Wrap rdfbuilders.FileBuilder.set_file_type to match the different - fileType representations. - This method does not make much sense as it converts the file type (e.g. SOURCE) - to rdf format (e.g. fileType_source) which is then converted back. - But this seems to be the kind of workflow that is currently in use here. - """ - - return super(FileBuilder, self).set_file_type(doc, f"namespace#fileType_{type_value.lower()}") - - def set_file_copyright(self, doc, text): - """ - Raise OrderError if no file defined. - Raise CardinalityError if more than one. - """ - if not self.has_file(doc): - raise OrderError("File::CopyRight") - if self.file_copytext_set: - raise CardinalityError("File::CopyRight") - self.file_copytext_set = True - self.file(doc).copyright = text - return True - - def set_file_license_comment(self, doc, text): - """ - Raise OrderError if no file defined. - Raise CardinalityError if more than one per file. - """ - if not self.has_file(doc): - raise OrderError("File::LicenseComment") - if self.file_license_comment_set: - raise CardinalityError("File::LicenseComment") - self.file(doc).license_comment = text - return True - - def set_file_attribution_text(self, doc, text): - """ - Set the file's attribution text. - """ - if self.has_file(doc): - self.file(doc).attribution_text = text - return True - - def set_file_comment(self, doc, text): - """ - Raise OrderError if no file defined. - Raise CardinalityError if more than one comment set. - """ - if not self.has_file(doc): - raise OrderError("File::Comment") - if self.file_comment_set: - raise CardinalityError("File::Comment") - self.file_comment_set = True - self.file(doc).comment = text - return True - - -class AnnotationBuilder(tagvaluebuilders.AnnotationBuilder): - def __init__(self): - super(AnnotationBuilder, self).__init__() - - def add_annotation_comment(self, doc, comment): - """ - Set the annotation comment. - Raise CardinalityError if already set. - Raise OrderError if no annotator defined before. - """ - if len(doc.annotations) != 0: - if not self.annotation_comment_set: - self.annotation_comment_set = True - doc.annotations[-1].comment = comment - return True - else: - raise CardinalityError("AnnotationComment") - else: - raise OrderError("AnnotationComment") - - -class RelationshipBuilder(tagvaluebuilders.RelationshipBuilder): - def __init__(self): - super(RelationshipBuilder, self).__init__() - - def add_relationship_comment(self, doc, comment): - """ - Set the relationship comment. - Raise CardinalityError if already set. - Raise OrderError if no annotator defined before. - """ - if len(doc.relationships) != 0: - if not self.relationship_comment_set: - self.relationship_comment_set = True - doc.relationships[-1].comment = comment - return True - else: - raise CardinalityError("RelationshipComment") - else: - raise OrderError("RelationshipComment") - - -class Builder( - DocBuilder, - CreationInfoBuilder, - ExternalDocumentRefsBuilder, - EntityBuilder, - SnippetBuilder, - ReviewBuilder, - LicenseBuilder, - FileBuilder, - PackageBuilder, - AnnotationBuilder, - RelationshipBuilder, -): - """ - SPDX document builder. - """ - - def __init__(self): - super(Builder, self).__init__() - # FIXME: this state does not make sense - self.reset() - - def reset(self): - """ - Reset builder's state for building new documents. - Must be called between usage with different documents. - """ - # FIXME: this state does not make sense - self.reset_creation_info() - self.reset_document() - self.reset_package() - self.reset_file_stat() - self.reset_reviews() - self.reset_annotations() - self.reset_relationship() - self.reset_extr_lics() diff --git a/spdx/parsers/lexers/tagvalue.py b/spdx/parsers/lexers/tagvalue.py deleted file mode 100644 index 83c709a0d..000000000 --- a/spdx/parsers/lexers/tagvalue.py +++ /dev/null @@ -1,252 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 ply import lex - - -class Lexer(object): - reserved = { - # Top level fields - "SPDXVersion": "DOC_VERSION", - "DataLicense": "DOC_LICENSE", - "DocumentName": "DOC_NAME", - "SPDXID": "SPDX_ID", - "DocumentComment": "DOC_COMMENT", - "DocumentNamespace": "DOC_NAMESPACE", - "ExternalDocumentRef": "EXT_DOC_REF", - # Creation info - "Creator": "CREATOR", - "Created": "CREATED", - "CreatorComment": "CREATOR_COMMENT", - "LicenseListVersion": "LIC_LIST_VER", - # Review info - "Reviewer": "REVIEWER", - "ReviewDate": "REVIEW_DATE", - "ReviewComment": "REVIEW_COMMENT", - # Annotation info - "Annotator": "ANNOTATOR", - "AnnotationDate": "ANNOTATION_DATE", - "AnnotationComment": "ANNOTATION_COMMENT", - "AnnotationType": "ANNOTATION_TYPE", - "SPDXREF": "ANNOTATION_SPDX_ID", - # Relationships - "Relationship": "RELATIONSHIP", - "RelationshipComment": "RELATIONSHIP_COMMENT", - # Package Fields - "PackageName": "PKG_NAME", - "PackageVersion": "PKG_VERSION", - "PackageDownloadLocation": "PKG_DOWN", - "FilesAnalyzed": "PKG_FILES_ANALYZED", - "PackageSummary": "PKG_SUM", - "PackageSourceInfo": "PKG_SRC_INFO", - "PackageFileName": "PKG_FILE_NAME", - "PackageSupplier": "PKG_SUPPL", - "PackageOriginator": "PKG_ORIG", - "PackageChecksum": "PKG_CHKSUM", - "PackageVerificationCode": "PKG_VERF_CODE", - "PackageDescription": "PKG_DESC", - "PackageComment": "PKG_COMMENT", - "PackageLicenseDeclared": "PKG_LICS_DECL", - "PackageLicenseConcluded": "PKG_LICS_CONC", - "PackageLicenseInfoFromFiles": "PKG_LICS_FFILE", - "PackageLicenseComments": "PKG_LICS_COMMENT", - "PackageCopyrightText": "PKG_CPY_TEXT", - "PackageHomePage": "PKG_HOME", - "ExternalRef": "PKG_EXT_REF", - "ExternalRefComment": "PKG_EXT_REF_COMMENT", - "PackageAttributionText": "PKG_ATTRIBUTION_TEXT", - "PrimaryPackagePurpose": "PRIMARY_PACKAGE_PURPOSE", - "BuiltDate": "BUILT_DATE", - "ReleaseDate": "RELEASE_DATE", - "ValidUntilDate": "VALID_UNTIL_DATE", - # Files - "FileName": "FILE_NAME", - "FileType": "FILE_TYPE", - "FileChecksum": "FILE_CHKSUM", - "LicenseConcluded": "FILE_LICS_CONC", - "LicenseInfoInFile": "FILE_LICS_INFO", - "FileCopyrightText": "FILE_CR_TEXT", - "LicenseComments": "FILE_LICS_COMMENT", - "FileComment": "FILE_COMMENT", - "FileNotice": "FILE_NOTICE", - "FileContributor": "FILE_CONTRIB", - "FileDependency": "FILE_DEP", - "ArtifactOfProjectName": "ART_PRJ_NAME", - "ArtifactOfProjectHomePage": "ART_PRJ_HOME", - "ArtifactOfProjectURI": "ART_PRJ_URI", - "FileAttributionText": "FILE_ATTRIBUTION_TEXT", - # License - "LicenseID": "LICS_ID", - "ExtractedText": "LICS_TEXT", - "LicenseName": "LICS_NAME", - "LicenseCrossReference": "LICS_CRS_REF", - "LicenseComment": "LICS_COMMENT", - # Snippet - "SnippetSPDXID": "SNIPPET_SPDX_ID", - "SnippetName": "SNIPPET_NAME", - "SnippetComment": "SNIPPET_COMMENT", - "SnippetCopyrightText": "SNIPPET_CR_TEXT", - "SnippetLicenseComments": "SNIPPET_LICS_COMMENT", - "SnippetFromFileSPDXID": "SNIPPET_FILE_SPDXID", - "SnippetLicenseConcluded": "SNIPPET_LICS_CONC", - "LicenseInfoInSnippet": "SNIPPET_LICS_INFO", - "SnippetAttributionText": "SNIPPET_ATTRIBUTION_TEXT", - "SnippetByteRange": "SNIPPET_BYTE_RANGE", - "SnippetLineRange": "SNIPPET_LINE_RANGE", - # Common - "NOASSERTION": "NO_ASSERT", - "UNKNOWN": "UN_KNOWN", - "NONE": "NONE", - "SOURCE": "SOURCE", - "BINARY": "BINARY", - "ARCHIVE": "ARCHIVE", - "APPLICATION": "APPLICATION", - "AUDIO": "AUDIO", - "IMAGE": "IMAGE", - "TEXT": "FILETYPE_TEXT", - "VIDEO": "VIDEO", - "DOCUMENTATION": "DOCUMENTATION", - "SPDX": "SPDX", - "OTHER": "OTHER", - "REVIEW": "REVIEW", - "FRAMEWORK": "FRAMEWORK", - "LIBRARY": "LIBRARY", - "CONTAINER": "CONTAINER", - "OPERATING-SYSTEM": "OPERATING_SYSTEM", - "DEVICE": "DEVICE", - "FIRMWARE": "FIRMWARE", - "FILE": "FILE", - "INSTALL": "INSTALL" - } - states = (("text", "exclusive"),) - - tokens = [ - "TEXT", - "TOOL_VALUE", - "UNKNOWN_TAG", - "ORG_VALUE", - "PERSON_VALUE", - "DATE", - "LINE", - "RANGE", - "CHKSUM", - "DOC_REF_ID", - "DOC_URI", - "EXT_DOC_REF_CHKSUM", - ] + list(reserved.values()) - - def t_text(self, t): - r":\s*" - t.lexer.text_start = t.lexer.lexpos - len("") - t.lexer.begin("text") - - def t_text_end(self, t): - r"\s*" - t.type = "TEXT" - t.value = t.lexer.lexdata[t.lexer.text_start: t.lexer.lexpos] - t.lexer.lineno += t.value.count("\n") - t.value = t.value.strip() - t.lexer.begin("INITIAL") - return t - - def t_text_any(self, t): - r".|\n" - pass - - def t_text_error(self, t): - print("Lexer error in text state") - - def t_CHKSUM(self, t): - r":\s*(ADLER32|BLAKE2b-256|BLAKE2b-384|BLAKE2b-512|BLAKE3|MD2|MD4|MD5|MD6|" \ - "SHA1|SHA224|SHA256|SHA384|SHA512|SHA3-256|SHA3-384|SHA3-512):\s*([a-fA-F0-9]*)" - t.value = t.value[1:].strip() - return t - - def t_RANGE(self, t): - r":\s*\d+:\d+" - t.value = t.value[1:].strip() - return t - - def t_DOC_REF_ID(self, t): - r":\s*DocumentRef-([A-Za-z0-9\+\.\-]+)" - t.value = t.value[1:].strip() - return t - - def t_DOC_URI(self, t): - r"\s*((ht|f)tps?:\/\/\S*)" - t.value = t.value.strip() - return t - - def t_EXT_DOC_REF_CHKSUM(self, t): - r"\s*SHA1:\s*[a-f0-9]{40,40}" - t.value = t.value[1:].strip() - return t - - def t_TOOL_VALUE(self, t): - r":\s*Tool:.+" - t.value = t.value[1:].strip() - return t - - def t_ORG_VALUE(self, t): - r":\s*Organization:.+" - t.value = t.value[1:].strip() - return t - - def t_PERSON_VALUE(self, t): - r":\s*Person:.+" - t.value = t.value[1:].strip() - return t - - def t_DATE(self, t): - r":\s*\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ" - t.value = t.value[1:].strip() - return t - - def t_KEYWORD_AS_TAG(self, t): - r"[a-zA-Z]+" - t.type = self.reserved.get(t.value, "UNKNOWN_TAG") - t.value = t.value.strip() - return t - - def t_LINE_OR_KEYWORD_VALUE(self, t): - r":.+" - t.value = t.value[1:].strip() - if t.value in self.reserved.keys(): - t.type = self.reserved[t.value] - else: - t.type = "LINE" - return t - - def t_comment(self, t): - r"\#.*" - pass - - def t_newline(self, t): - r"\n+" - t.lexer.lineno += len(t.value) - - def t_whitespace(self, t): - r"\s+" - pass - - def build(self, **kwargs): - self.lexer = lex.lex(module=self, **kwargs) - - def token(self): - return self.lexer.token() - - def input(self, data): - self.lexer.input(data) - - def t_error(self, t): - t.lexer.skip(1) - t.value = "Lexer error" - return t diff --git a/spdx/parsers/loggers.py b/spdx/parsers/loggers.py deleted file mode 100644 index a74715f77..000000000 --- a/spdx/parsers/loggers.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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. - - -class StandardLogger(object): - def log(self, msg): - print(msg) - - -class FileLogger(object): - def __init__(self, logfile): - self.dest = logfile - - def log(self, msg): - self.dest.write(msg + "\n") - - -class ErrorMessages: - - def __init__(self): - self.messages = [] - self.context = [] - - def push_context(self, context): - """push some context information to better identify where is the problem""" - self.context.append(context) - - def pop_context(self): - """pop the last context information""" - self.context.pop() - - def append(self, message, *args, **kwargs): - """add a message with standard python format - the current context is prefixed to the message - """ - message = message.format(*args, **kwargs) - message = "".join([c + ": " for c in self.context if c]) + message - self.messages.append(message) - - def __iter__(self): - return self.messages.__iter__() - - def __bool__(self): - return len(self.messages)>0 - - def __nonzero__(self): - return len(self.messages)>0 - - def __eq__(self, b): - if isinstance(b, ErrorMessages): - return self.messages == b.messages - return self.messages == b diff --git a/spdx/parsers/parse_anything.py b/spdx/parsers/parse_anything.py deleted file mode 100644 index 329ea6944..000000000 --- a/spdx/parsers/parse_anything.py +++ /dev/null @@ -1,51 +0,0 @@ -# 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. - - -import spdx.file as spdxfile -from spdx.parsers import jsonparser -from spdx.parsers import yamlparser -from spdx.parsers import rdf -from spdx.parsers import xmlparser -from spdx.parsers import tagvalue -from spdx.parsers.loggers import StandardLogger -from spdx.parsers import jsonyamlxmlbuilders, tagvaluebuilders, rdfbuilders -from spdx.parsers.builderexceptions import FileTypeError - - -def parse_file(fn): - builder_module = jsonyamlxmlbuilders - read_data = False - if fn.endswith(".rdf") or fn.endswith(".rdf.xml"): - parsing_module = rdf - builder_module = rdfbuilders - elif fn.endswith(".tag") or fn.endswith(".spdx"): - parsing_module = tagvalue - builder_module = tagvaluebuilders - read_data = True - elif fn.endswith(".json"): - parsing_module = jsonparser - elif fn.endswith(".xml"): - parsing_module = xmlparser - elif fn.endswith(".yaml") or fn.endswith(".yml"): - parsing_module = yamlparser - else: - raise FileTypeError("FileType Not Supported" + str(fn)) - - p = parsing_module.Parser(builder_module.Builder(), StandardLogger()) - if hasattr(p, "build"): - p.build() - with open(fn) as f: - if read_data: - data = f.read() - return p.parse(data) - else: - return p.parse(f) diff --git a/spdx/parsers/rdf.py b/spdx/parsers/rdf.py deleted file mode 100644 index c78d3ca56..000000000 --- a/spdx/parsers/rdf.py +++ /dev/null @@ -1,1518 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 re - -from functools import reduce - -from rdflib import Graph -from rdflib import Namespace -from rdflib import RDF -from rdflib import RDFS - -from spdx import document -from spdx import license -from spdx import utils -from spdx.checksum import Checksum, ChecksumAlgorithm -from spdx.parsers.builderexceptions import CardinalityError -from spdx.parsers.builderexceptions import SPDXValueError -from spdx.parsers.loggers import ErrorMessages - - -ERROR_MESSAGES = { - "DOC_VERS_VALUE": "Invalid specVersion '{0}' must be SPDX-M.N where M and N are numbers.", - "DOC_D_LICS": "Invalid dataLicense '{0}' must be http://spdx.org/licenses/CC0-1.0.", - "DOC_SPDX_ID_VALUE": "Invalid SPDXID value, SPDXID must be the document namespace appended " - 'by "#SPDXRef-DOCUMENT", line: {0}', - "DOC_NAMESPACE_VALUE": 'Invalid DocumentNamespace value {0}, must contain a scheme (e.g. "https:") ' - 'and should not contain the "#" delimiter.', - "LL_VALUE": "Invalid licenseListVersion '{0}' must be of the format N.N where N is a number", - "CREATED_VALUE": "Invalid created value '{0}' must be date in ISO 8601 format.", - "CREATOR_VALUE": "Invalid creator value '{0}' must be Organization, Tool or Person.", - "EXT_DOC_REF_VALUE": "Failed to extract {0} from ExternalDocumentRef.", - "PKG_SPDX_ID_VALUE": 'SPDXID must be "SPDXRef-[idstring]" where [idstring] is a unique string containing ' - 'letters, numbers, ".", "-".', - "PKG_SUPPL_VALUE": "Invalid package supplier value '{0}' must be Organization, Person or NOASSERTION.", - "PKG_ORIGINATOR_VALUE": "Invalid package supplier value '{0}' must be Organization, Person or NOASSERTION.", - "PKG_DOWN_LOC": "Invalid package download location value '{0}' must be a url or NONE or NOASSERTION", - "PKG_FILES_ANALYZED_VALUE": "FilesAnalyzed must be a boolean value, line: {0}", - "PKG_CONC_LIST": "Package concluded license list must have more than one member", - "LICS_LIST_MEMBER": "Declarative or Conjunctive license set member must be a license url or identifier", - "PKG_SINGLE_LICS": "Package concluded license must be a license url or spdx:noassertion or spdx:none.", - "PKG_LICS_INFO_FILES": "Package licenseInfoFromFiles must be a license or spdx:none or spdx:noassertion", - "FILE_SPDX_ID_VALUE": 'SPDXID must be "SPDXRef-[idstring]" where [idstring] is a unique string containing ' - 'letters, numbers, ".", "-".', - "PKG_EXT_REF_CATEGORY": '\'{0}\' must be "SECURITY", "PACKAGE-MANAGER", or "OTHER".', - "PKG_EXT_REF_TYPE": '{0} must be a unique string containing letters, numbers, ".", or "-".', - "FILE_TYPE": "Unknown file type.", - "FILE_SINGLE_LICS": "File concluded license must be a license url or spdx:noassertion or spdx:none.", - "REVIEWER_VALUE": "Invalid reviewer value '{0}' must be Organization, Tool or Person.", - "REVIEW_DATE": "Invalid review date value '{0}' must be date in ISO 8601 format.", - "ANNOTATOR_VALUE": "Invalid annotator value '{0}' must be Organization, Tool or Person.", - "ANNOTATION_DATE": "Invalid annotation date value '{0}' must be date in ISO 8601 format.", - "SNIPPET_SPDX_ID_VALUE": 'SPDXID must be "SPDXRef-[idstring]" where [idstring] is a unique string ' - 'containing letters, numbers, ".", "-".', - "SNIPPET_SINGLE_LICS": "Snippet Concluded License must be a license url or spdx:noassertion or spdx:none.", - "SNIPPET_LIC_INFO": "License Information in Snippet must be a license url or a reference " - "to the license, denoted by LicenseRef-[idstring] or spdx:noassertion or spdx:none.", - "RELATIONSHIP": "relationship type must be of supported type", -} - - -def convert_rdf_checksum_algorithm(rdf_checksum_algorithm: str) -> ChecksumAlgorithm: - split_string = rdf_checksum_algorithm.split('#') - if len(split_string) != 2: - raise SPDXValueError('Unknown checksum algorithm {}'.format(rdf_checksum_algorithm)) - checksum_algorithm = ChecksumAlgorithm.checksum_from_rdf(split_string[1]) - return checksum_algorithm - - -class BaseParser(object): - """ - Base class for all parsers. - Contains logger, doap_namespace, spdx_namespace and model builder. - Also provides utility functions used by the deriving parsers. - """ - - def __init__(self, builder, logger): - self.logger = logger - self.doap_namespace = Namespace("http://usefulinc.com/ns/doap#") - self.spdx_namespace = Namespace("http://spdx.org/rdf/terms#") - self.builder = builder - - def more_than_one_error(self, field): - """ - Logs a more than one error. - field is the field/property that has more than one defined. - """ - msg = "More than one {0} defined.".format(field) - self.logger.log(msg) - self.error = True - - def value_error(self, key, bad_value): - """ - Report a value error using ERROR_MESSAGES dict. - key - key to use for ERROR_MESSAGES. - bad_value - is passed to format which is called on what key maps to - in ERROR_MESSAGES. - """ - msg = ERROR_MESSAGES[key].format(bad_value) - self.logger.log(msg) - self.error = True - - def to_special_value(self, value): - """ - Check if value is a special SPDX value such as - NONE, NOASSERTION or UNKNOWN if so returns proper model. - else returns value - """ - if value == self.spdx_namespace.none: - return utils.SPDXNone() - elif value == self.spdx_namespace.noassertion: - return utils.NoAssert() - elif value == self.spdx_namespace.unknown: - return utils.UnKnown() - else: - return str(value) - - -class LicenseParser(BaseParser): - """ - Helper class for parsing extracted licenses and license lists. - """ - - LICS_REF_REGEX = re.compile("LicenseRef-.+", re.UNICODE) - - def __init__(self, builder, logger): - super(LicenseParser, self).__init__(builder, logger) - - def handle_lics(self, lics): - """ - Return a License from a `lics` license resource. - """ - # Handle extracted licensing info type. - if ( - lics, - RDF.type, - self.spdx_namespace["ExtractedLicensingInfo"], - ) in self.graph: - return self.parse_only_extr_license(lics) - - # Assume resource, hence the path separator - ident_start = lics.rfind("/") + 1 - if ident_start == 0: - # special values such as spdx:noassertion - special = self.to_special_value(lics) - if special == lics: - if self.LICS_REF_REGEX.match(lics): - # Is a license ref i.e LicenseRef-1 - return license.License.from_identifier(str(lics)) - else: - # Not a known license form - raise SPDXValueError("License") - else: - # is a special value - return special - else: - # license url - return license.License.from_identifier(lics[ident_start:]) - - def get_extr_license_ident(self, extr_lic): - """ - Return a license identifier from an ExtractedLicense or None. - """ - identifier_triples = list( - self.graph.triples((extr_lic, self.spdx_namespace["licenseId"], None)) - ) - - if not identifier_triples: - self.error = True - msg = "Extracted license must have licenseId property." - self.logger.log(msg) - return - - if len(identifier_triples) > 1: - self.more_than_one_error("extracted license identifier_triples") - return - - identifier_triple = identifier_triples[0] - _s, _p, identifier = identifier_triple - return str(identifier) - - def get_extr_license_text(self, extr_lic): - """ - Return extracted text from an ExtractedLicense or None. - """ - text_triples = list( - self.graph.triples((extr_lic, self.spdx_namespace["extractedText"], None)) - ) - if not text_triples: - self.error = True - msg = "Extracted license must have extractedText property" - self.logger.log(msg) - return - - if len(text_triples) > 1: - self.more_than_one_error("extracted license text") - return - - text_triple = text_triples[0] - _s, _p, text = text_triple - return str(text) - - def get_extr_lic_name(self, extr_lic): - """ - Return the license name from an ExtractedLicense or None - """ - extr_name_list = list( - self.graph.triples((extr_lic, self.spdx_namespace["licenseName"], None)) - ) - if len(extr_name_list) > 1: - self.more_than_one_error("extracted license name") - return - elif len(extr_name_list) == 0: - return - return str(self.to_special_value(extr_name_list[0][2])) - - def get_extr_lics_xref(self, extr_lic): - """ - Return a list of cross references. - """ - xrefs = list(self.graph.triples((extr_lic, RDFS.seeAlso, None))) - return list(map(lambda xref_triple: xref_triple[2], xrefs)) - - def get_extr_lics_comment(self, extr_lics): - """ - Return license comment or None. - """ - comment_list = list(self.graph.triples((extr_lics, RDFS.comment, None))) - if len(comment_list) > 1: - self.more_than_one_error("extracted license comment") - return - elif len(comment_list) == 1: - return str(comment_list[0][2]) - else: - return - - def parse_only_extr_license(self, extr_lic): - """ - Return an ExtractedLicense object to represent a license object. - But does not add it to the SPDXDocument model. - Return None if failed. - """ - # Grab all possible values - ident = self.get_extr_license_ident(extr_lic) - text = self.get_extr_license_text(extr_lic) - comment = self.get_extr_lics_comment(extr_lic) - xrefs = self.get_extr_lics_xref(extr_lic) - name = self.get_extr_lic_name(extr_lic) - - if not ident: - # Must have identifier - return - - # Set fields - # FIXME: the constructor of the license should always accept a name - lic = license.ExtractedLicense(ident) - if text is not None: - lic.text = text - if name is not None: - lic.full_name = name - if comment is not None: - lic.comment = comment - lic.cross_ref = list(map(lambda x: str(x), xrefs)) - return lic - - def handle_extracted_license(self, extr_lic): - """ - Build and return an ExtractedLicense or None. - Note that this function adds the license to the document. - """ - lic = self.parse_only_extr_license(extr_lic) - if lic is not None: - self.doc.add_extr_lic(lic) - return lic - - def _handle_license_list(self, lics_set, cls=None): - """ - Return a license representing a `cls` object (LicenseConjunction - or LicenseDisjunction) from a list of license resources or None. - """ - licenses = [] - for _, _, lics_member in self.graph.triples( - (lics_set, self.spdx_namespace["member"], None) - ): - try: - licenses.append(self.handle_lics(lics_member)) - except CardinalityError: - self.value_error("LICS_LIST_MEMBER", lics_member) - break - if len(licenses) > 1: - return reduce(lambda a, b: cls(a, b), licenses) - else: - self.value_error("PKG_CONC_LIST", "") - return - - def handle_conjunctive_list(self, lics_set): - """ - Return a license representing the conjunction from a list of - license resources or None. - """ - return self._handle_license_list(lics_set, cls=license.LicenseConjunction) - - def handle_disjunctive_list(self, lics_set): - """ - Return a license representing the disjunction from a list of - license resources or None. - """ - return self._handle_license_list(lics_set, cls=license.LicenseDisjunction) - - -class PackageParser(LicenseParser): - """ - Helper class for parsing packages. - """ - - def __init__(self, builder, logger): - super(PackageParser, self).__init__(builder, logger) - - def parse_package(self, p_term): - """ - Parse package fields. - """ - # Check there is a package name - if not (p_term, self.spdx_namespace["name"], None) in self.graph: - self.error = True - self.logger.log("Package must have a name.") - # Create dummy package so that we may continue parsing the rest of - # the package fields. - self.builder.create_package(self.doc, "dummy_package") - else: - for _s, _p, o in self.graph.triples( - (p_term, self.spdx_namespace["name"], None) - ): - try: - self.builder.create_package(self.doc, str(o)) - except CardinalityError: - self.more_than_one_error("Package name") - break - # Set SPDXID - try: - if p_term.count("#", 0, len(p_term)) == 1: - pkg_spdx_id = p_term.split("#")[-1] - self.builder.set_pkg_spdx_id(self.doc, pkg_spdx_id) - else: - self.value_error("PKG_SPDX_ID_VALUE", p_term) - except SPDXValueError: - self.value_error("PKG_SPDX_ID_VALUE", p_term) - - self.p_pkg_vinfo(p_term, self.spdx_namespace["versionInfo"]) - self.p_pkg_fname(p_term, self.spdx_namespace["packageFileName"]) - self.p_pkg_suppl(p_term, self.spdx_namespace["supplier"]) - self.p_pkg_originator(p_term, self.spdx_namespace["originator"]) - self.p_pkg_down_loc(p_term, self.spdx_namespace["downloadLocation"]) - self.p_pkg_files_analyzed(p_term, self.spdx_namespace["filesAnalyzed"]) - self.p_pkg_homepg(p_term, self.doap_namespace["homepage"]) - self.p_pkg_checksum(p_term, self.spdx_namespace["checksum"]) - self.p_pkg_src_info(p_term, self.spdx_namespace["sourceInfo"]) - self.p_pkg_verif_code(p_term, self.spdx_namespace["packageVerificationCode"]) - self.p_pkg_attribution_text(p_term, self.spdx_namespace["attributionText"]) - self.p_pkg_lic_conc(p_term, self.spdx_namespace["licenseConcluded"]) - self.p_pkg_lic_decl(p_term, self.spdx_namespace["licenseDeclared"]) - self.p_pkg_lics_info_from_files( - p_term, self.spdx_namespace["licenseInfoFromFiles"] - ) - self.p_pkg_comments_on_lics(p_term, self.spdx_namespace["licenseComments"]) - self.p_pkg_cr_text(p_term, self.spdx_namespace["copyrightText"]) - self.p_pkg_summary(p_term, self.spdx_namespace["summary"]) - self.p_pkg_descr(p_term, self.spdx_namespace["description"]) - self.p_pkg_comment(p_term, self.spdx_namespace["comment"]) - - def p_pkg_cr_text(self, p_term, predicate): - try: - for _, _, text in self.graph.triples((p_term, predicate, None)): - self.builder.set_pkg_cr_text( - self.doc, str(self.to_special_value(text)) - ) - except CardinalityError: - self.more_than_one_error("package copyright text") - - def p_pkg_summary(self, p_term, predicate): - try: - for _, _, summary in self.graph.triples((p_term, predicate, None)): - self.builder.set_pkg_summary(self.doc, str(summary)) - except CardinalityError: - self.more_than_one_error("package summary") - - def p_pkg_descr(self, p_term, predicate): - try: - for _, _, desc in self.graph.triples((p_term, predicate, None)): - self.builder.set_pkg_desc(self.doc, str(desc)) - except CardinalityError: - self.more_than_one_error("package description") - - def p_pkg_comment(self, p_term, predicate): - try: - for _, _, comment in self.graph.triples((p_term, predicate, None)): - self.builder.set_pkg_comment(self.doc, str(comment)) - except CardinalityError: - self.more_than_one_error("package comment") - - def p_pkg_attribution_text(self, p_term, predicate): - try: - for _, _, attribute_text in self.graph.triples((p_term, predicate, None)): - self.builder.set_pkg_attribution_text( - self.doc, str(attribute_text) - ) - except CardinalityError: - self.more_than_one_error("package attribution text") - - def p_pkg_comments_on_lics(self, p_term, predicate): - for _, _, comment in self.graph.triples((p_term, predicate, None)): - try: - self.builder.set_pkg_license_comment(self.doc, str(comment)) - except CardinalityError: - self.more_than_one_error("package comments on license") - break - - def p_pkg_lics_info_from_files(self, p_term, predicate): - for _, _, lics in self.graph.triples((p_term, predicate, None)): - try: - if ( - lics, - RDF.type, - self.spdx_namespace["ExtractedLicensingInfo"], - ) in self.graph: - self.builder.set_pkg_license_from_file( - self.doc, self.parse_only_extr_license(lics) - ) - else: - self.builder.set_pkg_license_from_file( - self.doc, self.handle_lics(lics) - ) - - except SPDXValueError: - self.value_error("PKG_LICS_INFO_FILES", lics) - - def p_pkg_lic_decl(self, p_term, predicate): - self.handle_pkg_lic(p_term, predicate, self.builder.set_pkg_license_declared) - - def handle_pkg_lic(self, p_term, predicate, builder_func): - """ - Handle package lics concluded or declared. - """ - try: - for _, _, licenses in self.graph.triples((p_term, predicate, None)): - if ( - licenses, - RDF.type, - self.spdx_namespace["ConjunctiveLicenseSet"], - ) in self.graph: - lics = self.handle_conjunctive_list(licenses) - builder_func(self.doc, lics) - - elif ( - licenses, - RDF.type, - self.spdx_namespace["DisjunctiveLicenseSet"], - ) in self.graph: - lics = self.handle_disjunctive_list(licenses) - builder_func(self.doc, lics) - - else: - try: - lics = self.handle_lics(licenses) - builder_func(self.doc, lics) - except SPDXValueError: - self.value_error("PKG_SINGLE_LICS", licenses) - except CardinalityError: - self.more_than_one_error("package {0}".format(predicate)) - - def p_pkg_lic_conc(self, p_term, predicate): - self.handle_pkg_lic(p_term, predicate, self.builder.set_pkg_licenses_concluded) - - def p_pkg_verif_code(self, p_term, predicate): - for _, _, verifcode in self.graph.triples((p_term, predicate, None)): - # Parse verification code - for _, _, code in self.graph.triples( - (verifcode, self.spdx_namespace["packageVerificationCodeValue"], None) - ): - try: - self.builder.set_pkg_verif_code(self.doc, str(code)) - except CardinalityError: - self.more_than_one_error("package verification code") - break - # Parse excluded file - for _, _, filename in self.graph.triples( - ( - verifcode, - self.spdx_namespace["packageVerificationCodeExcludedFile"], - None, - ) - ): - try: - self.builder.set_pkg_excl_file(self.doc, str(filename)) - except CardinalityError: - self.more_than_one_error("package verification code excluded file") - break - - def p_pkg_src_info(self, p_term, predicate): - for _, _, o in self.graph.triples((p_term, predicate, None)): - try: - self.builder.set_pkg_source_info(self.doc, str(o)) - except CardinalityError: - self.more_than_one_error("package source info") - break - - def p_pkg_checksum(self, p_term, predicate): - for _s, _p, pkg_checksum in self.graph.triples((p_term, predicate, None)): - for _, _, value in self.graph.triples( - (pkg_checksum, self.spdx_namespace["checksumValue"], None) - ): - for _, _, algo in self.graph.triples( - (pkg_checksum, self.spdx_namespace["algorithm"], None) - ): - algorithm_identifier = convert_rdf_checksum_algorithm(str(algo)) - checksum = Checksum(algorithm_identifier, str(value)) - self.builder.set_pkg_checksum(self.doc, checksum) - - def p_pkg_homepg(self, p_term, predicate): - for _s, _p, o in self.graph.triples((p_term, predicate, None)): - try: - self.builder.set_pkg_home( - self.doc, str(self.to_special_value(o)) - ) - except CardinalityError: - self.more_than_one_error("Package home page") - break - except SPDXValueError: - self.value_error("PKG_HOME_PAGE", o) - - def p_pkg_down_loc(self, p_term, predicate): - for _s, _p, o in self.graph.triples((p_term, predicate, None)): - try: - self.builder.set_pkg_down_location( - self.doc, str(self.to_special_value(o)) - ) - except CardinalityError: - self.more_than_one_error("Package download location") - break - except SPDXValueError: - self.value_error("PKG_DOWN_LOC", o) - - def p_pkg_files_analyzed(self, p_term, predicate): - for _s, _p, o in self.graph.triples((p_term, predicate, None)): - try: - self.builder.set_pkg_files_analyzed(self.doc, str(o)) - except CardinalityError: - self.more_than_one_error("Package Files Analyzed") - break - except SPDXValueError: - self.value_error("PKG_FILES_ANALYZED_VALUE", o) - - def p_pkg_originator(self, p_term, predicate): - for _s, _p, o in self.graph.triples((p_term, predicate, None)): - try: - if o == "NOASSERTION": - self.builder.set_pkg_originator(self.doc, utils.NoAssert()) - else: - ent = self.builder.create_entity(self.doc, str(o)) - self.builder.set_pkg_originator(self.doc, ent) - except CardinalityError: - self.more_than_one_error("Package originator") - break - except SPDXValueError: - self.value_error("PKG_ORIGINATOR_VALUE", o) - - def p_pkg_suppl(self, p_term, predicate): - for _s, _p, o in self.graph.triples((p_term, predicate, None)): - try: - if o == "NOASSERTION": - self.builder.set_pkg_supplier(self.doc, utils.NoAssert()) - else: - ent = self.builder.create_entity(self.doc, str(o)) - self.builder.set_pkg_supplier(self.doc, ent) - except CardinalityError: - self.more_than_one_error("Package supplier") - break - except SPDXValueError: - self.value_error("PKG_SUPPL_VALUE", o) - - def p_pkg_fname(self, p_term, predicate): - for _s, _p, o in self.graph.triples((p_term, predicate, None)): - try: - self.builder.set_pkg_file_name(self.doc, str(o)) - except CardinalityError: - self.more_than_one_error("Package file name") - break - - def p_pkg_vinfo(self, p_term, predicate): - for _s, _p, o in self.graph.triples((p_term, predicate, None)): - try: - self.builder.set_pkg_vers(self.doc, str(o)) - except CardinalityError: - self.more_than_one_error("Package version info") - break - - -class FileParser(LicenseParser): - """ - Helper class for parsing files. - """ - - def __init__(self, builder, logger): - super(FileParser, self).__init__(builder, logger) - - def parse_file(self, f_term): - if not (f_term, self.spdx_namespace["fileName"], None) in self.graph: - self.error = True - self.logger.log("File must have a name.") - # Dummy name to continue - self.builder.set_file_name(self.doc, "Dummy file") - else: - for _, _, name in self.graph.triples( - (f_term, self.spdx_namespace["fileName"], None) - ): - self.builder.set_file_name(self.doc, str(name)) - - self.p_file_spdx_id(f_term, self.spdx_namespace["File"]) - self.p_file_type(f_term, self.spdx_namespace["fileType"]) - self.p_file_checksum(f_term, self.spdx_namespace["checksum"]) - self.p_file_lic_conc(f_term, self.spdx_namespace["licenseConcluded"]) - self.p_file_lic_info(f_term, self.spdx_namespace["licenseInfoInFile"]) - self.p_file_comments_on_lics(f_term, self.spdx_namespace["licenseComments"]) - self.p_file_attribution_text(f_term, self.spdx_namespace["attributionText"]) - self.p_file_cr_text(f_term, self.spdx_namespace["copyrightText"]) - self.p_file_artifact(f_term, self.spdx_namespace["artifactOf"]) - self.p_file_comment(f_term, RDFS.comment) - self.p_file_notice(f_term, self.spdx_namespace["noticeText"]) - self.p_file_contributor(f_term, self.spdx_namespace["fileContributor"]) - self.p_file_depends(f_term, self.spdx_namespace["fileDependency"]) - - def get_file_name(self, f_term): - """Returns first found fileName property or None if not found.""" - for _, _, name in self.graph.triples( - (f_term, self.spdx_namespace["fileName"], None) - ): - return name - return - - def p_file_depends(self, f_term, predicate): - """ - Set file dependencies. - """ - for _, _, other_file in self.graph.triples((f_term, predicate, None)): - name = self.get_file_name(other_file) - if name is not None: - self.builder.add_file_dep(str(name)) - else: - self.error = True - msg = "File depends on file with no name" - self.logger.log(msg) - - def p_file_contributor(self, f_term, predicate): - """ - Parse all file contributors and adds them to the model. - """ - for _, _, contributor in self.graph.triples((f_term, predicate, None)): - self.builder.add_file_contribution(self.doc, str(contributor)) - - def p_file_notice(self, f_term, predicate): - """ - Set file notice text. - """ - try: - for _, _, notice in self.graph.triples((f_term, predicate, None)): - self.builder.set_file_notice(self.doc, str(notice)) - except CardinalityError: - self.more_than_one_error("file notice") - - def p_file_comment(self, f_term, predicate): - """ - Set file comment text. - """ - try: - for _, _, comment in self.graph.triples((f_term, predicate, None)): - self.builder.set_file_comment(self.doc, str(comment)) - except CardinalityError: - self.more_than_one_error("file comment") - - def p_file_attribution_text(self, f_term, predicate): - """ - Set file attribution text - """ - try: - for _, _, attribute_text in self.graph.triples((f_term, predicate, None)): - self.builder.set_file_attribution_text( - self.doc, str(attribute_text) - ) - except CardinalityError: - self.more_than_one_error("file attribution text") - - def p_file_artifact(self, f_term, predicate): - """ - Handle file artifactOf. - Note: does not handle artifact of project URI. - """ - for _, _, project in self.graph.triples((f_term, predicate, None)): - if (project, RDF.type, self.doap_namespace["Project"]): - self.p_file_project(project) - else: - self.error = True - msg = "File must be artifact of doap:Project" - self.logger.log(msg) - - def p_file_project(self, project): - """ - Helper function for parsing doap:project name and homepage. - and setting them using the file builder. - """ - for _, _, name in self.graph.triples( - (project, self.doap_namespace["name"], None) - ): - self.builder.set_file_atrificat_of_project( - self.doc, "name", str(name) - ) - for _, _, homepage in self.graph.triples( - (project, self.doap_namespace["homepage"], None) - ): - self.builder.set_file_atrificat_of_project( - self.doc, "home", str(homepage) - ) - - def p_file_cr_text(self, f_term, predicate): - """ - Set file copyright text. - """ - try: - for _, _, cr_text in self.graph.triples((f_term, predicate, None)): - self.builder.set_file_copyright(self.doc, str(cr_text)) - except CardinalityError: - self.more_than_one_error("file copyright text") - - def p_file_comments_on_lics(self, f_term, predicate): - """ - Set file license comment. - """ - try: - for _, _, comment in self.graph.triples((f_term, predicate, None)): - self.builder.set_file_license_comment(self.doc, str(comment)) - except CardinalityError: - self.more_than_one_error("file comments on license") - - def p_file_lic_info(self, f_term, predicate): - """ - Set file license information. - """ - for _, _, info in self.graph.triples((f_term, predicate, None)): - lic = self.handle_lics(info) - if lic is not None: - self.builder.set_file_license_in_file(self.doc, lic) - - def p_file_spdx_id(self, f_term, predicate): - try: - try: - self.builder.set_file_spdx_id(self.doc, str(f_term)) - except SPDXValueError: - self.value_error("FILE_SPDX_ID_VALUE", f_term) - except CardinalityError: - self.more_than_one_error("FILE_SPDX_ID_VALUE") - - def p_file_type(self, f_term, predicate): - """ - Set file type. - """ - try: - for _, _, ftype in self.graph.triples((f_term, predicate, None)): - try: - self.builder.set_file_type(self.doc, ftype) - except SPDXValueError: - self.value_error("FILE_TYPE", ftype) - except CardinalityError: - self.more_than_one_error("file type") - - def p_file_checksum(self, f_term, predicate): - """ - Set file checksum. - """ - for _s, _p, file_checksum in self.graph.triples((f_term, predicate, None)): - for _, _, value in self.graph.triples( - (file_checksum, self.spdx_namespace["checksumValue"], None) - ): - for _, _, algo in self.graph.triples( - (file_checksum, self.spdx_namespace["algorithm"], None) - ): - algorithm_identifier = convert_rdf_checksum_algorithm(str(algo)) - checksum = Checksum(algorithm_identifier, str(value)) - self.builder.set_file_checksum(self.doc, checksum) - - def p_file_lic_conc(self, f_term, predicate): - """ - Set file licenses concluded. - """ - try: - for _, _, licenses in self.graph.triples((f_term, predicate, None)): - if ( - licenses, - RDF.type, - self.spdx_namespace["ConjunctiveLicenseSet"], - ) in self.graph: - lics = self.handle_conjunctive_list(licenses) - self.builder.set_concluded_license(self.doc, lics) - - elif ( - licenses, - RDF.type, - self.spdx_namespace["DisjunctiveLicenseSet"], - ) in self.graph: - lics = self.handle_disjunctive_list(licenses) - self.builder.set_concluded_license(self.doc, lics) - - else: - try: - lics = self.handle_lics(licenses) - self.builder.set_concluded_license(self.doc, lics) - except SPDXValueError: - self.value_error("FILE_SINGLE_LICS", licenses) - except CardinalityError: - self.more_than_one_error("file {0}".format(predicate)) - - -class SnippetParser(LicenseParser): - """ - Helper class for parsing snippet information. - """ - - def __init__(self, builder, logger): - super(SnippetParser, self).__init__(builder, logger) - - def parse_snippet(self, snippet_term): - try: - self.builder.create_snippet(self.doc, snippet_term) - except SPDXValueError: - self.value_error("SNIPPET_SPDX_ID_VALUE", snippet_term) - - for _s, _p, o in self.graph.triples( - (snippet_term, self.spdx_namespace["name"], None) - ): - try: - self.builder.set_snippet_name(self.doc, str(o)) - except CardinalityError: - self.more_than_one_error("snippetName") - break - - for _s, _p, o in self.graph.triples( - (snippet_term, self.spdx_namespace["licenseComments"], None) - ): - try: - self.builder.set_snippet_lic_comment(self.doc, str(o)) - except CardinalityError: - self.more_than_one_error("licenseComments") - break - - for _s, _p, o in self.graph.triples((snippet_term, RDFS.comment, None)): - try: - self.builder.set_snippet_comment(self.doc, str(o)) - except CardinalityError: - self.more_than_one_error("comment") - break - - for _s, _p, o in self.graph.triples( - (snippet_term, self.spdx_namespace["copyrightText"], None) - ): - try: - self.builder.set_snippet_copyright( - self.doc, self.to_special_value(str(o)) - ) - except CardinalityError: - self.more_than_one_error("copyrightText") - break - - try: - for _, _, licenses in self.graph.triples( - (snippet_term, self.spdx_namespace["licenseConcluded"], None) - ): - if ( - licenses, - RDF.type, - self.spdx_namespace["ConjunctiveLicenseSet"], - ) in self.graph: - lics = self.handle_conjunctive_list(licenses) - self.builder.set_snip_concluded_license(self.doc, lics) - - elif ( - licenses, - RDF.type, - self.spdx_namespace["DisjunctiveLicenseSet"], - ) in self.graph: - lics = self.handle_disjunctive_list(licenses) - self.builder.set_snip_concluded_license(self.doc, lics) - - else: - try: - lics = self.handle_lics(licenses) - self.builder.set_snip_concluded_license(self.doc, lics) - except SPDXValueError: - self.value_error("SNIPPET_CONCLUDED_LICENSE", licenses) - except CardinalityError: - self.more_than_one_error( - "package {0}".format(self.spdx_namespace["licenseConcluded"]) - ) - - for _, _, info in self.graph.triples( - (snippet_term, self.spdx_namespace["licenseInfoInSnippet"], None) - ): - lic = self.handle_lics(info) - if lic is not None: - try: - self.builder.set_snippet_lics_info(self.doc, lic) - except SPDXValueError: - self.value_error("SNIPPET_LIC_INFO", lic) - - for _s, _p, o in self.graph.triples( - (snippet_term, self.spdx_namespace["snippetFromFile"], None) - ): - try: - self.builder.set_snip_from_file_spdxid(self.doc, str(o)) - except CardinalityError: - self.more_than_one_error("snippetFromFile") - break - - try: - for _, _, attribute_text in self.graph.triples( - (snippet_term, self.spdx_namespace["attributionText"], None) - ): - self.builder.set_snippet_attribution_text( - self.doc, str(attribute_text) - ) - except CardinalityError: - self.more_than_one_error("snippetAttributionText") - - -class ReviewParser(BaseParser): - """ - Helper class for parsing review information. - """ - - def __init__(self, builder, logger): - super(ReviewParser, self).__init__(builder, logger) - - def parse_review(self, r_term): - reviewer = self.get_reviewer(r_term) - reviewed_date = self.get_review_date(r_term) - if reviewer is not None: - self.builder.add_reviewer(self.doc, reviewer) - if reviewed_date is not None: - try: - self.builder.add_review_date(self.doc, reviewed_date) - except SPDXValueError: - self.value_error("REVIEW_DATE", reviewed_date) - comment = self.get_review_comment(r_term) - if comment is not None: - self.builder.add_review_comment(self.doc, comment) - - def get_review_comment(self, r_term): - """ - Return review comment or None if found none or more than one. - Report errors. - """ - comment_list = list(self.graph.triples((r_term, RDFS.comment, None))) - if len(comment_list) > 1: - self.error = True - msg = "Review can have at most one comment" - self.logger.log(msg) - return - else: - return str(comment_list[0][2]) - - def get_review_date(self, r_term): - """ - Return review date or None if not found. - Report error on failure. - Note does not check value format. - """ - reviewed_list = list( - self.graph.triples((r_term, self.spdx_namespace["reviewDate"], None)) - ) - if len(reviewed_list) != 1: - self.error = True - msg = "Review must have exactly one review date" - self.logger.log(msg) - return - return str(reviewed_list[0][2]) - - def get_reviewer(self, r_term): - """ - Return reviewer as creator object or None if failed. - Report errors on failure. - """ - reviewer_list = list( - self.graph.triples((r_term, self.spdx_namespace["reviewer"], None)) - ) - if len(reviewer_list) != 1: - self.error = True - msg = "Review must have exactly one reviewer" - self.logger.log(msg) - return - try: - return self.builder.create_entity( - self.doc, str(reviewer_list[0][2]) - ) - except SPDXValueError: - self.value_error("REVIEWER_VALUE", reviewer_list[0][2]) - - -class AnnotationParser(BaseParser): - """ - Helper class for parsing annotation information. - """ - - def __init__(self, builder, logger): - super(AnnotationParser, self).__init__(builder, logger) - - def parse_annotation(self, r_term): - annotator = self.get_annotator(r_term) - annotation_date = self.get_annotation_date(r_term) - if annotator is not None: - self.builder.add_annotator(self.doc, annotator) - if annotation_date is not None: - try: - self.builder.add_annotation_date(self.doc, annotation_date) - except SPDXValueError: - self.value_error("ANNOTATION_DATE", annotation_date) - comment = self.get_annotation_comment(r_term) - if comment is not None: - self.builder.add_annotation_comment(self.doc, comment) - annotation_type = self.get_annotation_type(r_term) - self.builder.add_annotation_type(self.doc, annotation_type) - try: - self.builder.set_annotation_spdx_id(self.doc, str(r_term)) - except CardinalityError: - self.more_than_one_error("SPDX Identifier Reference") - - def get_annotation_type(self, r_term): - """ - Return annotation type or None if found none or more than one. - Report errors on failure. - """ - for _, _, typ in self.graph.triples( - (r_term, self.spdx_namespace["annotationType"], None) - ): - if typ is not None: - return str(typ) - else: - self.error = True - msg = "Annotation must have exactly one annotation type." - self.logger.log(msg) - return - - def get_annotation_comment(self, r_term): - """ - Return annotation comment or None if found none or more than one. - Report errors. - """ - comment_list = list(self.graph.triples((r_term, RDFS.comment, None))) - if len(comment_list) > 1: - self.error = True - msg = "Annotation can have at most one comment." - self.logger.log(msg) - return - else: - return str(comment_list[0][2]) - - def get_annotation_date(self, r_term): - """ - Return annotation date or None if not found. - Report error on failure. - Note does not check value format. - """ - annotation_date_list = list( - self.graph.triples((r_term, self.spdx_namespace["annotationDate"], None)) - ) - if len(annotation_date_list) != 1: - self.error = True - msg = "Annotation must have exactly one annotation date." - self.logger.log(msg) - return - return str(annotation_date_list[0][2]) - - def get_annotator(self, r_term): - """ - Return annotator as creator object or None if failed. - Report errors on failure. - """ - annotator_list = list( - self.graph.triples((r_term, self.spdx_namespace["annotator"], None)) - ) - if len(annotator_list) != 1: - self.error = True - msg = "Annotation must have exactly one annotator" - self.logger.log(msg) - return - try: - return self.builder.create_entity( - self.doc, str(annotator_list[0][2]) - ) - except SPDXValueError: - self.value_error("ANNOTATOR_VALUE", annotator_list[0][2]) - - -class RelationshipParser(BaseParser): - """ - Helper Class for parsing relationship information - """ - - def __init__(self, builder, logger): - super(RelationshipParser, self).__init__(builder, logger) - - def parse_relationship(self, subject_term, relation_term): - relationship = self.get_relationship(subject_term, relation_term) - relationship_comment = self.get_relationship_comment(relation_term) - if relationship is not None: - relationship_added: bool = self.builder.add_relationship(self.doc, relationship) - if relationship_comment is not None and relationship_added: - self.builder.add_relationship_comment(self.doc, relationship_comment) - - def get_relationship(self, subject_term, relation_term): - """ - Returns a string with relationship type and the related elements. - """ - relation_subject = str(subject_term.split("#")[1]) - - for _, _, rtype in self.graph.triples( - (relation_term, self.spdx_namespace["relationshipType"], None) - ): - try: - if rtype.endswith("describes"): - rtype = "DESCRIBES" - elif rtype.endswith("describedBy"): - rtype = "DESCRIBED_BY" - elif rtype.endswith("contains"): - rtype = "CONTAINS" - elif rtype.endswith("containedBy"): - rtype = "CONTAINED_BY" - elif rtype.endswith("dependsOn"): - rtype = "DEPENDS_ON" - elif rtype.endswith("dependencyOf"): - rtype = "DEPENDENCY_OF" - elif rtype.endswith("dependencyManifestOf"): - rtype = "DEPENDENCY_MANIFEST_OF" - elif rtype.endswith("buildDependencyOf"): - rtype = "BUILD_DEPENDENCY_OF" - elif rtype.endswith("devDependencyOf"): - rtype = "DEV_DEPENDENCY_OF" - elif rtype.endswith("optionalDependencyOf"): - rtype = "OPTIONAL_DEPENDENCY_OF" - elif rtype.endswith("providedDependencyOf"): - rtype = "PROVIDED_DEPENDENCY_OF" - elif rtype.endswith("testDependencyOf"): - rtype = "TEST_DEPENDENCY_OF" - elif rtype.endswith("runtimeDependencyOf"): - rtype = "RUNTIME_DEPENDENCY_OF" - elif rtype.endswith("exampleOf"): - rtype = "EXAMPLE_OF" - elif rtype.endswith("generates"): - rtype = "GENERATES" - elif rtype.endswith("generatedFrom"): - rtype = "GENERATED_FROM" - elif rtype.endswith("ancestorOf"): - rtype = "ANCESTOR_OF" - elif rtype.endswith("descendantOf"): - rtype = "DESCENDANT_OF" - elif rtype.endswith("variantOf"): - rtype = "VARIANT_OF" - elif rtype.endswith("distributionArtifact"): - rtype = "DISTRIBUTION_ARTIFACT" - elif rtype.endswith("patchFor"): - rtype = "PATCH_FOR" - elif rtype.endswith("patchApplied"): - rtype = "PATCH_APPLIED" - elif rtype.endswith("copyOf"): - rtype = "COPY_OF" - elif rtype.endswith("fileAdded"): - rtype = "FILE_ADDED" - elif rtype.endswith("fileDeleted"): - rtype = "FILE_DELETED" - elif rtype.endswith("fileModified"): - rtype = "FILE_MODIFIED" - elif rtype.endswith("expandedFromArchive"): - rtype = "EXPANDED_FROM_ARCHIVE" - elif rtype.endswith("dynamicLink"): - rtype = "DYNAMIC_LINK" - elif rtype.endswith("staticLink"): - rtype = "STATIC_LINK" - elif rtype.endswith("dataFileOf"): - rtype = "DATA_FILE_OF" - elif rtype.endswith("testCaseOf"): - rtype = "TEST_CASE_OF" - elif rtype.endswith("buildToolOf"): - rtype = "BUILD_TOOL_OF" - elif rtype.endswith("devToolOf"): - rtype = "DEV_TOOL_OF" - elif rtype.endswith("testOf"): - rtype = "TEST_OF" - elif rtype.endswith("testToolOf"): - rtype = "TEST_TOOL_OF" - elif rtype.endswith("documentationOf"): - rtype = "DOCUMENTATION_OF" - elif rtype.endswith("optionalComponentOf"): - rtype = "OPTIONAL_COMPONENT_OF" - elif rtype.endswith("metafileOf"): - rtype = "METAFILE_OF" - elif rtype.endswith("packageOf"): - rtype = "PACKAGE_OF" - elif rtype.endswith("amends"): - rtype = "AMENDS" - elif rtype.endswith("prerequisiteFor"): - rtype = "PREREQUISITE_FOR" - elif rtype.endswith("hasPrerequisite"): - rtype = "HAS_PREREQUISITE" - elif rtype.endswith("other"): - rtype = "OTHER" - elif rtype.endswith("specificationFor"): - rtype = "SPECIFICATION_FOR" - elif rtype.endswith("requirementDescriptionFor"): - rtype = "REQUIREMENT_DESCRIPTION_FOR" - - except SPDXValueError: - self.value_error("RELATIONSHIP", rtype) - - try: - for sub, pre, rel_ele in self.graph.triples( - (relation_term, self.spdx_namespace["relatedSpdxElement"], None) - ): - related_element = str(rel_ele.split("#")[1]) if '#' in rel_ele else rel_ele - except: - related_element = None - - try: - if related_element == None: - return str(relation_subject + " " + rtype) - else: - return str( - relation_subject + " " + rtype + " " + related_element - ) - - except SPDXValueError: - self.value_error("RELATIONSHIP_VALUE", relation_subject + " " + rtype) - - def get_relationship_comment(self, relation_term): - """ - Returns relationship comment or None if found none or more than one. - Reports errors. - """ - - comment_list = list(self.graph.triples((relation_term, RDFS.comment, None))) - if len(comment_list) == 0: - return None - else: - if len(comment_list) > 1: - self.error = True - msg = "Relationship can have at most one comment." - self.logger.log(msg) - return - else: - return str(comment_list[0][2]) - - -class Parser( - PackageParser, - FileParser, - SnippetParser, - ReviewParser, - AnnotationParser, - RelationshipParser, -): - """ - RDF/XML file parser. - """ - - def __init__(self, builder, logger): - super(Parser, self).__init__(builder, logger) - - def parse(self, fil): - """ - Parse a file and returns a document object. - fil is a file like object. - """ - self.error = False - self.graph = Graph() - self.graph.parse(file=fil, format="xml") - self.doc = document.Document() - - for s, _p, o in self.graph.triples( - (None, RDF.type, self.spdx_namespace["SpdxDocument"]) - ): - self.parse_doc_fields(s) - - for s, _p, o in self.graph.triples( - (None, RDF.type, self.spdx_namespace["ExternalDocumentRef"]) - ): - self.parse_ext_doc_ref(s) - - for s, _p, o in self.graph.triples( - (None, RDF.type, self.spdx_namespace["CreationInfo"]) - ): - self.parse_creation_info(s) - - for s, _p, o in self.graph.triples( - (None, None, self.spdx_namespace["ExtractedLicensingInfo"]) - ): - self.handle_extracted_license(s) - - for s, _p, o in self.graph.triples( - (None, RDF.type, self.spdx_namespace["Package"]) - ): - self.parse_package(s) - - for s, _p, o in self.graph.triples( - (None, RDF.type, self.spdx_namespace["ExternalRef"]) - ): - self.parse_pkg_ext_ref(s) - - for s, _p, o in self.graph.triples( - (None, self.spdx_namespace["referencesFile"], None) - ): - self.parse_file(o) - - for s, _p, o in self.graph.triples( - (None, RDF.type, self.spdx_namespace["Snippet"]) - ): - self.parse_snippet(s) - - for s, _p, o in self.graph.triples( - (None, self.spdx_namespace["reviewed"], None) - ): - self.parse_review(o) - - for s, _p, o in self.graph.triples( - (None, self.spdx_namespace["annotation"], None) - ): - self.parse_annotation(o) - - for s, _p, o in self.graph.triples( - (None, self.spdx_namespace["relationship"], None) - ): - self.parse_relationship(s, o) - - validation_messages = ErrorMessages() - # Report extra errors if self.error is False otherwise there will be - # redundant messages - validation_messages = self.doc.validate(validation_messages) - if not self.error: - if validation_messages: - for msg in validation_messages: - self.logger.log(msg) - self.error = True - return self.doc, self.error - - def parse_creation_info(self, ci_term): - """ - Parse creators, created and comment. - """ - for _s, _p, o in self.graph.triples( - (ci_term, self.spdx_namespace["creator"], None) - ): - try: - ent = self.builder.create_entity(self.doc, str(o)) - self.builder.add_creator(self.doc, ent) - except SPDXValueError: - self.value_error("CREATOR_VALUE", o) - - for _s, _p, o in self.graph.triples( - (ci_term, self.spdx_namespace["created"], None) - ): - try: - self.builder.set_created_date(self.doc, str(o)) - except SPDXValueError: - self.value_error("CREATED_VALUE", o) - except CardinalityError: - self.more_than_one_error("created") - break - - for _s, _p, o in self.graph.triples((ci_term, RDFS.comment, None)): - try: - self.builder.set_creation_comment(self.doc, str(o)) - except CardinalityError: - self.more_than_one_error("CreationInfo comment") - break - for _s, _p, o in self.graph.triples( - (ci_term, self.spdx_namespace["licenseListVersion"], None) - ): - try: - self.builder.set_lics_list_ver(self.doc, str(o)) - except CardinalityError: - self.more_than_one_error("licenseListVersion") - break - except SPDXValueError: - self.value_error("LL_VALUE", o) - - def parse_doc_fields(self, doc_term): - """ - Parse the version, data license, name, SPDX Identifier, namespace, - and comment. - """ - try: - self.builder.set_doc_spdx_id(self.doc, str(doc_term)) - except SPDXValueError: - self.value_error("DOC_SPDX_ID_VALUE", doc_term) - try: - if doc_term.count("#", 0, len(doc_term)) <= 1: - doc_namespace = doc_term.split("#")[0] - self.builder.set_doc_namespace(self.doc, doc_namespace) - else: - self.value_error("DOC_NAMESPACE_VALUE", doc_term) - except SPDXValueError: - self.value_error("DOC_NAMESPACE_VALUE", doc_term) - for _s, _p, o in self.graph.triples( - (doc_term, self.spdx_namespace["specVersion"], None) - ): - try: - self.builder.set_doc_version(self.doc, str(o)) - except SPDXValueError: - self.value_error("DOC_VERS_VALUE", o) - except CardinalityError: - self.more_than_one_error("specVersion") - break - for _s, _p, o in self.graph.triples( - (doc_term, self.spdx_namespace["dataLicense"], None) - ): - try: - self.builder.set_doc_data_lic(self.doc, str(o)) - except SPDXValueError: - self.value_error("DOC_D_LICS", o) - except CardinalityError: - self.more_than_one_error("dataLicense") - break - for _s, _p, o in self.graph.triples( - (doc_term, self.spdx_namespace["name"], None) - ): - try: - self.builder.set_doc_name(self.doc, str(o)) - except CardinalityError: - self.more_than_one_error("name") - break - for _s, _p, o in self.graph.triples((doc_term, RDFS.comment, None)): - try: - self.builder.set_doc_comment(self.doc, str(o)) - except CardinalityError: - self.more_than_one_error("Document comment") - break - - def parse_ext_doc_ref(self, ext_doc_ref_term): - """ - Parse the External Document ID, SPDX Document URI and Checksum. - """ - for _s, _p, o in self.graph.triples( - (ext_doc_ref_term, self.spdx_namespace["externalDocumentId"], None) - ): - try: - self.builder.set_ext_doc_id(self.doc, str(o)) - except SPDXValueError: - self.value_error("EXT_DOC_REF_VALUE", "External Document ID") - break - - for _s, _p, o in self.graph.triples( - (ext_doc_ref_term, self.spdx_namespace["spdxDocument"], None) - ): - try: - self.builder.set_spdx_doc_uri(self.doc, str(o)) - except SPDXValueError: - self.value_error("EXT_DOC_REF_VALUE", "SPDX Document URI") - break - - for _s, _p, checksum in self.graph.triples( - (ext_doc_ref_term, self.spdx_namespace["checksum"], None) - ): - for _, _, value in self.graph.triples( - (checksum, self.spdx_namespace["checksumValue"], None) - ): - try: - self.builder.set_chksum(self.doc, str(value)) - except SPDXValueError: - self.value_error("EXT_DOC_REF_VALUE", "Checksum") - break - - def parse_pkg_ext_ref(self, pkg_ext_term): - """ - Parse the category, type, locator, and comment. - """ - for _s, _p, o in self.graph.triples( - (pkg_ext_term, self.spdx_namespace["referenceCategory"], None) - ): - try: - self.builder.set_pkg_ext_ref_category(self.doc, str(o)) - except SPDXValueError: - self.value_error( - "PKG_EXT_REF_CATEGORY", "Package External Reference Category" - ) - break - - for _s, _p, o in self.graph.triples( - (pkg_ext_term, self.spdx_namespace["referenceType"], None) - ): - try: - self.builder.set_pkg_ext_ref_type(self.doc, str(o)) - except SPDXValueError: - self.value_error("PKG_EXT_REF_TYPE", "Package External Reference Type") - break - - for _s, _p, o in self.graph.triples( - (pkg_ext_term, self.spdx_namespace["referenceLocator"], None) - ): - self.builder.set_pkg_ext_ref_locator(self.doc, str(o)) - - for _s, _p, o in self.graph.triples((pkg_ext_term, RDFS.comment, None)): - try: - self.builder.set_pkg_ext_ref_comment(self.doc, str(o)) - except CardinalityError: - self.more_than_one_error("Package External Reference Comment") - break diff --git a/spdx/parsers/rdfbuilders.py b/spdx/parsers/rdfbuilders.py deleted file mode 100644 index c7845ae4d..000000000 --- a/spdx/parsers/rdfbuilders.py +++ /dev/null @@ -1,669 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 re -from typing import Dict, Union - -from spdx import file -from spdx import license -from spdx import package -from spdx import version -from spdx.checksum import Checksum, ChecksumAlgorithm -from spdx.document import Document -from spdx.parsers.builderexceptions import CardinalityError -from spdx.parsers.builderexceptions import OrderError -from spdx.parsers.builderexceptions import SPDXValueError -from spdx.parsers import tagvaluebuilders -from spdx.parsers import validations -from spdx.parsers.rdf import convert_rdf_checksum_algorithm - - -class DocBuilder(object): - VERS_STR_REGEX = re.compile(r"SPDX-(\d+)\.(\d+)", re.UNICODE) - - def __init__(self): - # FIXME: this state does not make sense - self.reset_document() - - def set_doc_version(self, doc, value): - """ - Set the document version. - Raise SPDXValueError if malformed value. - Raise CardinalityError if already defined. - """ - if not self.doc_version_set: - self.doc_version_set = True - m = self.VERS_STR_REGEX.match(value) - if m is None: - raise SPDXValueError("Document::Version") - else: - doc.version = version.Version( - major=int(m.group(1)), minor=int(m.group(2)) - ) - return True - else: - raise CardinalityError("Document::Version") - - def set_doc_data_lic(self, doc, res): - """ - Set the document data license. - Raise SPDXValueError if malformed value. - Raise CardinalityError if already defined. - """ - if not self.doc_data_lics_set: - self.doc_data_lics_set = True - # TODO: what is this split? - res_parts = res.split("/") - if len(res_parts) != 0: - identifier = res_parts[-1] - doc.data_license = license.License.from_identifier(identifier) - else: - raise SPDXValueError("Document::License") - else: - raise CardinalityError("Document::License") - - def set_doc_name(self, doc, name): - """ - Set the document name. - Raise CardinalityError if already defined. - """ - if not self.doc_name_set: - doc.name = name - self.doc_name_set = True - return True - else: - raise CardinalityError("Document::Name") - - def set_doc_spdx_id(self, doc, doc_spdx_id_line): - """ - Set the document SPDX Identifier. - Raise value error if malformed value. - Raise CardinalityError if already defined. - """ - if not self.doc_spdx_id_set: - if validations.validate_doc_spdx_id(doc_spdx_id_line): - doc.spdx_id = doc_spdx_id_line - self.doc_spdx_id_set = True - return True - else: - raise SPDXValueError("Document::SPDXID") - else: - raise CardinalityError("Document::SPDXID") - - def set_doc_comment(self, doc, comment): - """ - Set document comment. - Raise CardinalityError if comment already set. - """ - if not self.doc_comment_set: - self.doc_comment_set = True - doc.comment = comment - else: - raise CardinalityError("Document::Comment") - - def set_doc_namespace(self, doc, namespace): - """ - Set the document namespace. - Raise SPDXValueError if malformed value. - Raise CardinalityError if already defined. - """ - if not self.doc_namespace_set: - self.doc_namespace_set = True - if validations.validate_doc_namespace(namespace): - doc.namespace = namespace - return True - else: - raise SPDXValueError("Document::Namespace") - else: - raise CardinalityError("Document::Comment") - - def reset_document(self): - """ - Reset the internal state to allow building new document - """ - # FIXME: this state does not make sense - self.doc_version_set = False - self.doc_comment_set = False - self.doc_namespace_set = False - self.doc_data_lics_set = False - self.doc_name_set = False - self.doc_spdx_id_set = False - - -class ExternalDocumentRefBuilder(tagvaluebuilders.ExternalDocumentRefBuilder): - def set_chksum(self, doc, chk_sum): - """ - Set the external document reference's check sum, if not already set. - chk_sum - The checksum value in the form of a string. - """ - if chk_sum: - doc.ext_document_references[-1].checksum = Checksum( - ChecksumAlgorithm.SHA1, chk_sum - ) - else: - raise SPDXValueError("ExternalDocumentRef::Checksum") - - -class EntityBuilder(tagvaluebuilders.EntityBuilder): - def __init__(self): - super(EntityBuilder, self).__init__() - - def create_entity(self, doc, value): - if self.tool_re.match(value): - return self.build_tool(doc, value) - elif self.person_re.match(value): - return self.build_person(doc, value) - elif self.org_re.match(value): - return self.build_org(doc, value) - else: - raise SPDXValueError("Entity") - - -class CreationInfoBuilder(tagvaluebuilders.CreationInfoBuilder): - def __init__(self): - super(CreationInfoBuilder, self).__init__() - - def set_creation_comment(self, doc, comment): - """ - Set creation comment. - Raise CardinalityError if comment already set. - """ - if not self.creation_comment_set: - self.creation_comment_set = True - doc.creation_info.comment = comment - return True - else: - raise CardinalityError("CreationInfo::Comment") - - -class PackageBuilder(tagvaluebuilders.PackageBuilder): - def __init__(self): - super(PackageBuilder, self).__init__() - - def set_pkg_checksum(self, doc, checksum: Union[Checksum, Dict]): - """ - Set the package checksum. - checksum - A Checksum or a Dict - Raise SPDXValueError if checksum type invalid. - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - if isinstance(checksum, dict): - algo = checksum.get('algorithm') or ChecksumAlgorithm.SHA1 - if algo.startswith('checksumAlgorithm_'): - algo = convert_rdf_checksum_algorithm(algo) or ChecksumAlgorithm.SHA1 - else: - algo = ChecksumAlgorithm.checksum_algorithm_from_string(algo) - doc.packages[-1].set_checksum(Checksum(identifier=algo, value=checksum.get('checksumValue'))) - elif isinstance(checksum, Checksum): - doc.packages[-1].set_checksum(checksum) - elif isinstance(checksum, str): - # kept for backwards compatibility - doc.packages[-1].set_checksum(Checksum(identifier=ChecksumAlgorithm.SHA1, value=checksum)) - else: - raise SPDXValueError("Invalid value for package checksum.") - - def set_pkg_source_info(self, doc, text): - """ - Set the package's source information, if not already set. - text - Free form text. - Raise CardinalityError if already defined. - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - if not self.package_source_info_set: - self.package_source_info_set = True - doc.packages[-1].source_info = text - return True - else: - raise CardinalityError("Package::SourceInfo") - - def set_pkg_verif_code(self, doc, code): - """ - Set the package verification code, if not already set. - code - A string. - Raise CardinalityError if already defined. - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - if not self.package_verif_set: - self.package_verif_set = True - doc.packages[-1].verif_code = code - else: - raise CardinalityError("Package::VerificationCode") - - def set_pkg_excl_file(self, doc, filename): - """ - Set the package's verification code excluded file. - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - doc.packages[-1].add_exc_file(filename) - - def set_pkg_license_comment(self, doc, text): - """ - Set the package's license comment. - Raise OrderError if no package previously defined. - Raise CardinalityError if already set. - """ - self.assert_package_exists() - if not self.package_license_comment_set: - self.package_license_comment_set = True - doc.packages[-1].license_comment = text - return True - else: - raise CardinalityError("Package::LicenseComment") - - def set_pkg_attribution_text(self, doc, text): - """ - Set the package's attribution text. - """ - self.assert_package_exists() - doc.packages[-1].attribution_text = text - return True - - def set_pkg_cr_text(self, doc, text): - """ - Set the package's license comment. - Raise OrderError if no package previously defined. - Raise CardinalityError if already set. - """ - self.assert_package_exists() - if not self.package_cr_text_set: - self.package_cr_text_set = True - doc.packages[-1].cr_text = text - else: - raise CardinalityError("Package::CopyrightText") - - def set_pkg_summary(self, doc, text): - """ - Set the package summary. - Raise CardinalityError if summary already set. - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - if not self.package_summary_set: - self.package_summary_set = True - doc.packages[-1].summary = text - else: - raise CardinalityError("Package::Summary") - - def set_pkg_desc(self, doc, text): - """ - Set the package's description. - Raise CardinalityError if description already set. - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - if not self.package_desc_set: - self.package_desc_set = True - doc.packages[-1].description = text - else: - raise CardinalityError("Package::Description") - - def set_pkg_comment(self, doc, text): - """ - Set the package's comment. - Raise CardinalityError if comment already set. - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - if not self.package_comment_set: - self.package_comment_set = True - doc.packages[-1].comment = text - else: - raise CardinalityError("Package::Comment") - - def set_pkg_ext_ref_category(self, doc, category): - """ - Set the package's external reference locator. - Raise OrderError if no package previously defined. - Raise SPDXValueError if malformed value. - """ - self.assert_package_exists() - category = category.split("_")[-1] - - if category.lower() == "packagemanager": - category = "PACKAGE-MANAGER" - - if validations.validate_pkg_ext_ref_category(category): - if ( - len(doc.packages[-1].pkg_ext_refs) - and doc.packages[-1].pkg_ext_refs[-1].category is None - ): - doc.packages[-1].pkg_ext_refs[-1].category = category - else: - doc.packages[-1].add_pkg_ext_refs( - package.ExternalPackageRef(category=category) - ) - else: - raise SPDXValueError("ExternalRef::Category") - - def set_pkg_ext_ref_type(self, doc, typ): - """ - Set the package's external reference type. - Raise OrderError if no package previously defined. - Raise SPDXValueError if malformed value. - """ - self.assert_package_exists() - if "#" in typ: - typ = typ.split("#")[-1] - else: - typ = typ.split("/")[-1] - - if validations.validate_pkg_ext_ref_type(typ): - if ( - len(doc.packages[-1].pkg_ext_refs) - and doc.packages[-1].pkg_ext_refs[-1].pkg_ext_ref_type is None - ): - doc.packages[-1].pkg_ext_refs[-1].pkg_ext_ref_type = typ - else: - doc.packages[-1].add_pkg_ext_refs( - package.ExternalPackageRef(pkg_ext_ref_type=typ) - ) - else: - raise SPDXValueError("ExternalRef::Type") - - def set_pkg_ext_ref_comment(self, doc, comment): - """ - Set the package's external reference comment. - Raise CardinalityError if comment already set. - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - if not len(doc.packages[-1].pkg_ext_refs): - raise OrderError("Package::ExternalRef") - if not self.pkg_ext_comment_set: - self.pkg_ext_comment_set = True - doc.packages[-1].pkg_ext_refs[-1].comment = comment - return True - else: - raise CardinalityError("ExternalRef::Comment") - - -class FileBuilder(tagvaluebuilders.FileBuilder): - def __init__(self): - super(FileBuilder, self).__init__() - - def set_file_checksum(self, doc: Document, chk_sum: Union[Checksum, Dict, str]): - """ - Set the file check sum, if not already set. - chk_sum - A checksum.Checksum or a dict - """ - if self.has_file(doc): - if isinstance(chk_sum, dict): - identifier = ChecksumAlgorithm.checksum_algorithm_from_string(chk_sum.get('algorithm')) - self.file(doc).set_checksum(Checksum(identifier, - chk_sum.get('checksumValue'))) - elif isinstance(chk_sum, Checksum): - self.file(doc).set_checksum(chk_sum) - elif isinstance(chk_sum, str): - # kept for backwards compatibility - self.file(doc).set_checksum(Checksum(ChecksumAlgorithm.SHA1, chk_sum)) - return True - - def set_file_license_comment(self, doc, text): - """ - Raise OrderError if no package or file defined. - Raise CardinalityError if more than one per file. - """ - if self.has_package(doc) and self.has_file(doc): - if not self.file_license_comment_set: - self.file_license_comment_set = True - self.file(doc).license_comment = text - return True - else: - raise CardinalityError("File::LicenseComment") - else: - raise OrderError("File::LicenseComment") - - def set_file_attribution_text(self, doc, text): - """ - Set the file's attribution text. - """ - if self.has_package(doc) and self.has_file(doc): - self.assert_package_exists() - self.file(doc).attribution_text = text - return True - - def set_file_copyright(self, doc, text): - """ - Raise OrderError if no package or file defined. - Raise CardinalityError if more than one. - """ - if self.has_package(doc) and self.has_file(doc): - if not self.file_copytext_set: - self.file_copytext_set = True - self.file(doc).copyright = text - return True - else: - raise CardinalityError("File::CopyRight") - else: - raise OrderError("File::CopyRight") - - def set_file_comment(self, doc, text): - """ - Raise OrderError if no package or no file defined. - Raise CardinalityError if more than one comment set. - """ - if self.has_package(doc) and self.has_file(doc): - if not self.file_comment_set: - self.file_comment_set = True - self.file(doc).comment = text - return True - else: - raise CardinalityError("File::Comment") - else: - raise OrderError("File::Comment") - - def set_file_notice(self, doc, text): - """ - Raise OrderError if no package or file defined. - Raise CardinalityError if more than one. - """ - if self.has_package(doc) and self.has_file(doc): - if not self.file_notice_set: - self.file_notice_set = True - self.file(doc).notice = tagvaluebuilders.str_from_text(text) - return True - else: - raise CardinalityError("File::Notice") - else: - raise OrderError("File::Notice") - - def set_file_type(self, doc, filetype): - """ - Set the file type for RDF values. - """ - if not self.has_file(doc): - raise OrderError("File::FileType") - - split_string = filetype.split('#') - if len(split_string) != 2: - raise SPDXValueError('Unknown file type {}'.format(filetype)) - file_type = file.file_type_from_rdf(filetype) - - spdx_file = self.file(doc) - if file_type in spdx_file.file_types: - raise CardinalityError("File::FileType") - - spdx_file.file_types.append(file_type) - - -class SnippetBuilder(tagvaluebuilders.SnippetBuilder): - def __init__(self): - super(SnippetBuilder, self).__init__() - - def set_snippet_lic_comment(self, doc, lic_comment): - """ - Set the snippet's license comment. - Raise OrderError if no snippet previously defined. - Raise CardinalityError if already set. - """ - self.assert_snippet_exists() - if not self.snippet_lic_comment_set: - self.snippet_lic_comment_set = True - doc.snippet[-1].license_comment = lic_comment - else: - CardinalityError("Snippet::licenseComments") - - def set_snippet_comment(self, doc, comment): - """ - Set general comments about the snippet. - Raise OrderError if no snippet previously defined. - Raise CardinalityError if comment already set. - """ - self.assert_snippet_exists() - if not self.snippet_comment_set: - self.snippet_comment_set = True - doc.snippet[-1].comment = comment - return True - else: - raise CardinalityError("Snippet::comment") - - def set_snippet_attribution_text(self, doc, text): - """ - Set the snippet's attribution text. - """ - self.assert_snippet_exists() - doc.snippet[-1].attribution_text = text - return True - - def set_snippet_copyright(self, doc, copyright): - """ - Set the snippet's copyright text. - Raise OrderError if no snippet previously defined. - Raise CardinalityError if already set. - """ - self.assert_snippet_exists() - if not self.snippet_copyright_set: - self.snippet_copyright_set = True - doc.snippet[-1].copyright = copyright - else: - raise CardinalityError("Snippet::copyrightText") - - -class ReviewBuilder(tagvaluebuilders.ReviewBuilder): - def __init__(self): - super(ReviewBuilder, self).__init__() - - def add_review_comment(self, doc, comment): - """ - Set the review comment. - Raise CardinalityError if already set. - Raise OrderError if no reviewer defined before. - """ - if len(doc.reviews) != 0: - if not self.review_comment_set: - self.review_comment_set = True - doc.reviews[-1].comment = comment - return True - else: - raise CardinalityError("ReviewComment") - else: - raise OrderError("ReviewComment") - - -class AnnotationBuilder(tagvaluebuilders.AnnotationBuilder): - def __init__(self): - super(AnnotationBuilder, self).__init__() - - def add_annotation_comment(self, doc, comment): - """ - Set the annotation comment. - Raise CardinalityError if already set. - Raise OrderError if no annotator defined before. - """ - if len(doc.annotations) != 0: - if not self.annotation_comment_set: - self.annotation_comment_set = True - doc.annotations[-1].comment = comment - return True - else: - raise CardinalityError("AnnotationComment") - else: - raise OrderError("AnnotationComment") - - def add_annotation_type(self, doc, annotation_type): - """ - Set the annotation type. - Raise CardinalityError if already set. - Raise OrderError if no annotator defined before. - """ - if len(doc.annotations) != 0: - if not self.annotation_type_set: - if annotation_type.endswith("annotationType_other"): - self.annotation_type_set = True - doc.annotations[-1].annotation_type = "OTHER" - return True - elif annotation_type.endswith("annotationType_review"): - self.annotation_type_set = True - doc.annotations[-1].annotation_type = "REVIEW" - return True - else: - raise SPDXValueError("Annotation::AnnotationType") - else: - raise CardinalityError("Annotation::AnnotationType") - else: - raise OrderError("Annotation::AnnotationType") - - -class RelationshipBuilder(tagvaluebuilders.RelationshipBuilder): - def __init__(self): - super(RelationshipBuilder, self).__init__() - - def add_relationship_comment(self, doc, comment): - """ - Set the relationship comment. - Raise CardinalityError if already set. - Raise OrderError if no annotator defined before. - """ - if len(doc.relationships) != 0: - if not self.relationship_comment_set: - self.relationship_comment_set = True - doc.relationships[-1].comment = comment - return True - else: - raise CardinalityError("RelationshipComment") - else: - raise OrderError("RelationshipComment") - - -class Builder( - DocBuilder, - EntityBuilder, - CreationInfoBuilder, - PackageBuilder, - FileBuilder, - SnippetBuilder, - ReviewBuilder, - ExternalDocumentRefBuilder, - AnnotationBuilder, - RelationshipBuilder, -): - def __init__(self): - super(Builder, self).__init__() - # FIXME: this state does not make sense - self.reset() - - def reset(self): - """ - Reset builder's state for building new documents. - Must be called between usage with different documents. - """ - # FIXME: this state does not make sense - self.reset_creation_info() - self.reset_document() - self.reset_package() - self.reset_file_stat() - self.reset_reviews() - self.reset_annotations() - self.reset_relationship() diff --git a/spdx/parsers/tagvalue.py b/spdx/parsers/tagvalue.py deleted file mode 100644 index bf2e937af..000000000 --- a/spdx/parsers/tagvalue.py +++ /dev/null @@ -1,1822 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 re - -from ply import yacc - -from spdx import config -from spdx import license -from spdx import utils -from spdx.parsers.builderexceptions import CardinalityError -from spdx.parsers.builderexceptions import OrderError -from spdx.parsers.builderexceptions import SPDXValueError -from spdx.parsers.lexers.tagvalue import Lexer -from spdx.parsers.loggers import ErrorMessages -from spdx import document - -ERROR_MESSAGES = { - "TOOL_VALUE": "Invalid tool value {0} at line: {1}", - "ORG_VALUE": "Invalid organization value {0} at line: {1}", - "PERSON_VALUE": "Invalid person value {0} at line: {1}", - "CREATED_VALUE_TYPE": "Created value must be date in ISO 8601 format, line: {0}", - "MORE_THAN_ONE": "Only one {0} allowed, extra at line: {1}", - "CREATOR_COMMENT_VALUE_TYPE": "CreatorComment value must be free form text between tags or" - "single line of text, line:{0}", - "DOC_LICENSE_VALUE": "Invalid DataLicense value '{0}', line:{1} must be CC0-1.0", - "DOC_LICENSE_VALUE_TYPE": "DataLicense must be CC0-1.0, line: {0}", - "DOC_VERSION_VALUE": "Invalid SPDXVersion '{0}' must be SPDX-M.N where M and N are numbers. Line: {1}", - "DOC_VERSION_VALUE_TYPE": "Invalid SPDXVersion value, must be SPDX-M.N where M and N are numbers. Line: {0}", - "DOC_NAME_VALUE": "DocumentName must be single line of text, line: {0}", - "DOC_SPDX_ID_VALUE": "Invalid SPDXID value, SPDXID must be SPDXRef-DOCUMENT, line: {0}", - "EXT_DOC_REF_VALUE": "ExternalDocumentRef must contain External Document ID, SPDX Document URI and Checksum" - "in the standard format, line:{0}.", - "DOC_COMMENT_VALUE_TYPE": "DocumentComment value must be free form text between tags" - "or single line of text, line:{0}", - "DOC_NAMESPACE_VALUE": 'Invalid DocumentNamespace value {0}, must contain a scheme (e.g. "https:") ' - 'and should not contain the "#" delimiter, line:{1}', - "DOC_NAMESPACE_VALUE_TYPE": 'Invalid DocumentNamespace value, must contain a scheme (e.g. "https:") ' - 'and should not contain the "#" delimiter, line: {0}', - "REVIEWER_VALUE_TYPE": "Invalid Reviewer value must be a Person, Organization or Tool. Line: {0}", - "CREATOR_VALUE_TYPE": "Invalid Reviewer value must be a Person, Organization or Tool. Line: {0}", - "REVIEW_DATE_VALUE_TYPE": "ReviewDate value must be date in ISO 8601 format, line: {0}", - "REVIEW_COMMENT_VALUE_TYPE": "ReviewComment value must be free form text between tags" - "or single line of text, line:{0}", - "ANNOTATOR_VALUE_TYPE": "Invalid Annotator value must be a Person, Organization or Tool. Line: {0}", - "ANNOTATION_DATE_VALUE_TYPE": "AnnotationDate value must be date in ISO 8601 format, line: {0}", - "ANNOTATION_COMMENT_VALUE_TYPE": "AnnotationComment value must be free form text between tags" - "or single line of text, line:{0}", - "ANNOTATION_TYPE_VALUE": 'AnnotationType must be "REVIEW" or "OTHER". Line: {0}', - "ANNOTATION_SPDX_ID_VALUE": 'SPDXREF must be ["DocumentRef-"[idstring]":"]SPDXID where' - '["DocumentRef-"[idstring]":"] is an optional reference to an external SPDX document' - 'and SPDXID is a unique string containing letters, numbers, ".","-".', - "A_BEFORE_B": "{0} Can not appear before {1}, line: {2}", - "PACKAGE_NAME_VALUE": "PackageName must be single line of text, line: {0}", - "PKG_SPDX_ID_VALUE": 'SPDXID must be "SPDXRef-[idstring]" where [idstring] is a unique string containing ' - 'letters, numbers, ".", "-".', - "PKG_VERSION_VALUE": "PackageVersion must be single line of text, line: {0}", - "PKG_FILE_NAME_VALUE": "PackageFileName must be single line of text, line: {0}", - "PKG_SUPPL_VALUE": "PackageSupplier must be Organization, Person or NOASSERTION, line: {0}", - "PKG_ORIG_VALUE": "PackageOriginator must be Organization, Person or NOASSERTION, line: {0}", - "PKG_DOWN_VALUE": "PackageDownloadLocation must be a url or NONE or NOASSERTION, line: {0}", - "PKG_FILES_ANALYZED_VALUE": "FilesAnalyzed must be a boolean value, line: {0}", - "PKG_HOME_VALUE": "PackageHomePage must be a url or NONE or NOASSERTION, line: {0}", - "PKG_SRC_INFO_VALUE": "PackageSourceInfo must be free form text or single line of text, line: {0}", - "PKG_CHKSUM_VALUE": "PackageChecksum must be a single line of text, line: {0}", - "PKG_LICS_CONC_VALUE": "PackageLicenseConcluded must be NOASSERTION, NONE, license identifier " - "or license list, line: {0}", - "PKG_LIC_FFILE_VALUE": "PackageLicenseInfoFromFiles must be, line: {0}", - "PKG_LICS_DECL_VALUE": "PackageLicenseDeclared must be NOASSERTION, NONE, license identifier " - "or license list, line: {0}", - "PKG_LICS_COMMENT_VALUE": "PackageLicenseComments must be free form text or single line of text, line: {0}", - "PKG_ATTRIBUTION_TEXT_VALUE": "PackageAttributionText must be free form text or single line of text, line: {0}", - "PKG_SUM_VALUE": "PackageSummary must be free form text or single line of text, line: {0}", - "PKG_DESC_VALUE": "PackageDescription must be free form text or single line of text, line: {0}", - "PKG_COMMENT_VALUE": "PackageComment must be free form text or single line of text, line: {0}", - "PKG_EXT_REF_VALUE": "ExternalRef must contain category, type, and locator in the standard format, line:{0}.", - "PKG_EXT_REF_COMMENT_VALUE": "ExternalRefComment must be free form text or single line of text, line:{0}", - "PKG_VERF_CODE_VALUE": "VerificationCode doesn't match verifcode form, line:{0}", - "PRIMARY_PACKAGE_PURPOSE_VALUE": 'PrimaryPackagePurpose must be one of APPLICATION, FRAMEWORK, LIBRARY, CONTAINER, ' - 'OPERATING-SYSTEM, DEVICE, FIRMWARE, SOURCE, ARCHIVE, FILE, INSTALL, OTHER', - "BUILT_DATE_VALUE_TYPE": "Built date value must be date in ISO 8601 format, line: {0}", - "RELEASE_DATE_VALUE_TYPE": "Release date value must be date in ISO 8601 format, line: {0}", - "VALID_UNTIL_DATE_VALUE_TYPE": "Valid until date value must be date in ISO 8601 format, line: {0}", - "FILE_NAME_VALUE": "FileName must be a single line of text, line: {0}", - "FILE_COMMENT_VALUE": "FileComment must be free form text or single line of text, line:{0}", - "FILE_TYPE_VALUE": 'FileType must be one of SOURCE, BINARY, ARCHIVE, APPLICATION, AUDIO, IMAGE, TEXT, VIDEO, ' - 'DOCUMENTATION, SPDX, OTHER, line: {0}', - "FILE_SPDX_ID_VALUE": 'SPDXID must be "SPDXRef-[idstring]" where [idstring] is a unique string containing ' - 'letters, numbers, ".", "-".', - "FILE_ATTRIBUTION_TEXT_VALUE": "FileAttributionText must be free form text or single line of text, line: {0}", - "FILE_CHKSUM_VALUE": "FileChecksum must be a single line of text starting with 'SHA1:', line:{0}", - "FILE_LICS_CONC_VALUE": "LicenseConcluded must be NOASSERTION, NONE, license identifier or license list, line:{0}", - "FILE_LICS_INFO_VALUE": "LicenseInfoInFile must be NOASSERTION, NONE or license identifier, line: {0}", - "FILE_LICS_COMMENT_VALUE": "FileLicenseComments must be free form text or single line of text, line: {0}", - "FILE_CR_TEXT_VALUE": "FileCopyrightText must be one of NOASSERTION, NONE, free form text or single line of text," - "line: {0}", - "FILE_NOTICE_VALUE": "FileNotice must be free form text or single line of text, line: {0}", - "FILE_CONTRIB_VALUE": "FileContributor must be a single line, line: {0}", - "FILE_DEP_VALUE": "FileDependency must be a single line, line: {0}", - "ART_PRJ_NAME_VALUE": "ArtifactOfProjectName must be a single line, line: {0}", - "FILE_ART_OPT_ORDER": "ArtificatOfProjectHomePage and ArtificatOfProjectURI must immediately " - "follow ArtifactOfProjectName, line: {0}", - "ART_PRJ_HOME_VALUE": "ArtificatOfProjectHomePage must be a URL or UNKNOWN, line: {0}", - "ART_PRJ_URI_VALUE": "ArtificatOfProjectURI must be a URI or UNKNOWN, line: {0}", - "UNKNOWN_TAG": "Found unknown tag : {0} at line: {1}", - "LICS_ID_VALUE": "LicenseID must start with 'LicenseRef-', line: {0}", - "LICS_TEXT_VALUE": "ExtractedText must be free form text or single line of text, line: {0}", - "LICS_NAME_VALE": "LicenseName must be single line of text or NOASSERTION, line: {0}", - "LICS_COMMENT_VALUE": "LicenseComment must be free form text or single line of text, line: {0}", - "LICS_CRS_REF_VALUE": "LicenseCrossReference must be uri as single line of text, line: {0}", - "RELATIONSHIP_VALUE": "Relationship types must be one of the defined types, line: {0}", - "RELATIONSHIP_COMMENT_VALUE": "RelationshipComment value must be free form text between tags " - "or single line of text, line:{0}", - "PKG_CPY_TEXT_VALUE": "Package copyright text must be free form text or single line of text, line: {0}", - "SNIP_SPDX_ID_VALUE": 'SPDXID must be "SPDXRef-[idstring]" where [idstring] is a unique string ' - 'containing letters, numbers, ".", "-".', - "SNIPPET_NAME_VALUE": "SnippetName must be a single line of text, line: {0}", - "SNIP_COMMENT_VALUE": "SnippetComment must be free form text or single line of text, line: {0}", - "SNIP_COPYRIGHT_VALUE": "SnippetCopyrightText must be one of NOASSERTION, NONE, " - "free form text or single line of text, line: {0}", - "SNIP_LICS_COMMENT_VALUE": "SnippetLicenseComments must be free form text or single line of text, line: {0}", - "SNIPPET_ATTRIBUTION_TEXT_VALUE": "SnippetAttributionText must be free form text or single line of text, line: {0}", - "SNIP_FILE_SPDXID_VALUE": 'SnippetFromFileSPDXID must be ["DocumentRef-"[idstring]":"] SPDXID ' - "where DocumentRef-[idstring]: is an optional reference to an external" - "SPDX Document and SPDXID is a string containing letters, " - 'numbers, ".", "-".', - "SNIP_LICS_CONC_VALUE": "SnippetLicenseConcluded must be NOASSERTION, NONE, license identifier " - "or license list, line:{0}", - "SNIP_LICS_INFO_VALUE": "LicenseInfoInSnippet must be NOASSERTION, NONE or license identifier, line: {0}", -} - - -class Parser(object): - def __init__(self, builder, logger): - self.tokens = Lexer.tokens - self.builder = builder - self.logger = logger - self.error = False - self.license_list_parser = utils.LicenseListParser() - self.license_list_parser.build(write_tables=0, debug=0) - - def p_start_1(self, p): - "start : start attrib " - pass - - def p_start_2(self, p): - "start : attrib " - pass - - def p_attrib(self, p): - """attrib : spdx_version - | spdx_id - | data_lics - | doc_name - | ext_doc_ref - | doc_comment - | doc_namespace - | creator - | created - | creator_comment - | locs_list_ver - | reviewer - | review_date - | review_comment - | annotator - | annotation_date - | annotation_comment - | annotation_type - | annotation_spdx_id - | relationship - | relationship_comment - | package_name - | package_version - | pkg_down_location - | pkg_files_analyzed - | pkg_home - | pkg_summary - | pkg_src_info - | pkg_file_name - | pkg_supplier - | pkg_orig - | pkg_chksum - | pkg_verif - | pkg_desc - | pkg_comment - | pkg_attribution_text - | pkg_lic_decl - | pkg_lic_conc - | pkg_lic_ff - | pkg_lic_comment - | pkg_cr_text - | pkg_ext_ref - | pkg_ext_ref_comment - | primary_package_purpose - | built_date - | release_date - | valid_until_date - | file_name - | file_type - | file_chksum - | file_conc - | file_lics_info - | file_cr_text - | file_lics_comment - | file_attribution_text - | file_notice - | file_comment - | file_contrib - | file_dep - | file_artifact - | snip_spdx_id - | snip_name - | snip_comment - | snippet_attribution_text - | snip_cr_text - | snip_lic_comment - | snip_file_spdx_id - | snip_lics_conc - | snip_lics_info - | snip_byte_range - | snip_line_range - | extr_lic_id - | extr_lic_text - | extr_lic_name - | lic_xref - | lic_comment - | unknown_tag - """ - pass - - def more_than_one_error(self, tag, line): - self.error = True - msg = ERROR_MESSAGES["MORE_THAN_ONE"].format(tag, line) - self.logger.log(msg) - - def order_error(self, first_tag, second_tag, line): - """Reports an OrderError. Error message will state that - first_tag came before second_tag. - """ - self.error = True - msg = ERROR_MESSAGES["A_BEFORE_B"].format(first_tag, second_tag, line) - self.logger.log(msg) - - def p_lic_xref_1(self, p): - """lic_xref : LICS_CRS_REF LINE""" - try: - value = p[2] - self.builder.add_lic_xref(self.document, value) - except OrderError: - self.order_error("LicenseCrossReference", "LicenseName", p.lineno(1)) - - def p_lic_xref_2(self, p): - """lic_xref : LICS_CRS_REF error""" - self.error = True - msg = ERROR_MESSAGES["LICS_CRS_REF_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_lic_comment_1(self, p): - """lic_comment : LICS_COMMENT text_or_line""" - try: - value = p[2] - self.builder.set_lic_comment(self.document, value) - except OrderError: - self.order_error("LicenseComment", "LicenseID", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("LicenseComment", p.lineno(1)) - - def p_lic_comment_2(self, p): - """lic_comment : LICS_COMMENT error""" - self.error = True - msg = ERROR_MESSAGES["LICS_COMMENT_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_extr_lic_name_1(self, p): - """extr_lic_name : LICS_NAME extr_lic_name_value""" - try: - self.builder.set_lic_name(self.document, p[2]) - except OrderError: - self.order_error("LicenseName", "LicenseID", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("LicenseName", p.lineno(1)) - - def p_extr_lic_name_2(self, p): - """extr_lic_name : LICS_NAME error""" - self.error = True - msg = ERROR_MESSAGES["LICS_NAME_VALE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_extr_lic_name_value_1(self, p): - """extr_lic_name_value : LINE""" - p[0] = p[1] - - def p_extr_lic_name_value_2(self, p): - """extr_lic_name_value : NO_ASSERT""" - p[0] = utils.NoAssert() - - def p_extr_lic_text_1(self, p): - """extr_lic_text : LICS_TEXT text_or_line""" - try: - value = p[2] - self.builder.set_lic_text(self.document, value) - except OrderError: - self.order_error("ExtractedText", "LicenseID", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("ExtractedText", p.lineno(1)) - - def p_extr_lic_text_2(self, p): - """extr_lic_text : LICS_TEXT error""" - self.error = True - msg = ERROR_MESSAGES["LICS_TEXT_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_extr_lic_id_1(self, p): - """extr_lic_id : LICS_ID LINE""" - try: - value = p[2] - self.builder.set_lic_id(self.document, value) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["LICS_ID_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_extr_lic_id_2(self, p): - """extr_lic_id : LICS_ID error""" - self.error = True - msg = ERROR_MESSAGES["LICS_ID_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_unknown_tag(self, p): - """unknown_tag : UNKNOWN_TAG LINE""" - self.error = True - msg = ERROR_MESSAGES["UNKNOWN_TAG"].format(p[1], p.lineno(1)) - self.logger.log(msg) - - def p_file_artifact_1(self, p): - """file_artifact : prj_name_art file_art_rest - | prj_name_art - """ - pass - - def p_file_artifact_2(self, p): - """file_artifact : prj_name_art error""" - self.error = True - msg = ERROR_MESSAGES["FILE_ART_OPT_ORDER"].format(p.lineno(2)) - self.logger.log(msg) - - def p_file_art_rest(self, p): - """file_art_rest : prj_home_art prj_uri_art - | prj_uri_art prj_home_art - | prj_home_art - | prj_uri_art - """ - pass - - def p_prj_uri_art_1(self, p): - """prj_uri_art : ART_PRJ_URI UN_KNOWN""" - try: - self.builder.set_file_atrificat_of_project( - self.document, "uri", utils.UnKnown() - ) - except OrderError: - self.order_error("ArtificatOfProjectURI", "FileName", p.lineno(1)) - - def p_prj_uri_art_2(self, p): - """prj_uri_art : ART_PRJ_URI LINE""" - try: - value = p[2] - self.builder.set_file_atrificat_of_project(self.document, "uri", value) - except OrderError: - self.order_error("ArtificatOfProjectURI", "FileName", p.lineno(1)) - - def p_prj_uri_art_3(self, p): - """prj_uri_art : ART_PRJ_URI error""" - self.error = True - msg = ERROR_MESSAGES["ART_PRJ_URI_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_prj_home_art_1(self, p): - """prj_home_art : ART_PRJ_HOME LINE""" - try: - self.builder.set_file_atrificat_of_project(self.document, "home", p[2]) - except OrderError: - self.order_error("ArtificatOfProjectHomePage", "FileName", p.lineno(1)) - - def p_prj_home_art_2(self, p): - """prj_home_art : ART_PRJ_HOME UN_KNOWN""" - try: - self.builder.set_file_atrificat_of_project( - self.document, "home", utils.UnKnown() - ) - except OrderError: - self.order_error("ArtifactOfProjectName", "FileName", p.lineno(1)) - - def p_prj_home_art_3(self, p): - """prj_home_art : ART_PRJ_HOME error""" - self.error = True - msg = ERROR_MESSAGES["ART_PRJ_HOME_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_prj_name_art_1(self, p): - """prj_name_art : ART_PRJ_NAME LINE""" - try: - value = p[2] - self.builder.set_file_atrificat_of_project(self.document, "name", value) - except OrderError: - self.order_error("ArtifactOfProjectName", "FileName", p.lineno(1)) - - def p_prj_name_art_2(self, p): - """prj_name_art : ART_PRJ_NAME error""" - self.error = True - msg = ERROR_MESSAGES["ART_PRJ_NAME_VALUE"].format(p.lineno()) - self.logger.log(msg) - - def p_file_dep_1(self, p): - """file_dep : FILE_DEP LINE""" - try: - value = p[2] - self.builder.add_file_dep(self.document, value) - except OrderError: - self.order_error("FileDependency", "FileName", p.lineno(1)) - - def p_file_dep_2(self, p): - """file_dep : FILE_DEP error""" - self.error = True - msg = ERROR_MESSAGES["FILE_DEP_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_file_contrib_1(self, p): - """file_contrib : FILE_CONTRIB LINE""" - try: - value = p[2] - self.builder.add_file_contribution(self.document, value) - except OrderError: - self.order_error("FileContributor", "FileName", p.lineno(1)) - - def p_file_contrib_2(self, p): - """file_contrib : FILE_CONTRIB error""" - self.error = True - msg = ERROR_MESSAGES["FILE_CONTRIB_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_file_notice_1(self, p): - """file_notice : FILE_NOTICE text_or_line""" - try: - value = p[2] - self.builder.set_file_notice(self.document, value) - except OrderError: - self.order_error("FileNotice", "FileName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("FileNotice", p.lineno(1)) - - def p_file_notice_2(self, p): - """file_notice : FILE_NOTICE error""" - self.error = True - msg = ERROR_MESSAGES["FILE_NOTICE_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_file_cr_text_1(self, p): - """file_cr_text : FILE_CR_TEXT file_cr_value""" - try: - self.builder.set_file_copyright(self.document, p[2]) - except OrderError: - self.order_error("FileCopyrightText", "FileName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("FileCopyrightText", p.lineno(1)) - - def p_file_cr_text_2(self, p): - """file_cr_text : FILE_CR_TEXT error""" - self.error = True - msg = ERROR_MESSAGES["FILE_CR_TEXT_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_file_cr_value_1(self, p): - """file_cr_value : text_or_line""" - p[0] = p[1] - - def p_file_cr_value_2(self, p): - """file_cr_value : NONE""" - p[0] = utils.SPDXNone() - - def p_file_cr_value_3(self, p): - """file_cr_value : NO_ASSERT""" - p[0] = utils.NoAssert() - - def p_file_lics_comment_1(self, p): - """file_lics_comment : FILE_LICS_COMMENT text_or_line""" - try: - value = p[2] - self.builder.set_file_license_comment(self.document, value) - except OrderError: - self.order_error("LicenseComments", "FileName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("LicenseComments", p.lineno(1)) - - def p_file_lics_comment_2(self, p): - """file_lics_comment : FILE_LICS_COMMENT error""" - self.error = True - msg = ERROR_MESSAGES["FILE_LICS_COMMENT_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_file_attribution_text_1(self, p): - """file_attribution_text : FILE_ATTRIBUTION_TEXT text_or_line""" - try: - value = p[2] - self.builder.set_file_attribution_text(self.document, value) - except CardinalityError: - self.more_than_one_error("FileAttributionText", p.lineno(1)) - except OrderError: - self.order_error("FileAttributionText", "FileAttributionText", p.lineno(1)) - - def p_file_attribution_text_2(self, p): - """file_attribution_text : FILE_ATTRIBUTION_TEXT error""" - self.error = True - msg = ERROR_MESSAGES["FILE_ATTRIBUTION_TEXT_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_file_lics_info_1(self, p): - """file_lics_info : FILE_LICS_INFO file_lic_info_value""" - try: - self.builder.set_file_license_in_file(self.document, p[2]) - except OrderError: - self.order_error("LicenseInfoInFile", "FileName", p.lineno(1)) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["FILE_LICS_INFO_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_file_lics_info_2(self, p): - """file_lics_info : FILE_LICS_INFO error""" - self.error = True - msg = ERROR_MESSAGES["FILE_LICS_INFO_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_file_lic_info_value_1(self, p): - """file_lic_info_value : NONE""" - p[0] = utils.SPDXNone() - - def p_file_lic_info_value_2(self, p): - """file_lic_info_value : NO_ASSERT""" - p[0] = utils.NoAssert() - - # License Identifier - def p_file_lic_info_value_3(self, p): - """file_lic_info_value : LINE""" - value = p[1] - p[0] = license.License.from_identifier(value) - - def p_conc_license_1(self, p): - """conc_license : NO_ASSERT""" - p[0] = utils.NoAssert() - - def p_conc_license_2(self, p): - """conc_license : NONE""" - p[0] = utils.SPDXNone() - - def p_conc_license_3(self, p): - """conc_license : LINE""" - value = p[1] - ref_re = re.compile("LicenseRef-.+", re.UNICODE) - if (p[1] in config.LICENSE_MAP.keys()) or (ref_re.match(p[1]) is not None): - p[0] = license.License.from_identifier(value) - else: - p[0] = self.license_list_parser.parse(value) - - def p_file_name_1(self, p): - """file_name : FILE_NAME LINE""" - try: - value = p[2] - self.builder.set_file_name(self.document, value) - self.builder.set_current_file_name(value) - self.builder.set_current_file_id(None) - - except OrderError: - self.order_error("FileName", "PackageName", p.lineno(1)) - - def p_file_name_2(self, p): - """file_name : FILE_NAME error""" - self.error = True - msg = ERROR_MESSAGES["FILE_NAME_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_spdx_id(self, p): - """spdx_id : SPDX_ID LINE""" - value = p[2] - try: - # first parsed spdx id belongs to the document - if not self.builder.doc_spdx_id_set: - self.builder.set_doc_spdx_id(self.document, value) - - # else if a package is in scope that doesn't have an id yet, the parsed spdx id belongs to the package - elif self.builder.current_package_has_name() \ - and not self.builder.current_package_has_id(): - self.builder.set_pkg_spdx_id(self.document, value) - self.builder.set_current_package_id(value) - - # else if a file is in scope that doesn't have an id yet, the parsed spdx id belongs to the file - elif self.builder.current_file_has_name() \ - and not self.builder.current_file_has_id(): - self.builder.set_file_spdx_id(self.document, value) - self.builder.set_current_file_id(value) - if self.builder.has_current_package(): - self.builder.add_relationship(self.document, - self.builder.current_package["spdx_id"] + " CONTAINS " + value) - else: - raise SPDXValueError("SPDX ID couldn't be assigned properly. Line no. {0}") - except SPDXValueError as err: - self.error = True - self.logger.log(err.msg.format(p.lineno(2))) - - def p_file_comment_1(self, p): - """file_comment : FILE_COMMENT text_or_line""" - try: - value = p[2] - self.builder.set_file_comment(self.document, value) - except OrderError: - self.order_error("FileComment", "FileName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("FileComment", p.lineno(1)) - - def p_file_comment_2(self, p): - """file_comment : FILE_COMMENT error""" - self.error = True - msg = ERROR_MESSAGES["FILE_COMMENT_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_file_type_1(self, p): - """file_type : FILE_TYPE file_type_value""" - try: - self.builder.set_file_type(self.document, p[2]) - except OrderError: - self.order_error("FileType", "FileName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("FileType", p.lineno(1)) - - def p_file_type_2(self, p): - """file_type : FILE_TYPE error""" - self.error = True - msg = ERROR_MESSAGES["FILE_TYPE_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_file_chksum_1(self, p): - """file_chksum : FILE_CHKSUM CHKSUM""" - try: - value = p[2] - self.builder.set_file_checksum(self.document, value) - except OrderError: - self.order_error("FileChecksum", "FileName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("FileChecksum", p.lineno(1)) - - def p_file_chksum_2(self, p): - """file_chksum : FILE_CHKSUM error""" - self.error = True - msg = ERROR_MESSAGES["FILE_CHKSUM_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_file_conc_1(self, p): - """file_conc : FILE_LICS_CONC conc_license""" - try: - self.builder.set_concluded_license(self.document, p[2]) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["FILE_LICS_CONC_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - except OrderError: - self.order_error("LicenseConcluded", "FileName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("LicenseConcluded", p.lineno(1)) - - def p_file_conc_2(self, p): - """file_conc : FILE_LICS_CONC error""" - self.error = True - msg = ERROR_MESSAGES["FILE_LICS_CONC_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_file_type_value(self, p): - """file_type_value : SOURCE - | BINARY - | ARCHIVE - | APPLICATION - | AUDIO - | IMAGE - | FILETYPE_TEXT - | VIDEO - | DOCUMENTATION - | SPDX - | OTHER - """ - p[0] = p[1] - - def p_annotation_type_value(self, p): - """annotation_type_value : OTHER - | REVIEW - """ - p[0] = p[1] - - def p_pkg_desc_1(self, p): - """pkg_desc : PKG_DESC text_or_line""" - try: - value = p[2] - self.builder.set_pkg_desc(self.document, value) - except CardinalityError: - self.more_than_one_error("PackageDescription", p.lineno(1)) - except OrderError: - self.order_error("PackageDescription", "PackageFileName", p.lineno(1)) - - def p_pkg_desc_2(self, p): - """pkg_desc : PKG_DESC error""" - self.error = True - msg = ERROR_MESSAGES["PKG_DESC_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_comment_1(self, p): - """pkg_comment : PKG_COMMENT text_or_line""" - try: - value = p[2] - self.builder.set_pkg_comment(self.document, value) - except CardinalityError: - self.more_than_one_error("PackageComment", p.lineno(1)) - except OrderError: - self.order_error("PackageComment", "PackageFileName", p.lineno(1)) - - def p_pkg_comment_2(self, p): - """pkg_comment : PKG_COMMENT error""" - self.error = True - msg = ERROR_MESSAGES["PKG_COMMENT_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_attribution_text_1(self, p): - """pkg_attribution_text : PKG_ATTRIBUTION_TEXT text_or_line""" - try: - value = p[2] - self.builder.set_pkg_attribution_text(self.document, value) - except CardinalityError: - self.more_than_one_error("PackageAttributionText", p.lineno(1)) - except OrderError: - self.order_error( - "PackageAttributionText", "PackageAttributionText", p.lineno(1) - ) - - def p_pkg_attribution_text_2(self, p): - """pkg_attribution_text : PKG_ATTRIBUTION_TEXT error""" - self.error = True - msg = ERROR_MESSAGES["PKG_ATTRIBUTION_TEXT_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_summary_1(self, p): - """pkg_summary : PKG_SUM text_or_line""" - try: - value = p[2] - self.builder.set_pkg_summary(self.document, value) - except OrderError: - self.order_error("PackageSummary", "PackageFileName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("PackageSummary", p.lineno(1)) - - def p_pkg_summary_2(self, p): - """pkg_summary : PKG_SUM error""" - self.error = True - msg = ERROR_MESSAGES["PKG_SUM_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_cr_text_1(self, p): - """pkg_cr_text : PKG_CPY_TEXT pkg_cr_text_value""" - try: - self.builder.set_pkg_cr_text(self.document, p[2]) - except OrderError: - self.order_error("PackageCopyrightText", "PackageFileName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("PackageCopyrightText", p.lineno(1)) - - def p_pkg_cr_text_2(self, p): - """pkg_cr_text : PKG_CPY_TEXT error""" - self.error = True - msg = ERROR_MESSAGES["PKG_CPY_TEXT_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_ext_refs_1(self, p): - """pkg_ext_ref : PKG_EXT_REF LINE""" - try: - pkg_ext_info = p[2] - if len(pkg_ext_info.split()) != 3: - raise SPDXValueError(ERROR_MESSAGES["PKG_EXT_REF_VALUE"].format(p.lineno(2))) - else: - pkg_ext_category, pkg_ext_type, pkg_ext_locator = pkg_ext_info.split() - self.builder.add_pkg_ext_refs( - self.document, pkg_ext_category, pkg_ext_type, pkg_ext_locator - ) - except SPDXValueError as err: - self.error = True - self.logger.log(err.msg) - - def p_pkg_ext_refs_2(self, p): - """pkg_ext_ref : PKG_EXT_REF error""" - self.error = True - msg = ERROR_MESSAGES["PKG_EXT_REF_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_ext_ref_comment_1(self, p): - """pkg_ext_ref_comment : PKG_EXT_REF_COMMENT text_or_line""" - try: - value = p[2] - self.builder.add_pkg_ext_ref_comment(self.document, value) - except CardinalityError: - self.more_than_one_error("ExternalRefComment", p.lineno(1)) - - def p_pkg_ext_ref_comment_2(self, p): - """pkg_ext_ref_comment : PKG_EXT_REF_COMMENT error""" - self.error = True - msg = ERROR_MESSAGES["PKG_EXT_REF_COMMENT_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_cr_text_value_1(self, p): - """pkg_cr_text_value : text_or_line""" - p[0] = p[1] - - def p_pkg_cr_text_value_2(self, p): - """pkg_cr_text_value : NONE""" - p[0] = utils.SPDXNone() - - def p_pkg_cr_text_value_3(self, p): - """pkg_cr_text_value : NO_ASSERT""" - p[0] = utils.NoAssert() - - def p_pkg_lic_comment_1(self, p): - """pkg_lic_comment : PKG_LICS_COMMENT text_or_line""" - try: - value = p[2] - self.builder.set_pkg_license_comment(self.document, value) - except OrderError: - self.order_error("PackageLicenseComments", "PackageFileName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("PackageLicenseComments", p.lineno(1)) - - def p_pkg_lic_comment_2(self, p): - """pkg_lic_comment : PKG_LICS_COMMENT error""" - self.error = True - msg = ERROR_MESSAGES["PKG_LICS_COMMENT_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_lic_decl_1(self, p): - """pkg_lic_decl : PKG_LICS_DECL conc_license""" - try: - self.builder.set_pkg_license_declared(self.document, p[2]) - except OrderError: - self.order_error("PackageLicenseDeclared", "PackageName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("PackageLicenseDeclared", p.lineno(1)) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["PKG_LICS_DECL_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_lic_decl_2(self, p): - """pkg_lic_decl : PKG_LICS_DECL error""" - self.error = True - msg = ERROR_MESSAGES["PKG_LICS_DECL_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_lic_ff_1(self, p): - """pkg_lic_ff : PKG_LICS_FFILE pkg_lic_ff_value""" - try: - self.builder.set_pkg_license_from_file(self.document, p[2]) - except OrderError: - self.order_error("PackageLicenseInfoFromFiles", "PackageName", p.lineno(1)) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["PKG_LIC_FFILE_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_lic_ff_value_1(self, p): - """pkg_lic_ff_value : NONE""" - p[0] = utils.SPDXNone() - - def p_pkg_lic_ff_value_2(self, p): - """pkg_lic_ff_value : NO_ASSERT""" - p[0] = utils.NoAssert() - - def p_pkg_lic_ff_value_3(self, p): - """pkg_lic_ff_value : LINE""" - value = p[1] - p[0] = license.License.from_identifier(value) - - def p_pkg_lic_ff_2(self, p): - """pkg_lic_ff : PKG_LICS_FFILE error""" - self.error = True - msg = ERROR_MESSAGES["PKG_LIC_FFILE_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_lic_conc_1(self, p): - """pkg_lic_conc : PKG_LICS_CONC conc_license""" - try: - self.builder.set_pkg_licenses_concluded(self.document, p[2]) - except CardinalityError: - self.more_than_one_error("PackageLicenseConcluded", p.lineno(1)) - except OrderError: - self.order_error("PackageLicenseConcluded", "PackageFileName", p.lineno(1)) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["PKG_LICS_CONC_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_lic_conc_2(self, p): - """pkg_lic_conc : PKG_LICS_CONC error""" - self.error = True - msg = ERROR_MESSAGES["PKG_LICS_CONC_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_src_info_1(self, p): - """pkg_src_info : PKG_SRC_INFO text_or_line""" - try: - value = p[2] - self.builder.set_pkg_source_info(self.document, value) - except CardinalityError: - self.more_than_one_error("PackageSourceInfo", p.lineno(1)) - except OrderError: - self.order_error("PackageSourceInfo", "PackageFileName", p.lineno(1)) - - def p_pkg_src_info_2(self, p): - """pkg_src_info : PKG_SRC_INFO error""" - self.error = True - msg = ERROR_MESSAGES["PKG_SRC_INFO_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_chksum_1(self, p): - """pkg_chksum : PKG_CHKSUM CHKSUM""" - try: - value = p[2] - self.builder.set_pkg_checksum(self.document, value) - except OrderError: - self.order_error("PackageChecksum", "PackageFileName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("PackageChecksum", p.lineno(1)) - - def p_pkg_chksum_2(self, p): - """pkg_chksum : PKG_CHKSUM error""" - self.error = True - msg = ERROR_MESSAGES["PKG_CHKSUM_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_verif_1(self, p): - """pkg_verif : PKG_VERF_CODE LINE""" - try: - value = p[2] - self.builder.set_pkg_verif_code(self.document, value) - except OrderError: - self.order_error("PackageVerificationCode", "PackageName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("PackageVerificationCode", p.lineno(1)) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["PKG_VERF_CODE_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_verif_2(self, p): - """pkg_verif : PKG_VERF_CODE error""" - self.error = True - msg = ERROR_MESSAGES["PKG_VERF_CODE_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_home_1(self, p): - """pkg_home : PKG_HOME pkg_home_value""" - try: - self.builder.set_pkg_home(self.document, p[2]) - except OrderError: - self.order_error("PackageHomePage", "PackageName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("PackageHomePage", p.lineno(1)) - - def p_pkg_home_2(self, p): - """pkg_home : PKG_HOME error""" - self.error = True - msg = ERROR_MESSAGES["PKG_HOME_VALUE"] - self.logger.log(msg) - - def p_pkg_home_value_1(self, p): - """pkg_home_value : LINE""" - p[0] = p[1] - - def p_pkg_home_value_2(self, p): - """pkg_home_value : NONE""" - p[0] = utils.SPDXNone() - - def p_pkg_home_value_3(self, p): - """pkg_home_value : NO_ASSERT""" - p[0] = utils.NoAssert() - - def p_pkg_down_location_1(self, p): - """pkg_down_location : PKG_DOWN pkg_down_value""" - try: - self.builder.set_pkg_down_location(self.document, p[2]) - except OrderError: - self.order_error("PackageDownloadLocation", "PackageName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("PackageDownloadLocation", p.lineno(1)) - - def p_pkg_down_location_2(self, p): - """pkg_down_location : PKG_DOWN error""" - self.error = True - msg = ERROR_MESSAGES["PKG_DOWN_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_files_analyzed_1(self, p): - """pkg_files_analyzed : PKG_FILES_ANALYZED LINE""" - try: - value = p[2] - self.builder.set_pkg_files_analyzed(self.document, value) - except CardinalityError: - self.more_than_one_error("FilesAnalyzed", p.lineno(1)) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["PKG_FILES_ANALYZED_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_files_analyzed_2(self, p): - """pkg_files_analyzed : PKG_FILES_ANALYZED error""" - self.error = True - msg = ERROR_MESSAGES["PKG_FILES_ANALYZED_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_down_value_1(self, p): - """pkg_down_value : LINE """ - p[0] = p[1] - - def p_pkg_down_value_2(self, p): - """pkg_down_value : NONE""" - p[0] = utils.SPDXNone() - - def p_pkg_down_value_3(self, p): - """pkg_down_value : NO_ASSERT""" - p[0] = utils.NoAssert() - - def p_pkg_orig_1(self, p): - """pkg_orig : PKG_ORIG pkg_supplier_values""" - try: - self.builder.set_pkg_originator(self.document, p[2]) - except OrderError: - self.order_error("PackageOriginator", "PackageName", p.lineno(1)) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["PKG_ORIG_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - except CardinalityError: - self.more_than_one_error("PackageOriginator", p.lineno(1)) - - def p_pkg_orig_2(self, p): - """pkg_orig : PKG_ORIG error""" - self.error = True - msg = ERROR_MESSAGES["PKG_ORIG_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_supplier_1(self, p): - """pkg_supplier : PKG_SUPPL pkg_supplier_values""" - try: - self.builder.set_pkg_supplier(self.document, p[2]) - except OrderError: - self.order_error("PackageSupplier", "PackageName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("PackageSupplier", p.lineno(1)) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["PKG_SUPPL_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_supplier_2(self, p): - """pkg_supplier : PKG_SUPPL error""" - self.error = True - msg = ERROR_MESSAGES["PKG_SUPPL_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_pkg_supplier_values_1(self, p): - """pkg_supplier_values : NO_ASSERT""" - p[0] = utils.NoAssert() - - def p_pkg_supplier_values_2(self, p): - """pkg_supplier_values : entity""" - p[0] = p[1] - - def p_pkg_file_name(self, p): - """pkg_file_name : PKG_FILE_NAME LINE""" - try: - value = p[2] - self.builder.set_pkg_file_name(self.document, value) - except OrderError: - self.order_error("PackageFileName", "PackageName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("PackageFileName", p.lineno(1)) - - def p_pkg_file_name_1(self, p): - """pkg_file_name : PKG_FILE_NAME error""" - self.error = True - msg = ERROR_MESSAGES["PKG_FILE_NAME_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_package_version_1(self, p): - """package_version : PKG_VERSION LINE""" - try: - value = p[2] - self.builder.set_pkg_vers(self.document, value) - except OrderError: - self.order_error("PackageVersion", "PackageName", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("PackageVersion", p.lineno(1)) - - def p_package_version_2(self, p): - """package_version : PKG_VERSION error""" - self.error = True - msg = ERROR_MESSAGES["PKG_VERSION_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_package_name(self, p): - """package_name : PKG_NAME LINE""" - try: - value = p[2] - self.builder.create_package(self.document, value) - self.builder.set_current_package_name(value) - self.builder.set_current_package_id(None) - self.builder.set_current_file_name(None) # start of a new package implies new file - self.builder.set_current_file_id(None) - except CardinalityError: - self.more_than_one_error("PackageName", p.lineno(1)) - - def p_package_name_1(self, p): - """package_name : PKG_NAME error""" - self.error = True - msg = ERROR_MESSAGES["PACKAGE_NAME_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_primary_package_purpose_1(self, p): - """primary_package_purpose : PRIMARY_PACKAGE_PURPOSE primary_package_purpose_value""" - try: - self.builder.set_pkg_primary_package_purpose(self.document, p[2]) - except CardinalityError: - self.more_than_one_error("PrimaryPackagePurpose", p.lineno(1)) - - def p_primary_package_purpose_2(self, p): - """primary_package_purpose : PRIMARY_PACKAGE_PURPOSE error""" - self.error = True - msg = ERROR_MESSAGES["PRIMARY_PACKAGE_PURPOSE_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_primary_package_purpose_value(self, p): - """primary_package_purpose_value : APPLICATION - | FRAMEWORK - | LIBRARY - | CONTAINER - | OPERATING_SYSTEM - | DEVICE - | FIRMWARE - | SOURCE - | ARCHIVE - | FILE - | INSTALL - | OTHER - """ - p[0] = p[1] - - def p_built_date_1(self, p): - """built_date : BUILT_DATE DATE""" - try: - value = p[2] - self.builder.set_pkg_built_date(self.document, value) - except CardinalityError: - self.more_than_one_error("BuiltDate", p.lineno(1)) - - def p_built_date_2(self, p): - """built_date : BUILT_DATE error""" - self.error = True - msg = ERROR_MESSAGES["BUILT_DATE_VALUE_TYPE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_release_date_1(self, p): - """release_date : RELEASE_DATE DATE""" - try: - value = p[2] - self.builder.set_pkg_release_date(self.document, value) - except CardinalityError: - self.more_than_one_error("ReleaseDate", p.lineno(1)) - - def p_release_date_2(self, p): - """release_date : RELEASE_DATE error""" - self.error = True - msg = ERROR_MESSAGES["RELEASE_DATE_VALUE_TYPE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_valid_until_date_1(self, p): - """valid_until_date : VALID_UNTIL_DATE DATE""" - try: - value = p[2] - self.builder.set_pkg_valid_until_date(self.document, value) - except CardinalityError: - self.more_than_one_error("ValidUntilDate", p.lineno(1)) - - def p_valid_until_date_2(self, p): - """valid_until_date : VALID_UNTIL_DATE error""" - self.error = True - msg = ERROR_MESSAGES["VALID_UNTIL_DATE_VALUE_TYPE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_snip_spdx_id(self, p): - """snip_spdx_id : SNIPPET_SPDX_ID LINE""" - try: - value = p[2] - self.builder.create_snippet(self.document, value) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["SNIP_SPDX_ID_VALUE"].format(p.lineno(2)) - self.logger.log(msg) - - def p_snip_spdx_id_1(self, p): - """snip_spdx_id : SNIPPET_SPDX_ID error""" - self.error = True - msg = ERROR_MESSAGES["SNIP_SPDX_ID_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_snippet_name(self, p): - """snip_name : SNIPPET_NAME LINE""" - try: - value = p[2] - self.builder.set_snippet_name(self.document, value) - except OrderError: - self.order_error("SnippetName", "SnippetSPDXID", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("SnippetName", p.lineno(1)) - - def p_snippet_name_1(self, p): - """snip_name : SNIPPET_NAME error""" - self.error = True - msg = ERROR_MESSAGES["SNIPPET_NAME_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_snippet_comment(self, p): - """snip_comment : SNIPPET_COMMENT text_or_line""" - try: - value = p[2] - self.builder.set_snippet_comment(self.document, value) - except OrderError: - self.order_error("SnippetComment", "SnippetSPDXID", p.lineno(1)) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["SNIP_COMMENT_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - except CardinalityError: - self.more_than_one_error("SnippetComment", p.lineno(1)) - - def p_snippet_comment_1(self, p): - """snip_comment : SNIPPET_COMMENT error""" - self.error = True - msg = ERROR_MESSAGES["SNIP_COMMENT_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_snippet_attribution_text_1(self, p): - """snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT text_or_line""" - try: - value = p[2] - self.builder.set_snippet_attribution_text(self.document, value) - except CardinalityError: - self.more_than_one_error("SnippetAttributionText", p.lineno(1)) - except OrderError: - self.order_error( - "SnippetAttributionText", "SnippetAttributionText", p.lineno(1) - ) - - def p_snippet_attribution_text_2(self, p): - """snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT error""" - self.error = True - msg = ERROR_MESSAGES["SNIPPET_ATTRIBUTION_TEXT_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_snippet_cr_text(self, p): - """snip_cr_text : SNIPPET_CR_TEXT snip_cr_value""" - try: - self.builder.set_snippet_copyright(self.document, p[2]) - except OrderError: - self.order_error("SnippetCopyrightText", "SnippetSPDXID", p.lineno(1)) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["SNIP_COPYRIGHT_VALUE"].format(p.lineno(2)) - self.logger.log(msg) - except CardinalityError: - self.more_than_one_error("SnippetCopyrightText", p.lineno(1)) - - def p_snippet_cr_text_1(self, p): - """snip_cr_text : SNIPPET_CR_TEXT error""" - self.error = True - msg = ERROR_MESSAGES["SNIP_COPYRIGHT_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_snippet_cr_value_1(self, p): - """snip_cr_value : text_or_line""" - p[0] = p[1] - - def p_snippet_cr_value_2(self, p): - """snip_cr_value : NONE""" - p[0] = utils.SPDXNone() - - def p_snippet_cr_value_3(self, p): - """snip_cr_value : NO_ASSERT""" - p[0] = utils.NoAssert() - - def p_snippet_lic_comment(self, p): - """snip_lic_comment : SNIPPET_LICS_COMMENT text_or_line""" - try: - value = p[2] - self.builder.set_snippet_lic_comment(self.document, value) - except OrderError: - self.order_error("SnippetLicenseComments", "SnippetSPDXID", p.lineno(1)) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["SNIP_LICS_COMMENT_VALUE"].format(p.lineno(2)) - self.logger.log(msg) - except CardinalityError: - self.more_than_one_error("SnippetLicenseComments", p.lineno(1)) - - def p_snippet_lic_comment_1(self, p): - """snip_lic_comment : SNIPPET_LICS_COMMENT error""" - self.error = True - msg = ERROR_MESSAGES["SNIP_LICS_COMMENT_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_text_or_line_value_1(self, p): - """text_or_line : TEXT""" - p[0] = p[1] - - def p_text_or_line_value_2(self, p): - """text_or_line : LINE""" - p[0] = p[1] - - def p_snip_from_file_spdxid(self, p): - """snip_file_spdx_id : SNIPPET_FILE_SPDXID LINE""" - try: - value = p[2] - self.builder.set_snip_from_file_spdxid(self.document, value) - except OrderError: - self.order_error("SnippetFromFileSPDXID", "SnippetSPDXID", p.lineno(1)) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["SNIP_FILE_SPDXID_VALUE"].format(p.lineno(2)) - self.logger.log(msg) - except CardinalityError: - self.more_than_one_error("SnippetFromFileSPDXID", p.lineno(1)) - - def p_snip_from_file_spdxid_1(self, p): - """snip_file_spdx_id : SNIPPET_FILE_SPDXID error""" - self.error = True - msg = ERROR_MESSAGES["SNIP_FILE_SPDXID_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_snippet_concluded_license(self, p): - """snip_lics_conc : SNIPPET_LICS_CONC conc_license""" - try: - self.builder.set_snip_concluded_license(self.document, p[2]) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["SNIP_LICS_CONC_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - except OrderError: - self.order_error("SnippetLicenseConcluded", "SnippetSPDXID", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("SnippetLicenseConcluded", p.lineno(1)) - - def p_snippet_concluded_license_1(self, p): - """snip_lics_conc : SNIPPET_LICS_CONC error""" - self.error = True - msg = ERROR_MESSAGES["SNIP_LICS_CONC_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_snippet_lics_info(self, p): - """snip_lics_info : SNIPPET_LICS_INFO snip_lic_info_value""" - try: - self.builder.set_snippet_lics_info(self.document, p[2]) - except OrderError: - self.order_error("LicenseInfoInSnippet", "SnippetSPDXID", p.lineno(1)) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["SNIP_LICS_INFO_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_snippet_lics_info_1(self, p): - """snip_lics_info : SNIPPET_LICS_INFO error""" - self.error = True - msg = ERROR_MESSAGES["SNIP_LICS_INFO_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_snippet_byte_range(self, p): - """snip_byte_range : SNIPPET_BYTE_RANGE RANGE""" - try: - self.builder.set_snippet_byte_range(self.document, p[2]) - except OrderError: - self.order_error("SnippetByteRange", "SnippetSPDXID", p.lineno(1)) - except SPDXValueError: - self.error = True - msg = "Value for Snippet ByteRange invalid in line {}.".format(p.lineno(1)) - self.logger.log(msg) - - def p_snippet_byte_range_1(self, p): - """snip_byte_range : SNIPPET_BYTE_RANGE error""" - - self.error = True - msg = "Reading of SnippetByteRange failed for line {}.".format(p.lineno(1)) - self.logger.log(msg) - - def p_snippet_line_range(self, p): - """snip_line_range : SNIPPET_LINE_RANGE RANGE""" - try: - self.builder.set_snippet_line_range(self.document, p[2]) - except OrderError: - self.order_error("SnippetLineRange", "SnippetSPDXID", p.lineno(1)) - except SPDXValueError: - self.error = True - msg = "Value for Snippet LineRange invalid in line {}.".format(p.lineno(1)) - self.logger.log(msg) - - def p_snippet_line_range_1(self, p): - """snip_line_range : SNIPPET_LINE_RANGE error""" - - self.error = True - msg = "Reading of SnippetLineRange failed for line {}.".format(p.lineno(1)) - self.logger.log(msg) - - def p_snip_lic_info_value_1(self, p): - """snip_lic_info_value : NONE""" - p[0] = utils.SPDXNone() - - def p_snip_lic_info_value_2(self, p): - """snip_lic_info_value : NO_ASSERT""" - p[0] = utils.NoAssert() - - def p_snip_lic_info_value_3(self, p): - """snip_lic_info_value : LINE""" - value = p[1] - p[0] = license.License.from_identifier(value) - - def p_reviewer_1(self, p): - """reviewer : REVIEWER entity""" - self.builder.add_reviewer(self.document, p[2]) - - def p_reviewer_2(self, p): - """reviewer : REVIEWER error""" - self.error = True - msg = ERROR_MESSAGES["REVIEWER_VALUE_TYPE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_review_date_1(self, p): - """review_date : REVIEW_DATE DATE""" - try: - value = p[2] - self.builder.add_review_date(self.document, value) - except CardinalityError: - self.more_than_one_error("ReviewDate", p.lineno(1)) - except OrderError: - self.order_error("ReviewDate", "Reviewer", p.lineno(1)) - - def p_review_date_2(self, p): - """review_date : REVIEW_DATE error""" - self.error = True - msg = ERROR_MESSAGES["REVIEW_DATE_VALUE_TYPE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_review_comment_1(self, p): - """review_comment : REVIEW_COMMENT text_or_line""" - try: - value = p[2] - self.builder.add_review_comment(self.document, value) - except CardinalityError: - self.more_than_one_error("ReviewComment", p.lineno(1)) - except OrderError: - self.order_error("ReviewComment", "Reviewer", p.lineno(1)) - - def p_review_comment_2(self, p): - """review_comment : REVIEW_COMMENT error""" - self.error = True - msg = ERROR_MESSAGES["REVIEW_COMMENT_VALUE_TYPE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_annotator_1(self, p): - """annotator : ANNOTATOR entity""" - self.builder.add_annotator(self.document, p[2]) - - def p_annotator_2(self, p): - """annotator : ANNOTATOR error""" - self.error = True - msg = ERROR_MESSAGES["ANNOTATOR_VALUE_TYPE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_annotation_date_1(self, p): - """annotation_date : ANNOTATION_DATE DATE""" - try: - value = p[2] - self.builder.add_annotation_date(self.document, value) - except CardinalityError: - self.more_than_one_error("AnnotationDate", p.lineno(1)) - except OrderError: - self.order_error("AnnotationDate", "Annotator", p.lineno(1)) - - def p_annotation_date_2(self, p): - """annotation_date : ANNOTATION_DATE error""" - self.error = True - msg = ERROR_MESSAGES["ANNOTATION_DATE_VALUE_TYPE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_annotation_comment_1(self, p): - """annotation_comment : ANNOTATION_COMMENT text_or_line""" - try: - value = p[2] - self.builder.add_annotation_comment(self.document, value) - except CardinalityError: - self.more_than_one_error("AnnotationComment", p.lineno(1)) - except OrderError: - self.order_error("AnnotationComment", "Annotator", p.lineno(1)) - - def p_annotation_comment_2(self, p): - """annotation_comment : ANNOTATION_COMMENT error""" - self.error = True - msg = ERROR_MESSAGES["ANNOTATION_COMMENT_VALUE_TYPE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_annotation_type_1(self, p): - """annotation_type : ANNOTATION_TYPE annotation_type_value""" - try: - value = p[2] - self.builder.add_annotation_type(self.document, value) - except CardinalityError: - self.more_than_one_error("AnnotationType", p.lineno(1)) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["ANNOTATION_TYPE_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - except OrderError: - self.order_error("AnnotationType", "Annotator", p.lineno(1)) - - def p_annotation_type_2(self, p): - """annotation_type : ANNOTATION_TYPE error""" - self.error = True - msg = ERROR_MESSAGES["ANNOTATION_TYPE_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_annotation_spdx_id_1(self, p): - """annotation_spdx_id : ANNOTATION_SPDX_ID LINE""" - try: - value = p[2] - self.builder.set_annotation_spdx_id(self.document, value) - except CardinalityError: - self.more_than_one_error("SPDXREF", p.lineno(1)) - except OrderError: - self.order_error("SPDXREF", "Annotator", p.lineno(1)) - - def p_annotation_spdx_id_2(self, p): - """annotation_spdx_id : ANNOTATION_SPDX_ID error""" - self.error = True - msg = ERROR_MESSAGES["ANNOTATION_SPDX_ID_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_relationship_1(self, p): - """relationship : RELATIONSHIP relationship_value""" - try: - value = p[2] - self.builder.add_relationship(self.document, value) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["RELATIONSHIP_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - except OrderError: - self.order_error("Relationship_type", "Relationship", p.lineno(1)) - - def p_relationship_2(self, p): - """relationship : RELATIONSHIP error""" - self.error = True - msg = ERROR_MESSAGES["RELATIONSHIP_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_relationship_value_with_doc_ref(self, p): - """relationship_value : DOC_REF_ID LINE""" - p[0] = p[1] + ":" + p[2] - - def p_relationship_value_without_doc_ref(self, p): - """relationship_value : LINE""" - p[0] = p[1] - - def p_relationship_comment_1(self, p): - """relationship_comment : RELATIONSHIP_COMMENT text_or_line""" - try: - value = p[2] - self.builder.add_relationship_comment(self.document, value) - except OrderError: - self.order_error("RelationshipComment", "Relationship", p.lineno(1)) - except CardinalityError: - self.more_than_one_error("RelationshipComment", p.lineno(1)) - - def p_relationship_comment_2(self, p): - """relationship_comment : RELATIONSHIP_COMMENT error""" - self.error = True - msg = ERROR_MESSAGES["RELATIONSHIP_COMMENT_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_lics_list_ver_1(self, p): - """locs_list_ver : LIC_LIST_VER LINE""" - try: - value = p[2] - self.builder.set_lics_list_ver(self.document, value) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["LIC_LIST_VER_VALUE"].format(p[2], p.lineno(2)) - self.logger.log(msg) - except CardinalityError: - self.more_than_one_error("LicenseListVersion", p.lineno(1)) - - def p_lics_list_ver_2(self, p): - """locs_list_ver : LIC_LIST_VER error""" - self.error = True - msg = ERROR_MESSAGES["LIC_LIST_VER_VALUE_TYPE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_doc_comment_1(self, p): - """doc_comment : DOC_COMMENT text_or_line""" - try: - value = p[2] - self.builder.set_doc_comment(self.document, value) - except CardinalityError: - self.more_than_one_error("DocumentComment", p.lineno(1)) - - def p_doc_comment_2(self, p): - """doc_comment : DOC_COMMENT error""" - self.error = True - msg = ERROR_MESSAGES["DOC_COMMENT_VALUE_TYPE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_doc_namespace_1(self, p): - """doc_namespace : DOC_NAMESPACE LINE""" - try: - value = p[2] - self.builder.set_doc_namespace(self.document, value) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["DOC_NAMESPACE_VALUE"].format(p[2], p.lineno(2)) - self.logger.log(msg) - except CardinalityError: - self.more_than_one_error("DocumentNamespace", p.lineno(1)) - - def p_doc_namespace_2(self, p): - """doc_namespace : DOC_NAMESPACE error""" - self.error = True - msg = ERROR_MESSAGES["DOC_NAMESPACE_VALUE_TYPE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_data_license_1(self, p): - """data_lics : DOC_LICENSE LINE""" - try: - value = p[2] - self.builder.set_doc_data_lics(self.document, value) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["DOC_LICENSE_VALUE"].format(p[2], p.lineno(2)) - self.logger.log(msg) - except CardinalityError: - self.more_than_one_error("DataLicense", p.lineno(1)) - - def p_data_license_2(self, p): - """data_lics : DOC_LICENSE error""" - self.error = True - msg = ERROR_MESSAGES["DOC_LICENSE_VALUE_TYPE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_doc_name_1(self, p): - """doc_name : DOC_NAME LINE""" - try: - value = p[2] - self.builder.set_doc_name(self.document, value) - except CardinalityError: - self.more_than_one_error("DocumentName", p.lineno(1)) - - def p_doc_name_2(self, p): - """doc_name : DOC_NAME error""" - self.error = True - msg = ERROR_MESSAGES["DOC_NAME_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_ext_doc_refs_1(self, p): - """ext_doc_ref : EXT_DOC_REF DOC_REF_ID DOC_URI EXT_DOC_REF_CHKSUM""" - try: - doc_ref_id = p[2] - doc_uri = p[3] - ext_doc_chksum = p[4] - - self.builder.add_ext_doc_refs( - self.document, doc_ref_id, doc_uri, ext_doc_chksum - ) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["EXT_DOC_REF_VALUE"].format(p.lineno(2)) - self.logger.log(msg) - - def p_ext_doc_refs_2(self, p): - """ext_doc_ref : EXT_DOC_REF error""" - self.error = True - msg = ERROR_MESSAGES["EXT_DOC_REF_VALUE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_spdx_version_1(self, p): - """spdx_version : DOC_VERSION LINE""" - try: - value = p[2] - self.builder.set_doc_version(self.document, value) - except CardinalityError: - self.more_than_one_error("SPDXVersion", p.lineno(1)) - except SPDXValueError: - self.error = True - msg = ERROR_MESSAGES["DOC_VERSION_VALUE"].format(p[2], p.lineno(1)) - self.logger.log(msg) - - def p_spdx_version_2(self, p): - """spdx_version : DOC_VERSION error""" - self.error = True - msg = ERROR_MESSAGES["DOC_VERSION_VALUE_TYPE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_creator_comment_1(self, p): - """creator_comment : CREATOR_COMMENT text_or_line""" - try: - value = p[2] - self.builder.set_creation_comment(self.document, value) - except CardinalityError: - self.more_than_one_error("CreatorComment", p.lineno(1)) - - def p_creator_comment_2(self, p): - """creator_comment : CREATOR_COMMENT error""" - self.error = True - msg = ERROR_MESSAGES["CREATOR_COMMENT_VALUE_TYPE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_creator_1(self, p): - """creator : CREATOR entity""" - self.builder.add_creator(self.document, p[2]) - - def p_creator_2(self, p): - """creator : CREATOR error""" - self.error = True - msg = ERROR_MESSAGES["CREATOR_VALUE_TYPE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_created_1(self, p): - """created : CREATED DATE""" - try: - value = p[2] - self.builder.set_created_date(self.document, value) - except CardinalityError: - self.more_than_one_error("Created", p.lineno(1)) - - def p_created_2(self, p): - """created : CREATED error""" - self.error = True - msg = ERROR_MESSAGES["CREATED_VALUE_TYPE"].format(p.lineno(1)) - self.logger.log(msg) - - def p_entity_1(self, p): - """entity : TOOL_VALUE - """ - try: - value = p[1] - p[0] = self.builder.build_tool(self.document, value) - except SPDXValueError: - msg = ERROR_MESSAGES["TOOL_VALUE"].format(p[1], p.lineno(1)) - self.logger.log(msg) - self.error = True - p[0] = None - - def p_entity_2(self, p): - """entity : ORG_VALUE - """ - try: - value = p[1] - p[0] = self.builder.build_org(self.document, value) - except SPDXValueError: - msg = ERROR_MESSAGES["ORG_VALUE"].format(p[1], p.lineno(1)) - self.logger.log(msg) - self.error = True - p[0] = None - - def p_entity_3(self, p): - """entity : PERSON_VALUE - """ - try: - value = p[1] - p[0] = self.builder.build_person(self.document, value) - except SPDXValueError: - msg = ERROR_MESSAGES["PERSON_VALUE"].format(p[1], p.lineno(1)) - self.logger.log(msg) - self.error = True - p[0] = None - - def p_error(self, p): - pass - - def build(self, **kwargs): - self.lex = Lexer() - self.lex.build(reflags=re.UNICODE) - self.yacc = yacc.yacc(module=self, **kwargs) - - def parse(self, text): - self.document = document.Document() - self.error = False - self.yacc.parse(text, lexer=self.lex) - # FIXME: this state does not make sense - self.builder.reset() - validation_messages = ErrorMessages() - # Report extra errors if self.error is False otherwise there will be - # redundant messages - validation_messages = self.document.validate(validation_messages) - if not self.error: - if validation_messages: - for msg in validation_messages: - self.logger.log(msg) - self.error = True - return self.document, self.error diff --git a/spdx/parsers/tagvaluebuilders.py b/spdx/parsers/tagvaluebuilders.py deleted file mode 100644 index 2d380ead6..000000000 --- a/spdx/parsers/tagvaluebuilders.py +++ /dev/null @@ -1,1717 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 re -from typing import Dict, List - -from spdx import annotation -from spdx import creationinfo -from spdx import file -from spdx import license -from spdx import package -from spdx import review -from spdx import snippet -from spdx import utils -from spdx import version -from spdx.checksum import Checksum -from spdx.document import ExternalDocumentRef, Document -from spdx.package import PackagePurpose -from spdx.parsers import validations -from spdx.parsers.builderexceptions import CardinalityError -from spdx.parsers.builderexceptions import OrderError -from spdx.parsers.builderexceptions import SPDXValueError -from spdx.relationship import Relationship - - -def str_from_text(text) -> str: - """ - Return content of a free form text block as a string. - """ - REGEX = re.compile("((.|\n)+)", re.UNICODE) - match = REGEX.match(text) - if match: - return match.group(1) - elif isinstance(text, str): - return text - else: - return None - - -class DocBuilder(object): - """ - Set the fields of the top level document model. - """ - - VERS_STR_REGEX = re.compile(r"SPDX-(\d+)\.(\d+)", re.UNICODE) - - def __init__(self): - # FIXME: this state does not make sense - self.reset_document() - - def set_doc_version(self, doc, value): - """ - Set the document version. - Raise SPDXValueError if malformed value. - Raise CardinalityError if already defined. - """ - if self.doc_version_set: - raise CardinalityError("Document::Version") - - m = self.VERS_STR_REGEX.match(value) - if m is None: - raise SPDXValueError("Document::Version") - - self.doc_version_set = True - doc.version = version.Version( - major=int(m.group(1)), minor=int(m.group(2)) - ) - return True - - def set_doc_data_lics(self, doc, lics): - """ - Set the document data license. - Raise value error if malformed value - Raise CardinalityError if already defined. - """ - if self.doc_data_lics_set: - raise CardinalityError("Document::DataLicense") - - if not validations.validate_data_lics(lics): - raise SPDXValueError("Document::DataLicense") - - self.doc_data_lics_set = True - doc.data_license = license.License.from_identifier(lics) - return True - - def set_doc_name(self, doc, name): - """ - Set the document name. - Raise CardinalityError if already defined. - """ - if self.doc_name_set: - raise CardinalityError("Document::Name") - - self.doc_name_set = True - doc.name = name - return True - - def set_doc_spdx_id(self, doc, doc_spdx_id_line): - """ - Set the document SPDX Identifier. - Raise value error if malformed value. - Raise CardinalityError if already defined. - """ - if self.doc_spdx_id_set: - raise CardinalityError("Document::SPDXID") - - if not doc_spdx_id_line == "SPDXRef-DOCUMENT": - raise SPDXValueError("Document::SPDXID") - - doc.spdx_id = doc_spdx_id_line - self.doc_spdx_id_set = True - return True - - def set_doc_comment(self, doc, comment): - """ - Set document comment. - Raise CardinalityError if comment already set. - Raise SPDXValueError if comment is not free form text or single line of text. - """ - if self.doc_comment_set: - raise CardinalityError("Document::Comment") - - if not validations.validate_doc_comment(comment): - raise SPDXValueError("Document::Comment") - - self.doc_comment_set = True - doc.comment = str_from_text(comment) - return True - - def set_doc_namespace(self, doc, namespace): - """ - Set the document namespace. - Raise SPDXValueError if malformed value. - Raise CardinalityError if already defined. - """ - if self.doc_namespace_set: - raise CardinalityError("Document::Namespace") - - if not validations.validate_doc_namespace(namespace): - raise SPDXValueError("Document::Namespace") - - self.doc_namespace_set = True - doc.namespace = namespace - return True - - def reset_document(self): - """ - Reset the state to allow building new documents - """ - # FIXME: this state does not make sense - self.doc_version_set = False - self.doc_comment_set = False - self.doc_namespace_set = False - self.doc_data_lics_set = False - self.doc_name_set = False - self.doc_spdx_id_set = False - - -class ExternalDocumentRefBuilder(object): - def set_ext_doc_id(self, doc, ext_doc_id): - """ - Set the `external_document_id` attribute of the `ExternalDocumentRef` object. - """ - doc.add_ext_document_reference( - ExternalDocumentRef(external_document_id=ext_doc_id) - ) - - def set_spdx_doc_uri(self, doc, spdx_doc_uri): - """ - Set the `spdx_document_uri` attribute of the `ExternalDocumentRef` object. - """ - if not validations.validate_doc_namespace(spdx_doc_uri): - raise SPDXValueError("Document::ExternalDocumentRef") - - doc.ext_document_references[-1].spdx_document_uri = spdx_doc_uri - - def set_chksum(self, doc, chksum): - """ - Set the `check_sum` attribute of the `ExternalDocumentRef` object. - """ - doc.ext_document_references[-1].checksum = Checksum.checksum_from_string(chksum) - - def add_ext_doc_refs(self, doc, ext_doc_id, spdx_doc_uri, chksum): - self.set_ext_doc_id(doc, ext_doc_id) - self.set_spdx_doc_uri(doc, spdx_doc_uri) - self.set_chksum(doc, chksum) - - -class EntityBuilder(object): - tool_re = re.compile(r"Tool:\s*(.+)", re.UNICODE) - person_re = re.compile(r"Person:\s*(([^(])+)(\((.*)\))?", re.UNICODE) - org_re = re.compile(r"Organization:\s*(([^(])+)(\((.*)\))?", re.UNICODE) - PERSON_NAME_GROUP = 1 - PERSON_EMAIL_GROUP = 4 - ORG_NAME_GROUP = 1 - ORG_EMAIL_GROUP = 4 - TOOL_NAME_GROUP = 1 - - def build_tool(self, doc, entity): - """ - Build a tool object out of a string representation. - Return built tool. - Raise SPDXValueError if failed to extract tool name or name is malformed - """ - match = self.tool_re.match(entity) - if not match or not validations.validate_tool_name(match.group(self.TOOL_NAME_GROUP)): - raise SPDXValueError("Failed to extract tool name") - - name = match.group(self.TOOL_NAME_GROUP) - return creationinfo.Tool(name) - - def build_org(self, doc, entity): - """ - Build an organization object of of a string representation. - Return built organization. - Raise SPDXValueError if failed to extract name. - """ - match = self.org_re.match(entity) - if not match or not validations.validate_org_name(match.group(self.ORG_NAME_GROUP)): - raise SPDXValueError("Failed to extract Organization name") - - name = match.group(self.ORG_NAME_GROUP).strip() - email = match.group(self.ORG_EMAIL_GROUP) - if (email is not None) and (len(email) != 0): - return creationinfo.Organization(name=name, email=email.strip()) - else: - return creationinfo.Organization(name=name, email=None) - - def build_person(self, doc, entity): - """ - Build an organization object of of a string representation. - Return built organization. Raise SPDXValueError if failed to extract name. - """ - match = self.person_re.match(entity) - if not match or not validations.validate_person_name(match.group(self.PERSON_NAME_GROUP)): - raise SPDXValueError("Failed to extract person name") - - name = match.group(self.PERSON_NAME_GROUP).strip() - email = match.group(self.PERSON_EMAIL_GROUP) - if (email is not None) and (len(email) != 0): - return creationinfo.Person(name=name, email=email.strip()) - else: - return creationinfo.Person(name=name, email=None) - - -class CreationInfoBuilder(object): - def __init__(self): - # FIXME: this state does not make sense - self.reset_creation_info() - - def add_creator(self, doc, creator): - """ - Add a creator to the document's creation info. - Return true if creator is valid. - Creator must be built by an EntityBuilder. - Raise SPDXValueError if not a creator type. - """ - if not validations.validate_creator(creator): - raise SPDXValueError("CreationInfo::Creator") - - doc.creation_info.add_creator(creator) - return True - - def set_created_date(self, doc, created): - """ - Set created date. - Raise CardinalityError if created date already set. - Raise SPDXValueError if created is not a date. - """ - if self.created_date_set: - raise CardinalityError("CreationInfo::Created") - - date = utils.datetime_from_iso_format(created) - if date is None: - raise SPDXValueError("CreationInfo::Date") - - self.created_date_set = True - doc.creation_info.created = date - return True - - def set_creation_comment(self, doc, comment): - """ - Set creation comment. - Raise CardinalityError if comment already set. - Raise SPDXValueError if not free form text or single line of text. - """ - if self.creation_comment_set: - raise CardinalityError("CreationInfo::Comment") - - if not validations.validate_creation_comment(comment): - raise SPDXValueError("CreationInfo::Comment") - - self.creation_comment_set = True - doc.creation_info.comment = str_from_text(comment) - return True - - def set_lics_list_ver(self, doc, value): - """ - Set the license list version. - Raise CardinalityError if already set. - Raise SPDXValueError if incorrect value. - """ - if self.lics_list_ver_set: - raise CardinalityError("CreationInfo::LicenseListVersion") - - vers = version.Version.from_str(value) - if vers is None: - raise SPDXValueError("CreationInfo::LicenseListVersion") - - self.lics_list_ver_set = True - doc.creation_info.license_list_version = vers - return True - - def reset_creation_info(self): - """ - Reset builder state to allow building new creation info. - """ - # FIXME: this state does not make sense - self.created_date_set = False - self.creation_comment_set = False - self.lics_list_ver_set = False - - -class ReviewBuilder(object): - def __init__(self): - # FIXME: this state does not make sense - self.reset_reviews() - - def reset_reviews(self): - """ - Reset the builder's state to allow building new reviews. - """ - # FIXME: this state does not make sense - self.review_date_set = False - self.review_comment_set = False - - def add_reviewer(self, doc, reviewer): - """ - Adds a reviewer to the SPDX Document. - Reviewer is an entity created by an EntityBuilder. - Raise SPDXValueError if not a valid reviewer type. - """ - # Each reviewer marks the start of a new review object. - # FIXME: this state does not make sense - self.reset_reviews() - if not validations.validate_reviewer(reviewer): - raise SPDXValueError("Review::Reviewer") - - doc.add_review(review.Review(reviewer=reviewer)) - return True - - def add_review_date(self, doc, reviewed): - """ - Set the review date. - Raise CardinalityError if already set. - Raise OrderError if no reviewer defined before. - Raise SPDXValueError if invalid reviewed value. - """ - if len(doc.reviews) == 0: - raise OrderError("Review::ReviewDate") - - if self.review_date_set: - raise CardinalityError("Review::ReviewDate") - - date = utils.datetime_from_iso_format(reviewed) - if date is None: - raise SPDXValueError("Review::ReviewDate") - - self.review_date_set = True - doc.reviews[-1].review_date = date - return True - - def add_review_comment(self, doc, comment): - """ - Set the review comment. - Raise CardinalityError if already set. - Raise OrderError if no reviewer defined before. - Raise SPDXValueError if comment is not free form text or single line of text. - """ - if len(doc.reviews) == 0: - raise OrderError("ReviewComment") - - if self.review_comment_set: - raise CardinalityError("ReviewComment") - - if not validations.validate_review_comment(comment): - raise SPDXValueError("ReviewComment::Comment") - - self.review_comment_set = True - doc.reviews[-1].comment = str_from_text(comment) - return True - - -class AnnotationBuilder(object): - def __init__(self): - # FIXME: this state does not make sense - self.reset_annotations() - - def reset_annotations(self): - """ - Reset the builder's state to allow building new annotations. - """ - # FIXME: this state does not make sense - self.annotation_date_set = False - self.annotation_comment_set = False - self.annotation_type_set = False - self.annotation_spdx_id_set = False - - def add_annotator(self, doc, annotator): - """ - Add an annotator to the SPDX Document. - Annotator is an entity created by an EntityBuilder. - Raise SPDXValueError if not a valid annotator type. - """ - # Each annotator marks the start of a new annotation object. - # FIXME: this state does not make sense - self.reset_annotations() - if not validations.validate_annotator(annotator): - raise SPDXValueError("Annotation::Annotator") - - doc.add_annotation(annotation.Annotation(annotator=annotator)) - return True - - def add_annotation_date(self, doc, annotation_date): - """ - Set the annotation date. - Raise CardinalityError if already set. - Raise OrderError if no annotator defined before. - Raise SPDXValueError if invalid value. - """ - if len(doc.annotations) == 0: - raise OrderError("Annotation::AnnotationDate") - - if self.annotation_date_set: - raise CardinalityError("Annotation::AnnotationDate") - - date = utils.datetime_from_iso_format(annotation_date) - if date is None: - raise SPDXValueError("Annotation::AnnotationDate") - - self.annotation_date_set = True - doc.annotations[-1].annotation_date = date - return True - - def add_annotation_comment(self, doc, comment): - """ - Set the annotation comment. - Raise CardinalityError if already set. - Raise OrderError if no annotator defined before. - Raise SPDXValueError if comment is not free form text or single line of text. - """ - if len(doc.annotations) == 0: - raise OrderError("AnnotationComment::Comment") - - if self.annotation_comment_set: - raise CardinalityError("AnnotationComment::Comment") - - if not validations.validate_annotation_comment(comment): - raise SPDXValueError("AnnotationComment::Comment") - - self.annotation_comment_set = True - doc.annotations[-1].comment = str_from_text(comment) - return True - - def add_annotation_type(self, doc, annotation_type): - """ - Set the annotation type. - Raise CardinalityError if already set. - Raise OrderError if no annotator defined before. - Raise SPDXValueError if invalid value. - """ - if len(doc.annotations) == 0: - raise OrderError("Annotation::AnnotationType") - - if self.annotation_type_set: - raise CardinalityError("Annotation::AnnotationType") - - if not validations.validate_annotation_type(annotation_type): - raise SPDXValueError("Annotation::AnnotationType") - - self.annotation_type_set = True - doc.annotations[-1].annotation_type = annotation_type - return True - - def set_annotation_spdx_id(self, doc, spdx_id): - """ - Set the annotation SPDX Identifier. - Raise CardinalityError if already set. - Raise OrderError if no annotator defined before. - """ - if len(doc.annotations) == 0: - raise OrderError("Annotation::SPDXREF") - - if self.annotation_spdx_id_set: - raise CardinalityError("Annotation::SPDXREF") - - self.annotation_spdx_id_set = True - doc.annotations[-1].spdx_id = spdx_id - return True - - -class RelationshipBuilder(object): - def __init__(self): - # FIXME: this state does not make sense - self.reset_relationship() - - def reset_relationship(self): - """ - Reset the builder's state to allow building new relationships. - """ - # FIXME: this state does not make sense - self.relationship_comment_set = False - - def add_relationship(self, doc: Document, relationship_term: str) -> bool: - """ - Raise SPDXValueError if type is unknown. - """ - self.reset_relationship() - relationship_to_add = Relationship(relationship_term) - existing_relationships: List[Relationship] = doc.relationships - - if relationship_to_add not in existing_relationships: - doc.add_relationship(relationship_to_add) - return True - - existing_relationship: Relationship = existing_relationships[existing_relationships.index(relationship_to_add)] - - # If the relationship already exists without comment, we remove the old one and re-append it at the end. This - # allows to add a comment to the relationship (since a comment will always be added to the latest - # relationship). If an equal relationship with comment already exists, we ignore the new relationship. - if not existing_relationship.has_comment: - existing_relationships.remove(relationship_to_add) - doc.add_relationship(relationship_to_add) - return True - - return False - - def add_relationship_comment(self, doc: Document, comment: str) -> bool: - """ - Set the annotation comment. - Raise CardinalityError if already set. - Raise OrderError if no relationship defined before it. - Raise SPDXValueError if comment is not free form text or single line of text. - """ - if len(doc.relationships) == 0: - raise OrderError("RelationshipComment::Comment") - - if self.relationship_comment_set: - raise CardinalityError("RelationshipComment::Comment") - - if not validations.validate_relationship_comment(comment): - raise SPDXValueError("RelationshipComment::Comment") - - self.relationship_comment_set = True - doc.relationships[-1].comment = str_from_text(comment) - return True - - -class PackageBuilder(object): - VERIF_CODE_REGEX = re.compile(r"([0-9a-f]+)\s*(\(\s*(.+)\))?", re.UNICODE) - VERIF_CODE_CODE_GRP = 1 - VERIF_CODE_EXC_FILES_GRP = 3 - - def __init__(self): - # FIXME: this state does not make sense - self.reset_package() - - def reset_package(self): - """Resets the builder's state in order to build new packages.""" - # FIXME: this state does not make sense - self.package_set = False - self.package_spdx_id_set = False - self.package_vers_set = False - self.package_file_name_set = False - self.package_supplier_set = False - self.package_originator_set = False - self.package_down_location_set = False - self.package_files_analyzed_set = False - self.package_home_set = False - self.package_verif_set = False - self.package_chk_sum_set = False - self.package_source_info_set = False - self.package_conc_lics_set = False - self.package_license_declared_set = False - self.package_license_comment_set = False - self.package_cr_text_set = False - self.package_summary_set = False - self.package_desc_set = False - self.package_comment_set = False - self.package_primary_purpose_set = False - self.package_built_date_set = False - self.package_release_date_set = False - self.package_valid_until_date_set = False - # self.package_attribution_text_set = False - self.pkg_ext_comment_set = False - - def create_package(self, doc, name): - """ - Create a package for the SPDX Document. - name - any string. - Raise CardinalityError if package already defined. - """ - self.reset_package() - self.package_set = True - doc.add_package(package.Package(name=name)) - return True - - def set_pkg_spdx_id(self, doc, spdx_id): - """ - Set the Package SPDX Identifier. - Raise SPDXValueError if malformed value. - Raise CardinalityError if already defined. - """ - self.assert_package_exists() - if self.package_spdx_id_set: - raise CardinalityError("Package::SPDXID") - - if not validations.validate_pkg_spdx_id(spdx_id): - raise SPDXValueError("Package::SPDXID") - - self.package_spdx_id_set = True - doc.packages[-1].spdx_id = spdx_id - return True - - def set_pkg_vers(self, doc, version): - """ - Set package version, if not already set. - version - Any string. - Raise CardinalityError if already has a version. - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - if self.package_vers_set: - raise CardinalityError("Package::Version") - - self.package_vers_set = True - doc.packages[-1].version = version - return True - - def set_pkg_file_name(self, doc, name): - """ - Set the package file name, if not already set. - name - Any string. - Raise CardinalityError if already has a file_name. - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - if self.package_file_name_set: - raise CardinalityError("Package::FileName") - - self.package_file_name_set = True - doc.packages[-1].file_name = name - return True - - def set_pkg_supplier(self, doc, entity): - """ - Set the package supplier, if not already set. - entity - Organization, Person or NoAssert. - Raise CardinalityError if already has a supplier. - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - if self.package_supplier_set: - raise CardinalityError("Package::Supplier") - - if not validations.validate_pkg_supplier(entity): - raise SPDXValueError("Package::Supplier") - - self.package_supplier_set = True - doc.packages[-1].supplier = entity - return True - - def set_pkg_originator(self, doc, entity): - """ - Set the package originator, if not already set. - entity - Organization, Person or NoAssert. - Raise CardinalityError if already has an originator. - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - if self.package_originator_set: - raise CardinalityError("Package::Originator") - - if not validations.validate_pkg_originator(entity): - raise SPDXValueError("Package::Originator") - - self.package_originator_set = True - doc.packages[-1].originator = entity - return True - - def set_pkg_down_location(self, doc, location): - """ - Set the package download location, if not already set. - location - A string - Raise CardinalityError if already defined. - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - if self.package_down_location_set: - raise CardinalityError("Package::DownloadLocation") - - self.package_down_location_set = True - doc.packages[-1].download_location = location - return True - - def set_pkg_files_analyzed(self, doc, files_analyzed): - """ - Set the package files analyzed, if not already set. - Raise SPDXValueError if malformed value, CardinalityError if - already defined. - """ - self.assert_package_exists() - if self.package_files_analyzed_set: - raise CardinalityError("Package::FilesAnalyzed") - - if files_analyzed is None: - return True - - if not validations.validate_pkg_files_analyzed(files_analyzed): - raise SPDXValueError("Package::FilesAnalyzed") - - self.package_files_analyzed_set = True - if isinstance(files_analyzed, str): - files_analyzed = files_analyzed.lower() == "true" - doc.packages[-1].files_analyzed = files_analyzed - # convert to boolean; - # validate_pkg_files_analyzed already checked if - # files_analyzed is in ['True', 'true', 'False', 'false'] - return True - - def set_pkg_home(self, doc, location): - """Set the package homepage location if not already set. - location - A string or None or NoAssert. - Raise CardinalityError if already defined. - Raise OrderError if no package previously defined. - Raise SPDXValueError if location has incorrect value. - """ - self.assert_package_exists() - if self.package_home_set: - raise CardinalityError("Package::HomePage") - - if not validations.validate_pkg_homepage(location): - raise SPDXValueError("Package::HomePage") - - self.package_home_set = True - doc.packages[-1].homepage = location - return True - - def set_pkg_verif_code(self, doc, code): - """ - Set the package verification code, if not already set. - code - A string. - Raise CardinalityError if already defined. - Raise OrderError if no package previously defined. - Raise Value error if doesn't match verifcode form - """ - self.assert_package_exists() - if self.package_verif_set: - raise CardinalityError("Package::VerificationCode") - - match = self.VERIF_CODE_REGEX.match(code) - if not match: - raise SPDXValueError("Package::VerificationCode") - - self.package_verif_set = True - doc.packages[-1].verif_code = match.group(self.VERIF_CODE_CODE_GRP) - - if match.group(self.VERIF_CODE_EXC_FILES_GRP) is not None: - doc.packages[-1].verif_exc_files = match.group( - self.VERIF_CODE_EXC_FILES_GRP - ).split(",") - return True - - def set_pkg_checksum(self, doc, checksum): - """ - Set the package checksum, if not already set. - checksum - A string - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - self.package_chk_sum_set = True - doc.packages[-1].set_checksum(Checksum.checksum_from_string(checksum)) - return True - - def set_pkg_source_info(self, doc, text): - """ - Set the package's source information, if not already set. - text - Free form text. - Raise CardinalityError if already defined. - Raise OrderError if no package previously defined. - SPDXValueError if text is not free form text or single line of text. - """ - self.assert_package_exists() - if self.package_source_info_set: - raise CardinalityError("Package::SourceInfo") - - if not validations.validate_pkg_src_info(text): - raise SPDXValueError("Package::SourceInfo") - - self.package_source_info_set = True - doc.packages[-1].source_info = str_from_text(text) - return True - - def set_pkg_licenses_concluded(self, doc, licenses): - """ - Set the package's concluded licenses. - licenses - License info. - Raise CardinalityError if already defined. - Raise OrderError if no package previously defined. - Raise SPDXValueError if data malformed. - """ - self.assert_package_exists() - if self.package_conc_lics_set: - raise CardinalityError("Package::ConcludedLicenses") - - if not validations.validate_lics_conc(licenses, optional=True): - raise SPDXValueError("Package::ConcludedLicenses") - - self.package_conc_lics_set = True - doc.packages[-1].conc_lics = licenses - return True - - def set_pkg_license_from_file(self, doc, lic): - """ - Add a license from a file to the package. - Raise SPDXValueError if data malformed. - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - if not validations.validate_lics_from_file(lic, optional=True): - raise SPDXValueError("Package::LicensesFromFile") - - doc.packages[-1].licenses_from_files.append(lic) - return True - - def set_pkg_license_declared(self, doc, lic): - """ - Set the package's declared license. - Raise SPDXValueError if data malformed. - Raise OrderError if no package previously defined. - Raise CardinalityError if already set. - """ - self.assert_package_exists() - if self.package_license_declared_set: - raise CardinalityError("Package::LicenseDeclared") - - if not validations.validate_lics_conc(lic, optional=True): - raise SPDXValueError("Package::LicenseDeclared") - - self.package_license_declared_set = True - doc.packages[-1].license_declared = lic - return True - - def set_pkg_license_comment(self, doc, text): - """ - Set the package's license comment. - Raise OrderError if no package previously defined. - Raise CardinalityError if already set. - Raise SPDXValueError if text is not free form text or single line of text. - """ - self.assert_package_exists() - if self.package_license_comment_set: - raise CardinalityError("Package::LicenseComment") - - if not validations.validate_pkg_lics_comment(text): - raise SPDXValueError("Package::LicenseComment") - - self.package_license_comment_set = True - doc.packages[-1].license_comment = str_from_text(text) - return True - - def set_pkg_attribution_text(self, doc, text): - """ - Set the package's attribution text . - Raise SPDXValueError if text is not free form text or single line of text. - """ - self.assert_package_exists() - if not validations.validate_pkg_attribution_text(text): - raise SPDXValueError("Package::AttributionText") - - doc.packages[-1].attribution_text = str_from_text(text) - return True - - def set_pkg_cr_text(self, doc, text): - """ - Set the package's copyright text. - Raise OrderError if no package previously defined. - Raise CardinalityError if already set. - Raise value error if text is not one of [None, NOASSERT, TEXT] or single line of text. - """ - self.assert_package_exists() - if self.package_cr_text_set: - raise CardinalityError("Package::CopyrightText") - - if not validations.validate_pkg_cr_text(text, optional=True): - raise SPDXValueError("Package::CopyrightText") - - self.package_cr_text_set = True - if isinstance(text, str): - doc.packages[-1].cr_text = str_from_text(text) - else: - doc.packages[-1].cr_text = text # None or NoAssert - - def set_pkg_summary(self, doc, text): - """ - Set the package summary. - Raise SPDXValueError if text is not free form text or single line of text. - Raise CardinalityError if summary already set. - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - if self.package_summary_set: - raise CardinalityError("Package::Summary") - - if not validations.validate_pkg_summary(text): - raise SPDXValueError("Package::Summary") - - self.package_summary_set = True - doc.packages[-1].summary = str_from_text(text) - - def set_pkg_desc(self, doc, text): - """ - Set the package's description. - Raise SPDXValueError if text is not free form text or single line of text. - Raise CardinalityError if description already set. - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - if self.package_desc_set: - raise CardinalityError("Package::Description") - - if not validations.validate_pkg_desc(text): - raise SPDXValueError("Package::Description") - - self.package_desc_set = True - doc.packages[-1].description = str_from_text(text) - - def set_pkg_comment(self, doc, text): - """ - Set the package's comment. - Raise SPDXValueError if text is not free form text or single line of text. - Raise CardinalityError if comment already set. - Raise OrderError if no package previously defined. - """ - self.assert_package_exists() - if self.package_comment_set: - raise CardinalityError("Package::Comment") - - if not validations.validate_pkg_comment(text): - raise SPDXValueError("Package::Comment") - - self.package_comment_set = True - doc.packages[-1].comment = str_from_text(text) - - def set_pkg_primary_package_purpose(self, doc, purpose): - """ - Set the package's primary purpose. - Raise CardinalityError if more than one purpose is set. - Raise SPDXValueError if purpose is unknown. - """ - self.assert_package_exists() - if self.package_primary_purpose_set: - raise CardinalityError("Package::PrimaryPackagePurpose") - - self.package_primary_purpose_set = True - purpose = purpose.replace("-", "_") - for purpose_enum in PackagePurpose: - if purpose == purpose_enum.name: - doc.packages[-1].primary_package_purpose = purpose_enum - return True - else: - raise SPDXValueError("Package::PrimaryPackagePurpose") - - def set_pkg_built_date(self, doc, built_date): - """ - Set the package`s built date. - Raise CardinalityError if built_date date already set. - Raise SPDXValueError if built_date is not a date. - """ - self.assert_package_exists() - if self.package_built_date_set: - raise CardinalityError("Package::BuiltDate") - - date = utils.datetime_from_iso_format(built_date) - if date is None: - raise SPDXValueError("Package::BuiltDate") - - self.package_built_date_set = True - doc.packages[-1].built_date = date - return True - - def set_pkg_release_date(self, doc, release_date): - """ - Set the package`s release date. - Raise CardinalityError if release_date date already set. - Raise SPDXValueError if release_date is not a date. - """ - self.assert_package_exists() - if self.package_release_date_set: - raise CardinalityError("Package::ReleaseDate") - - date = utils.datetime_from_iso_format(release_date) - if date is None: - raise SPDXValueError("Package::ReleaseDate") - - self.package_release_date_set = True - doc.packages[-1].release_date = date - return True - - def set_pkg_valid_until_date(self, doc, valid_until_date): - """ - Set the package`s valid_until date. - Raise CardinalityError if valid_until_date date already set. - Raise SPDXValueError if valid_until_date is not a date. - """ - self.assert_package_exists() - if self.package_valid_until_date_set: - raise CardinalityError("Package::ValidUntilDate") - - date = utils.datetime_from_iso_format(valid_until_date) - if date is None: - raise SPDXValueError("Package::ValidUntilDate") - - self.package_valid_until_date_set = True - doc.packages[-1].valid_until_date = date - return True - - def set_pkg_ext_ref_category(self, doc, category): - """ - Set the `category` attribute of the `ExternalPackageRef` object. - """ - self.assert_package_exists() - if not validations.validate_pkg_ext_ref_category(category): - raise SPDXValueError("ExternalRef::Category") - - if ( - len(doc.packages[-1].pkg_ext_refs) - and doc.packages[-1].pkg_ext_refs[-1].category is None - ): - doc.packages[-1].pkg_ext_refs[-1].category = category - else: - doc.packages[-1].add_pkg_ext_refs( - package.ExternalPackageRef(category=category) - ) - - def set_pkg_ext_ref_type(self, doc, pkg_ext_ref_type): - """ - Set the `pkg_ext_ref_type` attribute of the `ExternalPackageRef` object. - """ - self.assert_package_exists() - if not validations.validate_pkg_ext_ref_type(pkg_ext_ref_type): - raise SPDXValueError("ExternalRef::Type") - - if ( - len(doc.packages[-1].pkg_ext_refs) - and doc.packages[-1].pkg_ext_refs[-1].pkg_ext_ref_type is None - ): - doc.packages[-1].pkg_ext_refs[-1].pkg_ext_ref_type = pkg_ext_ref_type - else: - doc.packages[-1].add_pkg_ext_refs( - package.ExternalPackageRef(pkg_ext_ref_type=pkg_ext_ref_type) - ) - - def set_pkg_ext_ref_locator(self, doc, locator): - """ - Set the `locator` attribute of the `ExternalPackageRef` object. - """ - self.assert_package_exists() - if ( - len(doc.packages[-1].pkg_ext_refs) - and doc.packages[-1].pkg_ext_refs[-1].locator is None - ): - doc.packages[-1].pkg_ext_refs[-1].locator = locator - else: - doc.packages[-1].add_pkg_ext_refs(package.ExternalPackageRef(locator=locator)) - - def add_pkg_ext_ref_comment(self, doc, comment): - """ - Set the `comment` attribute of the `ExternalPackageRef` object. - """ - self.assert_package_exists() - if not len(doc.packages[-1].pkg_ext_refs): - raise OrderError("Package::ExternalRef") - - if not validations.validate_pkg_ext_ref_comment(comment): - raise SPDXValueError("ExternalRef::Comment") - - doc.packages[-1].pkg_ext_refs[-1].comment = str_from_text(comment) - - def add_pkg_ext_refs(self, doc, category, pkg_ext_ref_type, locator): - self.set_pkg_ext_ref_category(doc, category) - self.set_pkg_ext_ref_type(doc, pkg_ext_ref_type) - self.set_pkg_ext_ref_locator(doc, locator) - - def assert_package_exists(self): - if not self.package_set: - raise OrderError("Package") - - -class FileBuilder(object): - def __init__(self): - # FIXME: this state does not make sense - self.reset_file_stat() - - def set_file_name(self, doc, name): - doc.files.append(file.File(name)) - # A file name marks the start of a new file instance. - # The builder must be reset - # FIXME: this state does not make sense - self.reset_file_stat() - return True - - def set_file_spdx_id(self, doc, spdx_id): - """ - Set the file SPDX Identifier. - Raise OrderError if no package or no file defined. - Raise SPDXValueError if malformed value. - Raise CardinalityError if more than one spdx_id set. - """ - if not self.has_file(doc): - raise OrderError("File::SPDXID") - - if self.file_spdx_id_set: - raise CardinalityError("File::SPDXID") - - if not validations.validate_file_spdx_id(spdx_id): - raise SPDXValueError("File::SPDXID") - - self.file_spdx_id_set = True - self.file(doc).spdx_id = spdx_id - return True - - def set_file_comment(self, doc, text): - """ - Raise OrderError if no package or no file defined. - Raise CardinalityError if more than one comment set. - Raise SPDXValueError if text is not free form text or single line of text. - """ - if not self.has_file(doc): - raise OrderError("File::Comment") - - if self.file_comment_set: - raise CardinalityError("File::Comment") - - if not validations.validate_file_comment(text): - raise SPDXValueError("File::Comment") - - self.file_comment_set = True - self.file(doc).comment = str_from_text(text) - return True - - def set_file_attribution_text(self, doc, text): - """ - Set the file's attribution text . - Raise OrderError if no package or no file defined. - Raise SPDXValueError if text is not free form text or single line of text. - """ - if not self.has_file(doc): - raise OrderError("File::AttributionText") - - if not validations.validate_file_attribution_text(text): - raise SPDXValueError("File::AttributionText") - - self.file(doc).attribution_text = str_from_text(text) - return True - - def set_file_type(self, doc, type_value): - """ - Raise OrderError if no package or file defined. - Raise CardinalityError if more than one type set. - Raise SPDXValueError if type is unknown. - """ - if not self.has_file(doc): - raise OrderError("File::FileType") - - if type_value not in file.FileType.__members__: - raise SPDXValueError("File:FileType") - - file_type = file.FileType[type_value] - - spdx_file = self.file(doc) - if file_type in spdx_file.file_types: - raise CardinalityError("File::FileType") - - spdx_file.file_types.append(file_type) - - def set_file_checksum(self, doc: Document, checksum: str): - """ - Raise OrderError if no file defined. - """ - if self.has_file(doc): - new_checksum = Checksum.checksum_from_string(checksum) - self.file(doc).set_checksum(new_checksum) - else: - raise OrderError("File::CheckSum") - return True - - def set_concluded_license(self, doc, lic): - """ - Raise OrderError if no package or file defined. - Raise CardinalityError if already set. - Raise SPDXValueError if malformed. - """ - if not self.has_file(doc): - raise OrderError("File::ConcludedLicense") - - if self.file_conc_lics_set: - raise CardinalityError("File::ConcludedLicense") - - if not validations.validate_lics_conc(lic, optional=True): - raise SPDXValueError("File::ConcludedLicense") - - self.file_conc_lics_set = True - self.file(doc).conc_lics = lic - return True - - def set_file_license_in_file(self, doc, lic): - """ - Raise OrderError if no package or file defined. - Raise SPDXValueError if malformed value. - """ - if not self.has_file(doc): - raise OrderError("File::LicenseInFile") - - if not validations.validate_file_lics_in_file(lic): - raise SPDXValueError("File::LicenseInFile") - - self.file(doc).add_lics(lic) - return True - - def set_file_license_comment(self, doc, text): - """ - Raise OrderError if no package or file defined. - Raise SPDXValueError if text is not free form text or single line of text. - Raise CardinalityError if more than one per file. - """ - if not self.has_file(doc): - raise OrderError("File::LicenseComment") - - if self.file_license_comment_set: - raise CardinalityError("File::LicenseComment") - - if not validations.validate_file_lics_comment(text): - raise SPDXValueError("File::LicenseComment") - - self.file_license_comment_set = True - self.file(doc).license_comment = str_from_text(text) - - def set_file_copyright(self, doc, text): - """ - Raise OrderError if no package or file defined. - Raise SPDXValueError if not free form text or NONE or NO_ASSERT or single line of text. - Raise CardinalityError if more than one. - """ - if not self.has_file(doc): - raise OrderError("File::CopyRight") - - if self.file_copytext_set: - raise CardinalityError("File::CopyRight") - - if not validations.validate_file_cpyright(text, optional=True): - raise SPDXValueError("File::CopyRight") - - self.file_copytext_set = True - if isinstance(text, str): - self.file(doc).copyright = str_from_text(text) - else: - self.file(doc).copyright = text # None or NoAssert - return True - - def set_file_notice(self, doc, text): - """ - Raise OrderError if no package or file defined. - Raise SPDXValueError if not free form text or single line of text. - Raise CardinalityError if more than one. - """ - if not self.has_file(doc): - raise OrderError("File::Notice") - - if self.file_notice_set: - raise CardinalityError("File::Notice") - - if not validations.validate_file_notice(text): - raise SPDXValueError("File::Notice") - - self.file_notice_set = True - self.file(doc).notice = str_from_text(text) - - def add_file_contribution(self, doc, value): - """ - Raise OrderError if no package or file defined. - """ - if not self.has_file(doc): - raise OrderError("File::Contributor") - - self.file(doc).add_contrib(value) - - def add_file_dep(self, doc, value): - """ - Raise OrderError if no package or file defined. - """ - if not self.has_file(doc): - raise OrderError("File::Dependency") - - self.file(doc).add_depend(value) - - def set_file_atrificat_of_project(self, doc, symbol, value): - """ - Set a file name, uri or home artifact. - Raise OrderError if no package or file defined. - """ - if not self.has_file(doc): - raise OrderError("File::Artifact") - - self.file(doc).add_artifact(symbol, value) - - def file(self, doc): - """ - Return the last file in the document's file list. - """ - return doc.files[-1] - - def has_file(self, doc): - """ - Return true if the document has at least one file. - """ - return len(doc.files) != 0 - - def has_package(self, doc): - """ - Return true if the document has a package. - """ - return len(doc.packages) != 0 - - def reset_file_stat(self): - """ - Reset the builder's state to enable building new files. - """ - # FIXME: this state does not make sense - self.file_spdx_id_set = False - self.file_comment_set = False - self.file_type_set = False - self.file_chksum_set = False - self.file_conc_lics_set = False - self.file_license_comment_set = False - self.file_notice_set = False - self.file_copytext_set = False - - -class LicenseBuilder(object): - def __init__(self): - # FIXME: this state does not make sense - self.reset_extr_lics() - - def extr_lic(self, doc): - """ - Retrieve last license in extracted license list. - """ - return doc.extracted_licenses[-1] - - def has_extr_lic(self, doc): - return len(doc.extracted_licenses) != 0 - - def set_lic_id(self, doc, lic_id): - """ - Add a new extracted license to the document. - Raise SPDXValueError if data format is incorrect. - """ - # FIXME: this state does not make sense - self.reset_extr_lics() - if not validations.validate_extracted_lic_id(lic_id): - raise SPDXValueError("ExtractedLicense::id") - - doc.add_extr_lic(license.ExtractedLicense(lic_id)) - return True - - def set_lic_text(self, doc, text): - """ - Set license extracted text. - Raise SPDXValueError if text is not free form text or single line of text. - Raise OrderError if no license ID defined. - """ - if not self.has_extr_lic(doc): - raise OrderError("ExtractedLicense::text") - - if self.extr_text_set: - raise CardinalityError("ExtractedLicense::text") - - if not validations.validate_is_free_form_text_or_str(text): - raise SPDXValueError("ExtractedLicense::text") - - self.extr_text_set = True - self.extr_lic(doc).text = str_from_text(text) - return True - - def set_lic_name(self, doc, name): - """ - Set license name. - Raise SPDXValueError if name is not str or utils.NoAssert - Raise OrderError if no license id defined. - """ - if not self.has_extr_lic(doc): - raise OrderError("ExtractedLicense::Name") - - if self.extr_lic_name_set: - raise CardinalityError("ExtractedLicense::Name") - - if not validations.validate_extr_lic_name(name): - raise SPDXValueError("ExtractedLicense::Name") - - self.extr_lic_name_set = True - self.extr_lic(doc).full_name = name - return True - - def set_lic_comment(self, doc, comment): - """ - Set license comment. - Raise SPDXValueError if comment is not free form text or single line of text. - Raise OrderError if no license ID defined. - """ - if not self.has_extr_lic(doc): - raise OrderError("ExtractedLicense::comment") - - if self.extr_lic_comment_set: - raise CardinalityError("ExtractedLicense::comment") - - if not validations.validate_is_free_form_text_or_str(comment): - raise SPDXValueError("ExtractedLicense::comment") - - self.extr_lic_comment_set = True - self.extr_lic(doc).comment = str_from_text(comment) - return True - - def add_lic_xref(self, doc, ref): - """ - Add a license cross reference. - Raise OrderError if no License ID defined. - """ - if not self.has_extr_lic(doc): - raise OrderError("ExtractedLicense::CrossRef") - - self.extr_lic(doc).add_xref(ref) - return True - - def reset_extr_lics(self): - # FIXME: this state does not make sense - self.extr_text_set = False - self.extr_lic_name_set = False - self.extr_lic_comment_set = False - - -class SnippetBuilder(object): - def __init__(self): - # FIXME: this state does not make sense - self.reset_snippet() - - def create_snippet(self, doc, spdx_id): - """ - Create a snippet for the SPDX Document. - spdx_id - To uniquely identify any element in an SPDX document which - may be referenced by other elements. - Raise SPDXValueError if the data is a malformed value. - """ - self.reset_snippet() - spdx_id = spdx_id.split("#")[-1] - if not validations.validate_snippet_spdx_id(spdx_id): - raise SPDXValueError("Snippet::SnippetSPDXID") - - self.snippet_spdx_id_set = True - doc.add_snippet(snippet.Snippet(spdx_id=spdx_id)) - return True - - def set_snippet_name(self, doc, name): - """ - Set name of the snippet. - Raise OrderError if no snippet previously defined. - Raise CardinalityError if the name is already set. - """ - self.assert_snippet_exists() - if self.snippet_name_set: - raise CardinalityError("SnippetName") - - self.snippet_name_set = True - doc.snippet[-1].name = name - return True - - def set_snippet_comment(self, doc, comment): - """ - Set general comments about the snippet. - Raise OrderError if no snippet previously defined. - Raise SPDXValueError if the data is not free form text or single line of text. - Raise CardinalityError if comment already set. - """ - self.assert_snippet_exists() - if self.snippet_comment_set: - raise CardinalityError("Snippet::SnippetComment") - - if not validations.validate_snip_comment(comment): - raise SPDXValueError("Snippet::SnippetComment") - - self.snippet_comment_set = True - doc.snippet[-1].comment = str_from_text(comment) - return True - - def set_snippet_attribution_text(self, doc, text): - """ - Set the snippet's attribution text . - Raise SPDXValueError if text is not free form text or single line of text. - """ - self.assert_snippet_exists() - if not validations.validate_snippet_attribution_text(text): - raise SPDXValueError("Snippet::AttributionText") - - doc.snippet[-1].attribution_text = str_from_text(text) - return True - - def set_snippet_copyright(self, doc, text): - """Set the snippet's copyright text. - Raise OrderError if no snippet previously defined. - Raise CardinalityError if already set. - Raise SPDXValueError if text is not one of [None, NOASSERT, TEXT] or single line of text. - """ - self.assert_snippet_exists() - if self.snippet_copyright_set: - raise CardinalityError("Snippet::SnippetCopyrightText") - - if not validations.validate_snippet_copyright(text, optional=True): - raise SPDXValueError("Snippet::SnippetCopyrightText") - - self.snippet_copyright_set = True - if isinstance(text, str): - doc.snippet[-1].copyright = str_from_text(text) - else: - doc.snippet[-1].copyright = text # None or NoAssert - return True - - def set_snippet_lic_comment(self, doc, text): - """ - Set the snippet's license comment. - Raise OrderError if no snippet previously defined. - Raise CardinalityError if already set. - Raise SPDXValueError if the data is not free form text or single line of text. - """ - self.assert_snippet_exists() - if self.snippet_lic_comment_set: - raise CardinalityError("Snippet::SnippetLicenseComments") - - if not validations.validate_snip_lic_comment(text): - raise SPDXValueError("Snippet::SnippetLicenseComments") - - self.snippet_lic_comment_set = True - doc.snippet[-1].license_comment = str_from_text(text) - return True - - def set_snip_from_file_spdxid(self, doc, snip_from_file_spdxid): - """ - Set the snippet's 'Snippet from File SPDX Identifier'. - Raise OrderError if no snippet previously defined. - Raise CardinalityError if already set. - Raise SPDXValueError if the data is a malformed value. - """ - self.assert_snippet_exists() - snip_from_file_spdxid = snip_from_file_spdxid.split("#")[-1] - if self.snip_file_spdxid_set: - raise CardinalityError("Snippet::SnippetFromFileSPDXID") - - if not validations.validate_snip_file_spdxid(snip_from_file_spdxid): - raise SPDXValueError("Snippet::SnippetFromFileSPDXID") - - self.snip_file_spdxid_set = True - doc.snippet[-1].snip_from_file_spdxid = snip_from_file_spdxid - return True - - def set_snip_concluded_license(self, doc, conc_lics): - """ - Raise OrderError if no snippet previously defined. - Raise CardinalityError if already set. - Raise SPDXValueError if the data is a malformed value. - """ - self.assert_snippet_exists() - if self.snippet_conc_lics_set: - raise CardinalityError("Snippet::SnippetLicenseConcluded") - - if not validations.validate_lics_conc(conc_lics, optional=True): - raise SPDXValueError("Snippet::SnippetLicenseConcluded") - - self.snippet_conc_lics_set = True - doc.snippet[-1].conc_lics = conc_lics - return True - - def set_snippet_lics_info(self, doc, lics_info): - """ - Raise OrderError if no snippet previously defined. - Raise SPDXValueError if the data is a malformed value. - """ - self.assert_snippet_exists() - if not validations.validate_snip_lics_info(lics_info, optional=True): - raise SPDXValueError("Snippet::LicenseInfoInSnippet") - - doc.snippet[-1].add_lics(lics_info) - return True - - def set_snippet_byte_range(self, doc, parsed): - """ - Raise OrderError if no snippet previously defined. - Raise SPDXValueError if the data is malformed. - """ - self.assert_snippet_exists() - startpoint = int(parsed.split(":")[0]) - endpoint = int(parsed.split(":")[-1]) - if startpoint <= endpoint: - doc.snippet[-1].byte_range = (startpoint, endpoint) - else: - raise SPDXValueError("Snippet::ByteRange") - - def set_snippet_line_range(self, doc, parsed): - """ - Raise OrderError if no snippet previously defined. - Raise SPDXValueError if the data is malformed. - """ - self.assert_snippet_exists() - startpoint = int(parsed.split(":")[0]) - endpoint = int(parsed.split(":")[-1]) - if startpoint <= endpoint: - doc.snippet[-1].line_range = (startpoint, endpoint) - else: - raise SPDXValueError("Snippet::LineRange") - - def reset_snippet(self): - # FIXME: this state does not make sense - self.snippet_spdx_id_set = False - self.snippet_name_set = False - self.snippet_comment_set = False - self.snippet_copyright_set = False - self.snippet_lic_comment_set = False - self.snip_file_spdxid_set = False - self.snippet_conc_lics_set = False - - def assert_snippet_exists(self): - if not self.snippet_spdx_id_set: - raise OrderError("Snippet") - - -class Builder( - DocBuilder, - CreationInfoBuilder, - EntityBuilder, - ReviewBuilder, - PackageBuilder, - FileBuilder, - LicenseBuilder, - SnippetBuilder, - ExternalDocumentRefBuilder, - AnnotationBuilder, - RelationshipBuilder, -): - """ - SPDX document builder. - """ - - def __init__(self): - super(Builder, self).__init__() - # FIXME: this state does not make sense - self.reset() - self.current_package: Dict = dict() - self.current_file: Dict = dict() - - def set_current_package_name(self, name: str) -> None: - self.current_package["name"] = name - - def set_current_file_name(self, name: str) -> None: - self.current_file["name"] = name - - def set_current_file_id(self, spdx_id: str) -> None: - self.current_file["spdx_id"] = spdx_id - - def set_current_package_id(self, spdx_id: str) -> None: - self.current_package["spdx_id"] = spdx_id - - def current_package_has_name(self) -> bool: - return bool(("name" in self.current_package) and (self.current_package["name"])) - - def current_file_has_name(self) -> bool: - return bool(("name" in self.current_file) and (self.current_file["name"])) - - def current_package_has_id(self) -> bool: - return bool("spdx_id" in self.current_package) and (self.current_package["spdx_id"]) - - def current_file_has_id(self) -> bool: - return bool("spdx_id" in self.current_file) and (self.current_file["spdx_id"]) - - def has_current_package(self) -> bool: - return bool(self.current_package) - - def reset(self): - """ - Reset builder's state for building new documents. - Must be called between usage with different documents. - """ - # FIXME: this state does not make sense - self.reset_creation_info() - self.reset_document() - self.reset_package() - self.reset_file_stat() - self.reset_reviews() - self.reset_annotations() - self.reset_extr_lics() - self.reset_snippet() - self.reset_relationship() diff --git a/spdx/parsers/validations.py b/spdx/parsers/validations.py deleted file mode 100644 index d8a8b4cf9..000000000 --- a/spdx/parsers/validations.py +++ /dev/null @@ -1,345 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 re - -import rdflib - -from spdx import creationinfo -from spdx import license -from spdx import utils - - -def validate_is_free_form_text_or_str(value, optional=False) -> bool: - if value is None: - return optional - if not isinstance(value, str): - return False - if "\n" in value: - TEXT_RE = re.compile(r"(.|\n)*", re.UNICODE) - match = TEXT_RE.match(value) - return match is not None - return True - - -def validate_tool_name(value, optional=False): - striped_value = value.strip() - if optional: - if len(striped_value) == 0: - return True - else: - return False - else: - return not (len(striped_value) == 0) - - -def validate_person_name(value, optional=False): - return validate_tool_name(value, optional) - - -def validate_org_name(value, optional=False): - return validate_tool_name(value, optional) - - -def validate_data_lics(value): - return value == "CC0-1.0" - - -def validate_doc_name(value, optional=False): - return validate_tool_name(value, optional) - - -def validate_pkg_supplier(value, optional=False): - if optional and value is None: - return True - elif isinstance( - value, (utils.NoAssert, creationinfo.Person, creationinfo.Organization) - ): - return True - else: - return False - - -def validate_pkg_originator(value, optional=False): - return validate_pkg_supplier(value, optional) - - -def validate_pkg_homepage(value, optional=False): - if value is None: - return optional - elif isinstance(value, (str, utils.NoAssert, utils.SPDXNone)): - return True - else: - return False - - -def validate_pkg_cr_text(value, optional=False): - if isinstance(value, (utils.NoAssert, utils.SPDXNone)): - return True - elif validate_is_free_form_text_or_str(value, optional): - return True - elif value is None: - return optional - else: - return False - - -def validate_pkg_summary(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_pkg_desc(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_pkg_comment(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_pkg_attribution_text(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_file_attribution_text(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_snippet_attribution_text(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_pkg_ext_ref_category(value, optional=False): - # PACKAGE_MANAGER is used in the json schema for 2.2. For now, we simply allow both versions - if value.upper() in ["SECURITY", "OTHER", "PACKAGE-MANAGER", "PACKAGE_MANAGER"]: - return True - else: - return False - - -def validate_pkg_ext_ref_type(value, optional=False): - if re.match(r"^\S+$", value) is not None: - return True - else: - return False - - -def validate_pkg_ext_ref_comment(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_doc_comment(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_doc_spdx_id(value, optional=False): - if value is None: - return optional - elif value.endswith("#SPDXRef-DOCUMENT"): - return True - else: - return False - - -def validate_doc_namespace(value, optional=False): - if value is None: - return optional - elif ( - value.startswith("http://") - or value.startswith("https://") - or value.startswith("ftp://") - ) and ("#" not in value): - return True - else: - return False - - -def validate_creator(value, optional=False): - if value is None: - return optional - else: - return isinstance(value, creationinfo.Creator) - - -def validate_creation_comment(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_reviewer(value, optional=False): - return validate_creator(value, optional) - - -def validate_review_comment(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_annotator(value, optional=False): - return validate_creator(value, optional) - - -def validate_annotation_comment(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_annotation_type(value, optional=False): - value = value.strip() - if value == "REVIEW" or value == "OTHER": - return True - else: - return False - - -def validate_relationship_comment(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_pkg_spdx_id(value, optional=False): - value = value.split("#")[-1] - TEXT_RE = re.compile(r"SPDXRef-([A-Za-z0-9\.\-]+)", re.UNICODE) - if value is None: - return optional - else: - return TEXT_RE.match(value) is not None - - -def validate_pkg_files_analyzed(value, optional=False): - if value in ["True", "true", "False", "false", True, False]: - return True - else: - return optional - - -def validate_pkg_src_info(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_pkg_lics_comment(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_file_spdx_id(value, optional=False): - value = value.split("#")[-1] - TEXT_RE = re.compile(r"SPDXRef-([A-Za-z0-9.\-]+)", re.UNICODE) - if value is None: - return optional - else: - return TEXT_RE.match(value) is not None - - -def validate_file_comment(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_file_lics_comment(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_file_cpyright(value, optional=False): - if isinstance(value, (utils.NoAssert, utils.SPDXNone)): - return True - elif validate_is_free_form_text_or_str(value, optional): - return True - else: - return False - - -def validate_lics_from_file(value, optional=False): - if value is None: - return optional - elif isinstance(value, (license.License, utils.SPDXNone, utils.NoAssert)): - return True - else: - return False - - -def validate_file_notice(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_lics_conc(value, optional=False): - if value is None: - return optional - elif isinstance(value, (utils.NoAssert, utils.SPDXNone, license.License)): - return True - else: - return False - - -def validate_file_lics_in_file(value, optional=False): - if value is None: - return optional - elif isinstance(value, (utils.NoAssert, utils.SPDXNone, license.License)): - return True - else: - return False - - -def validate_extracted_lic_id(value, optional=False): - if value is None: - return optional - else: - return value.startswith("LicenseRef-") - - -def validate_extr_lic_name(value, optional=False): - if value is None: - return optional - else: - return isinstance(value, (str, utils.NoAssert, rdflib.Literal)) - - -def validate_snippet_spdx_id(value, optional=False): - if value is None: - return optional - value = value.split("#")[-1] - if re.match(r"^SPDXRef[A-Za-z0-9.\-]+$", value) is not None: - return True - else: - return False - - -def validate_snip_comment(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_snippet_copyright(value, optional=False): - if validate_is_free_form_text_or_str(value, optional): - return True - elif isinstance(value, (utils.NoAssert, utils.SPDXNone)): - return True - elif value is None: - return optional - else: - return False - - -def validate_snip_lic_comment(value, optional=False): - return validate_is_free_form_text_or_str(value, optional) - - -def validate_snip_file_spdxid(value, optional=False): - if value is None: - return optional - if ( - re.match(r"(DocumentRef[A-Za-z0-9.\-]+:){0,1}SPDXRef[A-Za-z0-9.\-]+", value) - is not None - ): - return True - else: - return False - - -def validate_snip_lics_info(value, optional=False): - if value is None: - return optional - elif isinstance(value, (utils.NoAssert, utils.SPDXNone, license.License)): - return True - else: - return False diff --git a/spdx/parsers/xmlparser.py b/spdx/parsers/xmlparser.py deleted file mode 100644 index d57d8175e..000000000 --- a/spdx/parsers/xmlparser.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) Xavier Figueroa -# 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 collections import OrderedDict - -import xmltodict - -from spdx.parsers import jsonyamlxml - - -class Parser(jsonyamlxml.Parser): - """ - Wrapper class for jsonyamlxml.Parser to provide an interface similar to - RDF and TV Parser classes (i.e., spdx.parsers..Parser) for XML parser. - It also avoids to repeat jsonyamlxml.Parser.parse code for JSON, YAML and XML parsers - """ - - def __init__(self, builder, logger): - super(Parser, self).__init__(builder, logger) - self.LIST_LIKE_FIELDS = { - "creators", - "externalDocumentRefs", - "extractedLicenseInfos", - "seeAlsos", - "annotations", - "relationships", - "snippets", - "reviewers", - "fileTypes", - "licenseInfoFromFiles", - "licenseInfoInFiles", - "artifactOf", - "fileContributors", - "fileDependencies", - "files", - "documentDescribes", - "packages", - "checksums", - "hasFiles", - "externalRefs", - "ranges", - "licenseInfoInSnippets", - "packageVerificationCodeExcludedFiles", - } - - def parse(self, file): - parsed_xml = xmltodict.parse( - file.read(), strip_whitespace=False, encoding="utf-8" - ) - fixed_object = self._set_in_list(parsed_xml, self.LIST_LIKE_FIELDS) - self.document_object = fixed_object.get("Document") - return super(Parser, self).parse() - - def _set_in_list(self, data, keys): - """ - xmltodict parse list-like fields in different way when there is only one - of them than when there are several of them. - Set in lists those fields that are expected to be in them. - """ - if isinstance(data, (dict, OrderedDict)): - new_data = OrderedDict() - for k, v in data.items(): - if k in keys and not isinstance(v, list): - new_data[k] = [self._set_in_list(v, keys)] - else: - new_data[k] = self._set_in_list(v, keys) - return new_data - elif isinstance(data, list): - new_data = [] - for element in data: - new_data.append(self._set_in_list(element, keys)) - return new_data - return data diff --git a/spdx/parsers/yamlparser.py b/spdx/parsers/yamlparser.py deleted file mode 100644 index 1130e1ca7..000000000 --- a/spdx/parsers/yamlparser.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) Xavier Figueroa -# 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 yaml - -from spdx.parsers import jsonyamlxml - - -class Parser(jsonyamlxml.Parser): - """ - Wrapper class for jsonyamlxml.Parser to provide an interface similar to - RDF and TV Parser classes (i.e., spdx.parsers..Parser) for YAML parser. - It also avoids to repeat jsonyamlxml.Parser.parse code for JSON, YAML and XML parsers - """ - - def __init__(self, builder, logger): - super(Parser, self).__init__(builder, logger) - - def parse(self, file): - self.json_yaml_set_document(yaml.safe_load(file)) - return super(Parser, self).parse() diff --git a/spdx/relationship.py b/spdx/relationship.py deleted file mode 100644 index 5eaa62fa3..000000000 --- a/spdx/relationship.py +++ /dev/null @@ -1,111 +0,0 @@ -# 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. - -from enum import auto, Enum - -from spdx.parsers.loggers import ErrorMessages - - -class RelationshipType(Enum): - AMENDS = auto() - OTHER = auto() - COPY_OF = auto() - TEST_OF = auto() - ANCESTOR_OF = auto() - BUILD_DEPENDENCY_OF = auto() - BUILD_TOOL_OF = auto() - CONTAINED_BY = auto() - CONTAINS = auto() - DATA_FILE_OF = auto() - DEPENDENCY_MANIFEST_OF = auto() - DEPENDENCY_OF = auto() - DEPENDS_ON = auto() - DESCENDANT_OF = auto() - DESCRIBED_BY = auto() - DESCRIBES = auto() - DEV_DEPENDENCY_OF = auto() - DEV_TOOL_OF = auto() - DISTRIBUTION_ARTIFACT = auto() - DOCUMENTATION_OF = auto() - DYNAMIC_LINK = auto() - EXAMPLE_OF = auto() - EXPANDED_FROM_ARCHIVE = auto() - FILE_ADDED = auto() - FILE_DELETED = auto() - FILE_MODIFIED = auto() - GENERATED_FROM = auto() - GENERATES = auto() - HAS_PREREQUISITE = auto() - METAFILE_OF = auto() - OPTIONAL_COMPONENT_OF = auto() - OPTIONAL_DEPENDENCY_OF = auto() - PACKAGE_OF = auto() - PATCH_APPLIED = auto() - PATCH_FOR = auto() - PREREQUISITE_FOR = auto() - PROVIDED_DEPENDENCY_OF = auto() - RUNTIME_DEPENDENCY_OF = auto() - STATIC_LINK = auto() - TEST_CASE_OF = auto() - TEST_DEPENDENCY_OF = auto() - TEST_TOOL_OF = auto() - VARIANT_OF = auto() - REQUIREMENT_DESCRIPTION_FOR = auto() - SPECIFICATION_FOR = auto() - - -class Relationship(object): - """ - Document relationship information - Fields: - - relationship: provides information about the relationship between two SPDX elements. - - relationship_comment: place for the SPDX file creator to record any general comments. Optional, One - """ - - def __init__(self, relationship=None, relationship_comment=None): - self.relationship = relationship - self.relationship_comment = relationship_comment - - def __eq__(self, other): - return ( - isinstance(other, Relationship) - and self.relationship == other.relationship - ) - - @property - def has_comment(self): - return self.relationship_comment is not None - - @property - def spdx_element_id(self): - return self.relationship.split(" ")[0] - - @property - def relationship_type(self): - return self.relationship.split(" ")[1] - - @property - def related_spdx_element(self): - return self.relationship.split(" ")[2] - - def validate(self, messages: ErrorMessages) -> ErrorMessages: - """ - Check that all the fields are valid. - Appends any error messages to messages parameter shall be a ErrorMessages. - """ - r_type = self.relationship_type - if r_type not in [name for name, _ in RelationshipType.__members__.items()]: - messages.append( - "Relationship type must be one of the constants defined in " - "class spdx.relationship.Relationship" - ) - return messages diff --git a/spdx/review.py b/spdx/review.py deleted file mode 100644 index 42c4982ee..000000000 --- a/spdx/review.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 datetime import datetime -from functools import total_ordering - -from spdx.utils import datetime_iso_format - - -@total_ordering -class Review(object): - - """ - Document review information. - Fields: - - reviewer: Person, Organization or tool that reviewed the SPDX file. - Mandatory one. - - review_date: Review date, mandatory one. Type: datetime. - - comment: Review comment. Optional one. Type: str. - """ - - def __init__(self, reviewer=None, review_date=None, comment=None): - self.reviewer = reviewer - self.review_date = review_date - self.comment = comment - - def __eq__(self, other): - return ( - isinstance(other, Review) - and self.reviewer == other.reviewer - and self.review_date == other.review_date - and self.comment == other.comment - ) - - def __lt__(self, other): - return (self.reviewer, self.review_date, self.comment) < ( - other.reviewer, - other.review_date, - other.comment, - ) - - def set_review_date_now(self): - self.review_date = datetime.utcnow().replace(microsecond=0) - - @property - def review_date_iso_format(self): - return datetime_iso_format(self.review_date) - - @property - def has_comment(self): - return self.comment is not None - - def validate(self, messages): - """ - Check that all the fields are valid. - Appends any error messages to messages parameter shall be a ErrorMessages. - """ - self.validate_reviewer(messages) - self.validate_review_date(messages) - - return messages - - def validate_reviewer(self, messages): - if self.reviewer is None: - messages.append("Review missing reviewer.") - - return messages - - def validate_review_date(self, messages): - if self.review_date is None: - messages.append("Review missing review date.") - - return messages diff --git a/spdx/snippet.py b/spdx/snippet.py deleted file mode 100644 index cd79e3b55..000000000 --- a/spdx/snippet.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright (c) 2018 Yash M. Nisar -# 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 typing import Tuple, Optional - -from spdx import license -from spdx import utils - - -class Snippet(object): - """ - Represents an analyzed snippet. - Fields: - - spdx_id: Uniquely identify any element in an SPDX document which may be - referenced by other elements. Mandatory, one per snippet if the snippet - is present. - - name: Name of the snippet. Optional, one. Type: str. - - comment: General comments about the snippet. Optional, one. Type: str. - - copyright: Copyright text. Mandatory, one. Type: str. - - license_comment: Relevant background references or analysis that went - in to arriving at the Concluded License for a snippet. Optional, one. - - snip_from_file_spdxid: Uniquely identify the file in an SPDX document - which this snippet is associated with. Mandatory, one. Type: str. - - conc_lics: Contains the license the SPDX file creator has concluded as - governing the snippet or alternative values if the governing license - cannot be determined. Mandatory one. Type: license.License or - utils.NoAssert or utils.SPDXNone. - - licenses_in_snippet: The list of licenses found in the snippet. - Mandatory, one or more. Type: license.License or utils.SPDXNone or - utils.NoAssert. - - attribution_text: optional string. - - byte_range: Defines the byte range in the original host file that the - snippet information applies to. Mandatory, one. Type (int, int) - - line_range: Defines the line range in the original host file that the - snippet information applies to. Optional, one. Type (int, int) - """ - - def __init__( - self, spdx_id=None, copyright=None, snip_from_file_spdxid=None, conc_lics=None, byte_range=None - ): - self.spdx_id = spdx_id - self.name = None - self.comment = None - self.copyright = copyright - self.license_comment = None - self.attribution_text = None - self.snip_from_file_spdxid = snip_from_file_spdxid - self.conc_lics = conc_lics - self.licenses_in_snippet = [] - self.byte_range: Optional[Tuple[int, int]] = byte_range - self.line_range: Optional[Tuple[int, int]] = None - - def add_lics(self, lics): - self.licenses_in_snippet.append(lics) - - def validate(self, messages): - """ - Validate fields of the snippet and update the messages list with user - friendly error messages for display. - """ - self.validate_spdx_id(messages) - self.validate_copyright_text(messages) - self.validate_snip_from_file_spdxid(messages) - self.validate_concluded_license(messages) - self.validate_licenses_in_snippet(messages) - - return messages - - def validate_spdx_id(self, messages): - if self.spdx_id is None: - messages.append("Snippet has no SPDX Identifier.") - - def validate_copyright_text(self, messages): - if self.copyright and not isinstance( - self.copyright, - (str, utils.NoAssert, utils.SPDXNone), - ): - messages.append( - "Snippet copyright must be str or unicode or spdx.utils.NoAssert or spdx.utils.SPDXNone" - ) - - def validate_snip_from_file_spdxid(self, messages): - if self.snip_from_file_spdxid is None: - messages.append("Snippet has no Snippet from File SPDX Identifier.") - - def validate_concluded_license(self, messages): - if self.conc_lics and not isinstance( - self.conc_lics, (utils.SPDXNone, utils.NoAssert, license.License) - ): - messages.append( - "Snippet concluded license must be instance of " - "spdx.utils.SPDXNone or spdx.utils.NoAssert or " - "spdx.license.License" - ) - - def validate_licenses_in_snippet(self, messages): - for lic in self.licenses_in_snippet: - if not isinstance( - lic, (license.License, utils.NoAssert, utils.SPDXNone) - ): - messages.append( - "License in snippet must be instance of " - "spdx.utils.SPDXNone or spdx.utils.NoAssert or " - "spdx.license.License" - ) - - def has_optional_field(self, field): - return bool(getattr(self, field, None)) diff --git a/spdx/utils.py b/spdx/utils.py deleted file mode 100644 index 3695cd0bd..000000000 --- a/spdx/utils.py +++ /dev/null @@ -1,248 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 datetime -import hashlib -import re -from typing import Dict, List, TYPE_CHECKING - -from ply import lex -from ply import yacc - -from spdx.checksum import ChecksumAlgorithm - -if TYPE_CHECKING: - from spdx.file import File - from spdx.package import Package -from spdx.relationship import Relationship -from spdx import license - - -def datetime_iso_format(date): - """ - Return an ISO-8601 representation of a datetime object. - """ - return date.isoformat() + "Z" - - -# Matches an iso 8601 date representation -DATE_ISO_REGEX = re.compile( - r"(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z", re.UNICODE -) - -# Groups for retrieving values from DATE_ISO_REGEX matches. -DATE_ISO_YEAR_GRP = 1 -DATE_ISO_MONTH_GRP = 2 -DATE_ISO_DAY_GRP = 3 -DATE_ISO_HOUR_GRP = 4 -DATE_ISO_MIN_GRP = 5 -DATE_ISO_SEC_GRP = 6 - - -def datetime_from_iso_format(string): - """ - Return a datetime object from an iso 8601 representation. - Return None if string is non conforming. - """ - match = DATE_ISO_REGEX.match(string) - if match: - date = datetime.datetime( - year=int(match.group(DATE_ISO_YEAR_GRP)), - month=int(match.group(DATE_ISO_MONTH_GRP)), - day=int(match.group(DATE_ISO_DAY_GRP)), - hour=int(match.group(DATE_ISO_HOUR_GRP)), - second=int(match.group(DATE_ISO_SEC_GRP)), - minute=int(match.group(DATE_ISO_MIN_GRP)), - ) - return date - else: - return None - - -class NoAssert(object): - """ - Represent SPDX NOASSERTION value. - """ - - def to_value(self): - return "NOASSERTION" - - def __str__(self): - return self.to_value() - - def __repr__(self): - return self.to_value() - - -class UnKnown(object): - """ - Represent SPDX UNKNOWN value. - """ - - def to_value(self): - return "UNKNOWN" - - def __str__(self): - return self.to_value() - - def __repr__(self): - return self.to_value() - - def __eq__(self, other): - return self.to_value() == other.to_value() - - -class SPDXNone(object): - """ - Represent SPDX None value. - """ - - def to_value(self): - return "NONE" - - def __str__(self): - return self.to_value() - - -class LicenseListLexer(object): - tokens = ["LP", "RP", "AND", "OR", "LICENSE"] - - def t_LP(self, t): - r"\(" - return t - - def t_RP(self, t): - r"\)" - return t - - def t_AND(self, t): - r"\s(and|AND)\s" - t.value = t.value.strip() - return t - - def t_OR(self, t): - r"\s(or|OR)\s" - t.value = t.value.strip() - return t - - def t_whitespace(self, t): - r"\s+" - pass - - def t_LICENSE(self, t): - r"[A-Za-z.0-9\-+]+" - t.value = t.value.strip() - return t - - def t_error(self, t): - pass - - def input(self, data): - """Set input, data - str.""" - self.lexer.input(data) - - def token(self): - """Get the next token or None if exhausted input.""" - return self.lexer.token() - - def build(self, **kwargs): - """Build lexer, must be called before input or token methods. - Only need to build once. - """ - self.lexer = lex.lex(module=self, **kwargs) - - -class LicenseListParser(object): - def __init__(self): - self.lex = LicenseListLexer() - self.lex.build(reflags=re.UNICODE) - self.tokens = self.lex.tokens - - def p_disjunction_1(self, p): - """disjunction : disjunction OR conjunction - """ - p[0] = license.LicenseDisjunction(p[1], p[3]) - - def p_disjunction_2(self, p): - """disjunction : conjunction - """ - p[0] = p[1] - - def p_conjunction_1(self, p): - """conjunction : conjunction AND license_atom - """ - p[0] = license.LicenseConjunction(p[1], p[3]) - - def p_conjunction_2(self, p): - """conjunction : license_atom - """ - p[0] = p[1] - - def p_license_atom_1(self, p): - """license_atom : LICENSE - """ - p[0] = license.License.from_identifier(p[1]) - - def p_license_atom_2(self, p): - """license_atom : LP disjunction RP - """ - p[0] = p[2] - - def p_error(self, p): - pass - - def build(self, **kwargs): - """Must be called before parse.""" - self.yacc = yacc.yacc(module=self, **kwargs) - - def parse(self, data): - """Parses a license list and returns a License or None if it failed.""" - try: - return self.yacc.parse(data, lexer=self.lex) - except: - return None - - -def calc_verif_code(files: List['File']) -> str: - list_of_file_hashes = [] - hash_algorithm_name = ChecksumAlgorithm.SHA1 - for file in files: - file_checksum = file.get_checksum(hash_algorithm_name) - if file_checksum is not None: - file_checksum_value = file_checksum.value - else: - file_checksum_value = file.calculate_checksum(hash_algorithm_name) - list_of_file_hashes.append(file_checksum_value) - - list_of_file_hashes.sort() - - hasher = hashlib.new(hash_algorithm_name.name.lower()) - hasher.update("".join(list_of_file_hashes).encode("utf-8")) - return hasher.hexdigest() - - -def get_files_in_package(package: 'Package', files: List['File'], relationships: List[Relationship]) -> List['File']: - files_in_package = [] - for file in files: - if file.spdx_id in [relationship.related_spdx_element for relationship in relationships - if relationship.relationship_type == "CONTAINS" and relationship.spdx_element_id == package.spdx_id] \ - or file.spdx_id in [relationship.spdx_element_id for relationship in relationships - if relationship.relationship_type == "CONTAINED_BY" and relationship.related_spdx_element == package.spdx_id]: - files_in_package.append(file) - - return files_in_package - - -def update_dict_item_with_new_item(current_state: Dict, key: str, item_to_add: str) -> None: - if key not in current_state: - current_state[key] = [item_to_add] - elif item_to_add not in current_state[key]: - current_state[key].append(item_to_add) diff --git a/spdx/version.py b/spdx/version.py deleted file mode 100644 index 8be8e7ddc..000000000 --- a/spdx/version.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 functools import total_ordering -import re - - -@total_ordering -class Version(object): - """ - Version number composed of major and minor. - Fields: - - major: Major number, int. - - minor: Minor number, int. - """ - - VERS_STR_REGEX = re.compile(r"(\d+)\.(\d+)") - - def __init__(self, major, minor): - self.major = int(major) - self.minor = int(minor) - - @classmethod - def from_str(cls, value): - """Constructs a Version from a string. - Returns None if string not in N.N form where N represents a - number. - """ - m = cls.VERS_STR_REGEX.match(value) - if m is not None: - return cls(int(m.group(1)), int(m.group(2))) - else: - return None - - def __repr__(self): - return "Version" + repr((self.major, self.minor)) - - def __str__(self): - return "SPDX-{major}.{minor}".format(**self.__dict__) - - def __eq__(self, other): - return ( - isinstance(other, self.__class__) - and self.major == other.major - and self.minor == other.minor - ) - - def __lt__(self, other): - return self.major < other.major or ( - self.major == other.major and self.minor < other.minor - ) diff --git a/spdx/writers/__init__.py b/spdx/writers/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/spdx/writers/json.py b/spdx/writers/json.py deleted file mode 100644 index a2b226fdf..000000000 --- a/spdx/writers/json.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) Xavier Figueroa -# 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 json - -from spdx.writers.tagvalue import InvalidDocumentError -from spdx.writers.jsonyamlxml import Writer -from spdx.parsers.loggers import ErrorMessages -import datetime - - -def json_converter(obj): - if isinstance(obj, datetime.datetime): - return str(obj) - else: - raise TypeError("No implementation available to serialize objects of type " + type(obj).__name__) - - -def write_document(document, out, validate=True): - - if validate: - messages = ErrorMessages() - messages = document.validate(messages) - if messages: - raise InvalidDocumentError(messages) - - writer = Writer(document) - document_object = writer.create_document() - json.dump(document_object, out, indent=4, default=json_converter) diff --git a/spdx/writers/jsonyamlxml.py b/spdx/writers/jsonyamlxml.py deleted file mode 100644 index d36e21112..000000000 --- a/spdx/writers/jsonyamlxml.py +++ /dev/null @@ -1,627 +0,0 @@ -# Copyright (c) Xavier Figueroa -# 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 typing import Dict, List - -from rdflib import Literal - -from spdx import license, utils -from spdx.checksum import Checksum -from spdx.package import ExternalPackageRef -from spdx.relationship import Relationship -from spdx.utils import update_dict_item_with_new_item - - -class BaseWriter(object): - """ - Base class for all Writer classes. - Provide utility functions and stores shared fields. - - document: spdx.document class. Source of data to be written - - document_object: python dictionary representation of the entire spdx.document - """ - - def __init__(self, document): - self.document = document - self.document_object = dict() - - def license(self, license_field): - """ - Return a string representation of a license or spdx.utils special object - """ - if isinstance( - license_field, (license.LicenseDisjunction, license.LicenseConjunction) - ): - return "({})".format(license_field) - - if isinstance(license_field, license.License): - license_str = license_field.identifier.__str__() - else: - license_str = license_field.__str__() - return license_str - - def checksum_to_dict(self, checksum_field: Checksum) -> Dict: - """ - Return a dictionary representation of a checksum.Checksum object - """ - return {'algorithm': checksum_field.identifier.name, 'checksumValue': checksum_field.value} - - def spdx_id(self, spdx_id_field): - return spdx_id_field.__str__().split("#")[-1] - - -class CreationInfoWriter(BaseWriter): - """ - Represent spdx.creationinfo as json-serializable objects - """ - - def __init__(self, document): - super(CreationInfoWriter, self).__init__(document) - - def create_creation_info(self): - creation_info_object = dict() - creation_info = self.document.creation_info - creation_info_object["creators"] = list(map(str, creation_info.creators)) - creation_info_object["created"] = creation_info.created_iso_format - - if creation_info.license_list_version: - creation_info_object["licenseListVersion"] = "{0}.{1}".format( - creation_info.license_list_version.major, - creation_info.license_list_version.minor, - ) - - if creation_info.has_comment: - creation_info_object["comment"] = creation_info.comment - - return creation_info_object - - -class PackageWriter(BaseWriter): - """ - Represent spdx.package as python objects - """ - - def __init__(self, document): - super(PackageWriter, self).__init__(document) - - def package_verification_code(self, package): - """ - Represent the package verification code information as - as python dictionary - """ - - package_verification_code_object = dict() - - package_verification_code_object["packageVerificationCodeValue"] = package.verif_code - - if package.verif_exc_files: - package_verification_code_object[ - "packageVerificationCodeExcludedFiles" - ] = package.verif_exc_files - - return package_verification_code_object - - @staticmethod - def external_reference_as_dict(external_ref: ExternalPackageRef) -> dict: - """ - Create a dictionary representation of the provided external reference, renaming the properties as they should - appear in a json/yaml/xml document. - """ - external_ref_dict = dict() - external_ref_dict["referenceCategory"] = external_ref.category - external_ref_dict["referenceType"] = external_ref.pkg_ext_ref_type - external_ref_dict["referenceLocator"] = external_ref.locator - if external_ref.comment: - external_ref_dict["comment"] = external_ref.comment - return external_ref_dict - - def create_package_info(self, package, annotations_by_spdx_id): - package_object = dict() - package_object["SPDXID"] = self.spdx_id(package.spdx_id) - package_object["name"] = package.name - package_object["downloadLocation"] = package.download_location.__str__() - if package.files_analyzed is not None: - package_object["filesAnalyzed"] = package.files_analyzed - if package.files_analyzed is None or package.files_analyzed is True: - if package.verif_code: - package_object["packageVerificationCode"] = self.package_verification_code( - package - ) - if package.has_optional_field("licenses_from_files"): - package_object["licenseInfoFromFiles"] = list( - map(self.license, package.licenses_from_files) - ) - if package.has_optional_field("conc_lics"): - package_object["licenseConcluded"] = self.license(package.conc_lics) - - if package.has_optional_field("license_declared"): - package_object["licenseDeclared"] = self.license(package.license_declared) - - if package.has_optional_field("cr_text"): - package_object["copyrightText"] = package.cr_text.__str__() - - if package.has_optional_field("version"): - package_object["versionInfo"] = package.version - - if package.has_optional_field("summary"): - package_object["summary"] = package.summary - - if package.has_optional_field("attribution_text"): - package_object["attributionTexts"] = [package.attribution_text] - - if package.has_optional_field("source_info"): - package_object["sourceInfo"] = package.source_info - - if package.has_optional_field("file_name"): - package_object["packageFileName"] = package.file_name - - if package.has_optional_field("supplier"): - package_object["supplier"] = package.supplier.to_value() - - if package.has_optional_field("originator"): - package_object["originator"] = package.originator.to_value() - - for checksum in package.checksums.values(): - package_object.setdefault("checksums", []).append(self.checksum_to_dict(checksum)) - - if package.has_optional_field("description"): - package_object["description"] = package.description - - if package.has_optional_field("comment"): - package_object["comment"] = package.comment - - if package.has_optional_field("license_comment"): - package_object["licenseComments"] = package.license_comment - - if package.has_optional_field("homepage"): - package_object["homepage"] = package.homepage.__str__() - - if package.has_optional_field("primary_package_purpose"): - package_object["primaryPackagePurpose"] = package.primary_package_purpose.name.replace("_", "-") - - if package.has_optional_field("release_date"): - package_object["releaseDate"] = utils.datetime_iso_format(package.release_date) - - if package.has_optional_field("built_date"): - package_object["builtDate"] = utils.datetime_iso_format(package.built_date) - - if package.has_optional_field("valid_until_date"): - package_object["validUntilDate"] = utils.datetime_iso_format(package.valid_until_date) - - if package.has_optional_field("pkg_ext_refs"): - package_object["externalRefs"] = [self.external_reference_as_dict(external_ref) for external_ref in - package.pkg_ext_refs] - if package.spdx_id in annotations_by_spdx_id: - package_object["annotations"] = annotations_by_spdx_id[package.spdx_id] - - return package_object - - -class FileWriter(BaseWriter): - """ - Represent spdx.file as json-serializable objects - """ - - def __init__(self, document): - super(FileWriter, self).__init__(document) - - def create_artifact_info(self, file): - """ - Create the artifact json-serializable representation from a spdx.file.File object - """ - artifact_of_objects = [] - - for i in range(len(file.artifact_of_project_name)): - artifact_of_object = dict() - artifact_of_object["name"] = file.artifact_of_project_name[i].__str__() - artifact_of_object["homePage"] = file.artifact_of_project_home[i].__str__() - artifact_of_object["projectUri"] = file.artifact_of_project_uri[i].__str__() - artifact_of_objects.append(artifact_of_object) - - return artifact_of_objects - - def create_file_info(self, file, annotations_by_spdx_id): - file_object = dict() - - file_object["fileName"] = file.name - file_object["SPDXID"] = self.spdx_id(file.spdx_id) - for checksum in file.checksums.values(): - file_object.setdefault("checksums", []).append(self.checksum_to_dict(checksum)) - if file.has_optional_field("conc_lics"): - file_object["licenseConcluded"] = self.license(file.conc_lics) - - if file.has_optional_field("licenses_in_file"): - file_object["licenseInfoInFiles"] = list( - map(self.license, file.licenses_in_file) - ) - - if file.has_optional_field("copyright"): - file_object["copyrightText"] = file.copyright.__str__() - - if file.has_optional_field("comment"): - file_object["comment"] = file.comment - - if file.has_optional_field("file_types"): - types = [] - for file_type in file.file_types: - types.append(file_type.name) - file_object["fileTypes"] = types - - if file.has_optional_field("license_comment"): - file_object["licenseComments"] = file.license_comment - - if file.has_optional_field("attribution_text"): - file_object["attributionTexts"] = [file.attribution_text] - - if file.has_optional_field("notice"): - file_object["noticeText"] = file.notice - - if file.contributors: - file_object["fileContributors"] = file.contributors - - if file.dependencies: - file_object["fileDependencies"] = file.dependencies - - valid_artifacts = ( - file.artifact_of_project_name - and len(file.artifact_of_project_name) - == len(file.artifact_of_project_home) - and len(file.artifact_of_project_home) - == len(file.artifact_of_project_uri) - ) - - if valid_artifacts: - file_object["artifactOf"] = self.create_artifact_info(file) - - if file.spdx_id in annotations_by_spdx_id: - file_object["annotations"] = annotations_by_spdx_id[file.spdx_id] - - return file_object - - -class ReviewInfoWriter(BaseWriter): - """ - Represent spdx.review as json-serializable objects - """ - - def __init__(self, document): - super(ReviewInfoWriter, self).__init__(document) - - def create_review_info(self): - review_info_objects = [] - reviews = self.document.reviews - - for review in reviews: - review_object = dict() - review_object["reviewer"] = review.reviewer.__str__() - review_object["reviewDate"] = review.review_date_iso_format - if review.has_comment: - review_object["comment"] = review.comment - - review_info_objects.append(review_object) - - return review_info_objects - - -class AnnotationInfoWriter(BaseWriter): - """ - Represent spdx.annotation as json-serializable objects - """ - - def __init__(self, document): - super(AnnotationInfoWriter, self).__init__(document) - - def create_annotations_by_spdx_id(self) -> Dict: - """ - Create a dict with annotations_by_spdx_id and use the spdx_id of the element that is annotated as key. - These keys are then used to attach the annotation to the corresponding SPDX element. - """ - annotations_by_spdx_id = dict() - - if not self.document.annotations: - return annotations_by_spdx_id - - for annotation in self.document.annotations: - annotation_object = dict() - annotation_object["annotator"] = annotation.annotator.__str__() - annotation_object["annotationDate"] = annotation.annotation_date_iso_format - annotation_object["annotationType"] = annotation.annotation_type - annotation_object["comment"] = annotation.comment - - annotation_spdx_id = self.spdx_id(annotation.spdx_id) - if annotation_spdx_id not in annotations_by_spdx_id: - annotations_by_spdx_id[annotation_spdx_id] = [annotation_object] - else: - annotations_by_spdx_id[annotation_spdx_id].append(annotation_object) - - return annotations_by_spdx_id - - -class RelationshipInfoWriter(BaseWriter): - """ - Represent spdx.relationship as json-serializable objects - """ - - def __init__(self, document): - super(RelationshipInfoWriter, self).__init__(document) - - def create_relationship_info(self, relationship: Relationship): - relationship_object = dict() - relationship_object["spdxElementId"] = relationship.spdx_element_id - relationship_object[ - "relatedSpdxElement" - ] = relationship.related_spdx_element - relationship_object["relationshipType"] = relationship.relationship_type - if relationship.has_comment: - relationship_object["comment"] = relationship.relationship_comment - - return relationship_object - - -class SnippetWriter(BaseWriter): - """ - Represent spdx.snippet as json-serializable objects - """ - - def __init__(self, document): - super(SnippetWriter, self).__init__(document) - - def create_snippet_info(self, annotations_by_spdx_id): - snippet_info_objects = [] - snippets = self.document.snippet - - for snippet in snippets: - snippet_from_file_spdx_id = self.spdx_id(snippet.snip_from_file_spdxid) - snippet_object = dict() - snippet_object["SPDXID"] = self.spdx_id(snippet.spdx_id) - snippet_object["snippetFromFile"] = snippet_from_file_spdx_id - - if snippet.has_optional_field("copyright"): - snippet_object["copyrightText"] = snippet.copyright - - if snippet.has_optional_field("conc_lics"): - snippet_object["licenseConcluded"] = self.license(snippet.conc_lics) - - if snippet.has_optional_field("licenses_in_snippet"): - snippet_object["licenseInfoInSnippets"] = list( - map(self.license, snippet.licenses_in_snippet) - ) - byte_range = {"endPointer": {"offset": snippet.byte_range[1], "reference": snippet_from_file_spdx_id}, - "startPointer": {"offset": snippet.byte_range[0], "reference": snippet_from_file_spdx_id}} - snippet_object["ranges"] = [byte_range] - - if snippet.has_optional_field("name"): - snippet_object["name"] = snippet.name - - if snippet.has_optional_field("comment"): - snippet_object["comment"] = snippet.comment - - if snippet.has_optional_field("attribution_text"): - snippet_object["attributionTexts"] = [snippet.attribution_text] - - if snippet.has_optional_field("license_comment"): - snippet_object["licenseComments"] = snippet.license_comment - - if snippet.spdx_id in annotations_by_spdx_id: - snippet_object["annotations"] = annotations_by_spdx_id[snippet.spdx_id] - - if snippet.has_optional_field("line_range"): - line_range = { - "endPointer": {"lineNumber": snippet.line_range[1], "reference": snippet_from_file_spdx_id}, - "startPointer": {"lineNumber": snippet.line_range[0], "reference": snippet_from_file_spdx_id}} - snippet_object["ranges"].append(line_range) - - snippet_info_objects.append(snippet_object) - - return snippet_info_objects - - -class ExtractedLicenseWriter(BaseWriter): - """ - Represent spdx.document.ExtractedLicense as json-serializable objects - """ - - def __init__(self, document): - super(ExtractedLicenseWriter, self).__init__(document) - - def create_extracted_license(self): - extracted_license_objects = [] - unique_extracted_licenses = {} - for lic in self.document.extracted_licenses: - if lic.identifier not in unique_extracted_licenses.keys(): - unique_extracted_licenses[lic.identifier] = lic - - for extracted_license in unique_extracted_licenses.values(): - extracted_license_object = dict() - - if isinstance(extracted_license.identifier, Literal): - extracted_license_object[ - "licenseId" - ] = extracted_license.identifier.toPython() - else: - extracted_license_object["licenseId"] = extracted_license.identifier - - if isinstance(extracted_license.text, Literal): - extracted_license_object[ - "extractedText" - ] = extracted_license.text.toPython() - else: - extracted_license_object["extractedText"] = extracted_license.text - - if extracted_license.full_name: - if isinstance(extracted_license.full_name, Literal): - extracted_license_object[ - "name" - ] = extracted_license.full_name.toPython() - else: - extracted_license_object["name"] = extracted_license.full_name - - if extracted_license.cross_ref: - if isinstance(extracted_license.cross_ref, Literal): - extracted_license_object[ - "seeAlsos" - ] = extracted_license.cross_ref.toPython() - else: - extracted_license_object["seeAlsos"] = extracted_license.cross_ref - - if extracted_license.comment: - if isinstance(extracted_license.comment, Literal): - extracted_license_object[ - "comment" - ] = extracted_license.comment.toPython() - else: - extracted_license_object["comment"] = extracted_license.comment - - extracted_license_objects.append(extracted_license_object) - - return extracted_license_objects - - -class Writer( - CreationInfoWriter, - ReviewInfoWriter, - FileWriter, - PackageWriter, - AnnotationInfoWriter, - RelationshipInfoWriter, - SnippetWriter, - ExtractedLicenseWriter, -): - """ - Wrapper for the other writers. - Represent a whole SPDX Document as json-serializable objects to then - be written as json or yaml files. - """ - - def __init__(self, document): - self.doc_spdx_id = self.spdx_id(document.spdx_id) - super(Writer, self).__init__(document) - - def create_ext_document_references(self): - """ - Create the External Document References json-serializable representation - """ - ext_document_references_field = self.document.ext_document_references - ext_document_reference_objects = [] - for ext_document_reference in ext_document_references_field: - ext_document_reference_object = dict() - ext_document_reference_object[ - "externalDocumentId" - ] = ext_document_reference.external_document_id - ext_document_reference_object[ - "spdxDocument" - ] = ext_document_reference.spdx_document_uri - - ext_document_reference_object["checksum"] = self.checksum_to_dict( - ext_document_reference.checksum - ) - - ext_document_reference_objects.append(ext_document_reference_object) - - return ext_document_reference_objects - - def create_relationships(self) -> List[Dict]: - packages_spdx_ids = [package.spdx_id for package in self.document.packages] - files_spdx_ids = [file.spdx_id for file in self.document.files] - # we take the package_objects from document_object if any exist because we will modify them to add - # jsonyamlxml-specific fields - if "packages" in self.document_object: - packages_by_spdx_id = {package["SPDXID"]: package for package in self.document_object["packages"]} - else: - packages_by_spdx_id = {} - - relationship_objects = [] - for relationship in self.document.relationships: - if relationship.relationship_type == "CONTAINS" and relationship.spdx_element_id in packages_spdx_ids \ - and relationship.related_spdx_element in files_spdx_ids: - update_dict_item_with_new_item(packages_by_spdx_id[relationship.spdx_element_id], "hasFiles", - relationship.related_spdx_element) - if relationship.has_comment: - relationship_objects.append(self.create_relationship_info(relationship)) - - elif relationship.relationship_type == "CONTAINED_BY" and relationship.spdx_element_id in files_spdx_ids \ - and relationship.related_spdx_element in packages_spdx_ids: - update_dict_item_with_new_item(packages_by_spdx_id[relationship.related_spdx_element], - "hasFiles", relationship.spdx_element_id) - if relationship.has_comment: - relationship_objects.append(self.create_relationship_info(relationship)) - - elif relationship.relationship_type == "DESCRIBES" and relationship.spdx_element_id == self.document.spdx_id: - update_dict_item_with_new_item(self.document_object, "documentDescribes", - relationship.related_spdx_element) - if relationship.has_comment: - relationship_objects.append(self.create_relationship_info(relationship)) - - elif relationship.relationship_type == "DESCRIBED_BY" and relationship.related_spdx_element == self.document.spdx_id: - update_dict_item_with_new_item(self.document_object, "documentDescribes", - relationship.spdx_element_id) - if relationship.has_comment: - relationship_objects.append(self.create_relationship_info(relationship)) - - else: - relationship_objects.append(self.create_relationship_info(relationship)) - - return relationship_objects - - def create_document(self): - self.document_object = dict() - - self.document_object["spdxVersion"] = self.document.version.__str__() - self.document_object["documentNamespace"] = self.document.namespace.__str__() - self.document_object["creationInfo"] = self.create_creation_info() - self.document_object["dataLicense"] = self.license(self.document.data_license) - self.document_object["SPDXID"] = self.doc_spdx_id - self.document_object["name"] = self.document.name - annotations_by_spdx_id = self.create_annotations_by_spdx_id() - - unique_doc_packages = {} - for doc_package in self.document.packages: - if doc_package.spdx_id not in unique_doc_packages.keys(): - unique_doc_packages[doc_package.spdx_id] = doc_package - if unique_doc_packages: - package_objects = [] - for package in unique_doc_packages.values(): - package_info_object = self.create_package_info(package, annotations_by_spdx_id) - package_objects.append(package_info_object) - self.document_object["packages"] = package_objects - if self.document.files: - file_objects = [] - for file in self.document.files: - file_object = self.create_file_info(file, annotations_by_spdx_id) - file_objects.append(file_object) - self.document_object["files"] = file_objects - - if self.document.has_comment: - self.document_object["comment"] = self.document.comment - - if self.document.ext_document_references: - self.document_object[ - "externalDocumentRefs" - ] = self.create_ext_document_references() - - if self.document.extracted_licenses: - self.document_object[ - "hasExtractedLicensingInfos" - ] = self.create_extracted_license() - - if self.document.reviews: - self.document_object["reviewers"] = self.create_review_info() - - if self.document.snippet: - self.document_object["snippets"] = self.create_snippet_info(annotations_by_spdx_id) - - if self.doc_spdx_id in annotations_by_spdx_id: - self.document_object["annotations"] = annotations_by_spdx_id[self.doc_spdx_id] - - if self.document.relationships: - relationship_objects = self.create_relationships() - if relationship_objects: - self.document_object["relationships"] = relationship_objects - - return self.document_object diff --git a/spdx/writers/rdf.py b/spdx/writers/rdf.py deleted file mode 100644 index 7e2b898f7..000000000 --- a/spdx/writers/rdf.py +++ /dev/null @@ -1,1087 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 rdflib import BNode -from rdflib import Graph -from rdflib import Literal -from rdflib import Namespace -from rdflib import RDF -from rdflib import RDFS -from rdflib import URIRef -from rdflib.compare import to_isomorphic - -from spdx import config -from spdx import file -from spdx import license -from spdx import utils -from spdx.checksum import Checksum -from spdx.package import Package -from spdx.parsers.loggers import ErrorMessages -from spdx.relationship import Relationship -from spdx.utils import get_files_in_package -from spdx.writers.tagvalue import InvalidDocumentError - -import warnings - - -class BaseWriter(object): - """ - Base class for all Writer classes. - Provide utility functions and stores shared fields. - """ - - def __init__(self, document, out): - self.document = document - self.out = out - self.doap_namespace = Namespace("http://usefulinc.com/ns/doap#") - self.spdx_namespace = Namespace("http://spdx.org/rdf/terms#") - self.graph = Graph() - - def create_checksum_node(self, checksum: Checksum) -> BNode: - """ - Return a node representing spdx.checksum. - """ - algo = checksum.identifier.algorithm_to_rdf_representation() or 'checksumAlgorithm_sha1' - checksum_node = BNode() - type_triple = (checksum_node, RDF.type, self.spdx_namespace.Checksum) - self.graph.add(type_triple) - algorithm_triple = ( - checksum_node, - self.spdx_namespace.algorithm, - Literal('http://spdx.org/rdf/terms#' + algo), - ) - self.graph.add(algorithm_triple) - value_triple = ( - checksum_node, - self.spdx_namespace.checksumValue, - Literal(checksum.value), - ) - self.graph.add(value_triple) - return checksum_node - - def to_special_value(self, value): - """ - Return proper spdx term or Literal - """ - if isinstance(value, utils.NoAssert): - return self.spdx_namespace.noassertion - elif isinstance(value, utils.SPDXNone): - return self.spdx_namespace.none - else: - return Literal(value) - - -class LicenseWriter(BaseWriter): - """ - Handle all License classes from spdx.document module. - """ - - def __init__(self, document, out): - super(LicenseWriter, self).__init__(document, out) - - def licenses_from_tree_helper(self, current, licenses): - if isinstance( - current, (license.LicenseConjunction, license.LicenseDisjunction) - ): - self.licenses_from_tree_helper(current.license_1, licenses) - self.licenses_from_tree_helper(current.license_2, licenses) - else: - licenses.add(self.create_license_helper(current)) - - def licenses_from_tree(self, tree): - """ - Traverse conjunctions and disjunctions like trees and return a - set of all licenses in it as nodes. - """ - # FIXME: this is unordered! - licenses = set() - self.licenses_from_tree_helper(tree, licenses) - return licenses - - def create_conjunction_node(self, conjunction): - """ - Return a node representing a conjunction of licenses. - """ - node = BNode() - type_triple = (node, RDF.type, self.spdx_namespace.ConjunctiveLicenseSet) - self.graph.add(type_triple) - licenses = self.licenses_from_tree(conjunction) - for lic in licenses: - member_triple = (node, self.spdx_namespace.member, lic) - self.graph.add(member_triple) - return node - - def create_disjunction_node(self, disjunction): - """ - Return a node representing a disjunction of licenses. - """ - node = BNode() - type_triple = (node, RDF.type, self.spdx_namespace.DisjunctiveLicenseSet) - self.graph.add(type_triple) - licenses = self.licenses_from_tree(disjunction) - for lic in licenses: - member_triple = (node, self.spdx_namespace.member, lic) - self.graph.add(member_triple) - return node - - def create_license_helper(self, lic): - """ - Handle single(no conjunction/disjunction) licenses. - Return the created node. - """ - if isinstance(lic, license.ExtractedLicense): - return self.create_extracted_license(lic) - if lic.identifier.rstrip("+") in config.LICENSE_MAP: - return URIRef(lic.url) - else: - matches = [ - l - for l in self.document.extracted_licenses - if l.identifier == lic.identifier - ] - if len(matches) != 0: - return self.create_extracted_license(matches[0]) - else: - lic = license.ExtractedLicense(lic.identifier) - warnings.warn( - "Missing extracted license: {0}".format(lic.identifier) - ) - return self.create_extracted_license(lic) - - def create_extracted_license(self, lic): - """ - Handle extracted license. - Return the license node. - """ - licenses = list( - self.graph.triples((None, self.spdx_namespace.licenseId, lic.identifier)) - ) - if len(licenses) != 0: - return licenses[0][0] # return subject in first triple - else: - license_node = BNode() - type_triple = ( - license_node, - RDF.type, - self.spdx_namespace.ExtractedLicensingInfo, - ) - self.graph.add(type_triple) - ident_triple = ( - license_node, - self.spdx_namespace.licenseId, - Literal(lic.identifier), - ) - self.graph.add(ident_triple) - text_triple = ( - license_node, - self.spdx_namespace.extractedText, - Literal(lic.text), - ) - self.graph.add(text_triple) - if lic.full_name is not None: - name_triple = ( - license_node, - self.spdx_namespace.licenseName, - self.to_special_value(lic.full_name), - ) - self.graph.add(name_triple) - for ref in lic.cross_ref: - triple = (license_node, RDFS.seeAlso, URIRef(ref)) - self.graph.add(triple) - if lic.comment is not None: - comment_triple = (license_node, RDFS.comment, Literal(lic.comment)) - self.graph.add(comment_triple) - return license_node - - def create_license_node(self, lic): - """ - Return a node representing a license. - Could be a single license (extracted or part of license list.) or - a conjunction/disjunction of licenses. - """ - if isinstance(lic, license.LicenseConjunction): - return self.create_conjunction_node(lic) - elif isinstance(lic, license.LicenseDisjunction): - return self.create_disjunction_node(lic) - else: - return self.create_license_helper(lic) - - def license_or_special(self, lic): - """ - Check for special values spdx:none and spdx:noassertion. - Return the term for the special value or the result of passing - license to create_license_node. - """ - if isinstance(lic, utils.NoAssert): - return self.spdx_namespace.noassertion - elif isinstance(lic, utils.SPDXNone): - return self.spdx_namespace.none - else: - return self.create_license_node(lic) - - -class FileWriter(LicenseWriter): - """ - Write spdx.file.File - """ - - FILE_TYPES = { - file.FileType.SOURCE: "fileType_source", - file.FileType.OTHER: "fileType_other", - file.FileType.BINARY: "fileType_binary", - file.FileType.ARCHIVE: "fileType_archive", - } - - def __init__(self, document, out): - super(FileWriter, self).__init__(document, out) - - def create_file_node(self, doc_file): - """ - Create a node for spdx.file. - """ - file_node = URIRef( - "http://www.spdx.org/files#{id}".format(id=str(doc_file.spdx_id)) - ) - type_triple = (file_node, RDF.type, self.spdx_namespace.File) - self.graph.add(type_triple) - - name_triple = (file_node, self.spdx_namespace.fileName, Literal(doc_file.name)) - self.graph.add(name_triple) - - if doc_file.has_optional_field("comment"): - comment_triple = (file_node, RDFS.comment, Literal(doc_file.comment)) - self.graph.add(comment_triple) - - if doc_file.has_optional_field("file_types"): - for f_type in doc_file.file_types: - ftype = self.spdx_namespace[file.file_type_to_rdf(f_type)] - ftype_triple = (file_node, self.spdx_namespace.fileType, ftype) - self.graph.add(ftype_triple) - - for chk_sum in doc_file.checksums.values(): - self.graph.add( - ( - file_node, - self.spdx_namespace.checksum, - self.create_checksum_node(chk_sum), - ) - ) - - conc_lic_node = self.license_or_special(doc_file.conc_lics) - conc_lic_triple = ( - file_node, - self.spdx_namespace.licenseConcluded, - conc_lic_node, - ) - self.graph.add(conc_lic_triple) - - license_info_nodes = map(self.license_or_special, doc_file.licenses_in_file) - for lic in license_info_nodes: - triple = (file_node, self.spdx_namespace.licenseInfoInFile, lic) - self.graph.add(triple) - - if doc_file.has_optional_field("license_comment"): - comment_triple = ( - file_node, - self.spdx_namespace.licenseComments, - Literal(doc_file.license_comment), - ) - self.graph.add(comment_triple) - - if doc_file.has_optional_field("attribution_text"): - file_attribution_text_triple = ( - file_node, - self.spdx_namespace.attributionText, - Literal(doc_file.attribution_text), - ) - self.graph.add(file_attribution_text_triple) - - cr_text_node = self.to_special_value(doc_file.copyright) - cr_text_triple = (file_node, self.spdx_namespace.copyrightText, cr_text_node) - self.graph.add(cr_text_triple) - - if doc_file.has_optional_field("notice"): - notice_triple = (file_node, self.spdx_namespace.noticeText, doc_file.notice) - self.graph.add(notice_triple) - - contrib_nodes = map(lambda c: Literal(c), doc_file.contributors) - contrib_triples = [ - (file_node, self.spdx_namespace.fileContributor, node) - for node in contrib_nodes - ] - for triple in contrib_triples: - self.graph.add(triple) - - return file_node - - def files(self): - """ - Return list of file nodes. - """ - return map(self.create_file_node, self.document.files) - - def add_file_dependencies_helper(self, doc_file): - """ - Handle dependencies for a single file. - - doc_file - instance of spdx.file.File. - """ - subj_triples = list( - self.graph.triples( - (None, self.spdx_namespace.fileName, Literal(doc_file.name)) - ) - ) - if len(subj_triples) != 1: - raise InvalidDocumentError( - "Could not find dependency subject {0}".format(doc_file.name) - ) - subject_node = subj_triples[0][0] - for dependency in doc_file.dependencies: - dep_triples = list( - self.graph.triples( - (None, self.spdx_namespace.fileName, Literal(dependency)) - ) - ) - if len(dep_triples) == 1: - dep_node = dep_triples[0][0] - dep_triple = ( - subject_node, - self.spdx_namespace.fileDependency, - dep_node, - ) - self.graph.add(dep_triple) - else: - print( - "Warning could not resolve file dependency {0} -> {1}".format( - doc_file.name, dependency - ) - ) - - def add_file_dependencies(self): - """ - Add file dependencies to the graph. - Called after all files have been added. - """ - for doc_file in self.document.files: - self.add_file_dependencies_helper(doc_file) - - -class SnippetWriter(LicenseWriter): - """ - Write spdx.snippet.Snippet - """ - - def __init__(self, document, out): - super(SnippetWriter, self).__init__(document, out) - - def create_snippet_node(self, snippet): - """ - Return a snippet node. - """ - snippet_node = URIRef("http://spdx.org/rdf/terms/Snippet#" + snippet.spdx_id) - type_triple = (snippet_node, RDF.type, self.spdx_namespace.Snippet) - self.graph.add(type_triple) - - if snippet.has_optional_field("comment"): - comment_triple = (snippet_node, RDFS.comment, Literal(snippet.comment)) - self.graph.add(comment_triple) - - if snippet.has_optional_field("name"): - name_triple = ( - snippet_node, - self.spdx_namespace.name, - Literal(snippet.name), - ) - self.graph.add(name_triple) - - if snippet.has_optional_field("license_comment"): - lic_comment_triple = ( - snippet_node, - self.spdx_namespace.licenseComments, - Literal(snippet.license_comment), - ) - self.graph.add(lic_comment_triple) - - if snippet.has_optional_field("attribution_text"): - lic_attribution_text_triple = ( - snippet_node, - self.spdx_namespace.attributionText, - Literal(snippet.attribution_text), - ) - self.graph.add(lic_attribution_text_triple) - - cr_text_node = self.to_special_value(snippet.copyright) - cr_text_triple = (snippet_node, self.spdx_namespace.copyrightText, cr_text_node) - self.graph.add(cr_text_triple) - - snip_from_file_triple = ( - snippet_node, - self.spdx_namespace.snippetFromFile, - Literal(snippet.snip_from_file_spdxid), - ) - self.graph.add(snip_from_file_triple) - - conc_lic_node = self.license_or_special(snippet.conc_lics) - conc_lic_triple = ( - snippet_node, - self.spdx_namespace.licenseConcluded, - conc_lic_node, - ) - self.graph.add(conc_lic_triple) - - license_info_nodes = map(self.license_or_special, snippet.licenses_in_snippet) - for lic in license_info_nodes: - triple = (snippet_node, self.spdx_namespace.licenseInfoInSnippet, lic) - self.graph.add(triple) - - return snippet_node - - def snippets(self): - """ - Return list of snippet nodes. - """ - return map(self.create_snippet_node, self.document.snippet) - - -class ReviewInfoWriter(BaseWriter): - """ - Write spdx.review.Review - """ - - def __init__(self, document, out): - super(ReviewInfoWriter, self).__init__(document, out) - - def create_review_node(self, review): - """ - Return a review node. - """ - review_node = BNode() - type_triple = (review_node, RDF.type, self.spdx_namespace.Review) - self.graph.add(type_triple) - - reviewer_node = Literal(review.reviewer.to_value()) - self.graph.add((review_node, self.spdx_namespace.reviewer, reviewer_node)) - reviewed_date_node = Literal(review.review_date_iso_format) - reviewed_triple = ( - review_node, - self.spdx_namespace.reviewDate, - reviewed_date_node, - ) - self.graph.add(reviewed_triple) - if review.has_comment: - comment_node = Literal(review.comment) - comment_triple = (review_node, RDFS.comment, comment_node) - self.graph.add(comment_triple) - - return review_node - - def reviews(self): - "Returns a list of review nodes" - return map(self.create_review_node, self.document.reviews) - - -class AnnotationInfoWriter(BaseWriter): - """ - Write spdx.annotation.Annotation - """ - - def __init__(self, document, out): - super(AnnotationInfoWriter, self).__init__(document, out) - - def create_annotation_node(self, annotation): - """ - Return an annotation node. - """ - annotation_node = URIRef(str(annotation.spdx_id)) - type_triple = (annotation_node, RDF.type, self.spdx_namespace.Annotation) - self.graph.add(type_triple) - - annotator_node = Literal(annotation.annotator.to_value()) - self.graph.add((annotation_node, self.spdx_namespace.annotator, annotator_node)) - annotation_date_node = Literal(annotation.annotation_date_iso_format) - annotation_triple = ( - annotation_node, - self.spdx_namespace.annotationDate, - annotation_date_node, - ) - self.graph.add(annotation_triple) - if annotation.has_comment: - comment_node = Literal(annotation.comment) - comment_triple = (annotation_node, RDFS.comment, comment_node) - self.graph.add(comment_triple) - annotation_type_node = Literal(annotation.annotation_type) - annotation_type_triple = ( - annotation_node, - self.spdx_namespace.annotationType, - annotation_type_node, - ) - self.graph.add(annotation_type_triple) - - return annotation_node - - def annotations(self): - """ - Return a list of annotation nodes - """ - return map(self.create_annotation_node, self.document.annotations) - - -class RelationshipInfoWriter(BaseWriter): - """ - Write spdx.relationship.Relationship - """ - - def __init__(self, document, out): - super(RelationshipInfoWriter, self).__init__(document, out) - - def transform_relationship_type_to_rdf_model(self, relationship_type: str) -> str: - """ - Transform relationship type from upper snake case to camel case to match rdf-specific output e.g. - COPY_OF -> relationshipType_copyOf. - """ - return "relationshipType_" + relationship_type[0].lower() + relationship_type.title().replace('_', '')[1:] - - def create_relationship_node(self, relationship: Relationship) -> BNode: - """ - Return a relationship node. - """ - relationship_node = BNode() - type_triple = (relationship_node, RDF.type, self.spdx_namespace.Relationship) - self.graph.add(type_triple) - - relationship_spdx_element_id = Literal(relationship.spdx_element_id) - self.graph.add( - ( - relationship_node, - self.spdx_namespace.spdxElementId, - relationship_spdx_element_id, - ) - ) - relationship_type = self.transform_relationship_type_to_rdf_model(relationship.relationship_type) - relationship_type_node = self.spdx_namespace[relationship_type] - self.graph.add( - ( - relationship_node, - self.spdx_namespace.relationshipType, - relationship_type_node, - ) - ) - related_spdx_node = Literal(relationship.related_spdx_element) - related_spdx_triple = ( - relationship_node, - self.spdx_namespace.relatedSpdxElement, - related_spdx_node, - ) - self.graph.add(related_spdx_triple) - if relationship.has_comment: - comment_node = Literal(relationship.relationship_comment) - comment_triple = (relationship_node, RDFS.comment, comment_node) - self.graph.add(comment_triple) - - return relationship_node - - def relationships(self): - """ - Return a list of relationship nodes - """ - return map(self.create_relationship_node, self.document.relationships) - - -class CreationInfoWriter(BaseWriter): - """ - Write class spdx.creationinfo.CreationInfo - """ - - def __init__(self, document, out): - super(CreationInfoWriter, self).__init__(document, out) - - def creators(self): - """ - Return a list of creator nodes. - Note: Does not add anything to the graph. - """ - return map( - lambda c: Literal(c.to_value()), self.document.creation_info.creators - ) - - def create_creation_info(self): - """ - Add and return a creation info node to graph - """ - ci_node = BNode() - # Type property - type_triple = (ci_node, RDF.type, self.spdx_namespace.CreationInfo) - self.graph.add(type_triple) - - created_date = Literal(self.document.creation_info.created_iso_format) - created_triple = (ci_node, self.spdx_namespace.created, created_date) - self.graph.add(created_triple) - - creators = self.creators() - for creator in creators: - self.graph.add((ci_node, self.spdx_namespace.creator, creator)) - - if self.document.creation_info.has_comment: - comment_node = Literal(self.document.creation_info.comment) - comment_triple = (ci_node, RDFS.comment, comment_node) - self.graph.add(comment_triple) - - return ci_node - - -class ExternalDocumentRefWriter(BaseWriter): - """ - Write class spdx.external_document_ref.ExternalDocumentRef - """ - - def __init__(self, document, out): - super(ExternalDocumentRefWriter, self).__init__(document, out) - - def create_external_document_ref_node(self, ext_document_references): - """ - Add and return a creation info node to graph - """ - ext_doc_ref_node = BNode() - type_triple = ( - ext_doc_ref_node, - RDF.type, - self.spdx_namespace.ExternalDocumentRef, - ) - self.graph.add(type_triple) - - ext_doc_id = Literal(ext_document_references.external_document_id) - ext_doc_id_triple = ( - ext_doc_ref_node, - self.spdx_namespace.externalDocumentId, - ext_doc_id, - ) - self.graph.add(ext_doc_id_triple) - - doc_uri = Literal(ext_document_references.spdx_document_uri) - doc_uri_triple = (ext_doc_ref_node, self.spdx_namespace.spdxDocument, doc_uri) - self.graph.add(doc_uri_triple) - - checksum_node = self.create_checksum_node(ext_document_references.checksum) - self.graph.add((ext_doc_ref_node, self.spdx_namespace.checksum, checksum_node)) - - return ext_doc_ref_node - - def ext_doc_refs(self): - """ - Return a list of review nodes - """ - return map( - self.create_external_document_ref_node, - self.document.ext_document_references, - ) - - -class PackageWriter(LicenseWriter): - """ - Write spdx.package.Package - """ - - def __init__(self, document, out): - super(PackageWriter, self).__init__(document, out) - - def package_verif_node(self, package): - """ - Return a node representing package verification code. - """ - verif_node = BNode() - type_triple = ( - verif_node, - RDF.type, - self.spdx_namespace.PackageVerificationCode, - ) - self.graph.add(type_triple) - value_triple = ( - verif_node, - self.spdx_namespace.packageVerificationCodeValue, - Literal(package.verif_code), - ) - self.graph.add(value_triple) - excl_file_nodes = map(lambda excl: Literal(excl), package.verif_exc_files) - excl_predicate = self.spdx_namespace.packageVerificationCodeExcludedFile - excl_file_triples = [ - (verif_node, excl_predicate, xcl_file) for xcl_file in excl_file_nodes - ] - for trp in excl_file_triples: - self.graph.add(trp) - return verif_node - - def handle_package_literal_optional(self, package, package_node, predicate, field): - """ - Check if optional field is set. - If so it adds the triple (package_node, predicate, $) to the graph. - Where $ is a literal or special value term of the value of the field. - """ - if package.has_optional_field(field): - value = getattr(package, field, None) - value_node = self.to_special_value(value) - triple = (package_node, predicate, value_node) - self.graph.add(triple) - - def handle_pkg_optional_fields(self, package: Package, package_node): - """ - Write package optional fields. - """ - self.handle_package_literal_optional( - package, package_node, self.spdx_namespace.versionInfo, "version" - ) - self.handle_package_literal_optional( - package, package_node, self.spdx_namespace.packageFileName, "file_name" - ) - self.handle_package_literal_optional( - package, package_node, self.spdx_namespace.supplier, "supplier" - ) - self.handle_package_literal_optional( - package, package_node, self.spdx_namespace.originator, "originator" - ) - self.handle_package_literal_optional( - package, package_node, self.spdx_namespace.sourceInfo, "source_info" - ) - self.handle_package_literal_optional( - package, - package_node, - self.spdx_namespace.licenseComments, - "license_comment", - ) - self.handle_package_literal_optional( - package, package_node, self.spdx_namespace.summary, "summary" - ) - self.handle_package_literal_optional( - package, - package_node, - self.spdx_namespace.attributionText, - "attribution_text", - ) - self.handle_package_literal_optional( - package, package_node, self.spdx_namespace.description, "description" - ) - self.handle_package_literal_optional( - package, package_node, self.spdx_namespace.comment, "comment" - ) - self.handle_package_literal_optional( - package, package_node, self.spdx_namespace.filesAnalyzed, "files_analyzed" - ) - - if package.has_optional_field("checksums"): - for checksum in package.checksums.values(): - checksum_node = self.create_checksum_node(checksum) - self.graph.add((package_node, self.spdx_namespace.checksum, checksum_node)) - - if package.has_optional_field("homepage"): - homepage_node = URIRef(self.to_special_value(package.homepage)) - homepage_triple = ( - package_node, - self.doap_namespace.homepage, - homepage_node, - ) - self.graph.add(homepage_triple) - - def create_package_node(self, package): - """ - Return a Node representing the package. - Files must have been added to the graph before this method is called. - """ - package_node = URIRef("http://www.spdx.org/tools#SPDXRef-Package") - type_triple = (package_node, RDF.type, self.spdx_namespace.Package) - self.graph.add(type_triple) - # Package SPDXID - if package.spdx_id: - pkg_spdx_id = URIRef(package.spdx_id) - pkg_spdx_id_triple = ( - package_node, - self.spdx_namespace.Package, - pkg_spdx_id, - ) - self.graph.add(pkg_spdx_id_triple) - # Handle optional fields: - self.handle_pkg_optional_fields(package, package_node) - # package name - name_triple = (package_node, self.spdx_namespace.name, Literal(package.name)) - self.graph.add(name_triple) - # Package download location - down_loc_node = ( - package_node, - self.spdx_namespace.downloadLocation, - self.to_special_value(package.download_location), - ) - self.graph.add(down_loc_node) - # Handle package verification - if package.files_analyzed != False: - verif_node = self.package_verif_node(package) - verif_triple = ( - package_node, - self.spdx_namespace.packageVerificationCode, - verif_node, - ) - self.graph.add(verif_triple) - # Handle concluded license - if package.conc_lics: - conc_lic_node = self.license_or_special(package.conc_lics) - conc_lic_triple = ( - package_node, - self.spdx_namespace.licenseConcluded, - conc_lic_node, - ) - self.graph.add(conc_lic_triple) - # Handle declared license - if package.license_declared: - decl_lic_node = self.license_or_special(package.license_declared) - decl_lic_triple = ( - package_node, - self.spdx_namespace.licenseDeclared, - decl_lic_node, - ) - self.graph.add(decl_lic_triple) - # Package licenses from files - licenses_from_files_nodes = map( - lambda el: self.license_or_special(el), package.licenses_from_files - ) - lic_from_files_predicate = self.spdx_namespace.licenseInfoFromFiles - lic_from_files_triples = [ - (package_node, lic_from_files_predicate, node) - for node in licenses_from_files_nodes - ] - for triple in lic_from_files_triples: - self.graph.add(triple) - # Copyright Text - cr_text_node = self.to_special_value(package.cr_text) - cr_text_triple = (package_node, self.spdx_namespace.copyrightText, cr_text_node) - self.graph.add(cr_text_triple) - # Handle files - self.handle_package_has_file(package, package_node) - return package_node - - def packages(self): - """ - Return a node that represents the package in the graph. - Call this function to write package info. - """ - return [self.create_package_node(x) - for x in self.document.packages] - - def handle_package_has_file_helper(self, pkg_file): - """ - Return node representing pkg_file - pkg_file should be instance of spdx.file. - """ - nodes = list( - self.graph.triples( - (None, self.spdx_namespace.fileName, Literal(pkg_file.name)) - ) - ) - if len(nodes) == 1: - return nodes[0][0] - else: - raise InvalidDocumentError( - "handle_package_has_file_helper could not" - + " find file node for file: {0}".format(pkg_file.name) - ) - - def handle_package_has_file(self, package, package_node): - """ - Add hasFile triples to graph. - Must be called after files have been added. - """ - files = get_files_in_package(package, self.document.files, self.document.relationships) - file_nodes = map(self.handle_package_has_file_helper, files) - triples = [ - (package_node, self.spdx_namespace.hasFile, node) for node in file_nodes - ] - for triple in triples: - self.graph.add(triple) - - -class PackageExternalRefWriter(BaseWriter): - """ - Write class spdx.package.ExternalPackageRef - """ - - def __init__(self, document, out): - super(PackageExternalRefWriter, self).__init__(document, out) - - def create_package_external_ref_node(self, pkg_ext_refs): - """ - Add and return an external package reference node to graph. - """ - pkg_ext_ref_node = BNode() - pkg_ext_ref_triple = ( - pkg_ext_ref_node, - RDF.type, - self.spdx_namespace.ExternalRef, - ) - self.graph.add(pkg_ext_ref_triple) - - pkg_ext_ref_category = Literal(pkg_ext_refs.category) - pkg_ext_ref_category_triple = ( - pkg_ext_ref_node, - self.spdx_namespace.referenceCategory, - pkg_ext_ref_category, - ) - self.graph.add(pkg_ext_ref_category_triple) - - pkg_ext_ref_type = Literal(pkg_ext_refs.pkg_ext_ref_type) - pkg_ext_ref_type_triple = ( - pkg_ext_ref_node, - self.spdx_namespace.referenceType, - pkg_ext_ref_type, - ) - self.graph.add(pkg_ext_ref_type_triple) - - pkg_ext_ref_locator = Literal(pkg_ext_refs.locator) - pkg_ext_ref_locator_triple = ( - pkg_ext_ref_node, - self.spdx_namespace.referenceLocator, - pkg_ext_ref_locator, - ) - self.graph.add(pkg_ext_ref_locator_triple) - - if pkg_ext_refs.comment: - pkg_ext_ref_comment = Literal(pkg_ext_refs.comment) - pkg_ext_ref_comment_triple = ( - pkg_ext_ref_node, - RDFS.comment, - pkg_ext_ref_comment, - ) - self.graph.add(pkg_ext_ref_comment_triple) - - return pkg_ext_ref_node - - def pkg_ext_refs(self): - """ - Return a list of package external references. - """ - return [self.create_package_external_ref_node(ext_ref) for package in self.document.packages - for ext_ref in package.pkg_ext_refs] - - -class Writer( - CreationInfoWriter, - ReviewInfoWriter, - FileWriter, - PackageWriter, - PackageExternalRefWriter, - ExternalDocumentRefWriter, - AnnotationInfoWriter, - RelationshipInfoWriter, - SnippetWriter, -): - """ - Wrapper for other writers to write all fields of spdx.document.Document - Call `write()` to start writing. - """ - - def __init__(self, document, out): - """ - - document is spdx.document instance that will be written. - - out is a file-like object that will be written to. - """ - super(Writer, self).__init__(document, out) - - def create_doc(self): - """ - Add and return the root document node to graph. - """ - doc_node = URIRef("http://www.spdx.org/tools#SPDXRef-DOCUMENT") - # Doc type - self.graph.add((doc_node, RDF.type, self.spdx_namespace.SpdxDocument)) - # Version - vers_literal = Literal(str(self.document.version)) - self.graph.add((doc_node, self.spdx_namespace.specVersion, vers_literal)) - # Data license - data_lics = URIRef(self.document.data_license.url) - self.graph.add((doc_node, self.spdx_namespace.dataLicense, data_lics)) - if self.document.name: - doc_name = URIRef(self.document.name) - self.graph.add((doc_node, self.spdx_namespace.name, doc_name)) - return doc_node - - def write(self, doc_node=None): - if not doc_node: - doc_node = self.create_doc() - # Add creation info - creation_info_node = self.create_creation_info() - ci_triple = (doc_node, self.spdx_namespace.creationInfo, creation_info_node) - self.graph.add(ci_triple) - # Add review info - review_nodes = self.reviews() - for review in review_nodes: - self.graph.add((doc_node, self.spdx_namespace.reviewed, review)) - # Add external document references info - ext_doc_ref_nodes = self.ext_doc_refs() - for ext_doc_ref in ext_doc_ref_nodes: - ext_doc_ref_triple = ( - doc_node, - self.spdx_namespace.externalDocumentRef, - ext_doc_ref, - ) - self.graph.add(ext_doc_ref_triple) - # Add extracted licenses - licenses = map(self.create_extracted_license, self.document.extracted_licenses) - for lic in licenses: - self.graph.add( - (doc_node, self.spdx_namespace.hasExtractedLicensingInfo, lic) - ) - # Add files - files = self.files() - for file_node in files: - self.graph.add((doc_node, self.spdx_namespace.referencesFile, file_node)) - self.add_file_dependencies() - # Add package - for package_node in self.packages(): - package_triple = (doc_node, self.spdx_namespace.describesPackage, package_node) - self.graph.add(package_triple) - - # Add external package reference - for pkg_ext_ref_node in self.pkg_ext_refs(): - pkg_ext_ref_triple = ( - doc_node, - self.spdx_namespace.ExternalRef, - pkg_ext_ref_node, - ) - self.graph.add(pkg_ext_ref_triple) - - # Add relationship - for relate_node in self.relationships(): - relate_triple = (doc_node, self.spdx_namespace.relationship, relate_node) - self.graph.add(relate_triple) - - # Add snippet - snippet_nodes = self.snippets() - for snippet in snippet_nodes: - self.graph.add((doc_node, self.spdx_namespace.Snippet, snippet)) - - # normalize the graph to ensure that the sort order is stable - self.graph = to_isomorphic(self.graph) - - # Write file - self.graph.serialize(self.out, "pretty-xml", encoding="utf-8") - - -def write_document(document, out, validate=True): - """ - Write an SPDX RDF document. - - document - spdx.document instance. - - out - file like object that will be written to. - Optionally `validate` the document before writing and raise - InvalidDocumentError if document.validate returns False. - """ - - if validate: - messages = ErrorMessages() - messages = document.validate(messages) - if messages: - raise InvalidDocumentError(messages) - - writer = Writer(document, out) - writer.write() diff --git a/spdx/writers/tagvalue.py b/spdx/writers/tagvalue.py deleted file mode 100644 index 8e734bf7f..000000000 --- a/spdx/writers/tagvalue.py +++ /dev/null @@ -1,475 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 itertools import zip_longest -from typing import List, TextIO, Tuple, Dict - -from spdx import license, utils -from spdx import file as spdx_file -from spdx.document import Document -from spdx.file import File -from spdx.package import Package -from spdx.parsers.loggers import ErrorMessages -from spdx.relationship import Relationship -from spdx.snippet import Snippet -from spdx.version import Version - - -class InvalidDocumentError(Exception): - """ - Raised when attempting to write an invalid document. - """ - - pass - - -def write_separator(out): - out.write("\n") - - -def write_separators(out): - out.write("\n" * 2) - - -def format_verif_code(package): - if len(package.verif_exc_files) == 0: - return package.verif_code - else: - return "{0} ({1})".format(package.verif_code, ",".join(package.verif_exc_files)) - - -def write_value(tag, value, out): - out.write("{0}: {1}\n".format(tag, value)) - - -def write_range(tag, value, out): - out.write("{0}: {1}:{2}\n".format(tag, value[0], value[1])) - - -def write_text_value(tag, value, out): - if "\n" in value: - out.write("{0}: {1}\n".format(tag, value)) - else: - write_value(tag, value, out) - - -def write_creation_info(creation_info, out): - """ - Write the creation info to out. - """ - out.write("# Creation Info\n\n") - # Write sorted creators - for creator in sorted(creation_info.creators): - write_value("Creator", creator, out) - - # write created - write_value("Created", creation_info.created_iso_format, out) - # possible comment - if creation_info.has_comment: - write_text_value("CreatorComment", creation_info.comment, out) - - -def write_review(review, out): - """ - Write the fields of a single review to out. - """ - write_value("Reviewer", review.reviewer, out) - write_value("ReviewDate", review.review_date_iso_format, out) - if review.has_comment: - write_text_value("ReviewComment", review.comment, out) - - -def write_annotation(annotation, out): - """ - Write the fields of a single annotation to out. - """ - write_value("Annotator", annotation.annotator, out) - write_value("AnnotationDate", annotation.annotation_date_iso_format, out) - if annotation.has_comment: - write_text_value("AnnotationComment", annotation.comment, out) - write_value("AnnotationType", annotation.annotation_type, out) - if annotation.spdx_id: - write_value("SPDXREF", annotation.spdx_id, out) - - -def write_relationship(relationship_term, out): - """ - Write the fields of relationships to out. - """ - write_value("Relationship", relationship_term.relationship, out) - if relationship_term.has_comment: - write_text_value( - "RelationshipComment", relationship_term.relationship_comment, out - ) - - -def write_file_type(ftype, out): - write_value("FileType", ftype, out) - - -def write_file(spdx_file, out): - """ - Write a file fields to out. - """ - out.write("# File\n\n") - write_value("FileName", spdx_file.name, out) - if spdx_file.spdx_id: - write_value("SPDXID", spdx_file.spdx_id, out) - for file_type in spdx_file.file_types: - write_file_type(file_type.name, out) - for file_checksum in spdx_file.checksums.values(): - write_value("FileChecksum", file_checksum.to_tv(), out) - if spdx_file.has_optional_field("conc_lics"): - if isinstance( - spdx_file.conc_lics, (license.LicenseConjunction, license.LicenseDisjunction) - ): - write_value("LicenseConcluded", "({0})".format(spdx_file.conc_lics), out) - else: - write_value("LicenseConcluded", spdx_file.conc_lics, out) - - # write sorted list - for lics in sorted(spdx_file.licenses_in_file): - write_value("LicenseInfoInFile", lics, out) - - if spdx_file.has_optional_field("copyright"): - if isinstance(spdx_file.copyright, str): - write_text_value("FileCopyrightText", spdx_file.copyright, out) - else: - write_value("FileCopyrightText", spdx_file.copyright, out) - - if spdx_file.has_optional_field("license_comment"): - write_text_value("LicenseComments", spdx_file.license_comment, out) - - if spdx_file.has_optional_field("attribution_text"): - write_text_value("FileAttributionText", spdx_file.attribution_text, out) - - if spdx_file.has_optional_field("comment"): - write_text_value("FileComment", spdx_file.comment, out) - - if spdx_file.has_optional_field("notice"): - write_text_value("FileNotice", spdx_file.notice, out) - - for contributor in sorted(spdx_file.contributors): - write_value("FileContributor", contributor, out) - - for dependency in sorted(spdx_file.dependencies): - write_value("FileDependency", dependency, out) - - names = spdx_file.artifact_of_project_name - homepages = spdx_file.artifact_of_project_home - uris = spdx_file.artifact_of_project_uri - - for name, homepage, uri in sorted(zip_longest(names, homepages, uris)): - write_value("ArtifactOfProjectName", name, out) - if homepage is not None: - write_value("ArtifactOfProjectHomePage", homepage, out) - if uri is not None: - write_value("ArtifactOfProjectURI", uri, out) - - -def write_snippet(snippet, out): - """ - Write snippet fields to out. - """ - out.write("# Snippet\n\n") - write_value("SnippetSPDXID", snippet.spdx_id, out) - write_value("SnippetFromFileSPDXID", snippet.snip_from_file_spdxid, out) - if snippet.has_optional_field("copyright"): - write_text_value("SnippetCopyrightText", snippet.copyright, out) - write_range("SnippetByteRange", snippet.byte_range, out) - if snippet.has_optional_field("line_range"): - write_range("SnippetLineRange", snippet.line_range, out) - if snippet.has_optional_field("name"): - write_value("SnippetName", snippet.name, out) - if snippet.has_optional_field("comment"): - write_text_value("SnippetComment", snippet.comment, out) - if snippet.has_optional_field("license_comment"): - write_text_value("SnippetLicenseComments", snippet.license_comment, out) - if snippet.has_optional_field("attribution_text"): - write_text_value("SnippetAttributionText", snippet.attribution_text, out) - if snippet.has_optional_field("conc_lics"): - if isinstance( - snippet.conc_lics, (license.LicenseConjunction, license.LicenseDisjunction) - ): - write_value("SnippetLicenseConcluded", "({0})".format(snippet.conc_lics), out) - else: - write_value("SnippetLicenseConcluded", snippet.conc_lics, out) - # Write sorted list - for lics in sorted(snippet.licenses_in_snippet): - write_value("LicenseInfoInSnippet", lics, out) - - -def write_package(package, out): - """ - Write a package fields to out. - """ - out.write("# Package\n\n") - if package.name: - write_value("PackageName", package.name, out) - if package.spdx_id: - write_value("SPDXID", package.spdx_id, out) - if package.has_optional_field("version"): - write_value("PackageVersion", package.version, out) - write_value("PackageDownloadLocation", package.download_location, out) - - if package.has_optional_field("files_analyzed"): - write_value("FilesAnalyzed", package.files_analyzed, out) - - if package.has_optional_field("summary"): - write_text_value("PackageSummary", package.summary, out) - - if package.has_optional_field("attribution_text"): - write_text_value("PackageAttributionText", package.attribution_text, out) - - if package.has_optional_field("source_info"): - write_text_value("PackageSourceInfo", package.source_info, out) - - if package.has_optional_field("file_name"): - write_value("PackageFileName", package.file_name, out) - - if package.has_optional_field("supplier"): - write_value("PackageSupplier", package.supplier, out) - - if package.has_optional_field("originator"): - write_value("PackageOriginator", package.originator, out) - - for package_checksum in package.checksums.values(): - write_value("PackageChecksum", package_checksum.to_tv(), out) - - if package.has_optional_field("verif_code"): - write_value("PackageVerificationCode", format_verif_code(package), out) - - if package.has_optional_field("description"): - write_text_value("PackageDescription", package.description, out) - - if package.has_optional_field("comment"): - write_text_value("PackageComment", package.comment, out) - - if package.has_optional_field("license_declared"): - if isinstance( - package.license_declared, - (license.LicenseConjunction, license.LicenseDisjunction), - ): - write_value( - "PackageLicenseDeclared", "({0})".format(package.license_declared), out - ) - else: - write_value("PackageLicenseDeclared", package.license_declared, out) - - if package.has_optional_field("conc_lics"): - if isinstance( - package.conc_lics, (license.LicenseConjunction, license.LicenseDisjunction) - ): - write_value("PackageLicenseConcluded", "({0})".format(package.conc_lics), out) - else: - write_value("PackageLicenseConcluded", package.conc_lics, out) - - # Write sorted list of licenses. - for lics in sorted(package.licenses_from_files): - write_value("PackageLicenseInfoFromFiles", lics, out) - - if package.has_optional_field("license_comment"): - write_text_value("PackageLicenseComments", package.license_comment, out) - - if package.has_optional_field("cr_text"): - if isinstance(package.cr_text, str): - write_text_value("PackageCopyrightText", package.cr_text, out) - else: - write_value("PackageCopyrightText", package.cr_text, out) - - if package.has_optional_field("homepage"): - write_value("PackageHomePage", package.homepage, out) - - for pkg_ref in package.pkg_ext_refs: - pkg_ref_str = " ".join( - [pkg_ref.category, pkg_ref.pkg_ext_ref_type, pkg_ref.locator] - ) - write_value("ExternalRef", pkg_ref_str, out) - if pkg_ref.comment: - write_text_value("ExternalRefComment", pkg_ref.comment, out) - - if package.has_optional_field("primary_package_purpose"): - write_value("PrimaryPackagePurpose", package.primary_package_purpose.name.replace("_", "-"), out) - - if package.has_optional_field("built_date"): - write_value("BuiltDate", utils.datetime_iso_format(package.built_date), out) - - if package.has_optional_field("release_date"): - write_value("ReleaseDate", utils.datetime_iso_format(package.release_date), out) - - if package.has_optional_field("valid_until_date"): - write_value("ValidUntilDate", utils.datetime_iso_format(package.valid_until_date), out) - - -def write_extracted_licenses(lics, out): - """ - Write extracted licenses fields to out. - """ - write_value("LicenseID", lics.identifier, out) - - if lics.full_name is not None: - write_value("LicenseName", lics.full_name, out) - - if lics.comment is not None: - write_text_value("LicenseComment", lics.comment, out) - - for xref in sorted(lics.cross_ref): - write_value("LicenseCrossReference", xref, out) - - write_text_value("ExtractedText", lics.text, out) - - -def write_document(document, out, validate=True): - """ - Write an SPDX tag value document. - - document - spdx.document instance. - - out - file like object that will be written to. - Optionally `validate` the document before writing and raise - InvalidDocumentError if document.validate returns False. - """ - messages = ErrorMessages() - messages = document.validate(messages) - if validate and messages: - raise InvalidDocumentError(messages) - - # Write out document information - out.write("# Document Information\n\n") - write_value("SPDXVersion", str(document.version), out) - write_value("DataLicense", document.data_license.identifier, out) - if document.namespace: - write_value("DocumentNamespace", document.namespace, out) - if document.name: - write_value("DocumentName", document.name, out) - if document.creation_info.license_list_version: - version: Version = document.creation_info.license_list_version - write_value("LicenseListVersion", str(version.major) + "." + str(version.minor), out) - write_value("SPDXID", "SPDXRef-DOCUMENT", out) - if document.has_comment: - write_text_value("DocumentComment", document.comment, out) - for doc_ref in document.ext_document_references: - doc_ref_str = " ".join( - [ - doc_ref.external_document_id, - doc_ref.spdx_document_uri, - doc_ref.checksum.identifier.name + ": " + doc_ref.checksum.value, - ] - ) - write_value("ExternalDocumentRef", doc_ref_str, out) - write_separators(out) - # Write out creation info - write_creation_info(document.creation_info, out) - write_separators(out) - - # Write sorted reviews - if document.reviews: - out.write("# Reviews\n\n") - for review in sorted(document.reviews): - write_review(review, out) - write_separator(out) - write_separator(out) - - # Write sorted annotations - if document.annotations: - out.write("# Annotations\n\n") - for annotation in sorted(document.annotations): - write_annotation(annotation, out) - write_separator(out) - write_separator(out) - - relationships_to_write, contained_files_by_package_id = scan_relationships(document.relationships, - document.packages, document.files) - contained_snippets_by_file_id = determine_files_containing_snippets(document.snippet, document.files) - packaged_file_ids = [file.spdx_id for files_list in contained_files_by_package_id.values() - for file in files_list] - filed_snippet_ids = [snippet.spdx_id for snippets_list in contained_snippets_by_file_id.values() - for snippet in snippets_list] - - # Write Relationships - if relationships_to_write: - out.write("# Relationships\n\n") - for relationship in relationships_to_write: - write_relationship(relationship, out) - write_separators(out) - - # Write snippet info - for snippet in document.snippet: - if snippet.spdx_id not in filed_snippet_ids: - write_snippet(snippet, out) - write_separators(out) - - # Write file info - for file in document.files: - if file.spdx_id not in packaged_file_ids: - write_file(file, out) - write_separators(out) - if file.spdx_id in contained_snippets_by_file_id: - write_snippets(contained_snippets_by_file_id[file.spdx_id], out) - - # Write out package info - for package in document.packages: - write_package(package, out) - write_separators(out) - if package.spdx_id in contained_files_by_package_id: - for file in contained_files_by_package_id[package.spdx_id]: - write_file(file, out) - write_separators(out) - if file.spdx_id in contained_snippets_by_file_id: - write_snippets(contained_snippets_by_file_id[file.spdx_id], out) - break - - if document.extracted_licenses: - out.write("# Extracted Licenses\n\n") - for lic in sorted(document.extracted_licenses): - write_extracted_licenses(lic, out) - write_separator(out) - write_separator(out) - - -def write_snippets(snippets_to_write: List, out: TextIO) -> None: - for snippet in snippets_to_write: - write_snippet(snippet, out) - write_separators(out) - - -def scan_relationships(relationships: List[Relationship], packages: List[Package], files: List[File]) \ - -> Tuple[List, Dict]: - contained_files_by_package_id = dict() - relationships_to_write = [] - files_by_spdx_id = {file.spdx_id: file for file in files} - packages_spdx_ids = [package.spdx_id for package in packages] - for relationship in relationships: - if relationship.relationship_type == "CONTAINS" and \ - relationship.spdx_element_id in packages_spdx_ids and \ - relationship.related_spdx_element in files_by_spdx_id.keys(): - contained_files_by_package_id.setdefault(relationship.spdx_element_id, []).append( - files_by_spdx_id[relationship.related_spdx_element]) - if relationship.has_comment: - relationships_to_write.append(relationship) - elif relationship.relationship_type == "CONTAINED_BY" and \ - relationship.related_spdx_element in packages_spdx_ids and \ - relationship.spdx_element_id in files_by_spdx_id: - contained_files_by_package_id.setdefault(relationship.related_spdx_element, []).append( - files_by_spdx_id[relationship.spdx_element_id]) - if relationship.has_comment: - relationships_to_write.append(relationship) - else: - relationships_to_write.append(relationship) - - return relationships_to_write, contained_files_by_package_id - - -def determine_files_containing_snippets(snippets: List[Snippet], files: List[File]) -> Dict: - contained_snippets_by_file_id = dict() - for snippet in snippets: - if snippet.snip_from_file_spdxid in [file.spdx_id for file in files]: - contained_snippets_by_file_id.setdefault(snippet.snip_from_file_spdxid, []).append(snippet) - - return contained_snippets_by_file_id diff --git a/spdx/writers/write_anything.py b/spdx/writers/write_anything.py deleted file mode 100644 index 5e479ef6e..000000000 --- a/spdx/writers/write_anything.py +++ /dev/null @@ -1,38 +0,0 @@ -# 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 spdx.writers import json -from spdx.writers import yaml -from spdx.writers import rdf -from spdx.writers import xml -from spdx.writers import tagvalue -from spdx.parsers.builderexceptions import FileTypeError - - -def write_file(doc, fn, validate=True): - out_mode = "w" - if fn.endswith(".rdf") or fn.endswith(".rdf.xml"): - writer_module = rdf - out_mode = "wb" - elif fn.endswith(".tag") or fn.endswith(".spdx"): - writer_module = tagvalue - elif fn.endswith(".json"): - writer_module = json - elif fn.endswith(".xml"): - writer_module = xml - elif fn.endswith(".yaml"): - writer_module = yaml - else: - raise FileTypeError("FileType Not Supported") - - with open(fn, out_mode) as out: - p = writer_module.write_document(doc, out, validate) diff --git a/spdx/writers/xml.py b/spdx/writers/xml.py deleted file mode 100644 index 14bbd4b99..000000000 --- a/spdx/writers/xml.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) the SPDX tools authors -# 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 xmltodict - -from spdx.writers.tagvalue import InvalidDocumentError -from spdx.writers.jsonyamlxml import Writer -from spdx.parsers.loggers import ErrorMessages - - -def write_document(document, out, validate=True): - - if validate: - messages = ErrorMessages() - messages = document.validate(messages) - if messages: - raise InvalidDocumentError(messages) - - writer = Writer(document) - document_object = {"Document": writer.create_document()} - - xmltodict.unparse(document_object, out, encoding="utf-8", pretty=True) diff --git a/spdx/writers/yaml.py b/spdx/writers/yaml.py deleted file mode 100644 index 2ccc6884d..000000000 --- a/spdx/writers/yaml.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) Xavier Figueroa -# 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 yaml - -from spdx.writers.tagvalue import InvalidDocumentError -from spdx.writers.jsonyamlxml import Writer -from spdx.parsers.loggers import ErrorMessages - - -def write_document(document, out, validate=True): - - if validate: - messages = ErrorMessages() - messages = document.validate(messages) - if messages: - raise InvalidDocumentError(messages) - - writer = Writer(document) - document_object = writer.create_document() - - yaml.safe_dump(document_object, out, indent=2, explicit_start=True, encoding='utf-8') diff --git a/spdx/config.py b/src/config.py similarity index 98% rename from spdx/config.py rename to src/config.py index a10582043..e48aacdfa 100644 --- a/spdx/config.py +++ b/src/config.py @@ -13,8 +13,7 @@ import json import os -from spdx.version import Version - +from src.model.version import Version _base_dir = os.path.dirname(__file__) _licenses = os.path.join(_base_dir, "licenses.json") diff --git a/src/exceptions.json b/src/exceptions.json new file mode 100644 index 000000000..709a7d614 --- /dev/null +++ b/src/exceptions.json @@ -0,0 +1,408 @@ +{ + "licenseListVersion": "3.6", + "releaseDate": "2019-07-10", + "exceptions": [ + { + "reference": "./Libtool-exception.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Libtool-exception.json", + "referenceNumber": "1", + "name": "Libtool Exception", + "seeAlso": [ + "http://git.savannah.gnu.org/cgit/libtool.git/tree/m4/libtool.m4" + ], + "licenseExceptionId": "Libtool-exception" + }, + { + "reference": "./Linux-syscall-note.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Linux-syscall-note.json", + "referenceNumber": "2", + "name": "Linux Syscall Note", + "seeAlso": [ + "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/COPYING" + ], + "licenseExceptionId": "Linux-syscall-note" + }, + { + "reference": "./Autoconf-exception-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Autoconf-exception-3.0.json", + "referenceNumber": "3", + "name": "Autoconf exception 3.0", + "seeAlso": [ + "http://www.gnu.org/licenses/autoconf-exception-3.0.html" + ], + "licenseExceptionId": "Autoconf-exception-3.0" + }, + { + "reference": "./OCCT-exception-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OCCT-exception-1.0.json", + "referenceNumber": "4", + "name": "Open CASCADE Exception 1.0", + "seeAlso": [ + "http://www.opencascade.com/content/licensing" + ], + "licenseExceptionId": "OCCT-exception-1.0" + }, + { + "reference": "./openvpn-openssl-exception.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/openvpn-openssl-exception.json", + "referenceNumber": "5", + "name": "OpenVPN OpenSSL Exception", + "seeAlso": [ + "http://openvpn.net/index.php/license.html" + ], + "licenseExceptionId": "openvpn-openssl-exception" + }, + { + "reference": "./gnu-javamail-exception.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/gnu-javamail-exception.json", + "referenceNumber": "6", + "name": "GNU JavaMail exception", + "seeAlso": [ + "http://www.gnu.org/software/classpathx/javamail/javamail.html" + ], + "licenseExceptionId": "gnu-javamail-exception" + }, + { + "reference": "./OpenJDK-assembly-exception-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OpenJDK-assembly-exception-1.0.json", + "referenceNumber": "7", + "name": "OpenJDK Assembly exception 1.0", + "seeAlso": [ + "http://openjdk.java.net/legal/assembly-exception.html" + ], + "licenseExceptionId": "OpenJDK-assembly-exception-1.0" + }, + { + "reference": "./Bison-exception-2.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Bison-exception-2.2.json", + "referenceNumber": "8", + "name": "Bison exception 2.2", + "seeAlso": [ + "http://git.savannah.gnu.org/cgit/bison.git/tree/data/yacc.c?id\u003d193d7c7054ba7197b0789e14965b739162319b5e#n141" + ], + "licenseExceptionId": "Bison-exception-2.2" + }, + { + "reference": "./i2p-gpl-java-exception.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/i2p-gpl-java-exception.json", + "referenceNumber": "9", + "name": "i2p GPL+Java Exception", + "seeAlso": [ + "http://geti2p.net/en/get-involved/develop/licenses#java_exception" + ], + "licenseExceptionId": "i2p-gpl-java-exception" + }, + { + "reference": "./Universal-FOSS-exception-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Universal-FOSS-exception-1.0.json", + "referenceNumber": "10", + "name": "Universal FOSS Exception, Version 1.0", + "seeAlso": [ + "https://oss.oracle.com/licenses/universal-foss-exception/" + ], + "licenseExceptionId": "Universal-FOSS-exception-1.0" + }, + { + "reference": "./Qt-LGPL-exception-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Qt-LGPL-exception-1.1.json", + "referenceNumber": "11", + "name": "Qt LGPL exception 1.1", + "seeAlso": [ + "http://code.qt.io/cgit/qt/qtbase.git/tree/LGPL_EXCEPTION.txt" + ], + "licenseExceptionId": "Qt-LGPL-exception-1.1" + }, + { + "reference": "./389-exception.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/389-exception.json", + "referenceNumber": "12", + "name": "389 Directory Server Exception", + "seeAlso": [ + "http://directory.fedoraproject.org/wiki/GPL_Exception_License_Text" + ], + "licenseExceptionId": "389-exception" + }, + { + "reference": "./Classpath-exception-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Classpath-exception-2.0.json", + "referenceNumber": "13", + "name": "Classpath exception 2.0", + "seeAlso": [ + "http://www.gnu.org/software/classpath/license.html", + "https://fedoraproject.org/wiki/Licensing/GPL_Classpath_Exception" + ], + "licenseExceptionId": "Classpath-exception-2.0" + }, + { + "reference": "./Fawkes-Runtime-exception.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Fawkes-Runtime-exception.json", + "referenceNumber": "14", + "name": "Fawkes Runtime Exception", + "seeAlso": [ + "http://www.fawkesrobotics.org/about/license/" + ], + "licenseExceptionId": "Fawkes-Runtime-exception" + }, + { + "reference": "./PS-or-PDF-font-exception-20170817.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/PS-or-PDF-font-exception-20170817.json", + "referenceNumber": "15", + "name": "PS/PDF font exception (2017-08-17)", + "seeAlso": [ + "https://github.com/ArtifexSoftware/urw-base35-fonts/blob/65962e27febc3883a17e651cdb23e783668c996f/LICENSE" + ], + "licenseExceptionId": "PS-or-PDF-font-exception-20170817" + }, + { + "reference": "./Qt-GPL-exception-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Qt-GPL-exception-1.0.json", + "referenceNumber": "16", + "name": "Qt GPL exception 1.0", + "seeAlso": [ + "http://code.qt.io/cgit/qt/qtbase.git/tree/LICENSE.GPL3-EXCEPT" + ], + "licenseExceptionId": "Qt-GPL-exception-1.0" + }, + { + "reference": "./LZMA-exception.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/LZMA-exception.json", + "referenceNumber": "17", + "name": "LZMA exception", + "seeAlso": [ + "http://nsis.sourceforge.net/Docs/AppendixI.html#I.6" + ], + "licenseExceptionId": "LZMA-exception" + }, + { + "reference": "./freertos-exception-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/freertos-exception-2.0.json", + "referenceNumber": "18", + "name": "FreeRTOS Exception 2.0", + "seeAlso": [ + "https://web.archive.org/web/20060809182744/http://www.freertos.org/a00114.html" + ], + "licenseExceptionId": "freertos-exception-2.0" + }, + { + "reference": "./Qwt-exception-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Qwt-exception-1.0.json", + "referenceNumber": "19", + "name": "Qwt exception 1.0", + "seeAlso": [ + "http://qwt.sourceforge.net/qwtlicense.html" + ], + "licenseExceptionId": "Qwt-exception-1.0" + }, + { + "reference": "./CLISP-exception-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CLISP-exception-2.0.json", + "referenceNumber": "20", + "name": "CLISP exception 2.0", + "seeAlso": [ + "http://sourceforge.net/p/clisp/clisp/ci/default/tree/COPYRIGHT" + ], + "licenseExceptionId": "CLISP-exception-2.0" + }, + { + "reference": "./FLTK-exception.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/FLTK-exception.json", + "referenceNumber": "21", + "name": "FLTK exception", + "seeAlso": [ + "http://www.fltk.org/COPYING.php" + ], + "licenseExceptionId": "FLTK-exception" + }, + { + "reference": "./Bootloader-exception.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Bootloader-exception.json", + "referenceNumber": "22", + "name": "Bootloader Distribution Exception", + "seeAlso": [ + "https://github.com/pyinstaller/pyinstaller/blob/develop/COPYING.txt" + ], + "licenseExceptionId": "Bootloader-exception" + }, + { + "reference": "./Nokia-Qt-exception-1.1.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "http://spdx.org/licenses/Nokia-Qt-exception-1.1.json", + "referenceNumber": "23", + "name": "Nokia Qt LGPL exception 1.1", + "seeAlso": [ + "https://www.keepassx.org/dev/projects/keepassx/repository/revisions/b8dfb9cc4d5133e0f09cd7533d15a4f1c19a40f2/entry/LICENSE.NOKIA-LGPL-EXCEPTION" + ], + "licenseExceptionId": "Nokia-Qt-exception-1.1" + }, + { + "reference": "./LLVM-exception.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/LLVM-exception.json", + "referenceNumber": "24", + "name": "LLVM Exception", + "seeAlso": [ + "http://llvm.org/foundation/relicensing/LICENSE.txt" + ], + "licenseExceptionId": "LLVM-exception" + }, + { + "reference": "./WxWindows-exception-3.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/WxWindows-exception-3.1.json", + "referenceNumber": "25", + "name": "WxWindows Library Exception 3.1", + "seeAlso": [ + "http://www.opensource.org/licenses/WXwindows" + ], + "licenseExceptionId": "WxWindows-exception-3.1" + }, + { + "reference": "./DigiRule-FOSS-exception.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/DigiRule-FOSS-exception.json", + "referenceNumber": "26", + "name": "DigiRule FOSS License Exception", + "seeAlso": [ + "http://www.digirulesolutions.com/drupal/foss" + ], + "licenseExceptionId": "DigiRule-FOSS-exception" + }, + { + "reference": "./Swift-exception.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Swift-exception.json", + "referenceNumber": "27", + "name": "Swift Exception", + "seeAlso": [ + "https://swift.org/LICENSE.txt", + "https://github.com/apple/swift-package-manager/blob/7ab2275f447a5eb37497ed63a9340f8a6d1e488b/LICENSE.txt#L205" + ], + "licenseExceptionId": "Swift-exception" + }, + { + "reference": "./GCC-exception-3.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/GCC-exception-3.1.json", + "referenceNumber": "28", + "name": "GCC Runtime Library exception 3.1", + "seeAlso": [ + "http://www.gnu.org/licenses/gcc-exception-3.1.html" + ], + "licenseExceptionId": "GCC-exception-3.1" + }, + { + "reference": "./eCos-exception-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/eCos-exception-2.0.json", + "referenceNumber": "29", + "name": "eCos exception 2.0", + "seeAlso": [ + "http://ecos.sourceware.org/license-overview.html" + ], + "licenseExceptionId": "eCos-exception-2.0" + }, + { + "reference": "./Autoconf-exception-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Autoconf-exception-2.0.json", + "referenceNumber": "30", + "name": "Autoconf exception 2.0", + "seeAlso": [ + "http://ac-archive.sourceforge.net/doc/copyright.html", + "http://ftp.gnu.org/gnu/autoconf/autoconf-2.59.tar.gz" + ], + "licenseExceptionId": "Autoconf-exception-2.0" + }, + { + "reference": "./GPL-CC-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/GPL-CC-1.0.json", + "referenceNumber": "31", + "name": "GPL Cooperation Commitment 1.0", + "seeAlso": [ + "https://github.com/gplcc/gplcc/blob/master/Project/COMMITMENT", + "https://gplcc.github.io/gplcc/Project/README-PROJECT.html" + ], + "licenseExceptionId": "GPL-CC-1.0" + }, + { + "reference": "./Font-exception-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Font-exception-2.0.json", + "referenceNumber": "32", + "name": "Font exception 2.0", + "seeAlso": [ + "http://www.gnu.org/licenses/gpl-faq.html#FontException" + ], + "licenseExceptionId": "Font-exception-2.0" + }, + { + "reference": "./u-boot-exception-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/u-boot-exception-2.0.json", + "referenceNumber": "33", + "name": "U-Boot exception 2.0", + "seeAlso": [ + "http://git.denx.de/?p\u003du-boot.git;a\u003dblob;f\u003dLicenses/Exceptions" + ], + "licenseExceptionId": "u-boot-exception-2.0" + }, + { + "reference": "./GCC-exception-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/GCC-exception-2.0.json", + "referenceNumber": "34", + "name": "GCC Runtime Library exception 2.0", + "seeAlso": [ + "https://gcc.gnu.org/git/?p\u003dgcc.git;a\u003dblob;f\u003dgcc/libgcc1.c;h\u003d762f5143fc6eed57b6797c82710f3538aa52b40b;hb\u003dcb143a3ce4fb417c68f5fa2691a1b1b1053dfba9#l10" + ], + "licenseExceptionId": "GCC-exception-2.0" + }, + { + "reference": "./mif-exception.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/mif-exception.json", + "referenceNumber": "35", + "name": "Macros and Inline Functions Exception", + "seeAlso": [ + "http://www.scs.stanford.edu/histar/src/lib/cppsup/exception", + "http://dev.bertos.org/doxygen/", + "https://www.threadingbuildingblocks.org/licensing" + ], + "licenseExceptionId": "mif-exception" + }, + { + "reference": "./OCaml-LGPL-linking-exception.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OCaml-LGPL-linking-exception.json", + "referenceNumber": "36", + "name": "OCaml LGPL Linking Exception", + "seeAlso": [ + "https://caml.inria.fr/ocaml/license.en.html" + ], + "licenseExceptionId": "OCaml-LGPL-linking-exception" + } + ] +} diff --git a/src/licenses.json b/src/licenses.json new file mode 100644 index 000000000..5a78e2b05 --- /dev/null +++ b/src/licenses.json @@ -0,0 +1,4974 @@ +{ + "licenseListVersion": "3.6", + "licenses": [ + { + "reference": "./0BSD.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/0BSD.json", + "referenceNumber": "319", + "name": "BSD Zero Clause License", + "licenseId": "0BSD", + "seeAlso": [ + "http://landley.net/toybox/license.html" + ], + "isOsiApproved": true + }, + { + "reference": "./AAL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/AAL.json", + "referenceNumber": "21", + "name": "Attribution Assurance License", + "licenseId": "AAL", + "seeAlso": [ + "https://opensource.org/licenses/attribution" + ], + "isOsiApproved": true + }, + { + "reference": "./ADSL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/ADSL.json", + "referenceNumber": "19", + "name": "Amazon Digital Services License", + "licenseId": "ADSL", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/AmazonDigitalServicesLicense" + ], + "isOsiApproved": false + }, + { + "reference": "./AFL-1.1.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/AFL-1.1.json", + "referenceNumber": "118", + "name": "Academic Free License v1.1", + "licenseId": "AFL-1.1", + "seeAlso": [ + "http://opensource.linux-mirror.org/licenses/afl-1.1.txt", + "http://wayback.archive.org/web/20021004124254/http://www.opensource.org/licenses/academic.php" + ], + "isOsiApproved": true + }, + { + "reference": "./AFL-1.2.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/AFL-1.2.json", + "referenceNumber": "136", + "name": "Academic Free License v1.2", + "licenseId": "AFL-1.2", + "seeAlso": [ + "http://opensource.linux-mirror.org/licenses/afl-1.2.txt", + "http://wayback.archive.org/web/20021204204652/http://www.opensource.org/licenses/academic.php" + ], + "isOsiApproved": true + }, + { + "reference": "./AFL-2.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/AFL-2.0.json", + "referenceNumber": "115", + "name": "Academic Free License v2.0", + "licenseId": "AFL-2.0", + "seeAlso": [ + "http://wayback.archive.org/web/20060924134533/http://www.opensource.org/licenses/afl-2.0.txt" + ], + "isOsiApproved": true + }, + { + "reference": "./AFL-2.1.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/AFL-2.1.json", + "referenceNumber": "251", + "name": "Academic Free License v2.1", + "licenseId": "AFL-2.1", + "seeAlso": [ + "http://opensource.linux-mirror.org/licenses/afl-2.1.txt" + ], + "isOsiApproved": true + }, + { + "reference": "./AFL-3.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/AFL-3.0.json", + "referenceNumber": "216", + "name": "Academic Free License v3.0", + "licenseId": "AFL-3.0", + "seeAlso": [ + "http://www.rosenlaw.com/AFL3.0.htm", + "https://opensource.org/licenses/afl-3.0" + ], + "isOsiApproved": true + }, + { + "reference": "./AGPL-1.0.html", + "isDeprecatedLicenseId": true, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/AGPL-1.0.json", + "referenceNumber": "335", + "name": "Affero General Public License v1.0", + "licenseId": "AGPL-1.0", + "seeAlso": [ + "http://www.affero.org/oagpl.html" + ], + "isOsiApproved": false + }, + { + "reference": "./AGPL-1.0-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/AGPL-1.0-only.json", + "referenceNumber": "384", + "name": "Affero General Public License v1.0 only", + "licenseId": "AGPL-1.0-only", + "seeAlso": [ + "http://www.affero.org/oagpl.html" + ], + "isOsiApproved": false + }, + { + "reference": "./AGPL-1.0-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/AGPL-1.0-or-later.json", + "referenceNumber": "332", + "name": "Affero General Public License v1.0 or later", + "licenseId": "AGPL-1.0-or-later", + "seeAlso": [ + "http://www.affero.org/oagpl.html" + ], + "isOsiApproved": false + }, + { + "reference": "./AGPL-3.0.html", + "isDeprecatedLicenseId": true, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/AGPL-3.0.json", + "referenceNumber": "229", + "name": "GNU Affero General Public License v3.0", + "licenseId": "AGPL-3.0", + "seeAlso": [ + "https://www.gnu.org/licenses/agpl.txt", + "https://opensource.org/licenses/AGPL-3.0" + ], + "isOsiApproved": true + }, + { + "reference": "./AGPL-3.0-only.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/AGPL-3.0-only.json", + "referenceNumber": "95", + "name": "GNU Affero General Public License v3.0 only", + "licenseId": "AGPL-3.0-only", + "seeAlso": [ + "https://www.gnu.org/licenses/agpl.txt", + "https://opensource.org/licenses/AGPL-3.0" + ], + "isOsiApproved": true + }, + { + "reference": "./AGPL-3.0-or-later.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/AGPL-3.0-or-later.json", + "referenceNumber": "155", + "name": "GNU Affero General Public License v3.0 or later", + "licenseId": "AGPL-3.0-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/agpl.txt", + "https://opensource.org/licenses/AGPL-3.0" + ], + "isOsiApproved": true + }, + { + "reference": "./AMDPLPA.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/AMDPLPA.json", + "referenceNumber": "33", + "name": "AMD\u0027s plpa_map.c License", + "licenseId": "AMDPLPA", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/AMD_plpa_map_License" + ], + "isOsiApproved": false + }, + { + "reference": "./AML.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/AML.json", + "referenceNumber": "148", + "name": "Apple MIT License", + "licenseId": "AML", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Apple_MIT_License" + ], + "isOsiApproved": false + }, + { + "reference": "./AMPAS.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/AMPAS.json", + "referenceNumber": "191", + "name": "Academy of Motion Picture Arts and Sciences BSD", + "licenseId": "AMPAS", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/BSD#AMPASBSD" + ], + "isOsiApproved": false + }, + { + "reference": "./ANTLR-PD.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/ANTLR-PD.json", + "referenceNumber": "395", + "name": "ANTLR Software Rights Notice", + "licenseId": "ANTLR-PD", + "seeAlso": [ + "http://www.antlr2.org/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./APAFML.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/APAFML.json", + "referenceNumber": "195", + "name": "Adobe Postscript AFM License", + "licenseId": "APAFML", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/AdobePostscriptAFM" + ], + "isOsiApproved": false + }, + { + "reference": "./APL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/APL-1.0.json", + "referenceNumber": "252", + "name": "Adaptive Public License 1.0", + "licenseId": "APL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/APL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./APSL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/APSL-1.0.json", + "referenceNumber": "354", + "name": "Apple Public Source License 1.0", + "licenseId": "APSL-1.0", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Apple_Public_Source_License_1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./APSL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/APSL-1.1.json", + "referenceNumber": "324", + "name": "Apple Public Source License 1.1", + "licenseId": "APSL-1.1", + "seeAlso": [ + "http://www.opensource.apple.com/source/IOSerialFamily/IOSerialFamily-7/APPLE_LICENSE" + ], + "isOsiApproved": true + }, + { + "reference": "./APSL-1.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/APSL-1.2.json", + "referenceNumber": "34", + "name": "Apple Public Source License 1.2", + "licenseId": "APSL-1.2", + "seeAlso": [ + "http://www.samurajdata.se/opensource/mirror/licenses/apsl.php" + ], + "isOsiApproved": true + }, + { + "reference": "./APSL-2.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/APSL-2.0.json", + "referenceNumber": "109", + "name": "Apple Public Source License 2.0", + "licenseId": "APSL-2.0", + "seeAlso": [ + "http://www.opensource.apple.com/license/apsl/" + ], + "isOsiApproved": true + }, + { + "reference": "./Abstyles.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Abstyles.json", + "referenceNumber": "80", + "name": "Abstyles License", + "licenseId": "Abstyles", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Abstyles" + ], + "isOsiApproved": false + }, + { + "reference": "./Adobe-2006.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Adobe-2006.json", + "referenceNumber": "285", + "name": "Adobe Systems Incorporated Source Code License Agreement", + "licenseId": "Adobe-2006", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/AdobeLicense" + ], + "isOsiApproved": false + }, + { + "reference": "./Adobe-Glyph.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Adobe-Glyph.json", + "referenceNumber": "107", + "name": "Adobe Glyph List License", + "licenseId": "Adobe-Glyph", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/MIT#AdobeGlyph" + ], + "isOsiApproved": false + }, + { + "reference": "./Afmparse.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Afmparse.json", + "referenceNumber": "42", + "name": "Afmparse License", + "licenseId": "Afmparse", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Afmparse" + ], + "isOsiApproved": false + }, + { + "reference": "./Aladdin.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Aladdin.json", + "referenceNumber": "258", + "name": "Aladdin Free Public License", + "licenseId": "Aladdin", + "seeAlso": [ + "http://pages.cs.wisc.edu/~ghost/doc/AFPL/6.01/Public.htm" + ], + "isOsiApproved": false + }, + { + "reference": "./Apache-1.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/Apache-1.0.json", + "referenceNumber": "237", + "name": "Apache License 1.0", + "licenseId": "Apache-1.0", + "seeAlso": [ + "http://www.apache.org/licenses/LICENSE-1.0" + ], + "isOsiApproved": false + }, + { + "reference": "./Apache-1.1.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/Apache-1.1.json", + "referenceNumber": "84", + "name": "Apache License 1.1", + "licenseId": "Apache-1.1", + "seeAlso": [ + "http://apache.org/licenses/LICENSE-1.1", + "https://opensource.org/licenses/Apache-1.1" + ], + "isOsiApproved": true + }, + { + "reference": "./Apache-2.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/Apache-2.0.json", + "referenceNumber": "26", + "name": "Apache License 2.0", + "licenseId": "Apache-2.0", + "seeAlso": [ + "http://www.apache.org/licenses/LICENSE-2.0", + "https://opensource.org/licenses/Apache-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "./Artistic-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Artistic-1.0.json", + "referenceNumber": "165", + "name": "Artistic License 1.0", + "licenseId": "Artistic-1.0", + "seeAlso": [ + "https://opensource.org/licenses/Artistic-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./Artistic-1.0-Perl.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Artistic-1.0-Perl.json", + "referenceNumber": "377", + "name": "Artistic License 1.0 (Perl)", + "licenseId": "Artistic-1.0-Perl", + "seeAlso": [ + "http://dev.perl.org/licenses/artistic.html" + ], + "isOsiApproved": true + }, + { + "reference": "./Artistic-1.0-cl8.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Artistic-1.0-cl8.json", + "referenceNumber": "13", + "name": "Artistic License 1.0 w/clause 8", + "licenseId": "Artistic-1.0-cl8", + "seeAlso": [ + "https://opensource.org/licenses/Artistic-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./Artistic-2.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/Artistic-2.0.json", + "referenceNumber": "189", + "name": "Artistic License 2.0", + "licenseId": "Artistic-2.0", + "seeAlso": [ + "http://www.perlfoundation.org/artistic_license_2_0", + "https://opensource.org/licenses/artistic-license-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "./BSD-1-Clause.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/BSD-1-Clause.json", + "referenceNumber": "358", + "name": "BSD 1-Clause License", + "licenseId": "BSD-1-Clause", + "seeAlso": [ + "https://svnweb.freebsd.org/base/head/include/ifaddrs.h?revision\u003d326823" + ], + "isOsiApproved": false + }, + { + "reference": "./BSD-2-Clause.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/BSD-2-Clause.json", + "referenceNumber": "325", + "name": "BSD 2-Clause \"Simplified\" License", + "licenseId": "BSD-2-Clause", + "seeAlso": [ + "https://opensource.org/licenses/BSD-2-Clause" + ], + "isOsiApproved": true + }, + { + "reference": "./BSD-2-Clause-FreeBSD.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/BSD-2-Clause-FreeBSD.json", + "referenceNumber": "121", + "name": "BSD 2-Clause FreeBSD License", + "licenseId": "BSD-2-Clause-FreeBSD", + "seeAlso": [ + "http://www.freebsd.org/copyright/freebsd-license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./BSD-2-Clause-NetBSD.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/BSD-2-Clause-NetBSD.json", + "referenceNumber": "381", + "name": "BSD 2-Clause NetBSD License", + "licenseId": "BSD-2-Clause-NetBSD", + "seeAlso": [ + "http://www.netbsd.org/about/redistribution.html#default" + ], + "isOsiApproved": false + }, + { + "reference": "./BSD-2-Clause-Patent.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/BSD-2-Clause-Patent.json", + "referenceNumber": "169", + "name": "BSD-2-Clause Plus Patent License", + "licenseId": "BSD-2-Clause-Patent", + "seeAlso": [ + "https://opensource.org/licenses/BSDplusPatent" + ], + "isOsiApproved": true + }, + { + "reference": "./BSD-3-Clause.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause.json", + "referenceNumber": "270", + "name": "BSD 3-Clause \"New\" or \"Revised\" License", + "licenseId": "BSD-3-Clause", + "seeAlso": [ + "https://opensource.org/licenses/BSD-3-Clause" + ], + "isOsiApproved": true + }, + { + "reference": "./BSD-3-Clause-Attribution.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-Attribution.json", + "referenceNumber": "39", + "name": "BSD with attribution", + "licenseId": "BSD-3-Clause-Attribution", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/BSD_with_Attribution" + ], + "isOsiApproved": false + }, + { + "reference": "./BSD-3-Clause-Clear.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-Clear.json", + "referenceNumber": "212", + "name": "BSD 3-Clause Clear License", + "licenseId": "BSD-3-Clause-Clear", + "seeAlso": [ + "http://labs.metacarta.com/license-explanation.html#license" + ], + "isOsiApproved": false + }, + { + "reference": "./BSD-3-Clause-LBNL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-LBNL.json", + "referenceNumber": "337", + "name": "Lawrence Berkeley National Labs BSD variant license", + "licenseId": "BSD-3-Clause-LBNL", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/LBNLBSD" + ], + "isOsiApproved": true + }, + { + "reference": "./BSD-3-Clause-No-Nuclear-License.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.json", + "referenceNumber": "12", + "name": "BSD 3-Clause No Nuclear License", + "licenseId": "BSD-3-Clause-No-Nuclear-License", + "seeAlso": [ + "http://download.oracle.com/otn-pub/java/licenses/bsd.txt?AuthParam\u003d1467140197_43d516ce1776bd08a58235a7785be1cc" + ], + "isOsiApproved": false + }, + { + "reference": "./BSD-3-Clause-No-Nuclear-License-2014.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.json", + "referenceNumber": "137", + "name": "BSD 3-Clause No Nuclear License 2014", + "licenseId": "BSD-3-Clause-No-Nuclear-License-2014", + "seeAlso": [ + "https://java.net/projects/javaeetutorial/pages/BerkeleyLicense" + ], + "isOsiApproved": false + }, + { + "reference": "./BSD-3-Clause-No-Nuclear-Warranty.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.json", + "referenceNumber": "44", + "name": "BSD 3-Clause No Nuclear Warranty", + "licenseId": "BSD-3-Clause-No-Nuclear-Warranty", + "seeAlso": [ + "https://jogamp.org/git/?p\u003dgluegen.git;a\u003dblob_plain;f\u003dLICENSE.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./BSD-3-Clause-Open-MPI.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-Open-MPI.json", + "referenceNumber": "349", + "name": "BSD 3-Clause Open MPI variant", + "licenseId": "BSD-3-Clause-Open-MPI", + "seeAlso": [ + "https://www.open-mpi.org/community/license.php", + "http://www.netlib.org/lapack/LICENSE.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./BSD-4-Clause.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/BSD-4-Clause.json", + "referenceNumber": "162", + "name": "BSD 4-Clause \"Original\" or \"Old\" License", + "licenseId": "BSD-4-Clause", + "seeAlso": [ + "http://directory.fsf.org/wiki/License:BSD_4Clause" + ], + "isOsiApproved": false + }, + { + "reference": "./BSD-4-Clause-UC.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/BSD-4-Clause-UC.json", + "referenceNumber": "203", + "name": "BSD-4-Clause (University of California-Specific)", + "licenseId": "BSD-4-Clause-UC", + "seeAlso": [ + "http://www.freebsd.org/copyright/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./BSD-Protection.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/BSD-Protection.json", + "referenceNumber": "119", + "name": "BSD Protection License", + "licenseId": "BSD-Protection", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/BSD_Protection_License" + ], + "isOsiApproved": false + }, + { + "reference": "./BSD-Source-Code.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/BSD-Source-Code.json", + "referenceNumber": "308", + "name": "BSD Source Code Attribution", + "licenseId": "BSD-Source-Code", + "seeAlso": [ + "https://github.com/robbiehanson/CocoaHTTPServer/blob/master/LICENSE.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./BSL-1.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/BSL-1.0.json", + "referenceNumber": "224", + "name": "Boost Software License 1.0", + "licenseId": "BSL-1.0", + "seeAlso": [ + "http://www.boost.org/LICENSE_1_0.txt", + "https://opensource.org/licenses/BSL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./Bahyph.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Bahyph.json", + "referenceNumber": "366", + "name": "Bahyph License", + "licenseId": "Bahyph", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Bahyph" + ], + "isOsiApproved": false + }, + { + "reference": "./Barr.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Barr.json", + "referenceNumber": "333", + "name": "Barr License", + "licenseId": "Barr", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Barr" + ], + "isOsiApproved": false + }, + { + "reference": "./Beerware.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Beerware.json", + "referenceNumber": "17", + "name": "Beerware License", + "licenseId": "Beerware", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Beerware", + "https://people.freebsd.org/~phk/" + ], + "isOsiApproved": false + }, + { + "reference": "./BitTorrent-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/BitTorrent-1.0.json", + "referenceNumber": "218", + "name": "BitTorrent Open Source License v1.0", + "licenseId": "BitTorrent-1.0", + "seeAlso": [ + "http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/licenses/BitTorrent?r1\u003d1.1\u0026r2\u003d1.1.1.1\u0026diff_format\u003ds" + ], + "isOsiApproved": false + }, + { + "reference": "./BitTorrent-1.1.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/BitTorrent-1.1.json", + "referenceNumber": "179", + "name": "BitTorrent Open Source License v1.1", + "licenseId": "BitTorrent-1.1", + "seeAlso": [ + "http://directory.fsf.org/wiki/License:BitTorrentOSL1.1" + ], + "isOsiApproved": false + }, + { + "reference": "./BlueOak-1.0.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/BlueOak-1.0.0.json", + "referenceNumber": "23", + "name": "Blue Oak Model License 1.0.0", + "licenseId": "BlueOak-1.0.0", + "seeAlso": [ + "https://blueoakcouncil.org/license/1.0.0" + ], + "isOsiApproved": false + }, + { + "reference": "./Borceux.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Borceux.json", + "referenceNumber": "311", + "name": "Borceux license", + "licenseId": "Borceux", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Borceux" + ], + "isOsiApproved": false + }, + { + "reference": "./CATOSL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CATOSL-1.1.json", + "referenceNumber": "262", + "name": "Computer Associates Trusted Open Source License 1.1", + "licenseId": "CATOSL-1.1", + "seeAlso": [ + "https://opensource.org/licenses/CATOSL-1.1" + ], + "isOsiApproved": true + }, + { + "reference": "./CC-BY-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-1.0.json", + "referenceNumber": "128", + "name": "Creative Commons Attribution 1.0 Generic", + "licenseId": "CC-BY-1.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by/1.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-2.0.json", + "referenceNumber": "232", + "name": "Creative Commons Attribution 2.0 Generic", + "licenseId": "CC-BY-2.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by/2.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-2.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-2.5.json", + "referenceNumber": "129", + "name": "Creative Commons Attribution 2.5 Generic", + "licenseId": "CC-BY-2.5", + "seeAlso": [ + "https://creativecommons.org/licenses/by/2.5/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-3.0.json", + "referenceNumber": "256", + "name": "Creative Commons Attribution 3.0 Unported", + "licenseId": "CC-BY-3.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by/3.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-4.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/CC-BY-4.0.json", + "referenceNumber": "330", + "name": "Creative Commons Attribution 4.0 International", + "licenseId": "CC-BY-4.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by/4.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-NC-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-1.0.json", + "referenceNumber": "130", + "name": "Creative Commons Attribution Non Commercial 1.0 Generic", + "licenseId": "CC-BY-NC-1.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc/1.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-NC-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-2.0.json", + "referenceNumber": "244", + "name": "Creative Commons Attribution Non Commercial 2.0 Generic", + "licenseId": "CC-BY-NC-2.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc/2.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-NC-2.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-2.5.json", + "referenceNumber": "1", + "name": "Creative Commons Attribution Non Commercial 2.5 Generic", + "licenseId": "CC-BY-NC-2.5", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc/2.5/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-NC-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-3.0.json", + "referenceNumber": "255", + "name": "Creative Commons Attribution Non Commercial 3.0 Unported", + "licenseId": "CC-BY-NC-3.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc/3.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-NC-4.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-4.0.json", + "referenceNumber": "186", + "name": "Creative Commons Attribution Non Commercial 4.0 International", + "licenseId": "CC-BY-NC-4.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc/4.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-NC-ND-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-1.0.json", + "referenceNumber": "59", + "name": "Creative Commons Attribution Non Commercial No Derivatives 1.0 Generic", + "licenseId": "CC-BY-NC-ND-1.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nd-nc/1.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-NC-ND-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-2.0.json", + "referenceNumber": "36", + "name": "Creative Commons Attribution Non Commercial No Derivatives 2.0 Generic", + "licenseId": "CC-BY-NC-ND-2.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-nd/2.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-NC-ND-2.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-2.5.json", + "referenceNumber": "158", + "name": "Creative Commons Attribution Non Commercial No Derivatives 2.5 Generic", + "licenseId": "CC-BY-NC-ND-2.5", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-nd/2.5/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-NC-ND-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-3.0.json", + "referenceNumber": "48", + "name": "Creative Commons Attribution Non Commercial No Derivatives 3.0 Unported", + "licenseId": "CC-BY-NC-ND-3.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-nd/3.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-NC-ND-4.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-4.0.json", + "referenceNumber": "281", + "name": "Creative Commons Attribution Non Commercial No Derivatives 4.0 International", + "licenseId": "CC-BY-NC-ND-4.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-NC-SA-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-1.0.json", + "referenceNumber": "178", + "name": "Creative Commons Attribution Non Commercial Share Alike 1.0 Generic", + "licenseId": "CC-BY-NC-SA-1.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-sa/1.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-NC-SA-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-2.0.json", + "referenceNumber": "81", + "name": "Creative Commons Attribution Non Commercial Share Alike 2.0 Generic", + "licenseId": "CC-BY-NC-SA-2.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-sa/2.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-NC-SA-2.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-2.5.json", + "referenceNumber": "62", + "name": "Creative Commons Attribution Non Commercial Share Alike 2.5 Generic", + "licenseId": "CC-BY-NC-SA-2.5", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-sa/2.5/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-NC-SA-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-3.0.json", + "referenceNumber": "22", + "name": "Creative Commons Attribution Non Commercial Share Alike 3.0 Unported", + "licenseId": "CC-BY-NC-SA-3.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-sa/3.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-NC-SA-4.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-4.0.json", + "referenceNumber": "47", + "name": "Creative Commons Attribution Non Commercial Share Alike 4.0 International", + "licenseId": "CC-BY-NC-SA-4.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-ND-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-1.0.json", + "referenceNumber": "50", + "name": "Creative Commons Attribution No Derivatives 1.0 Generic", + "licenseId": "CC-BY-ND-1.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nd/1.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-ND-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-2.0.json", + "referenceNumber": "287", + "name": "Creative Commons Attribution No Derivatives 2.0 Generic", + "licenseId": "CC-BY-ND-2.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nd/2.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-ND-2.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-2.5.json", + "referenceNumber": "68", + "name": "Creative Commons Attribution No Derivatives 2.5 Generic", + "licenseId": "CC-BY-ND-2.5", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nd/2.5/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-ND-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-3.0.json", + "referenceNumber": "393", + "name": "Creative Commons Attribution No Derivatives 3.0 Unported", + "licenseId": "CC-BY-ND-3.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nd/3.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-ND-4.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-4.0.json", + "referenceNumber": "132", + "name": "Creative Commons Attribution No Derivatives 4.0 International", + "licenseId": "CC-BY-ND-4.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nd/4.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-SA-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-1.0.json", + "referenceNumber": "322", + "name": "Creative Commons Attribution Share Alike 1.0 Generic", + "licenseId": "CC-BY-SA-1.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-sa/1.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-SA-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-2.0.json", + "referenceNumber": "142", + "name": "Creative Commons Attribution Share Alike 2.0 Generic", + "licenseId": "CC-BY-SA-2.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-sa/2.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-SA-2.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-2.5.json", + "referenceNumber": "306", + "name": "Creative Commons Attribution Share Alike 2.5 Generic", + "licenseId": "CC-BY-SA-2.5", + "seeAlso": [ + "https://creativecommons.org/licenses/by-sa/2.5/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-SA-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-3.0.json", + "referenceNumber": "394", + "name": "Creative Commons Attribution Share Alike 3.0 Unported", + "licenseId": "CC-BY-SA-3.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-sa/3.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-BY-SA-4.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-4.0.json", + "referenceNumber": "32", + "name": "Creative Commons Attribution Share Alike 4.0 International", + "licenseId": "CC-BY-SA-4.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-sa/4.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CC-PDDC.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CC-PDDC.json", + "referenceNumber": "371", + "name": "Creative Commons Public Domain Dedication and Certification", + "licenseId": "CC-PDDC", + "seeAlso": [ + "https://creativecommons.org/licenses/publicdomain/" + ], + "isOsiApproved": false + }, + { + "reference": "./CC0-1.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/CC0-1.0.json", + "referenceNumber": "213", + "name": "Creative Commons Zero v1.0 Universal", + "licenseId": "CC0-1.0", + "seeAlso": [ + "https://creativecommons.org/publicdomain/zero/1.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "./CDDL-1.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/CDDL-1.0.json", + "referenceNumber": "138", + "name": "Common Development and Distribution License 1.0", + "licenseId": "CDDL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/cddl1" + ], + "isOsiApproved": true + }, + { + "reference": "./CDDL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CDDL-1.1.json", + "referenceNumber": "376", + "name": "Common Development and Distribution License 1.1", + "licenseId": "CDDL-1.1", + "seeAlso": [ + "http://glassfish.java.net/public/CDDL+GPL_1_1.html", + "https://javaee.github.io/glassfish/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "./CDLA-Permissive-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CDLA-Permissive-1.0.json", + "referenceNumber": "250", + "name": "Community Data License Agreement Permissive 1.0", + "licenseId": "CDLA-Permissive-1.0", + "seeAlso": [ + "https://cdla.io/permissive-1-0" + ], + "isOsiApproved": false + }, + { + "reference": "./CDLA-Sharing-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CDLA-Sharing-1.0.json", + "referenceNumber": "310", + "name": "Community Data License Agreement Sharing 1.0", + "licenseId": "CDLA-Sharing-1.0", + "seeAlso": [ + "https://cdla.io/sharing-1-0" + ], + "isOsiApproved": false + }, + { + "reference": "./CECILL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CECILL-1.0.json", + "referenceNumber": "223", + "name": "CeCILL Free Software License Agreement v1.0", + "licenseId": "CECILL-1.0", + "seeAlso": [ + "http://www.cecill.info/licences/Licence_CeCILL_V1-fr.html" + ], + "isOsiApproved": false + }, + { + "reference": "./CECILL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CECILL-1.1.json", + "referenceNumber": "300", + "name": "CeCILL Free Software License Agreement v1.1", + "licenseId": "CECILL-1.1", + "seeAlso": [ + "http://www.cecill.info/licences/Licence_CeCILL_V1.1-US.html" + ], + "isOsiApproved": false + }, + { + "reference": "./CECILL-2.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/CECILL-2.0.json", + "referenceNumber": "352", + "name": "CeCILL Free Software License Agreement v2.0", + "licenseId": "CECILL-2.0", + "seeAlso": [ + "http://www.cecill.info/licences/Licence_CeCILL_V2-en.html" + ], + "isOsiApproved": false + }, + { + "reference": "./CECILL-2.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CECILL-2.1.json", + "referenceNumber": "120", + "name": "CeCILL Free Software License Agreement v2.1", + "licenseId": "CECILL-2.1", + "seeAlso": [ + "http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.html" + ], + "isOsiApproved": true + }, + { + "reference": "./CECILL-B.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/CECILL-B.json", + "referenceNumber": "340", + "name": "CeCILL-B Free Software License Agreement", + "licenseId": "CECILL-B", + "seeAlso": [ + "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html" + ], + "isOsiApproved": false + }, + { + "reference": "./CECILL-C.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/CECILL-C.json", + "referenceNumber": "77", + "name": "CeCILL-C Free Software License Agreement", + "licenseId": "CECILL-C", + "seeAlso": [ + "http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html" + ], + "isOsiApproved": false + }, + { + "reference": "./CERN-OHL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CERN-OHL-1.1.json", + "referenceNumber": "341", + "name": "CERN Open Hardware License v1.1", + "licenseId": "CERN-OHL-1.1", + "seeAlso": [ + "https://www.ohwr.org/project/licenses/wikis/cern-ohl-v1.1" + ], + "isOsiApproved": false + }, + { + "reference": "./CERN-OHL-1.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CERN-OHL-1.2.json", + "referenceNumber": "3", + "name": "CERN Open Hardware Licence v1.2", + "licenseId": "CERN-OHL-1.2", + "seeAlso": [ + "https://www.ohwr.org/project/licenses/wikis/cern-ohl-v1.2" + ], + "isOsiApproved": false + }, + { + "reference": "./CNRI-Jython.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CNRI-Jython.json", + "referenceNumber": "94", + "name": "CNRI Jython License", + "licenseId": "CNRI-Jython", + "seeAlso": [ + "http://www.jython.org/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./CNRI-Python.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CNRI-Python.json", + "referenceNumber": "45", + "name": "CNRI Python License", + "licenseId": "CNRI-Python", + "seeAlso": [ + "https://opensource.org/licenses/CNRI-Python" + ], + "isOsiApproved": true + }, + { + "reference": "./CNRI-Python-GPL-Compatible.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CNRI-Python-GPL-Compatible.json", + "referenceNumber": "202", + "name": "CNRI Python Open Source GPL Compatible License Agreement", + "licenseId": "CNRI-Python-GPL-Compatible", + "seeAlso": [ + "http://www.python.org/download/releases/1.6.1/download_win/" + ], + "isOsiApproved": false + }, + { + "reference": "./CPAL-1.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/CPAL-1.0.json", + "referenceNumber": "170", + "name": "Common Public Attribution License 1.0", + "licenseId": "CPAL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/CPAL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./CPL-1.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/CPL-1.0.json", + "referenceNumber": "172", + "name": "Common Public License 1.0", + "licenseId": "CPL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/CPL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./CPOL-1.02.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CPOL-1.02.json", + "referenceNumber": "28", + "name": "Code Project Open License 1.02", + "licenseId": "CPOL-1.02", + "seeAlso": [ + "http://www.codeproject.com/info/cpol10.aspx" + ], + "isOsiApproved": false + }, + { + "reference": "./CUA-OPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CUA-OPL-1.0.json", + "referenceNumber": "365", + "name": "CUA Office Public License v1.0", + "licenseId": "CUA-OPL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/CUA-OPL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./Caldera.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Caldera.json", + "referenceNumber": "108", + "name": "Caldera License", + "licenseId": "Caldera", + "seeAlso": [ + "http://www.lemis.com/grog/UNIX/ancient-source-all.pdf" + ], + "isOsiApproved": false + }, + { + "reference": "./ClArtistic.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/ClArtistic.json", + "referenceNumber": "271", + "name": "Clarified Artistic License", + "licenseId": "ClArtistic", + "seeAlso": [ + "http://gianluca.dellavedova.org/2011/01/03/clarified-artistic-license/", + "http://www.ncftp.com/ncftp/doc/LICENSE.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./Condor-1.1.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/Condor-1.1.json", + "referenceNumber": "307", + "name": "Condor Public License v1.1", + "licenseId": "Condor-1.1", + "seeAlso": [ + "http://research.cs.wisc.edu/condor/license.html#condor", + "http://web.archive.org/web/20111123062036/http://research.cs.wisc.edu/condor/license.html#condor" + ], + "isOsiApproved": false + }, + { + "reference": "./Crossword.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Crossword.json", + "referenceNumber": "363", + "name": "Crossword License", + "licenseId": "Crossword", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Crossword" + ], + "isOsiApproved": false + }, + { + "reference": "./CrystalStacker.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/CrystalStacker.json", + "referenceNumber": "168", + "name": "CrystalStacker License", + "licenseId": "CrystalStacker", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing:CrystalStacker?rd\u003dLicensing/CrystalStacker" + ], + "isOsiApproved": false + }, + { + "reference": "./Cube.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Cube.json", + "referenceNumber": "370", + "name": "Cube License", + "licenseId": "Cube", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Cube" + ], + "isOsiApproved": false + }, + { + "reference": "./D-FSL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/D-FSL-1.0.json", + "referenceNumber": "182", + "name": "Deutsche Freie Software Lizenz", + "licenseId": "D-FSL-1.0", + "seeAlso": [ + "http://www.dipp.nrw.de/d-fsl/lizenzen/", + "http://www.dipp.nrw.de/d-fsl/index_html/lizenzen/de/D-FSL-1_0_de.txt", + "http://www.dipp.nrw.de/d-fsl/index_html/lizenzen/en/D-FSL-1_0_en.txt", + "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl", + "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/deutsche-freie-software-lizenz", + "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/german-free-software-license", + "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/D-FSL-1_0_de.txt/at_download/file", + "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/D-FSL-1_0_en.txt/at_download/file" + ], + "isOsiApproved": false + }, + { + "reference": "./DOC.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/DOC.json", + "referenceNumber": "160", + "name": "DOC License", + "licenseId": "DOC", + "seeAlso": [ + "http://www.cs.wustl.edu/~schmidt/ACE-copying.html" + ], + "isOsiApproved": false + }, + { + "reference": "./DSDP.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/DSDP.json", + "referenceNumber": "141", + "name": "DSDP License", + "licenseId": "DSDP", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/DSDP" + ], + "isOsiApproved": false + }, + { + "reference": "./Dotseqn.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Dotseqn.json", + "referenceNumber": "390", + "name": "Dotseqn License", + "licenseId": "Dotseqn", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Dotseqn" + ], + "isOsiApproved": false + }, + { + "reference": "./ECL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/ECL-1.0.json", + "referenceNumber": "396", + "name": "Educational Community License v1.0", + "licenseId": "ECL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/ECL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./ECL-2.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/ECL-2.0.json", + "referenceNumber": "298", + "name": "Educational Community License v2.0", + "licenseId": "ECL-2.0", + "seeAlso": [ + "https://opensource.org/licenses/ECL-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "./EFL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/EFL-1.0.json", + "referenceNumber": "150", + "name": "Eiffel Forum License v1.0", + "licenseId": "EFL-1.0", + "seeAlso": [ + "http://www.eiffel-nice.org/license/forum.txt", + "https://opensource.org/licenses/EFL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./EFL-2.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/EFL-2.0.json", + "referenceNumber": "161", + "name": "Eiffel Forum License v2.0", + "licenseId": "EFL-2.0", + "seeAlso": [ + "http://www.eiffel-nice.org/license/eiffel-forum-license-2.html", + "https://opensource.org/licenses/EFL-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "./EPL-1.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/EPL-1.0.json", + "referenceNumber": "214", + "name": "Eclipse Public License 1.0", + "licenseId": "EPL-1.0", + "seeAlso": [ + "http://www.eclipse.org/legal/epl-v10.html", + "https://opensource.org/licenses/EPL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./EPL-2.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/EPL-2.0.json", + "referenceNumber": "134", + "name": "Eclipse Public License 2.0", + "licenseId": "EPL-2.0", + "seeAlso": [ + "https://www.eclipse.org/legal/epl-2.0", + "https://www.opensource.org/licenses/EPL-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "./EUDatagrid.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/EUDatagrid.json", + "referenceNumber": "192", + "name": "EU DataGrid Software License", + "licenseId": "EUDatagrid", + "seeAlso": [ + "http://eu-datagrid.web.cern.ch/eu-datagrid/license.html", + "https://opensource.org/licenses/EUDatagrid" + ], + "isOsiApproved": true + }, + { + "reference": "./EUPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/EUPL-1.0.json", + "referenceNumber": "173", + "name": "European Union Public License 1.0", + "licenseId": "EUPL-1.0", + "seeAlso": [ + "http://ec.europa.eu/idabc/en/document/7330.html", + "http://ec.europa.eu/idabc/servlets/Doc027f.pdf?id\u003d31096" + ], + "isOsiApproved": false + }, + { + "reference": "./EUPL-1.1.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/EUPL-1.1.json", + "referenceNumber": "92", + "name": "European Union Public License 1.1", + "licenseId": "EUPL-1.1", + "seeAlso": [ + "https://joinup.ec.europa.eu/software/page/eupl/licence-eupl", + "https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl1.1.-licence-en_0.pdf", + "https://opensource.org/licenses/EUPL-1.1" + ], + "isOsiApproved": true + }, + { + "reference": "./EUPL-1.2.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/EUPL-1.2.json", + "referenceNumber": "387", + "name": "European Union Public License 1.2", + "licenseId": "EUPL-1.2", + "seeAlso": [ + "https://joinup.ec.europa.eu/page/eupl-text-11-12", + "https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl_v1.2_en.pdf", + "https://joinup.ec.europa.eu/sites/default/files/inline-files/EUPL%20v1_2%20EN(1).txt", + "http://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri\u003dCELEX:32017D0863", + "https://opensource.org/licenses/EUPL-1.1" + ], + "isOsiApproved": true + }, + { + "reference": "./Entessa.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Entessa.json", + "referenceNumber": "99", + "name": "Entessa Public License v1.0", + "licenseId": "Entessa", + "seeAlso": [ + "https://opensource.org/licenses/Entessa" + ], + "isOsiApproved": true + }, + { + "reference": "./ErlPL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/ErlPL-1.1.json", + "referenceNumber": "157", + "name": "Erlang Public License v1.1", + "licenseId": "ErlPL-1.1", + "seeAlso": [ + "http://www.erlang.org/EPLICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "./Eurosym.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Eurosym.json", + "referenceNumber": "113", + "name": "Eurosym License", + "licenseId": "Eurosym", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Eurosym" + ], + "isOsiApproved": false + }, + { + "reference": "./FSFAP.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/FSFAP.json", + "referenceNumber": "114", + "name": "FSF All Permissive License", + "licenseId": "FSFAP", + "seeAlso": [ + "https://www.gnu.org/prep/maintain/html_node/License-Notices-for-Other-Files.html" + ], + "isOsiApproved": false + }, + { + "reference": "./FSFUL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/FSFUL.json", + "referenceNumber": "193", + "name": "FSF Unlimited License", + "licenseId": "FSFUL", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/FSF_Unlimited_License" + ], + "isOsiApproved": false + }, + { + "reference": "./FSFULLR.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/FSFULLR.json", + "referenceNumber": "43", + "name": "FSF Unlimited License (with License Retention)", + "licenseId": "FSFULLR", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/FSF_Unlimited_License#License_Retention_Variant" + ], + "isOsiApproved": false + }, + { + "reference": "./FTL.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/FTL.json", + "referenceNumber": "240", + "name": "Freetype Project License", + "licenseId": "FTL", + "seeAlso": [ + "http://freetype.fis.uniroma2.it/FTL.TXT", + "http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT" + ], + "isOsiApproved": false + }, + { + "reference": "./Fair.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Fair.json", + "referenceNumber": "297", + "name": "Fair License", + "licenseId": "Fair", + "seeAlso": [ + "http://fairlicense.org/", + "https://opensource.org/licenses/Fair" + ], + "isOsiApproved": true + }, + { + "reference": "./Frameworx-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Frameworx-1.0.json", + "referenceNumber": "389", + "name": "Frameworx Open License 1.0", + "licenseId": "Frameworx-1.0", + "seeAlso": [ + "https://opensource.org/licenses/Frameworx-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./FreeImage.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/FreeImage.json", + "referenceNumber": "277", + "name": "FreeImage Public License v1.0", + "licenseId": "FreeImage", + "seeAlso": [ + "http://freeimage.sourceforge.net/freeimage-license.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./GFDL-1.1.html", + "isDeprecatedLicenseId": true, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/GFDL-1.1.json", + "referenceNumber": "98", + "name": "GNU Free Documentation License v1.1", + "licenseId": "GFDL-1.1", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./GFDL-1.1-only.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/GFDL-1.1-only.json", + "referenceNumber": "102", + "name": "GNU Free Documentation License v1.1 only", + "licenseId": "GFDL-1.1-only", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./GFDL-1.1-or-later.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/GFDL-1.1-or-later.json", + "referenceNumber": "348", + "name": "GNU Free Documentation License v1.1 or later", + "licenseId": "GFDL-1.1-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./GFDL-1.2.html", + "isDeprecatedLicenseId": true, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/GFDL-1.2.json", + "referenceNumber": "197", + "name": "GNU Free Documentation License v1.2", + "licenseId": "GFDL-1.2", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./GFDL-1.2-only.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/GFDL-1.2-only.json", + "referenceNumber": "236", + "name": "GNU Free Documentation License v1.2 only", + "licenseId": "GFDL-1.2-only", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./GFDL-1.2-or-later.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/GFDL-1.2-or-later.json", + "referenceNumber": "215", + "name": "GNU Free Documentation License v1.2 or later", + "licenseId": "GFDL-1.2-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./GFDL-1.3.html", + "isDeprecatedLicenseId": true, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/GFDL-1.3.json", + "referenceNumber": "112", + "name": "GNU Free Documentation License v1.3", + "licenseId": "GFDL-1.3", + "seeAlso": [ + "https://www.gnu.org/licenses/fdl-1.3.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./GFDL-1.3-only.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/GFDL-1.3-only.json", + "referenceNumber": "69", + "name": "GNU Free Documentation License v1.3 only", + "licenseId": "GFDL-1.3-only", + "seeAlso": [ + "https://www.gnu.org/licenses/fdl-1.3.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./GFDL-1.3-or-later.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/GFDL-1.3-or-later.json", + "referenceNumber": "4", + "name": "GNU Free Documentation License v1.3 or later", + "licenseId": "GFDL-1.3-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/fdl-1.3.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./GL2PS.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/GL2PS.json", + "referenceNumber": "124", + "name": "GL2PS License", + "licenseId": "GL2PS", + "seeAlso": [ + "http://www.geuz.org/gl2ps/COPYING.GL2PS" + ], + "isOsiApproved": false + }, + { + "reference": "./GPL-1.0.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "http://spdx.org/licenses/GPL-1.0.json", + "referenceNumber": "79", + "name": "GNU General Public License v1.0 only", + "licenseId": "GPL-1.0", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html" + ], + "isOsiApproved": false + }, + { + "reference": "./GPL-1.0+.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "http://spdx.org/licenses/GPL-1.0+.json", + "referenceNumber": "175", + "name": "GNU General Public License v1.0 or later", + "licenseId": "GPL-1.0+", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html" + ], + "isOsiApproved": false + }, + { + "reference": "./GPL-1.0-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/GPL-1.0-only.json", + "referenceNumber": "15", + "name": "GNU General Public License v1.0 only", + "licenseId": "GPL-1.0-only", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html" + ], + "isOsiApproved": false + }, + { + "reference": "./GPL-1.0-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/GPL-1.0-or-later.json", + "referenceNumber": "357", + "name": "GNU General Public License v1.0 or later", + "licenseId": "GPL-1.0-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html" + ], + "isOsiApproved": false + }, + { + "reference": "./GPL-2.0.html", + "isDeprecatedLicenseId": true, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/GPL-2.0.json", + "referenceNumber": "147", + "name": "GNU General Public License v2.0 only", + "licenseId": "GPL-2.0", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", + "https://opensource.org/licenses/GPL-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "./GPL-2.0+.html", + "isDeprecatedLicenseId": true, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/GPL-2.0+.json", + "referenceNumber": "75", + "name": "GNU General Public License v2.0 or later", + "licenseId": "GPL-2.0+", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", + "https://opensource.org/licenses/GPL-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "./GPL-2.0-only.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/GPL-2.0-only.json", + "referenceNumber": "233", + "name": "GNU General Public License v2.0 only", + "licenseId": "GPL-2.0-only", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", + "https://opensource.org/licenses/GPL-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "./GPL-2.0-or-later.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/GPL-2.0-or-later.json", + "referenceNumber": "56", + "name": "GNU General Public License v2.0 or later", + "licenseId": "GPL-2.0-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", + "https://opensource.org/licenses/GPL-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "./GPL-2.0-with-GCC-exception.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-GCC-exception.json", + "referenceNumber": "117", + "name": "GNU General Public License v2.0 w/GCC Runtime Library exception", + "licenseId": "GPL-2.0-with-GCC-exception", + "seeAlso": [ + "https://gcc.gnu.org/git/?p\u003dgcc.git;a\u003dblob;f\u003dgcc/libgcc1.c;h\u003d762f5143fc6eed57b6797c82710f3538aa52b40b;hb\u003dcb143a3ce4fb417c68f5fa2691a1b1b1053dfba9#l10" + ], + "isOsiApproved": false + }, + { + "reference": "./GPL-2.0-with-autoconf-exception.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-autoconf-exception.json", + "referenceNumber": "355", + "name": "GNU General Public License v2.0 w/Autoconf exception", + "licenseId": "GPL-2.0-with-autoconf-exception", + "seeAlso": [ + "http://ac-archive.sourceforge.net/doc/copyright.html" + ], + "isOsiApproved": false + }, + { + "reference": "./GPL-2.0-with-bison-exception.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-bison-exception.json", + "referenceNumber": "378", + "name": "GNU General Public License v2.0 w/Bison exception", + "licenseId": "GPL-2.0-with-bison-exception", + "seeAlso": [ + "http://git.savannah.gnu.org/cgit/bison.git/tree/data/yacc.c?id\u003d193d7c7054ba7197b0789e14965b739162319b5e#n141" + ], + "isOsiApproved": false + }, + { + "reference": "./GPL-2.0-with-classpath-exception.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-classpath-exception.json", + "referenceNumber": "60", + "name": "GNU General Public License v2.0 w/Classpath exception", + "licenseId": "GPL-2.0-with-classpath-exception", + "seeAlso": [ + "https://www.gnu.org/software/classpath/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./GPL-2.0-with-font-exception.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-font-exception.json", + "referenceNumber": "375", + "name": "GNU General Public License v2.0 w/Font exception", + "licenseId": "GPL-2.0-with-font-exception", + "seeAlso": [ + "https://www.gnu.org/licenses/gpl-faq.html#FontException" + ], + "isOsiApproved": false + }, + { + "reference": "./GPL-3.0.html", + "isDeprecatedLicenseId": true, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/GPL-3.0.json", + "referenceNumber": "242", + "name": "GNU General Public License v3.0 only", + "licenseId": "GPL-3.0", + "seeAlso": [ + "https://www.gnu.org/licenses/gpl-3.0-standalone.html", + "https://opensource.org/licenses/GPL-3.0" + ], + "isOsiApproved": true + }, + { + "reference": "./GPL-3.0+.html", + "isDeprecatedLicenseId": true, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/GPL-3.0+.json", + "referenceNumber": "73", + "name": "GNU General Public License v3.0 or later", + "licenseId": "GPL-3.0+", + "seeAlso": [ + "https://www.gnu.org/licenses/gpl-3.0-standalone.html", + "https://opensource.org/licenses/GPL-3.0" + ], + "isOsiApproved": true + }, + { + "reference": "./GPL-3.0-only.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/GPL-3.0-only.json", + "referenceNumber": "206", + "name": "GNU General Public License v3.0 only", + "licenseId": "GPL-3.0-only", + "seeAlso": [ + "https://www.gnu.org/licenses/gpl-3.0-standalone.html", + "https://opensource.org/licenses/GPL-3.0" + ], + "isOsiApproved": true + }, + { + "reference": "./GPL-3.0-or-later.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/GPL-3.0-or-later.json", + "referenceNumber": "196", + "name": "GNU General Public License v3.0 or later", + "licenseId": "GPL-3.0-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/gpl-3.0-standalone.html", + "https://opensource.org/licenses/GPL-3.0" + ], + "isOsiApproved": true + }, + { + "reference": "./GPL-3.0-with-GCC-exception.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "http://spdx.org/licenses/GPL-3.0-with-GCC-exception.json", + "referenceNumber": "221", + "name": "GNU General Public License v3.0 w/GCC Runtime Library exception", + "licenseId": "GPL-3.0-with-GCC-exception", + "seeAlso": [ + "https://www.gnu.org/licenses/gcc-exception-3.1.html" + ], + "isOsiApproved": true + }, + { + "reference": "./GPL-3.0-with-autoconf-exception.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "http://spdx.org/licenses/GPL-3.0-with-autoconf-exception.json", + "referenceNumber": "235", + "name": "GNU General Public License v3.0 w/Autoconf exception", + "licenseId": "GPL-3.0-with-autoconf-exception", + "seeAlso": [ + "https://www.gnu.org/licenses/autoconf-exception-3.0.html" + ], + "isOsiApproved": false + }, + { + "reference": "./Giftware.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Giftware.json", + "referenceNumber": "369", + "name": "Giftware License", + "licenseId": "Giftware", + "seeAlso": [ + "http://liballeg.org/license.html#allegro-4-the-giftware-license" + ], + "isOsiApproved": false + }, + { + "reference": "./Glide.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Glide.json", + "referenceNumber": "374", + "name": "3dfx Glide License", + "licenseId": "Glide", + "seeAlso": [ + "http://www.users.on.net/~triforce/glidexp/COPYING.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./Glulxe.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Glulxe.json", + "referenceNumber": "93", + "name": "Glulxe License", + "licenseId": "Glulxe", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Glulxe" + ], + "isOsiApproved": false + }, + { + "reference": "./HPND.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/HPND.json", + "referenceNumber": "264", + "name": "Historical Permission Notice and Disclaimer", + "licenseId": "HPND", + "seeAlso": [ + "https://opensource.org/licenses/HPND" + ], + "isOsiApproved": true + }, + { + "reference": "./HPND-sell-variant.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/HPND-sell-variant.json", + "referenceNumber": "145", + "name": "Historical Permission Notice and Disclaimer - sell variant", + "licenseId": "HPND-sell-variant", + "seeAlso": [ + "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/sunrpc/auth_gss/gss_generic_token.c?h\u003dv4.19" + ], + "isOsiApproved": false + }, + { + "reference": "./HaskellReport.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/HaskellReport.json", + "referenceNumber": "122", + "name": "Haskell Language Report License", + "licenseId": "HaskellReport", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Haskell_Language_Report_License" + ], + "isOsiApproved": false + }, + { + "reference": "./IBM-pibs.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/IBM-pibs.json", + "referenceNumber": "207", + "name": "IBM PowerPC Initialization and Boot Software", + "licenseId": "IBM-pibs", + "seeAlso": [ + "http://git.denx.de/?p\u003du-boot.git;a\u003dblob;f\u003darch/powerpc/cpu/ppc4xx/miiphy.c;h\u003d297155fdafa064b955e53e9832de93bfb0cfb85b;hb\u003d9fab4bf4cc077c21e43941866f3f2c196f28670d" + ], + "isOsiApproved": false + }, + { + "reference": "./ICU.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/ICU.json", + "referenceNumber": "194", + "name": "ICU License", + "licenseId": "ICU", + "seeAlso": [ + "http://source.icu-project.org/repos/icu/icu/trunk/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./IJG.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/IJG.json", + "referenceNumber": "55", + "name": "Independent JPEG Group License", + "licenseId": "IJG", + "seeAlso": [ + "http://dev.w3.org/cvsweb/Amaya/libjpeg/Attic/README?rev\u003d1.2" + ], + "isOsiApproved": false + }, + { + "reference": "./IPA.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/IPA.json", + "referenceNumber": "312", + "name": "IPA Font License", + "licenseId": "IPA", + "seeAlso": [ + "https://opensource.org/licenses/IPA" + ], + "isOsiApproved": true + }, + { + "reference": "./IPL-1.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/IPL-1.0.json", + "referenceNumber": "31", + "name": "IBM Public License v1.0", + "licenseId": "IPL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/IPL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./ISC.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/ISC.json", + "referenceNumber": "110", + "name": "ISC License", + "licenseId": "ISC", + "seeAlso": [ + "https://www.isc.org/downloads/software-support-policy/isc-license/", + "https://opensource.org/licenses/ISC" + ], + "isOsiApproved": true + }, + { + "reference": "./ImageMagick.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/ImageMagick.json", + "referenceNumber": "231", + "name": "ImageMagick License", + "licenseId": "ImageMagick", + "seeAlso": [ + "http://www.imagemagick.org/script/license.php" + ], + "isOsiApproved": false + }, + { + "reference": "./Imlib2.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/Imlib2.json", + "referenceNumber": "257", + "name": "Imlib2 License", + "licenseId": "Imlib2", + "seeAlso": [ + "http://trac.enlightenment.org/e/browser/trunk/imlib2/COPYING", + "https://git.enlightenment.org/legacy/imlib2.git/tree/COPYING" + ], + "isOsiApproved": false + }, + { + "reference": "./Info-ZIP.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Info-ZIP.json", + "referenceNumber": "104", + "name": "Info-ZIP License", + "licenseId": "Info-ZIP", + "seeAlso": [ + "http://www.info-zip.org/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./Intel.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/Intel.json", + "referenceNumber": "167", + "name": "Intel Open Source License", + "licenseId": "Intel", + "seeAlso": [ + "https://opensource.org/licenses/Intel" + ], + "isOsiApproved": true + }, + { + "reference": "./Intel-ACPI.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Intel-ACPI.json", + "referenceNumber": "88", + "name": "Intel ACPI Software License Agreement", + "licenseId": "Intel-ACPI", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Intel_ACPI_Software_License_Agreement" + ], + "isOsiApproved": false + }, + { + "reference": "./Interbase-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Interbase-1.0.json", + "referenceNumber": "83", + "name": "Interbase Public License v1.0", + "licenseId": "Interbase-1.0", + "seeAlso": [ + "https://web.archive.org/web/20060319014854/http://info.borland.com/devsupport/interbase/opensource/IPL.html" + ], + "isOsiApproved": false + }, + { + "reference": "./JPNIC.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/JPNIC.json", + "referenceNumber": "105", + "name": "Japan Network Information Center License", + "licenseId": "JPNIC", + "seeAlso": [ + "https://gitlab.isc.org/isc-projects/bind9/blob/master/COPYRIGHT#L366" + ], + "isOsiApproved": false + }, + { + "reference": "./JSON.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/JSON.json", + "referenceNumber": "372", + "name": "JSON License", + "licenseId": "JSON", + "seeAlso": [ + "http://www.json.org/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./JasPer-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/JasPer-2.0.json", + "referenceNumber": "239", + "name": "JasPer License", + "licenseId": "JasPer-2.0", + "seeAlso": [ + "http://www.ece.uvic.ca/~mdadams/jasper/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "./LAL-1.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/LAL-1.2.json", + "referenceNumber": "380", + "name": "Licence Art Libre 1.2", + "licenseId": "LAL-1.2", + "seeAlso": [ + "http://artlibre.org/licence/lal/licence-art-libre-12/" + ], + "isOsiApproved": false + }, + { + "reference": "./LAL-1.3.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/LAL-1.3.json", + "referenceNumber": "156", + "name": "Licence Art Libre 1.3", + "licenseId": "LAL-1.3", + "seeAlso": [ + "http://artlibre.org/" + ], + "isOsiApproved": false + }, + { + "reference": "./LGPL-2.0.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "http://spdx.org/licenses/LGPL-2.0.json", + "referenceNumber": "268", + "name": "GNU Library General Public License v2 only", + "licenseId": "LGPL-2.0", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html" + ], + "isOsiApproved": true + }, + { + "reference": "./LGPL-2.0+.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "http://spdx.org/licenses/LGPL-2.0+.json", + "referenceNumber": "52", + "name": "GNU Library General Public License v2 or later", + "licenseId": "LGPL-2.0+", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html" + ], + "isOsiApproved": true + }, + { + "reference": "./LGPL-2.0-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/LGPL-2.0-only.json", + "referenceNumber": "276", + "name": "GNU Library General Public License v2 only", + "licenseId": "LGPL-2.0-only", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html" + ], + "isOsiApproved": true + }, + { + "reference": "./LGPL-2.0-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/LGPL-2.0-or-later.json", + "referenceNumber": "217", + "name": "GNU Library General Public License v2 or later", + "licenseId": "LGPL-2.0-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html" + ], + "isOsiApproved": true + }, + { + "reference": "./LGPL-2.1.html", + "isDeprecatedLicenseId": true, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/LGPL-2.1.json", + "referenceNumber": "166", + "name": "GNU Lesser General Public License v2.1 only", + "licenseId": "LGPL-2.1", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", + "https://opensource.org/licenses/LGPL-2.1" + ], + "isOsiApproved": true + }, + { + "reference": "./LGPL-2.1+.html", + "isDeprecatedLicenseId": true, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/LGPL-2.1+.json", + "referenceNumber": "64", + "name": "GNU Library General Public License v2.1 or later", + "licenseId": "LGPL-2.1+", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", + "https://opensource.org/licenses/LGPL-2.1" + ], + "isOsiApproved": true + }, + { + "reference": "./LGPL-2.1-only.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/LGPL-2.1-only.json", + "referenceNumber": "2", + "name": "GNU Lesser General Public License v2.1 only", + "licenseId": "LGPL-2.1-only", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", + "https://opensource.org/licenses/LGPL-2.1" + ], + "isOsiApproved": true + }, + { + "reference": "./LGPL-2.1-or-later.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/LGPL-2.1-or-later.json", + "referenceNumber": "338", + "name": "GNU Lesser General Public License v2.1 or later", + "licenseId": "LGPL-2.1-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", + "https://opensource.org/licenses/LGPL-2.1" + ], + "isOsiApproved": true + }, + { + "reference": "./LGPL-3.0.html", + "isDeprecatedLicenseId": true, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/LGPL-3.0.json", + "referenceNumber": "210", + "name": "GNU Lesser General Public License v3.0 only", + "licenseId": "LGPL-3.0", + "seeAlso": [ + "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", + "https://opensource.org/licenses/LGPL-3.0" + ], + "isOsiApproved": true + }, + { + "reference": "./LGPL-3.0+.html", + "isDeprecatedLicenseId": true, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/LGPL-3.0+.json", + "referenceNumber": "152", + "name": "GNU Lesser General Public License v3.0 or later", + "licenseId": "LGPL-3.0+", + "seeAlso": [ + "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", + "https://opensource.org/licenses/LGPL-3.0" + ], + "isOsiApproved": true + }, + { + "reference": "./LGPL-3.0-only.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/LGPL-3.0-only.json", + "referenceNumber": "254", + "name": "GNU Lesser General Public License v3.0 only", + "licenseId": "LGPL-3.0-only", + "seeAlso": [ + "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", + "https://opensource.org/licenses/LGPL-3.0" + ], + "isOsiApproved": true + }, + { + "reference": "./LGPL-3.0-or-later.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/LGPL-3.0-or-later.json", + "referenceNumber": "301", + "name": "GNU Lesser General Public License v3.0 or later", + "licenseId": "LGPL-3.0-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", + "https://opensource.org/licenses/LGPL-3.0" + ], + "isOsiApproved": true + }, + { + "reference": "./LGPLLR.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/LGPLLR.json", + "referenceNumber": "103", + "name": "Lesser General Public License For Linguistic Resources", + "licenseId": "LGPLLR", + "seeAlso": [ + "http://www-igm.univ-mlv.fr/~unitex/lgpllr.html" + ], + "isOsiApproved": false + }, + { + "reference": "./LPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/LPL-1.0.json", + "referenceNumber": "89", + "name": "Lucent Public License Version 1.0", + "licenseId": "LPL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/LPL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./LPL-1.02.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/LPL-1.02.json", + "referenceNumber": "131", + "name": "Lucent Public License v1.02", + "licenseId": "LPL-1.02", + "seeAlso": [ + "http://plan9.bell-labs.com/plan9/license.html", + "https://opensource.org/licenses/LPL-1.02" + ], + "isOsiApproved": true + }, + { + "reference": "./LPPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/LPPL-1.0.json", + "referenceNumber": "259", + "name": "LaTeX Project Public License v1.0", + "licenseId": "LPPL-1.0", + "seeAlso": [ + "http://www.latex-project.org/lppl/lppl-1-0.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./LPPL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/LPPL-1.1.json", + "referenceNumber": "309", + "name": "LaTeX Project Public License v1.1", + "licenseId": "LPPL-1.1", + "seeAlso": [ + "http://www.latex-project.org/lppl/lppl-1-1.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./LPPL-1.2.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/LPPL-1.2.json", + "referenceNumber": "392", + "name": "LaTeX Project Public License v1.2", + "licenseId": "LPPL-1.2", + "seeAlso": [ + "http://www.latex-project.org/lppl/lppl-1-2.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./LPPL-1.3a.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/LPPL-1.3a.json", + "referenceNumber": "305", + "name": "LaTeX Project Public License v1.3a", + "licenseId": "LPPL-1.3a", + "seeAlso": [ + "http://www.latex-project.org/lppl/lppl-1-3a.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./LPPL-1.3c.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/LPPL-1.3c.json", + "referenceNumber": "326", + "name": "LaTeX Project Public License v1.3c", + "licenseId": "LPPL-1.3c", + "seeAlso": [ + "http://www.latex-project.org/lppl/lppl-1-3c.txt", + "https://opensource.org/licenses/LPPL-1.3c" + ], + "isOsiApproved": true + }, + { + "reference": "./Latex2e.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Latex2e.json", + "referenceNumber": "283", + "name": "Latex2e License", + "licenseId": "Latex2e", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Latex2e" + ], + "isOsiApproved": false + }, + { + "reference": "./Leptonica.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Leptonica.json", + "referenceNumber": "159", + "name": "Leptonica License", + "licenseId": "Leptonica", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Leptonica" + ], + "isOsiApproved": false + }, + { + "reference": "./LiLiQ-P-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/LiLiQ-P-1.1.json", + "referenceNumber": "379", + "name": "Licence Libre du Québec – Permissive version 1.1", + "licenseId": "LiLiQ-P-1.1", + "seeAlso": [ + "https://forge.gouv.qc.ca/licence/fr/liliq-v1-1/", + "http://opensource.org/licenses/LiLiQ-P-1.1" + ], + "isOsiApproved": true + }, + { + "reference": "./LiLiQ-R-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/LiLiQ-R-1.1.json", + "referenceNumber": "286", + "name": "Licence Libre du Québec – Réciprocité version 1.1", + "licenseId": "LiLiQ-R-1.1", + "seeAlso": [ + "https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-en-francais/licence-libre-du-quebec-reciprocite-liliq-r-v1-1/", + "http://opensource.org/licenses/LiLiQ-R-1.1" + ], + "isOsiApproved": true + }, + { + "reference": "./LiLiQ-Rplus-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/LiLiQ-Rplus-1.1.json", + "referenceNumber": "139", + "name": "Licence Libre du Québec – Réciprocité forte version 1.1", + "licenseId": "LiLiQ-Rplus-1.1", + "seeAlso": [ + "https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-en-francais/licence-libre-du-quebec-reciprocite-forte-liliq-r-v1-1/", + "http://opensource.org/licenses/LiLiQ-Rplus-1.1" + ], + "isOsiApproved": true + }, + { + "reference": "./Libpng.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Libpng.json", + "referenceNumber": "101", + "name": "libpng License", + "licenseId": "Libpng", + "seeAlso": [ + "http://www.libpng.org/pub/png/src/libpng-LICENSE.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./Linux-OpenIB.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Linux-OpenIB.json", + "referenceNumber": "5", + "name": "Linux Kernel Variant of OpenIB.org license", + "licenseId": "Linux-OpenIB", + "seeAlso": [ + "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/sa.h" + ], + "isOsiApproved": false + }, + { + "reference": "./MIT.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/MIT.json", + "referenceNumber": "201", + "name": "MIT License", + "licenseId": "MIT", + "seeAlso": [ + "https://opensource.org/licenses/MIT" + ], + "isOsiApproved": true + }, + { + "reference": "./MIT-0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/MIT-0.json", + "referenceNumber": "6", + "name": "MIT No Attribution", + "licenseId": "MIT-0", + "seeAlso": [ + "https://github.com/aws/mit-0", + "https://romanrm.net/mit-zero", + "https://github.com/awsdocs/aws-cloud9-user-guide/blob/master/LICENSE-SAMPLECODE" + ], + "isOsiApproved": true + }, + { + "reference": "./MIT-CMU.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/MIT-CMU.json", + "referenceNumber": "9", + "name": "CMU License", + "licenseId": "MIT-CMU", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing:MIT?rd\u003dLicensing/MIT#CMU_Style", + "https://github.com/python-pillow/Pillow/blob/fffb426092c8db24a5f4b6df243a8a3c01fb63cd/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "./MIT-advertising.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/MIT-advertising.json", + "referenceNumber": "8", + "name": "Enlightenment License (e16)", + "licenseId": "MIT-advertising", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/MIT_With_Advertising" + ], + "isOsiApproved": false + }, + { + "reference": "./MIT-enna.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/MIT-enna.json", + "referenceNumber": "25", + "name": "enna License", + "licenseId": "MIT-enna", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/MIT#enna" + ], + "isOsiApproved": false + }, + { + "reference": "./MIT-feh.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/MIT-feh.json", + "referenceNumber": "38", + "name": "feh License", + "licenseId": "MIT-feh", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/MIT#feh" + ], + "isOsiApproved": false + }, + { + "reference": "./MITNFA.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/MITNFA.json", + "referenceNumber": "294", + "name": "MIT +no-false-attribs license", + "licenseId": "MITNFA", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/MITNFA" + ], + "isOsiApproved": false + }, + { + "reference": "./MPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/MPL-1.0.json", + "referenceNumber": "49", + "name": "Mozilla Public License 1.0", + "licenseId": "MPL-1.0", + "seeAlso": [ + "http://www.mozilla.org/MPL/MPL-1.0.html", + "https://opensource.org/licenses/MPL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./MPL-1.1.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/MPL-1.1.json", + "referenceNumber": "304", + "name": "Mozilla Public License 1.1", + "licenseId": "MPL-1.1", + "seeAlso": [ + "http://www.mozilla.org/MPL/MPL-1.1.html", + "https://opensource.org/licenses/MPL-1.1" + ], + "isOsiApproved": true + }, + { + "reference": "./MPL-2.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/MPL-2.0.json", + "referenceNumber": "234", + "name": "Mozilla Public License 2.0", + "licenseId": "MPL-2.0", + "seeAlso": [ + "http://www.mozilla.org/MPL/2.0/", + "https://opensource.org/licenses/MPL-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "./MPL-2.0-no-copyleft-exception.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/MPL-2.0-no-copyleft-exception.json", + "referenceNumber": "303", + "name": "Mozilla Public License 2.0 (no copyleft exception)", + "licenseId": "MPL-2.0-no-copyleft-exception", + "seeAlso": [ + "http://www.mozilla.org/MPL/2.0/", + "https://opensource.org/licenses/MPL-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "./MS-PL.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/MS-PL.json", + "referenceNumber": "336", + "name": "Microsoft Public License", + "licenseId": "MS-PL", + "seeAlso": [ + "http://www.microsoft.com/opensource/licenses.mspx", + "https://opensource.org/licenses/MS-PL" + ], + "isOsiApproved": true + }, + { + "reference": "./MS-RL.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/MS-RL.json", + "referenceNumber": "280", + "name": "Microsoft Reciprocal License", + "licenseId": "MS-RL", + "seeAlso": [ + "http://www.microsoft.com/opensource/licenses.mspx", + "https://opensource.org/licenses/MS-RL" + ], + "isOsiApproved": true + }, + { + "reference": "./MTLL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/MTLL.json", + "referenceNumber": "181", + "name": "Matrix Template Library License", + "licenseId": "MTLL", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Matrix_Template_Library_License" + ], + "isOsiApproved": false + }, + { + "reference": "./MakeIndex.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/MakeIndex.json", + "referenceNumber": "187", + "name": "MakeIndex License", + "licenseId": "MakeIndex", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/MakeIndex" + ], + "isOsiApproved": false + }, + { + "reference": "./MirOS.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/MirOS.json", + "referenceNumber": "299", + "name": "MirOS License", + "licenseId": "MirOS", + "seeAlso": [ + "https://opensource.org/licenses/MirOS" + ], + "isOsiApproved": true + }, + { + "reference": "./Motosoto.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Motosoto.json", + "referenceNumber": "317", + "name": "Motosoto License", + "licenseId": "Motosoto", + "seeAlso": [ + "https://opensource.org/licenses/Motosoto" + ], + "isOsiApproved": true + }, + { + "reference": "./Multics.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Multics.json", + "referenceNumber": "63", + "name": "Multics License", + "licenseId": "Multics", + "seeAlso": [ + "https://opensource.org/licenses/Multics" + ], + "isOsiApproved": true + }, + { + "reference": "./Mup.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Mup.json", + "referenceNumber": "353", + "name": "Mup License", + "licenseId": "Mup", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Mup" + ], + "isOsiApproved": false + }, + { + "reference": "./NASA-1.3.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/NASA-1.3.json", + "referenceNumber": "87", + "name": "NASA Open Source Agreement 1.3", + "licenseId": "NASA-1.3", + "seeAlso": [ + "http://ti.arc.nasa.gov/opensource/nosa/", + "https://opensource.org/licenses/NASA-1.3" + ], + "isOsiApproved": true + }, + { + "reference": "./NBPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/NBPL-1.0.json", + "referenceNumber": "361", + "name": "Net Boolean Public License v1", + "licenseId": "NBPL-1.0", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d37b4b3f6cc4bf34e1d3dec61e69914b9819d8894" + ], + "isOsiApproved": false + }, + { + "reference": "./NCSA.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/NCSA.json", + "referenceNumber": "58", + "name": "University of Illinois/NCSA Open Source License", + "licenseId": "NCSA", + "seeAlso": [ + "http://otm.illinois.edu/uiuc_openSource", + "https://opensource.org/licenses/NCSA" + ], + "isOsiApproved": true + }, + { + "reference": "./NGPL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/NGPL.json", + "referenceNumber": "71", + "name": "Nethack General Public License", + "licenseId": "NGPL", + "seeAlso": [ + "https://opensource.org/licenses/NGPL" + ], + "isOsiApproved": true + }, + { + "reference": "./NLOD-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/NLOD-1.0.json", + "referenceNumber": "209", + "name": "Norwegian Licence for Open Government Data", + "licenseId": "NLOD-1.0", + "seeAlso": [ + "http://data.norge.no/nlod/en/1.0" + ], + "isOsiApproved": false + }, + { + "reference": "./NLPL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/NLPL.json", + "referenceNumber": "344", + "name": "No Limit Public License", + "licenseId": "NLPL", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/NLPL" + ], + "isOsiApproved": false + }, + { + "reference": "./NOSL.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/NOSL.json", + "referenceNumber": "383", + "name": "Netizen Open Source License", + "licenseId": "NOSL", + "seeAlso": [ + "http://bits.netizen.com.au/licenses/NOSL/nosl.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./NPL-1.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/NPL-1.0.json", + "referenceNumber": "328", + "name": "Netscape Public License v1.0", + "licenseId": "NPL-1.0", + "seeAlso": [ + "http://www.mozilla.org/MPL/NPL/1.0/" + ], + "isOsiApproved": false + }, + { + "reference": "./NPL-1.1.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/NPL-1.1.json", + "referenceNumber": "185", + "name": "Netscape Public License v1.1", + "licenseId": "NPL-1.1", + "seeAlso": [ + "http://www.mozilla.org/MPL/NPL/1.1/" + ], + "isOsiApproved": false + }, + { + "reference": "./NPOSL-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/NPOSL-3.0.json", + "referenceNumber": "222", + "name": "Non-Profit Open Software License 3.0", + "licenseId": "NPOSL-3.0", + "seeAlso": [ + "https://opensource.org/licenses/NOSL3.0" + ], + "isOsiApproved": true + }, + { + "reference": "./NRL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/NRL.json", + "referenceNumber": "53", + "name": "NRL License", + "licenseId": "NRL", + "seeAlso": [ + "http://web.mit.edu/network/isakmp/nrllicense.html" + ], + "isOsiApproved": false + }, + { + "reference": "./NTP.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/NTP.json", + "referenceNumber": "261", + "name": "NTP License", + "licenseId": "NTP", + "seeAlso": [ + "https://opensource.org/licenses/NTP" + ], + "isOsiApproved": true + }, + { + "reference": "./Naumen.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Naumen.json", + "referenceNumber": "278", + "name": "Naumen Public License", + "licenseId": "Naumen", + "seeAlso": [ + "https://opensource.org/licenses/Naumen" + ], + "isOsiApproved": true + }, + { + "reference": "./Net-SNMP.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Net-SNMP.json", + "referenceNumber": "284", + "name": "Net-SNMP License", + "licenseId": "Net-SNMP", + "seeAlso": [ + "http://net-snmp.sourceforge.net/about/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./NetCDF.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/NetCDF.json", + "referenceNumber": "46", + "name": "NetCDF license", + "licenseId": "NetCDF", + "seeAlso": [ + "http://www.unidata.ucar.edu/software/netcdf/copyright.html" + ], + "isOsiApproved": false + }, + { + "reference": "./Newsletr.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Newsletr.json", + "referenceNumber": "279", + "name": "Newsletr License", + "licenseId": "Newsletr", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Newsletr" + ], + "isOsiApproved": false + }, + { + "reference": "./Nokia.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/Nokia.json", + "referenceNumber": "327", + "name": "Nokia Open Source License", + "licenseId": "Nokia", + "seeAlso": [ + "https://opensource.org/licenses/nokia" + ], + "isOsiApproved": true + }, + { + "reference": "./Noweb.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Noweb.json", + "referenceNumber": "364", + "name": "Noweb License", + "licenseId": "Noweb", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Noweb" + ], + "isOsiApproved": false + }, + { + "reference": "./Nunit.html", + "isDeprecatedLicenseId": true, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/Nunit.json", + "referenceNumber": "288", + "name": "Nunit License", + "licenseId": "Nunit", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Nunit" + ], + "isOsiApproved": false + }, + { + "reference": "./OCCT-PL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OCCT-PL.json", + "referenceNumber": "282", + "name": "Open CASCADE Technology Public License", + "licenseId": "OCCT-PL", + "seeAlso": [ + "http://www.opencascade.com/content/occt-public-license" + ], + "isOsiApproved": false + }, + { + "reference": "./OCLC-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OCLC-2.0.json", + "referenceNumber": "111", + "name": "OCLC Research Public License 2.0", + "licenseId": "OCLC-2.0", + "seeAlso": [ + "http://www.oclc.org/research/activities/software/license/v2final.htm", + "https://opensource.org/licenses/OCLC-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "./ODC-By-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/ODC-By-1.0.json", + "referenceNumber": "144", + "name": "Open Data Commons Attribution License v1.0", + "licenseId": "ODC-By-1.0", + "seeAlso": [ + "https://opendatacommons.org/licenses/by/1.0/" + ], + "isOsiApproved": false + }, + { + "reference": "./ODbL-1.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/ODbL-1.0.json", + "referenceNumber": "246", + "name": "ODC Open Database License v1.0", + "licenseId": "ODbL-1.0", + "seeAlso": [ + "http://www.opendatacommons.org/licenses/odbl/1.0/" + ], + "isOsiApproved": false + }, + { + "reference": "./OFL-1.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/OFL-1.0.json", + "referenceNumber": "153", + "name": "SIL Open Font License 1.0", + "licenseId": "OFL-1.0", + "seeAlso": [ + "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL10_web" + ], + "isOsiApproved": false + }, + { + "reference": "./OFL-1.1.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/OFL-1.1.json", + "referenceNumber": "315", + "name": "SIL Open Font License 1.1", + "licenseId": "OFL-1.1", + "seeAlso": [ + "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL_web", + "https://opensource.org/licenses/OFL-1.1" + ], + "isOsiApproved": true + }, + { + "reference": "./OGL-UK-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OGL-UK-1.0.json", + "referenceNumber": "116", + "name": "Open Government Licence v1.0", + "licenseId": "OGL-UK-1.0", + "seeAlso": [ + "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/1/" + ], + "isOsiApproved": false + }, + { + "reference": "./OGL-UK-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OGL-UK-2.0.json", + "referenceNumber": "289", + "name": "Open Government Licence v2.0", + "licenseId": "OGL-UK-2.0", + "seeAlso": [ + "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/2/" + ], + "isOsiApproved": false + }, + { + "reference": "./OGL-UK-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OGL-UK-3.0.json", + "referenceNumber": "226", + "name": "Open Government Licence v3.0", + "licenseId": "OGL-UK-3.0", + "seeAlso": [ + "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/" + ], + "isOsiApproved": false + }, + { + "reference": "./OGTSL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OGTSL.json", + "referenceNumber": "125", + "name": "Open Group Test Suite License", + "licenseId": "OGTSL", + "seeAlso": [ + "http://www.opengroup.org/testing/downloads/The_Open_Group_TSL.txt", + "https://opensource.org/licenses/OGTSL" + ], + "isOsiApproved": true + }, + { + "reference": "./OLDAP-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OLDAP-1.1.json", + "referenceNumber": "97", + "name": "Open LDAP Public License v1.1", + "licenseId": "OLDAP-1.1", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d806557a5ad59804ef3a44d5abfbe91d706b0791f" + ], + "isOsiApproved": false + }, + { + "reference": "./OLDAP-1.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OLDAP-1.2.json", + "referenceNumber": "190", + "name": "Open LDAP Public License v1.2", + "licenseId": "OLDAP-1.2", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d42b0383c50c299977b5893ee695cf4e486fb0dc7" + ], + "isOsiApproved": false + }, + { + "reference": "./OLDAP-1.3.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OLDAP-1.3.json", + "referenceNumber": "106", + "name": "Open LDAP Public License v1.3", + "licenseId": "OLDAP-1.3", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003de5f8117f0ce088d0bd7a8e18ddf37eaa40eb09b1" + ], + "isOsiApproved": false + }, + { + "reference": "./OLDAP-1.4.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OLDAP-1.4.json", + "referenceNumber": "30", + "name": "Open LDAP Public License v1.4", + "licenseId": "OLDAP-1.4", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dc9f95c2f3f2ffb5e0ae55fe7388af75547660941" + ], + "isOsiApproved": false + }, + { + "reference": "./OLDAP-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OLDAP-2.0.json", + "referenceNumber": "266", + "name": "Open LDAP Public License v2.0 (or possibly 2.0A and 2.0B)", + "licenseId": "OLDAP-2.0", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dcbf50f4e1185a21abd4c0a54d3f4341fe28f36ea" + ], + "isOsiApproved": false + }, + { + "reference": "./OLDAP-2.0.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OLDAP-2.0.1.json", + "referenceNumber": "350", + "name": "Open LDAP Public License v2.0.1", + "licenseId": "OLDAP-2.0.1", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003db6d68acd14e51ca3aab4428bf26522aa74873f0e" + ], + "isOsiApproved": false + }, + { + "reference": "./OLDAP-2.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OLDAP-2.1.json", + "referenceNumber": "154", + "name": "Open LDAP Public License v2.1", + "licenseId": "OLDAP-2.1", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003db0d176738e96a0d3b9f85cb51e140a86f21be715" + ], + "isOsiApproved": false + }, + { + "reference": "./OLDAP-2.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OLDAP-2.2.json", + "referenceNumber": "362", + "name": "Open LDAP Public License v2.2", + "licenseId": "OLDAP-2.2", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d470b0c18ec67621c85881b2733057fecf4a1acc3" + ], + "isOsiApproved": false + }, + { + "reference": "./OLDAP-2.2.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OLDAP-2.2.1.json", + "referenceNumber": "339", + "name": "Open LDAP Public License v2.2.1", + "licenseId": "OLDAP-2.2.1", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d4bc786f34b50aa301be6f5600f58a980070f481e" + ], + "isOsiApproved": false + }, + { + "reference": "./OLDAP-2.2.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OLDAP-2.2.2.json", + "referenceNumber": "199", + "name": "Open LDAP Public License 2.2.2", + "licenseId": "OLDAP-2.2.2", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003ddf2cc1e21eb7c160695f5b7cffd6296c151ba188" + ], + "isOsiApproved": false + }, + { + "reference": "./OLDAP-2.3.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/OLDAP-2.3.json", + "referenceNumber": "164", + "name": "Open LDAP Public License v2.3", + "licenseId": "OLDAP-2.3", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dd32cf54a32d581ab475d23c810b0a7fbaf8d63c3" + ], + "isOsiApproved": false + }, + { + "reference": "./OLDAP-2.4.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OLDAP-2.4.json", + "referenceNumber": "66", + "name": "Open LDAP Public License v2.4", + "licenseId": "OLDAP-2.4", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dcd1284c4a91a8a380d904eee68d1583f989ed386" + ], + "isOsiApproved": false + }, + { + "reference": "./OLDAP-2.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OLDAP-2.5.json", + "referenceNumber": "183", + "name": "Open LDAP Public License v2.5", + "licenseId": "OLDAP-2.5", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d6852b9d90022e8593c98205413380536b1b5a7cf" + ], + "isOsiApproved": false + }, + { + "reference": "./OLDAP-2.6.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OLDAP-2.6.json", + "referenceNumber": "61", + "name": "Open LDAP Public License v2.6", + "licenseId": "OLDAP-2.6", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d1cae062821881f41b73012ba816434897abf4205" + ], + "isOsiApproved": false + }, + { + "reference": "./OLDAP-2.7.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/OLDAP-2.7.json", + "referenceNumber": "123", + "name": "Open LDAP Public License v2.7", + "licenseId": "OLDAP-2.7", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d47c2415c1df81556eeb39be6cad458ef87c534a2" + ], + "isOsiApproved": false + }, + { + "reference": "./OLDAP-2.8.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OLDAP-2.8.json", + "referenceNumber": "37", + "name": "Open LDAP Public License v2.8", + "licenseId": "OLDAP-2.8", + "seeAlso": [ + "http://www.openldap.org/software/release/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./OML.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OML.json", + "referenceNumber": "65", + "name": "Open Market License", + "licenseId": "OML", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Open_Market_License" + ], + "isOsiApproved": false + }, + { + "reference": "./OPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OPL-1.0.json", + "referenceNumber": "343", + "name": "Open Public License v1.0", + "licenseId": "OPL-1.0", + "seeAlso": [ + "http://old.koalateam.com/jackaroo/OPL_1_0.TXT", + "https://fedoraproject.org/wiki/Licensing/Open_Public_License" + ], + "isOsiApproved": false + }, + { + "reference": "./OSET-PL-2.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/OSET-PL-2.1.json", + "referenceNumber": "291", + "name": "OSET Public License version 2.1", + "licenseId": "OSET-PL-2.1", + "seeAlso": [ + "http://www.osetfoundation.org/public-license", + "https://opensource.org/licenses/OPL-2.1" + ], + "isOsiApproved": true + }, + { + "reference": "./OSL-1.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/OSL-1.0.json", + "referenceNumber": "85", + "name": "Open Software License 1.0", + "licenseId": "OSL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/OSL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./OSL-1.1.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/OSL-1.1.json", + "referenceNumber": "334", + "name": "Open Software License 1.1", + "licenseId": "OSL-1.1", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/OSL1.1" + ], + "isOsiApproved": false + }, + { + "reference": "./OSL-2.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/OSL-2.0.json", + "referenceNumber": "20", + "name": "Open Software License 2.0", + "licenseId": "OSL-2.0", + "seeAlso": [ + "http://web.archive.org/web/20041020171434/http://www.rosenlaw.com/osl2.0.html" + ], + "isOsiApproved": true + }, + { + "reference": "./OSL-2.1.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/OSL-2.1.json", + "referenceNumber": "24", + "name": "Open Software License 2.1", + "licenseId": "OSL-2.1", + "seeAlso": [ + "http://web.archive.org/web/20050212003940/http://www.rosenlaw.com/osl21.htm", + "https://opensource.org/licenses/OSL-2.1" + ], + "isOsiApproved": true + }, + { + "reference": "./OSL-3.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/OSL-3.0.json", + "referenceNumber": "100", + "name": "Open Software License 3.0", + "licenseId": "OSL-3.0", + "seeAlso": [ + "https://web.archive.org/web/20120101081418/http://rosenlaw.com:80/OSL3.0.htm", + "https://opensource.org/licenses/OSL-3.0" + ], + "isOsiApproved": true + }, + { + "reference": "./OpenSSL.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/OpenSSL.json", + "referenceNumber": "249", + "name": "OpenSSL License", + "licenseId": "OpenSSL", + "seeAlso": [ + "http://www.openssl.org/source/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./PDDL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/PDDL-1.0.json", + "referenceNumber": "14", + "name": "ODC Public Domain Dedication \u0026 License 1.0", + "licenseId": "PDDL-1.0", + "seeAlso": [ + "http://opendatacommons.org/licenses/pddl/1.0/" + ], + "isOsiApproved": false + }, + { + "reference": "./PHP-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/PHP-3.0.json", + "referenceNumber": "385", + "name": "PHP License v3.0", + "licenseId": "PHP-3.0", + "seeAlso": [ + "http://www.php.net/license/3_0.txt", + "https://opensource.org/licenses/PHP-3.0" + ], + "isOsiApproved": true + }, + { + "reference": "./PHP-3.01.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/PHP-3.01.json", + "referenceNumber": "316", + "name": "PHP License v3.01", + "licenseId": "PHP-3.01", + "seeAlso": [ + "http://www.php.net/license/3_01.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./Parity-6.0.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Parity-6.0.0.json", + "referenceNumber": "91", + "name": "The Parity Public License 6.0.0", + "licenseId": "Parity-6.0.0", + "seeAlso": [ + "https://paritylicense.com/versions/6.0.0.html" + ], + "isOsiApproved": false + }, + { + "reference": "./Plexus.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Plexus.json", + "referenceNumber": "225", + "name": "Plexus Classworlds License", + "licenseId": "Plexus", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Plexus_Classworlds_License" + ], + "isOsiApproved": false + }, + { + "reference": "./PostgreSQL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/PostgreSQL.json", + "referenceNumber": "247", + "name": "PostgreSQL License", + "licenseId": "PostgreSQL", + "seeAlso": [ + "http://www.postgresql.org/about/licence", + "https://opensource.org/licenses/PostgreSQL" + ], + "isOsiApproved": true + }, + { + "reference": "./Python-2.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/Python-2.0.json", + "referenceNumber": "35", + "name": "Python License 2.0", + "licenseId": "Python-2.0", + "seeAlso": [ + "https://opensource.org/licenses/Python-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "./QPL-1.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/QPL-1.0.json", + "referenceNumber": "27", + "name": "Q Public License 1.0", + "licenseId": "QPL-1.0", + "seeAlso": [ + "http://doc.qt.nokia.com/3.3/license.html", + "https://opensource.org/licenses/QPL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./Qhull.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Qhull.json", + "referenceNumber": "67", + "name": "Qhull License", + "licenseId": "Qhull", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Qhull" + ], + "isOsiApproved": false + }, + { + "reference": "./RHeCos-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/RHeCos-1.1.json", + "referenceNumber": "149", + "name": "Red Hat eCos Public License v1.1", + "licenseId": "RHeCos-1.1", + "seeAlso": [ + "http://ecos.sourceware.org/old-license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./RPL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/RPL-1.1.json", + "referenceNumber": "269", + "name": "Reciprocal Public License 1.1", + "licenseId": "RPL-1.1", + "seeAlso": [ + "https://opensource.org/licenses/RPL-1.1" + ], + "isOsiApproved": true + }, + { + "reference": "./RPL-1.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/RPL-1.5.json", + "referenceNumber": "227", + "name": "Reciprocal Public License 1.5", + "licenseId": "RPL-1.5", + "seeAlso": [ + "https://opensource.org/licenses/RPL-1.5" + ], + "isOsiApproved": true + }, + { + "reference": "./RPSL-1.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/RPSL-1.0.json", + "referenceNumber": "273", + "name": "RealNetworks Public Source License v1.0", + "licenseId": "RPSL-1.0", + "seeAlso": [ + "https://helixcommunity.org/content/rpsl", + "https://opensource.org/licenses/RPSL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./RSA-MD.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/RSA-MD.json", + "referenceNumber": "82", + "name": "RSA Message-Digest License ", + "licenseId": "RSA-MD", + "seeAlso": [ + "http://www.faqs.org/rfcs/rfc1321.html" + ], + "isOsiApproved": false + }, + { + "reference": "./RSCPL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/RSCPL.json", + "referenceNumber": "211", + "name": "Ricoh Source Code Public License", + "licenseId": "RSCPL", + "seeAlso": [ + "http://wayback.archive.org/web/20060715140826/http://www.risource.org/RPL/RPL-1.0A.shtml", + "https://opensource.org/licenses/RSCPL" + ], + "isOsiApproved": true + }, + { + "reference": "./Rdisc.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Rdisc.json", + "referenceNumber": "295", + "name": "Rdisc License", + "licenseId": "Rdisc", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Rdisc_License" + ], + "isOsiApproved": false + }, + { + "reference": "./Ruby.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/Ruby.json", + "referenceNumber": "263", + "name": "Ruby License", + "licenseId": "Ruby", + "seeAlso": [ + "http://www.ruby-lang.org/en/LICENSE.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./SAX-PD.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/SAX-PD.json", + "referenceNumber": "140", + "name": "Sax Public Domain Notice", + "licenseId": "SAX-PD", + "seeAlso": [ + "http://www.saxproject.org/copying.html" + ], + "isOsiApproved": false + }, + { + "reference": "./SCEA.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/SCEA.json", + "referenceNumber": "16", + "name": "SCEA Shared Source License", + "licenseId": "SCEA", + "seeAlso": [ + "http://research.scea.com/scea_shared_source_license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./SGI-B-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/SGI-B-1.0.json", + "referenceNumber": "90", + "name": "SGI Free Software License B v1.0", + "licenseId": "SGI-B-1.0", + "seeAlso": [ + "http://oss.sgi.com/projects/FreeB/SGIFreeSWLicB.1.0.html" + ], + "isOsiApproved": false + }, + { + "reference": "./SGI-B-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/SGI-B-1.1.json", + "referenceNumber": "241", + "name": "SGI Free Software License B v1.1", + "licenseId": "SGI-B-1.1", + "seeAlso": [ + "http://oss.sgi.com/projects/FreeB/" + ], + "isOsiApproved": false + }, + { + "reference": "./SGI-B-2.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/SGI-B-2.0.json", + "referenceNumber": "272", + "name": "SGI Free Software License B v2.0", + "licenseId": "SGI-B-2.0", + "seeAlso": [ + "http://oss.sgi.com/projects/FreeB/SGIFreeSWLicB.2.0.pdf" + ], + "isOsiApproved": false + }, + { + "reference": "./SHL-0.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/SHL-0.5.json", + "referenceNumber": "72", + "name": "Solderpad Hardware License v0.5", + "licenseId": "SHL-0.5", + "seeAlso": [ + "https://solderpad.org/licenses/SHL-0.5/" + ], + "isOsiApproved": false + }, + { + "reference": "./SHL-0.51.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/SHL-0.51.json", + "referenceNumber": "314", + "name": "Solderpad Hardware License, Version 0.51", + "licenseId": "SHL-0.51", + "seeAlso": [ + "https://solderpad.org/licenses/SHL-0.51/" + ], + "isOsiApproved": false + }, + { + "reference": "./SISSL.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/SISSL.json", + "referenceNumber": "74", + "name": "Sun Industry Standards Source License v1.1", + "licenseId": "SISSL", + "seeAlso": [ + "http://www.openoffice.org/licenses/sissl_license.html", + "https://opensource.org/licenses/SISSL" + ], + "isOsiApproved": true + }, + { + "reference": "./SISSL-1.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/SISSL-1.2.json", + "referenceNumber": "7", + "name": "Sun Industry Standards Source License v1.2", + "licenseId": "SISSL-1.2", + "seeAlso": [ + "http://gridscheduler.sourceforge.net/Gridengine_SISSL_license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./SMLNJ.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/SMLNJ.json", + "referenceNumber": "296", + "name": "Standard ML of New Jersey License", + "licenseId": "SMLNJ", + "seeAlso": [ + "https://www.smlnj.org/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./SMPPL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/SMPPL.json", + "referenceNumber": "127", + "name": "Secure Messaging Protocol Public License", + "licenseId": "SMPPL", + "seeAlso": [ + "https://github.com/dcblake/SMP/blob/master/Documentation/License.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./SNIA.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/SNIA.json", + "referenceNumber": "230", + "name": "SNIA Public License 1.1", + "licenseId": "SNIA", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/SNIA_Public_License" + ], + "isOsiApproved": false + }, + { + "reference": "./SPL-1.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/SPL-1.0.json", + "referenceNumber": "54", + "name": "Sun Public License v1.0", + "licenseId": "SPL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/SPL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./SSPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/SSPL-1.0.json", + "referenceNumber": "356", + "name": "Server Side Public License, v 1", + "licenseId": "SSPL-1.0", + "seeAlso": [ + "https://www.mongodb.com/licensing/server-side-public-license" + ], + "isOsiApproved": false + }, + { + "reference": "./SWL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/SWL.json", + "referenceNumber": "208", + "name": "Scheme Widget Library (SWL) Software License Agreement", + "licenseId": "SWL", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/SWL" + ], + "isOsiApproved": false + }, + { + "reference": "./Saxpath.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Saxpath.json", + "referenceNumber": "18", + "name": "Saxpath License", + "licenseId": "Saxpath", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Saxpath_License" + ], + "isOsiApproved": false + }, + { + "reference": "./Sendmail.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Sendmail.json", + "referenceNumber": "151", + "name": "Sendmail License", + "licenseId": "Sendmail", + "seeAlso": [ + "http://www.sendmail.com/pdfs/open_source/sendmail_license.pdf", + "https://web.archive.org/web/20160322142305/https://www.sendmail.com/pdfs/open_source/sendmail_license.pdf" + ], + "isOsiApproved": false + }, + { + "reference": "./Sendmail-8.23.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Sendmail-8.23.json", + "referenceNumber": "41", + "name": "Sendmail License 8.23", + "licenseId": "Sendmail-8.23", + "seeAlso": [ + "https://www.proofpoint.com/sites/default/files/sendmail-license.pdf", + "https://web.archive.org/web/20181003101040/https://www.proofpoint.com/sites/default/files/sendmail-license.pdf" + ], + "isOsiApproved": false + }, + { + "reference": "./SimPL-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/SimPL-2.0.json", + "referenceNumber": "184", + "name": "Simple Public License 2.0", + "licenseId": "SimPL-2.0", + "seeAlso": [ + "https://opensource.org/licenses/SimPL-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "./Sleepycat.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/Sleepycat.json", + "referenceNumber": "290", + "name": "Sleepycat License", + "licenseId": "Sleepycat", + "seeAlso": [ + "https://opensource.org/licenses/Sleepycat" + ], + "isOsiApproved": true + }, + { + "reference": "./Spencer-86.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Spencer-86.json", + "referenceNumber": "313", + "name": "Spencer License 86", + "licenseId": "Spencer-86", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Henry_Spencer_Reg-Ex_Library_License" + ], + "isOsiApproved": false + }, + { + "reference": "./Spencer-94.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Spencer-94.json", + "referenceNumber": "29", + "name": "Spencer License 94", + "licenseId": "Spencer-94", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Henry_Spencer_Reg-Ex_Library_License" + ], + "isOsiApproved": false + }, + { + "reference": "./Spencer-99.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Spencer-99.json", + "referenceNumber": "386", + "name": "Spencer License 99", + "licenseId": "Spencer-99", + "seeAlso": [ + "http://www.opensource.apple.com/source/tcl/tcl-5/tcl/generic/regfronts.c" + ], + "isOsiApproved": false + }, + { + "reference": "./StandardML-NJ.html", + "isDeprecatedLicenseId": true, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/StandardML-NJ.json", + "referenceNumber": "219", + "name": "Standard ML of New Jersey License", + "licenseId": "StandardML-NJ", + "seeAlso": [ + "http://www.smlnj.org//license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./SugarCRM-1.1.3.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/SugarCRM-1.1.3.json", + "referenceNumber": "292", + "name": "SugarCRM Public License v1.1.3", + "licenseId": "SugarCRM-1.1.3", + "seeAlso": [ + "http://www.sugarcrm.com/crm/SPL" + ], + "isOsiApproved": false + }, + { + "reference": "./TAPR-OHL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/TAPR-OHL-1.0.json", + "referenceNumber": "267", + "name": "TAPR Open Hardware License v1.0", + "licenseId": "TAPR-OHL-1.0", + "seeAlso": [ + "https://www.tapr.org/OHL" + ], + "isOsiApproved": false + }, + { + "reference": "./TCL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/TCL.json", + "referenceNumber": "265", + "name": "TCL/TK License", + "licenseId": "TCL", + "seeAlso": [ + "http://www.tcl.tk/software/tcltk/license.html", + "https://fedoraproject.org/wiki/Licensing/TCL" + ], + "isOsiApproved": false + }, + { + "reference": "./TCP-wrappers.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/TCP-wrappers.json", + "referenceNumber": "274", + "name": "TCP Wrappers License", + "licenseId": "TCP-wrappers", + "seeAlso": [ + "http://rc.quest.com/topics/openssh/license.php#tcpwrappers" + ], + "isOsiApproved": false + }, + { + "reference": "./TMate.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/TMate.json", + "referenceNumber": "253", + "name": "TMate Open Source License", + "licenseId": "TMate", + "seeAlso": [ + "http://svnkit.com/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./TORQUE-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/TORQUE-1.1.json", + "referenceNumber": "171", + "name": "TORQUE v2.5+ Software License v1.1", + "licenseId": "TORQUE-1.1", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/TORQUEv1.1" + ], + "isOsiApproved": false + }, + { + "reference": "./TOSL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/TOSL.json", + "referenceNumber": "360", + "name": "Trusster Open Source License", + "licenseId": "TOSL", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/TOSL" + ], + "isOsiApproved": false + }, + { + "reference": "./TU-Berlin-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/TU-Berlin-1.0.json", + "referenceNumber": "373", + "name": "Technische Universitaet Berlin License 1.0", + "licenseId": "TU-Berlin-1.0", + "seeAlso": [ + "https://github.com/swh/ladspa/blob/7bf6f3799fdba70fda297c2d8fd9f526803d9680/gsm/COPYRIGHT" + ], + "isOsiApproved": false + }, + { + "reference": "./TU-Berlin-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/TU-Berlin-2.0.json", + "referenceNumber": "391", + "name": "Technische Universitaet Berlin License 2.0", + "licenseId": "TU-Berlin-2.0", + "seeAlso": [ + "https://github.com/CorsixTH/deps/blob/fd339a9f526d1d9c9f01ccf39e438a015da50035/licences/libgsm.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./UPL-1.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/UPL-1.0.json", + "referenceNumber": "205", + "name": "Universal Permissive License v1.0", + "licenseId": "UPL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/UPL" + ], + "isOsiApproved": true + }, + { + "reference": "./Unicode-DFS-2015.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Unicode-DFS-2015.json", + "referenceNumber": "11", + "name": "Unicode License Agreement - Data Files and Software (2015)", + "licenseId": "Unicode-DFS-2015", + "seeAlso": [ + "https://web.archive.org/web/20151224134844/http://unicode.org/copyright.html" + ], + "isOsiApproved": false + }, + { + "reference": "./Unicode-DFS-2016.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Unicode-DFS-2016.json", + "referenceNumber": "382", + "name": "Unicode License Agreement - Data Files and Software (2016)", + "licenseId": "Unicode-DFS-2016", + "seeAlso": [ + "http://www.unicode.org/copyright.html" + ], + "isOsiApproved": false + }, + { + "reference": "./Unicode-TOU.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Unicode-TOU.json", + "referenceNumber": "70", + "name": "Unicode Terms of Use", + "licenseId": "Unicode-TOU", + "seeAlso": [ + "http://www.unicode.org/copyright.html" + ], + "isOsiApproved": false + }, + { + "reference": "./Unlicense.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/Unlicense.json", + "referenceNumber": "293", + "name": "The Unlicense", + "licenseId": "Unlicense", + "seeAlso": [ + "http://unlicense.org/" + ], + "isOsiApproved": false + }, + { + "reference": "./VOSTROM.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/VOSTROM.json", + "referenceNumber": "228", + "name": "VOSTROM Public License for Open Source", + "licenseId": "VOSTROM", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/VOSTROM" + ], + "isOsiApproved": false + }, + { + "reference": "./VSL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/VSL-1.0.json", + "referenceNumber": "180", + "name": "Vovida Software License v1.0", + "licenseId": "VSL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/VSL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./Vim.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/Vim.json", + "referenceNumber": "133", + "name": "Vim License", + "licenseId": "Vim", + "seeAlso": [ + "http://vimdoc.sourceforge.net/htmldoc/uganda.html" + ], + "isOsiApproved": false + }, + { + "reference": "./W3C.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/W3C.json", + "referenceNumber": "351", + "name": "W3C Software Notice and License (2002-12-31)", + "licenseId": "W3C", + "seeAlso": [ + "http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231.html", + "https://opensource.org/licenses/W3C" + ], + "isOsiApproved": true + }, + { + "reference": "./W3C-19980720.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/W3C-19980720.json", + "referenceNumber": "323", + "name": "W3C Software Notice and License (1998-07-20)", + "licenseId": "W3C-19980720", + "seeAlso": [ + "http://www.w3.org/Consortium/Legal/copyright-software-19980720.html" + ], + "isOsiApproved": false + }, + { + "reference": "./W3C-20150513.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/W3C-20150513.json", + "referenceNumber": "51", + "name": "W3C Software Notice and Document License (2015-05-13)", + "licenseId": "W3C-20150513", + "seeAlso": [ + "https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document" + ], + "isOsiApproved": false + }, + { + "reference": "./WTFPL.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/WTFPL.json", + "referenceNumber": "368", + "name": "Do What The F*ck You Want To Public License", + "licenseId": "WTFPL", + "seeAlso": [ + "http://sam.zoy.org/wtfpl/COPYING" + ], + "isOsiApproved": false + }, + { + "reference": "./Watcom-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Watcom-1.0.json", + "referenceNumber": "177", + "name": "Sybase Open Watcom Public License 1.0", + "licenseId": "Watcom-1.0", + "seeAlso": [ + "https://opensource.org/licenses/Watcom-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "./Wsuipa.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Wsuipa.json", + "referenceNumber": "135", + "name": "Wsuipa License", + "licenseId": "Wsuipa", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Wsuipa" + ], + "isOsiApproved": false + }, + { + "reference": "./X11.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/X11.json", + "referenceNumber": "188", + "name": "X11 License", + "licenseId": "X11", + "seeAlso": [ + "http://www.xfree86.org/3.3.6/COPYRIGHT2.html#3" + ], + "isOsiApproved": false + }, + { + "reference": "./XFree86-1.1.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/XFree86-1.1.json", + "referenceNumber": "243", + "name": "XFree86 License 1.1", + "licenseId": "XFree86-1.1", + "seeAlso": [ + "http://www.xfree86.org/current/LICENSE4.html" + ], + "isOsiApproved": false + }, + { + "reference": "./XSkat.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/XSkat.json", + "referenceNumber": "96", + "name": "XSkat License", + "licenseId": "XSkat", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/XSkat_License" + ], + "isOsiApproved": false + }, + { + "reference": "./Xerox.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Xerox.json", + "referenceNumber": "163", + "name": "Xerox License", + "licenseId": "Xerox", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Xerox" + ], + "isOsiApproved": false + }, + { + "reference": "./Xnet.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Xnet.json", + "referenceNumber": "388", + "name": "X.Net License", + "licenseId": "Xnet", + "seeAlso": [ + "https://opensource.org/licenses/Xnet" + ], + "isOsiApproved": true + }, + { + "reference": "./YPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/YPL-1.0.json", + "referenceNumber": "174", + "name": "Yahoo! Public License v1.0", + "licenseId": "YPL-1.0", + "seeAlso": [ + "http://www.zimbra.com/license/yahoo_public_license_1.0.html" + ], + "isOsiApproved": false + }, + { + "reference": "./YPL-1.1.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/YPL-1.1.json", + "referenceNumber": "57", + "name": "Yahoo! Public License v1.1", + "licenseId": "YPL-1.1", + "seeAlso": [ + "http://www.zimbra.com/license/yahoo_public_license_1.1.html" + ], + "isOsiApproved": false + }, + { + "reference": "./ZPL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/ZPL-1.1.json", + "referenceNumber": "359", + "name": "Zope Public License 1.1", + "licenseId": "ZPL-1.1", + "seeAlso": [ + "http://old.zope.org/Resources/License/ZPL-1.1" + ], + "isOsiApproved": false + }, + { + "reference": "./ZPL-2.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/ZPL-2.0.json", + "referenceNumber": "78", + "name": "Zope Public License 2.0", + "licenseId": "ZPL-2.0", + "seeAlso": [ + "http://old.zope.org/Resources/License/ZPL-2.0", + "https://opensource.org/licenses/ZPL-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "./ZPL-2.1.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/ZPL-2.1.json", + "referenceNumber": "345", + "name": "Zope Public License 2.1", + "licenseId": "ZPL-2.1", + "seeAlso": [ + "http://old.zope.org/Resources/ZPL/" + ], + "isOsiApproved": false + }, + { + "reference": "./Zed.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Zed.json", + "referenceNumber": "248", + "name": "Zed License", + "licenseId": "Zed", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Zed" + ], + "isOsiApproved": false + }, + { + "reference": "./Zend-2.0.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/Zend-2.0.json", + "referenceNumber": "198", + "name": "Zend License v2.0", + "licenseId": "Zend-2.0", + "seeAlso": [ + "https://web.archive.org/web/20130517195954/http://www.zend.com/license/2_00.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./Zimbra-1.3.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/Zimbra-1.3.json", + "referenceNumber": "40", + "name": "Zimbra Public License v1.3", + "licenseId": "Zimbra-1.3", + "seeAlso": [ + "http://web.archive.org/web/20100302225219/http://www.zimbra.com/license/zimbra-public-license-1-3.html" + ], + "isOsiApproved": false + }, + { + "reference": "./Zimbra-1.4.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/Zimbra-1.4.json", + "referenceNumber": "238", + "name": "Zimbra Public License v1.4", + "licenseId": "Zimbra-1.4", + "seeAlso": [ + "http://www.zimbra.com/legal/zimbra-public-license-1-4" + ], + "isOsiApproved": false + }, + { + "reference": "./Zlib.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/Zlib.json", + "referenceNumber": "320", + "name": "zlib License", + "licenseId": "Zlib", + "seeAlso": [ + "http://www.zlib.net/zlib_license.html", + "https://opensource.org/licenses/Zlib" + ], + "isOsiApproved": true + }, + { + "reference": "./blessing.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/blessing.json", + "referenceNumber": "331", + "name": "SQLite Blessing", + "licenseId": "blessing", + "seeAlso": [ + "https://www.sqlite.org/src/artifact/e33a4df7e32d742a?ln\u003d4-9", + "https://sqlite.org/src/artifact/df5091916dbb40e6" + ], + "isOsiApproved": false + }, + { + "reference": "./bzip2-1.0.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/bzip2-1.0.5.json", + "referenceNumber": "200", + "name": "bzip2 and libbzip2 License v1.0.5", + "licenseId": "bzip2-1.0.5", + "seeAlso": [ + "http://bzip.org/1.0.5/bzip2-manual-1.0.5.html" + ], + "isOsiApproved": false + }, + { + "reference": "./bzip2-1.0.6.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/bzip2-1.0.6.json", + "referenceNumber": "302", + "name": "bzip2 and libbzip2 License v1.0.6", + "licenseId": "bzip2-1.0.6", + "seeAlso": [ + "https://github.com/asimonov-im/bzip2/blob/master/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "./copyleft-next-0.3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/copyleft-next-0.3.0.json", + "referenceNumber": "176", + "name": "copyleft-next 0.3.0", + "licenseId": "copyleft-next-0.3.0", + "seeAlso": [ + "https://github.com/copyleft-next/copyleft-next/blob/master/Releases/copyleft-next-0.3.0" + ], + "isOsiApproved": false + }, + { + "reference": "./copyleft-next-0.3.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/copyleft-next-0.3.1.json", + "referenceNumber": "347", + "name": "copyleft-next 0.3.1", + "licenseId": "copyleft-next-0.3.1", + "seeAlso": [ + "https://github.com/copyleft-next/copyleft-next/blob/master/Releases/copyleft-next-0.3.1" + ], + "isOsiApproved": false + }, + { + "reference": "./curl.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/curl.json", + "referenceNumber": "260", + "name": "curl License", + "licenseId": "curl", + "seeAlso": [ + "https://github.com/bagder/curl/blob/master/COPYING" + ], + "isOsiApproved": false + }, + { + "reference": "./diffmark.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/diffmark.json", + "referenceNumber": "367", + "name": "diffmark license", + "licenseId": "diffmark", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/diffmark" + ], + "isOsiApproved": false + }, + { + "reference": "./dvipdfm.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/dvipdfm.json", + "referenceNumber": "143", + "name": "dvipdfm License", + "licenseId": "dvipdfm", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/dvipdfm" + ], + "isOsiApproved": false + }, + { + "reference": "./eCos-2.0.html", + "isDeprecatedLicenseId": true, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/eCos-2.0.json", + "referenceNumber": "329", + "name": "eCos license version 2.0", + "licenseId": "eCos-2.0", + "seeAlso": [ + "https://www.gnu.org/licenses/ecos-license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./eGenix.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/eGenix.json", + "referenceNumber": "204", + "name": "eGenix.com Public License 1.1.0", + "licenseId": "eGenix", + "seeAlso": [ + "http://www.egenix.com/products/eGenix.com-Public-License-1.1.0.pdf", + "https://fedoraproject.org/wiki/Licensing/eGenix.com_Public_License_1.1.0" + ], + "isOsiApproved": false + }, + { + "reference": "./gSOAP-1.3b.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/gSOAP-1.3b.json", + "referenceNumber": "346", + "name": "gSOAP Public License v1.3b", + "licenseId": "gSOAP-1.3b", + "seeAlso": [ + "http://www.cs.fsu.edu/~engelen/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "./gnuplot.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/gnuplot.json", + "referenceNumber": "10", + "name": "gnuplot License", + "licenseId": "gnuplot", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Gnuplot" + ], + "isOsiApproved": false + }, + { + "reference": "./iMatix.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/iMatix.json", + "referenceNumber": "342", + "name": "iMatix Standard Function Library Agreement", + "licenseId": "iMatix", + "seeAlso": [ + "http://legacy.imatix.com/html/sfl/sfl4.htm#license" + ], + "isOsiApproved": false + }, + { + "reference": "./libpng-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/libpng-2.0.json", + "referenceNumber": "76", + "name": "PNG Reference Library version 2", + "licenseId": "libpng-2.0", + "seeAlso": [ + "http://www.libpng.org/pub/png/src/libpng-LICENSE.txt" + ], + "isOsiApproved": false + }, + { + "reference": "./libtiff.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/libtiff.json", + "referenceNumber": "220", + "name": "libtiff License", + "licenseId": "libtiff", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/libtiff" + ], + "isOsiApproved": false + }, + { + "reference": "./mpich2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/mpich2.json", + "referenceNumber": "318", + "name": "mpich2 License", + "licenseId": "mpich2", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/MIT" + ], + "isOsiApproved": false + }, + { + "reference": "./psfrag.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/psfrag.json", + "referenceNumber": "245", + "name": "psfrag License", + "licenseId": "psfrag", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/psfrag" + ], + "isOsiApproved": false + }, + { + "reference": "./psutils.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/psutils.json", + "referenceNumber": "126", + "name": "psutils License", + "licenseId": "psutils", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/psutils" + ], + "isOsiApproved": false + }, + { + "reference": "./wxWindows.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "http://spdx.org/licenses/wxWindows.json", + "referenceNumber": "86", + "name": "wxWindows Library License", + "licenseId": "wxWindows", + "seeAlso": [ + "https://opensource.org/licenses/WXwindows" + ], + "isOsiApproved": false + }, + { + "reference": "./xinetd.html", + "isDeprecatedLicenseId": false, + "isFsfLibre": true, + "detailsUrl": "http://spdx.org/licenses/xinetd.json", + "referenceNumber": "146", + "name": "xinetd License", + "licenseId": "xinetd", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Xinetd_License" + ], + "isOsiApproved": false + }, + { + "reference": "./xpp.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/xpp.json", + "referenceNumber": "275", + "name": "XPP License", + "licenseId": "xpp", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/xpp" + ], + "isOsiApproved": false + }, + { + "reference": "./zlib-acknowledgement.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "http://spdx.org/licenses/zlib-acknowledgement.json", + "referenceNumber": "321", + "name": "zlib/libpng License with Acknowledgement", + "licenseId": "zlib-acknowledgement", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/ZlibWithAcknowledgement" + ], + "isOsiApproved": false + } + ], + "releaseDate": "2019-07-10" +} diff --git a/src/model/license.py b/src/model/license.py index f0ea53ac1..dce4cbee5 100644 --- a/src/model/license.py +++ b/src/model/license.py @@ -10,7 +10,7 @@ # limitations under the License. -from spdx import config +from src import config def determine_full_name(identifier: str, full_name: str): diff --git a/spdx/parsers/lexers/__init__.py b/tests/clitools/__init__.py similarity index 100% rename from spdx/parsers/lexers/__init__.py rename to tests/clitools/__init__.py diff --git a/tests/test_cli_convertor.py b/tests/clitools/test_cli_convertor.py similarity index 97% rename from tests/test_cli_convertor.py rename to tests/clitools/test_cli_convertor.py index 11413b933..3c0d25d89 100644 --- a/tests/test_cli_convertor.py +++ b/tests/clitools/test_cli_convertor.py @@ -12,8 +12,7 @@ import os from unittest import TestCase -from spdx.cli_tools.convertor import determine_infile_and_outfile - +from src.clitools.convertor import determine_infile_and_outfile from tests.testing_utils import raises diff --git a/tests/test_builder.py b/tests/test_builder.py deleted file mode 100644 index d88024d31..000000000 --- a/tests/test_builder.py +++ /dev/null @@ -1,837 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 tests.testing_utils as testing_utils - -from spdx.document import Document -from spdx.license import License -import spdx.parsers.tagvaluebuilders as builders -from spdx.version import Version - - -class TestDocumentBuilder(TestCase): - maxDiff = None - - def setUp(self): - self.document = Document() - self.builder = builders.DocBuilder() - - def test_correct_version(self): - version_str = "SPDX-2.1" - self.builder.set_doc_version(self.document, version_str) - assert self.document.version.major == 2 and self.document.version.minor == 1 - - @testing_utils.raises(builders.CardinalityError) - def test_version_cardinality(self): - version_str = "SPDX-2.1" - self.builder.set_doc_version(self.document, version_str) - self.builder.set_doc_version(self.document, version_str) - - @testing_utils.raises(builders.SPDXValueError) - def test_version_value(self): - version_str = "2.1" - self.builder.set_doc_version(self.document, version_str) - - def test_correct_data_lics(self): - lics_str = "CC0-1.0" - self.builder.set_doc_data_lics(self.document, lics_str) - assert self.document.data_license == License.from_identifier(lics_str) - - @testing_utils.raises(builders.SPDXValueError) - def test_data_lics_value(self): - lics_str = "GPL" - self.builder.set_doc_data_lics(self.document, lics_str) - - @testing_utils.raises(builders.CardinalityError) - def test_data_lics_cardinality(self): - lics_str = "CC0-1.0" - self.builder.set_doc_data_lics(self.document, lics_str) - self.builder.set_doc_data_lics(self.document, lics_str) - - def test_correct_name(self): - name_str = "Sample_Document-V2.1" - self.builder.set_doc_name(self.document, name_str) - assert self.document.name == name_str - - @testing_utils.raises(builders.CardinalityError) - def test_name_cardinality(self): - name_str = "Sample_Document-V2.1" - self.builder.set_doc_name(self.document, name_str) - self.builder.set_doc_name(self.document, name_str) - - def test_correct_doc_namespace(self): - doc_namespace_str = "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301" - self.builder.set_doc_namespace(self.document, doc_namespace_str) - assert self.document.namespace == doc_namespace_str - - @testing_utils.raises(builders.SPDXValueError) - def test_doc_namespace_value(self): - doc_namespace_str = "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DOCUMENT" - self.builder.set_doc_data_lics(self.document, doc_namespace_str) - - @testing_utils.raises(builders.CardinalityError) - def test_doc_namespace_cardinality(self): - doc_namespace_str = "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301" - self.builder.set_doc_namespace(self.document, doc_namespace_str) - self.builder.set_doc_namespace(self.document, doc_namespace_str) - - def test_correct_data_comment(self): - comment_str = "This is a comment." - comment_text = "" + comment_str + "" - self.builder.set_doc_comment(self.document, comment_text) - assert self.document.comment == comment_str - - @testing_utils.raises(builders.CardinalityError) - def test_comment_cardinality(self): - comment_str = "This is a comment." - comment_text = "" + comment_str + "" - self.builder.set_doc_comment(self.document, comment_text) - self.builder.set_doc_comment(self.document, comment_text) - - @testing_utils.raises(builders.SPDXValueError) - def test_comment_value(self): - comment = "sls\nlss" - self.add_relationship() - self.builder.add_relationship_comment(self.document, comment) - - def test_correct_relationship(self): - relationship = "SPDXRef-DOCUMENT DESCRIBES SPDXRef-File" - assert self.builder.add_relationship(self.document, relationship) - - def add_relationship(self): - relate_str = "SPDXRef-DOCUMENT DESCRIBES SPDXRef-File" - self.builder.add_relationship(self.document, relate_str) - - -class TestPackageBuilder(TestCase): - maxDiff = None - - def setUp(self): - self.builder = builders.PackageBuilder() - self.document = Document() - self.entity_builder = builders.EntityBuilder() - - def test_package_cardinality(self): - assert self.builder.create_package(self.document, "pkg1") - self.builder.create_package(self.document, "pkg2") - - def make_package(self): - self.builder.create_package(self.document, "pkg") - - def make_person(self): - per_str = "Person: Bob (bob@example.com)" - per = self.entity_builder.build_person(self.document, per_str) - return per - - @testing_utils.raises(builders.OrderError) - def test_vers_order(self): - self.builder.set_pkg_vers(self.document, "1.1") - - @testing_utils.raises(builders.OrderError) - def test_file_name_order(self): - self.builder.set_pkg_file_name(self.document, "test.jar") - - @testing_utils.raises(builders.OrderError) - def test_pkg_supplier_order(self): - self.builder.set_pkg_supplier(self.document, self.make_person()) - - @testing_utils.raises(builders.OrderError) - def test_pkg_originator_order(self): - self.builder.set_pkg_originator(self.document, self.make_person()) - - @testing_utils.raises(builders.OrderError) - def test_pkg_down_loc_order(self): - self.builder.set_pkg_down_location(self.document, "http://example.com/pkg") - - @testing_utils.raises(builders.OrderError) - def test_pkg_home_order(self): - self.builder.set_pkg_home(self.document, "http://example.com") - - @testing_utils.raises(builders.OrderError) - def test_pkg_verif_order(self): - self.builder.set_pkg_verif_code(self.document, "some code") - - @testing_utils.raises(builders.OrderError) - def test_pkg_chksum_order(self): - self.builder.set_pkg_checksum(self.document, "some code") - - @testing_utils.raises(builders.OrderError) - def test_pkg_source_info_order(self): - self.builder.set_pkg_source_info(self.document, "hello") - - @testing_utils.raises(builders.OrderError) - def test_pkg_licenses_concluded_order(self): - self.builder.set_pkg_licenses_concluded(self.document, "some license") - - @testing_utils.raises(builders.OrderError) - def test_pkg_lics_from_file_order(self): - self.builder.set_pkg_license_from_file(self.document, "some license") - - @testing_utils.raises(builders.OrderError) - def test_pkg_lics_decl_order(self): - self.builder.set_pkg_license_declared(self.document, "license") - - @testing_utils.raises(builders.OrderError) - def test_pkg_lics_comment_order(self): - self.builder.set_pkg_license_comment(self.document, "hello") - - @testing_utils.raises(builders.OrderError) - def test_pkg_attribution_text_order(self): - self.builder.set_pkg_attribution_text(self.document, "hello") - - def test_correct_pkg_attribution_text(self): - self.builder.create_package(self.document, "pkg") - self.builder.set_pkg_attribution_text(self.document, "something") - - @testing_utils.raises(builders.SPDXValueError) - def test_incorrect_pkg_attribution_text(self): - self.builder.create_package(self.document, "pkg") - self.builder.set_pkg_attribution_text(self.document, " text over multiple lines\n with wrong tag ") - - @testing_utils.raises(builders.OrderError) - def test_pkg_cr_text_order(self): - self.builder.set_pkg_cr_text(self.document, "Something") - - @testing_utils.raises(builders.OrderError) - def test_pkg_summary_order(self): - self.builder.set_pkg_summary(self.document, "Something") - - @testing_utils.raises(builders.OrderError) - def test_set_pkg_desc_order(self): - self.builder.set_pkg_desc(self.document, "something") - - @testing_utils.raises(builders.OrderError) - def test_set_pkg_spdx_id_order(self): - self.builder.set_pkg_spdx_id(self.document, "SPDXRe-Package") - - @testing_utils.raises(builders.OrderError) - def test_set_pkg_files_analyzed_order(self): - self.builder.set_pkg_files_analyzed(self.document, "True") - - @testing_utils.raises(builders.OrderError) - def test_set_pkg_comment_order(self): - self.builder.set_pkg_comment(self.document, "something") - - def test_correct_pkg_comment(self): - self.builder.create_package(self.document, "pkg") - self.builder.set_pkg_comment(self.document, "something") - - @testing_utils.raises(builders.SPDXValueError) - def test_incorrect_pkg_comment(self): - self.builder.create_package(self.document, "pkg") - self.builder.set_pkg_comment(self.document, "text in multiple lines\n with wrong tag ") - - def test_correct_pkg_spdx_id(self): - self.builder.create_package(self.document, "pkg") - assert self.builder.set_pkg_spdx_id(self.document, "SPDXRef-Package") - assert self.document.package.spdx_id == "SPDXRef-Package" - - @testing_utils.raises(builders.SPDXValueError) - def test_incorrect_pkg_spdx_id(self): - self.builder.create_package(self.document, "pkg") - assert self.builder.set_pkg_spdx_id(self.document, "SPDXRe-Package") - - @testing_utils.raises(builders.SPDXValueError) - def test_incorrect_pkg_files_analyzed(self): - self.builder.create_package(self.document, "pkg") - assert self.builder.set_pkg_files_analyzed(self.document, "XYZ") - - def test_correct_pkg_files_analyzed_1(self): - self.builder.create_package(self.document, "pkg") - assert self.builder.set_pkg_files_analyzed(self.document, "True") - - def test_correct_pkg_files_analyzed_2(self): - self.builder.create_package(self.document, "pkg") - assert self.builder.set_pkg_files_analyzed(self.document, "true") - - def test_correct_pkg_ext_ref_category(self): - category = "SECURITY" - self.builder.create_package(self.document, "pkg") - self.builder.set_pkg_ext_ref_category(self.document, category) - assert self.document.package.pkg_ext_refs[-1].category == category - - @testing_utils.raises(builders.SPDXValueError) - def test_incorrect_pkg_ext_ref_category(self): - category = "some_other_value" - self.builder.create_package(self.document, "pkg") - self.builder.set_pkg_ext_ref_category(self.document, category) - - def test_correct_pkg_ext_ref_type(self): - pkg_ext_ref_type = "cpe23Type" - self.builder.create_package(self.document, "pkg") - self.builder.set_pkg_ext_ref_type(self.document, pkg_ext_ref_type) - assert ( - self.document.package.pkg_ext_refs[-1].pkg_ext_ref_type == pkg_ext_ref_type - ) - - @testing_utils.raises(builders.SPDXValueError) - def test_incorrect_pkg_ext_ref_type(self): - pkg_ext_ref_type = "cpe23Type with whitespace" - self.builder.create_package(self.document, "pkg") - self.builder.set_pkg_ext_ref_type(self.document, pkg_ext_ref_type) - - def test_correct_pkg_ext_ref_locator(self): - locator = "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*" - self.builder.create_package(self.document, "pkg") - self.builder.set_pkg_ext_ref_locator(self.document, locator) - assert self.document.package.pkg_ext_refs[-1].locator == locator - - @testing_utils.raises(builders.OrderError) - def test_pkg_ext_ref_without_pkg(self): - locator = "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*" - self.builder.set_pkg_ext_ref_locator(self.document, locator) - - def test_correct_pkg_ext_comment(self): - comment_str = "This is a comment." - comment_text = "" + comment_str + "" - self.builder.create_package(self.document, "pkg") - self.builder.set_pkg_ext_ref_category(self.document, "SECURITY") - self.builder.add_pkg_ext_ref_comment(self.document, comment_text) - assert self.document.package.pkg_ext_refs[-1].comment == comment_str - - @testing_utils.raises(builders.OrderError) - def test_pkg_ext_comment_without_pkg_ext_ref(self): - comment_str = "This is a comment." - comment_text = "" + comment_str + "" - self.builder.create_package(self.document, "pkg") - self.builder.add_pkg_ext_ref_comment(self.document, comment_text) - - -class TestSnippetBuilder(TestCase): - maxDiff = None - - def setUp(self): - self.entity_builder = builders.EntityBuilder() - self.builder = builders.SnippetBuilder() - self.document = Document() - - def test_create_snippet(self): - assert self.builder.create_snippet(self.document, "SPDXRef-Snippet") - - @testing_utils.raises(builders.SPDXValueError) - def test_incorrect_snippet_spdx_id(self): - self.builder.create_snippet(self.document, "Some_value_with_$%") - - def test_snippet_name(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snippet_name(self.document, "Name_of_snippet") - - @testing_utils.raises(builders.OrderError) - def test_snippet_name_order(self): - self.builder.set_snippet_name(self.document, "Name_of_snippet") - - def test_snippet_comment(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snippet_comment(self.document, "Comment") - - @testing_utils.raises(builders.OrderError) - def test_snippet_comment_order(self): - self.builder.set_snippet_comment(self.document, "Comment") - - @testing_utils.raises(builders.SPDXValueError) - def test_snippet_comment_text_value(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snippet_comment(self.document, "Comment over multiple lines\n without enclosing tags.") - - @testing_utils.raises(builders.OrderError) - def test_snippet_attribution_text_order(self): - self.builder.set_snippet_attribution_text(self.document, "hello") - - def test_correct_snippet_attribution_text(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snippet_attribution_text( - self.document, "something" - ) - - @testing_utils.raises(builders.SPDXValueError) - def test_incorrect_snippet_attribution_text(self): - self.builder.create_snippet(self.document, "SPDXRef-Package") - self.builder.set_snippet_attribution_text(self.document, "Attribution text over multiple lines" - "\n without enclosing tags.") - - def test_snippet_copyright(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snippet_copyright( - self.document, "Copyright 2008-2010 John Smith" - ) - - @testing_utils.raises(builders.SPDXValueError) - def test_snippet_copyright_text_value(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snippet_copyright( - self.document, "Copyright 2008-2010 John Smith\n over multiple lines without enclosing tags." - ) - - @testing_utils.raises(builders.OrderError) - def test_snippet_copyright_order(self): - self.builder.set_snippet_copyright( - self.document, "Copyright 2008-2010 John Smith" - ) - - def test_snippet_lic_comment(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snippet_lic_comment(self.document, "Lic comment") - - @testing_utils.raises(builders.SPDXValueError) - def test_snippet_lic_comment_text_value(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snippet_lic_comment(self.document, "Lic comment over multiple lines\n without tags") - - @testing_utils.raises(builders.OrderError) - def test_snippet_lic_comment_order(self): - self.builder.set_snippet_lic_comment(self.document, "Lic comment") - - def test_snippet_from_file_spdxid(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snip_from_file_spdxid(self.document, "SPDXRef-DoapSource") - - @testing_utils.raises(builders.SPDXValueError) - def test_snippet_from_file_spdxid_value(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snip_from_file_spdxid(self.document, "#_$random_chars") - - @testing_utils.raises(builders.OrderError) - def test_snippet_from_file_spdxid_order(self): - self.builder.set_snip_from_file_spdxid(self.document, "SPDXRef-DoapSource") - - @testing_utils.raises(builders.CardinalityError) - def test_snippet_from_file_spdxid_cardinality(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snip_from_file_spdxid(self.document, "SPDXRef-DoapSource") - self.builder.set_snip_from_file_spdxid(self.document, "SPDXRef-somevalue") - - def test_snippet_conc_lics(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snip_concluded_license( - self.document, License.from_identifier("Apache-2.0") - ) - - @testing_utils.raises(builders.SPDXValueError) - def test_snippet_conc_lics_value(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snip_concluded_license(self.document, "Apache-2.0") - - @testing_utils.raises(builders.OrderError) - def test_snippet_conc_lics_order(self): - self.builder.set_snip_concluded_license( - self.document, License.from_identifier("Apache-2.0") - ) - - @testing_utils.raises(builders.CardinalityError) - def test_snippet_conc_lics_cardinality(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snip_concluded_license( - self.document, License.from_identifier("Apache-2.0") - ) - self.builder.set_snip_concluded_license( - self.document, License.from_identifier("Apache-2.0") - ) - - def test_snippet_lics_info(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snippet_lics_info( - self.document, License.from_identifier("Apache-2.0") - ) - self.builder.set_snippet_lics_info( - self.document, License.from_identifier("GPL-2.0-or-later") - ) - - @testing_utils.raises(builders.SPDXValueError) - def test_snippet_lics_info_value(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snippet_lics_info(self.document, "Apache-2.0") - - @testing_utils.raises(builders.OrderError) - def test_snippet_lics_info_order(self): - self.builder.set_snippet_lics_info( - self.document, License.from_identifier("Apache-2.0") - ) - - def test_snippet_byte_range(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snippet_byte_range(self.document, "310:420") - - @testing_utils.raises(builders.OrderError) - def test_snippet_byte_range_order(self): - self.builder.set_snippet_byte_range(self.document, "310:420") - - @testing_utils.raises(builders.SPDXValueError) - def test_snippet_byte_range_value(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snippet_byte_range(self.document, "310:30") - - def test_snippet_line_range(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snippet_line_range(self.document, "5:23") - - @testing_utils.raises(builders.OrderError) - def test_snippet_line_range_order(self): - self.builder.set_snippet_line_range(self.document, "5:23") - - @testing_utils.raises(builders.SPDXValueError) - def test_snippet_line_range_value(self): - self.builder.create_snippet(self.document, "SPDXRef-Snippet") - self.builder.set_snippet_line_range(self.document, "23:5") diff --git a/tests/test_config.py b/tests/test_config.py deleted file mode 100644 index 718c3885b..000000000 --- a/tests/test_config.py +++ /dev/null @@ -1,45 +0,0 @@ - -# Copyright (c) the SPDX tools authors -# 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 - -from spdx import config -from spdx.version import Version - - -class TestLicenseList(TestCase): - maxDiff = None - - def test_load_license_list(self): - version, licenses_map = config.load_license_list(config._licenses) - assert version == ('3', '6') - # Test some instances in licenses_map - assert licenses_map['MIT License'] == 'MIT' - assert licenses_map['MIT'] == 'MIT License' - assert licenses_map['Apache License 2.0'] == 'Apache-2.0' - assert licenses_map['Apache-2.0'] == 'Apache License 2.0' - assert licenses_map['GNU General Public License v3.0 only'] == 'GPL-3.0-only' - assert licenses_map['GPL-3.0-only'] == 'GNU General Public License v3.0 only' - - def test_config_license_list_version_constant(self): - assert config.LICENSE_LIST_VERSION == Version(major=3, minor=6) - - def test_load_exception_list(self): - version, exception_map = config.load_exception_list(config._exceptions) - assert version == ('3', '6') - # Test some instances in exception_map - assert exception_map['Bison exception 2.2'] == 'Bison-exception-2.2' - assert exception_map['Bison-exception-2.2'] == 'Bison exception 2.2' - assert exception_map['OpenVPN OpenSSL Exception'] == 'openvpn-openssl-exception' - assert exception_map['openvpn-openssl-exception'] == 'OpenVPN OpenSSL Exception' - assert exception_map['Qt GPL exception 1.0'] == 'Qt-GPL-exception-1.0' - assert exception_map['Qt-GPL-exception-1.0'] == 'Qt GPL exception 1.0' diff --git a/tests/test_conversion.py b/tests/test_conversion.py deleted file mode 100644 index 4d61300d1..000000000 --- a/tests/test_conversion.py +++ /dev/null @@ -1,274 +0,0 @@ - -# Copyright (c) 2014 Ahmed H. Ismail -# 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 codecs -import os -import tempfile -import unittest -from unittest import TestCase - -from spdx.parsers.rdf import Parser as RDFParser -from spdx.parsers.rdfbuilders import Builder as RDFBuilder -from spdx.parsers.loggers import StandardLogger -from spdx.parsers.tagvalue import Parser as TVParser -from spdx.parsers.tagvaluebuilders import Builder as TVBuilder -from spdx.parsers.jsonparser import Parser as JSONParser -from spdx.parsers.yamlparser import Parser as YAMLParser -from spdx.parsers.xmlparser import Parser as XMLParser -from spdx.parsers.jsonyamlxmlbuilders import Builder as JSONYAMLXMLBuilder -import spdx.writers.rdf as rdfwriter -import spdx.writers.tagvalue as tvwriter -import spdx.writers.json as jsonwriter -import spdx.writers.yaml as yamlwriter -import spdx.writers.xml as xmlwriter - -from tests import utils_test - - -def get_temp_file(extension=''): - """ - Return a unique new temporary file location to a non-existing - temporary file that can safely be created without a risk of name - collision. - """ - - if extension and not extension.startswith('.'): - extension = '.' + extension - file_name = 'temp_file' + extension - temp_dir = tempfile.mkdtemp() - return os.path.join(temp_dir, file_name) - - -class TestConversions(TestCase): - maxDiff = None - - def parse_rdf_file(self, file_name): - with open(file_name, mode='rb') as infile: - rdfparser = RDFParser(RDFBuilder(), StandardLogger()) - doc, error = rdfparser.parse(infile) - assert not error - assert doc.validate() == [] - return doc - - def parse_tagvalue_file(self, file_name): - with open(file_name, mode='r') as infile: - tvparser = TVParser(TVBuilder(), StandardLogger()) - tvparser.build() - doc, error = tvparser.parse(infile.read()) - assert not error - assert doc.validate() == [] - return doc - - def parse_json_file(self, file_name): - with open(file_name, mode='r') as infile: - jsonparser = JSONParser(JSONYAMLXMLBuilder(), StandardLogger()) - doc, error = jsonparser.parse(infile) - assert not error - assert doc.validate() == [] - return doc - - def parse_yaml_file(self, file_name): - with open(file_name, mode='r') as infile: - yamlparser = YAMLParser(JSONYAMLXMLBuilder(), StandardLogger()) - doc, error = yamlparser.parse(infile) - assert not error - assert doc.validate() == [] - return doc - - def parse_xml_file(self, file_name): - with open(file_name, mode='r') as infile: - xmlparser = XMLParser(JSONYAMLXMLBuilder(), StandardLogger()) - doc, error = xmlparser.parse(infile) - assert not error - assert doc.validate() == [] - return doc - - def write_tagvalue_file(self, document, file_name): - with codecs.open(file_name, mode='w', encoding='utf-8') as out: - tvwriter.write_document(document, out) - - def write_rdf_file(self, document, file_name): - with open(file_name, mode='wb') as out: - rdfwriter.write_document(document, out) - - def write_json_file(self, document, file_name): - with open(file_name, mode='w') as out: - jsonwriter.write_document(document, out) - - def write_yaml_file(self, document, file_name): - with open(file_name, mode='w') as out: - yamlwriter.write_document(document, out) - - def write_xml_file(self, document, file_name): - with open(file_name, mode='w') as out: - xmlwriter.write_document(document, out) - - def test_tagvalue_rdf(self): - doc = self.parse_tagvalue_file(utils_test.get_test_loc('formats/SPDXTagExample.tag')) - filename = get_temp_file('.rdf') - self.write_rdf_file(doc, filename) - self.parse_rdf_file(filename) - - def test_json_rdf(self): - doc = self.parse_json_file(utils_test.get_test_loc('formats/SPDXJsonExample.json')) - filename = get_temp_file('.rdf') - self.write_rdf_file(doc, filename) - self.parse_rdf_file(filename) - - def test_yaml_rdf(self): - doc = self.parse_yaml_file(utils_test.get_test_loc('formats/SPDXYamlExample.yaml')) - filename = get_temp_file('.rdf') - self.write_rdf_file(doc, filename) - self.parse_rdf_file(filename) - - def test_xml_rdf(self): - doc = self.parse_xml_file(utils_test.get_test_loc('formats/SPDXXmlExample.xml')) - filename = get_temp_file('.rdf') - self.write_rdf_file(doc, filename) - self.parse_rdf_file(filename) - - def test_rdf_rdf(self): - doc = self.parse_rdf_file(utils_test.get_test_loc('formats/SPDXRdfExample.rdf')) - filename = get_temp_file('.rdf') - self.write_rdf_file(doc, filename) - self.parse_rdf_file(filename) - - def test_tagvalue_tagvalue(self): - doc = self.parse_tagvalue_file(utils_test.get_test_loc('formats/SPDXTagExample.tag')) - filename = get_temp_file('.tag') - self.write_tagvalue_file(doc, filename) - self.parse_tagvalue_file(filename) - - @unittest.skip("This fails because we can read/write the mandatory field snippet_byte_range so far only for " - "tv-, json-, yaml- or xml-files, not for rdf-files. https://github.com/spdx/tools-python/issues/274") - def test_rdf_tagvalue(self): - doc = self.parse_rdf_file(utils_test.get_test_loc('formats/SPDXRdfExample.rdf')) - filename = get_temp_file('.tag') - self.write_tagvalue_file(doc, filename) - self.parse_tagvalue_file(filename) - - def test_json_tagvalue(self): - doc = self.parse_json_file(utils_test.get_test_loc('formats/SPDXJsonExample.json')) - filename = get_temp_file('.tag') - self.write_tagvalue_file(doc, filename) - self.parse_tagvalue_file(filename) - - def test_yaml_tagvalue(self): - doc = self.parse_yaml_file(utils_test.get_test_loc('formats/SPDXYamlExample.yaml')) - filename = get_temp_file('.tag') - self.write_tagvalue_file(doc, filename) - self.parse_tagvalue_file(filename) - - def test_xml_tagvalue(self): - doc = self.parse_xml_file(utils_test.get_test_loc('formats/SPDXXmlExample.xml')) - filename = get_temp_file('.tag') - self.write_tagvalue_file(doc, filename) - self.parse_tagvalue_file(filename) - - - def test_tagvalue_json(self): - doc = self.parse_tagvalue_file(utils_test.get_test_loc('formats/SPDXTagExample.tag')) - filename = get_temp_file('.json') - self.write_json_file(doc, filename) - self.parse_json_file(filename) - - @unittest.skip("This fails because we can read/write the mandatory field snippet_byte_range so far only for " - "tv-, json-, yaml- or xml-files, not for rdf-files. https://github.com/spdx/tools-python/issues/274") - def test_rdf_json(self): - doc = self.parse_rdf_file(utils_test.get_test_loc('formats/SPDXRdfExample.rdf')) - filename = get_temp_file('.json') - self.write_json_file(doc, filename) - self.parse_json_file(filename) - - def test_yaml_json(self): - doc = self.parse_yaml_file(utils_test.get_test_loc('formats/SPDXYamlExample.yaml')) - filename = get_temp_file('.json') - self.write_json_file(doc, filename) - self.parse_json_file(filename) - - def test_xml_json(self): - doc = self.parse_xml_file(utils_test.get_test_loc('formats/SPDXXmlExample.xml')) - filename = get_temp_file('.json') - self.write_json_file(doc, filename) - self.parse_json_file(filename) - - def test_json_json(self): - doc = self.parse_json_file(utils_test.get_test_loc('formats/SPDXJsonExample.json')) - filename = get_temp_file('.json') - self.write_json_file(doc, filename) - self.parse_json_file(filename) - - - def test_tagvalue_yaml(self): - doc = self.parse_tagvalue_file(utils_test.get_test_loc('formats/SPDXTagExample.tag')) - filename = get_temp_file('.yaml') - self.write_yaml_file(doc, filename) - self.parse_yaml_file(filename) - - @unittest.skip("This fails because we can read/write the mandatory field snippet_byte_range so far only for " - "tv-, json-, yaml- or xml-files, not for rdf-files. https://github.com/spdx/tools-python/issues/274") - def test_rdf_yaml(self): - doc = self.parse_rdf_file(utils_test.get_test_loc('formats/SPDXRdfExample.rdf')) - filename = get_temp_file('.yaml') - self.write_yaml_file(doc, filename) - self.parse_yaml_file(filename) - - def test_json_yaml(self): - doc = self.parse_json_file(utils_test.get_test_loc('formats/SPDXJsonExample.json')) - filename = get_temp_file('.yaml') - self.write_yaml_file(doc, filename) - self.parse_yaml_file(filename) - - def test_xml_yaml(self): - doc = self.parse_xml_file(utils_test.get_test_loc('formats/SPDXXmlExample.xml')) - filename = get_temp_file('.yaml') - self.write_yaml_file(doc, filename) - self.parse_yaml_file(filename) - - def test_yaml_yaml(self): - doc = self.parse_yaml_file(utils_test.get_test_loc('formats/SPDXYamlExample.yaml')) - filename = get_temp_file('.yaml') - self.write_yaml_file(doc, filename) - self.parse_yaml_file(filename) - - - def test_tagvalue_xml(self): - doc = self.parse_tagvalue_file(utils_test.get_test_loc('formats/SPDXTagExample.tag')) - filename = get_temp_file('.xml') - self.write_xml_file(doc, filename) - self.parse_xml_file(filename) - - @unittest.skip("This fails because we can read/write the mandatory field snippet_byte_range so far only for " - "tv-, json-, yaml- or xml-files, not for rdf-files. https://github.com/spdx/tools-python/issues/274") - def test_rdf_xml(self): - doc = self.parse_rdf_file(utils_test.get_test_loc('formats/SPDXRdfExample.rdf')) - filename = get_temp_file('.xml') - self.write_xml_file(doc, filename) - self.parse_xml_file(filename) - - def test_json_xml(self): - doc = self.parse_json_file(utils_test.get_test_loc('formats/SPDXJsonExample.json')) - filename = get_temp_file('.xml') - self.write_xml_file(doc, filename) - self.parse_xml_file(filename) - - def test_yaml_xml(self): - doc = self.parse_yaml_file(utils_test.get_test_loc('formats/SPDXYamlExample.yaml')) - filename = get_temp_file('.xml') - self.write_xml_file(doc, filename) - self.parse_xml_file(filename) - - def test_xml_xml(self): - doc = self.parse_xml_file(utils_test.get_test_loc('formats/SPDXXmlExample.xml')) - filename = get_temp_file('.xml') - self.write_xml_file(doc, filename) - self.parse_xml_file(filename) diff --git a/tests/test_creationinfo.py b/tests/test_creationinfo.py deleted file mode 100644 index 16201829c..000000000 --- a/tests/test_creationinfo.py +++ /dev/null @@ -1,67 +0,0 @@ - -# Copyright (c) 2014 Ahmed H. Ismail -# 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 datetime import datetime -from unittest import TestCase - -import spdx.config as config -from spdx.creationinfo import CreationInfo -from spdx.creationinfo import Tool -from spdx.creationinfo import Organization -from spdx.creationinfo import Person -from spdx.version import Version - - -class TestCreationInfo(TestCase): - maxDiff = None - - def test_timestamp(self): - dt = datetime(2014, 4, 8, 13, 42, 12) - ci_time = CreationInfo(created=dt) - assert ci_time.created == dt - - def test_iso_format(self): - dt = datetime(2014, 4, 8, 13, 42, 12) - ci_time = CreationInfo(created=dt) - assert ci_time.created_iso_format == "2014-04-08T13:42:12Z" - - def test_comment(self): - ci_default = CreationInfo() - assert ci_default.comment is None - ci_comment = CreationInfo(comment='Hello There') - assert ci_comment.comment == 'Hello There' - - def test_creators(self): - ci = CreationInfo() - assert len(ci.creators) == 0 - person = Person(name='Alice', email='alice@example.com') - tool = Tool(name='spdxtron-9000') - org = Organization(name='Acme', email='acme@example.com') - ci.add_creator(tool) - ci.add_creator(org) - ci.add_creator(person) - assert len(ci.creators) == 3 - assert tool in ci.creators - assert org in ci.creators - assert person in ci.creators - ci.remove_creator(person) - assert len(ci.creators) == 2 - assert tool in ci.creators - assert org in ci.creators - assert person not in ci.creators - - def test_license_list_version(self): - ci = CreationInfo() - assert ci.license_list_version == config.LICENSE_LIST_VERSION - ci = CreationInfo(license_list_version=Version(major=1, minor=0)) - assert ci.license_list_version == Version(major=1, minor=0) - assert not ci.license_list_version == Version(major=1, minor=2) diff --git a/tests/test_document.py b/tests/test_document.py deleted file mode 100644 index 85167ae4b..000000000 --- a/tests/test_document.py +++ /dev/null @@ -1,618 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 os -import shutil -import tempfile -import unittest -from datetime import datetime -from unittest import TestCase - -from spdx.checksum import Checksum, ChecksumAlgorithm -from spdx.config import LICENSE_MAP, EXCEPTION_MAP -from spdx.creationinfo import Tool -from spdx.document import Document, ExternalDocumentRef -from spdx.license import License -from spdx.file import File, FileType -from spdx.package import Package, PackagePurpose -from spdx.parsers.loggers import ErrorMessages -from spdx.relationship import Relationship, RelationshipType -from spdx.utils import NoAssert -from spdx.version import Version - -from tests import utils_test - - -class TestVersion(TestCase): - maxDiff = None - - def test_creation(self): - v = Version(major=2, minor=1) - assert v.major == 2 - assert v.minor == 1 - - def test_comparison(self): - v1 = Version(major=1, minor=2) - v2 = Version(major=2, minor=1) - assert v1 != v2 - assert v1 < v2 - assert v1 <= v2 - assert v2 > v1 - assert v2 >= v1 - v3 = Version(major=1, minor=2) - assert v3 == v1 - assert not v1 < v3 - assert v1 <= v3 - - -class TestDocument(TestCase): - maxDiff = None - - def test_creation(self): - document = Document( - version=Version(major=2, minor=1), - data_license=License(full_name='Academic Free License v1.1', - identifier='AFL-1.1') - ) - document.add_ext_document_reference( - ExternalDocumentRef('DocumentRef-spdx-tool-2.1', - 'https://spdx.org/spdxdocs/spdx-tools-v2.1-3F2504E0-4F89-41D3-9A0C-0305E82C3301', - Checksum(ChecksumAlgorithm.SHA1, 'SOME-SHA1')) - ) - assert document.comment is None - assert document.version == Version(2, 1) - assert document.data_license.identifier == 'AFL-1.1' - assert document.ext_document_references[-1].external_document_id == 'DocumentRef-spdx-tool-2.1' - assert document.ext_document_references[-1].spdx_document_uri == 'https://spdx.org/spdxdocs/spdx-tools-v2.1-3F2504E0-4F89-41D3-9A0C-0305E82C3301' - assert document.ext_document_references[-1].checksum.identifier.name == 'SHA1' - assert document.ext_document_references[-1].checksum.value == 'SOME-SHA1' - - def test_document_validate_failures_returns_informative_messages(self): - doc = Document(Version(2, 1), License.from_identifier('CC0-1.0'), - 'Sample_Document-V2.1', spdx_id='SPDXRef-DOCUMENT', - namespace='https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301') - pack = doc.package = Package('some/path', NoAssert()) - pack.set_checksum(Checksum(ChecksumAlgorithm.SHA256, 'SOME-SHA256')) - file1 = File('./some/path/tofile') - file1.name = './some/path/tofile' - file1.spdx_id = 'SPDXRef-File' - file1.set_checksum(Checksum(ChecksumAlgorithm.SHA1, 'SOME-SHA1')) - lic1 = License.from_identifier('LGPL-2.1-only') - file1.add_lics(lic1) - pack.add_lics_from_file(lic1) - messages = ErrorMessages() - messages = doc.validate(messages) - expected = ['Sample_Document-V2.1: Creation info missing created date.', - 'Sample_Document-V2.1: No creators defined, must have at least one.', - 'Sample_Document-V2.1: some/path: Package download_location can not be None.'] - assert sorted(expected) == sorted(messages) - - def test_document_is_valid_when_using_or_later_licenses(self): - doc = Document(Version(2, 1), License.from_identifier('CC0-1.0'), - 'Sample_Document-V2.1', spdx_id='SPDXRef-DOCUMENT', - namespace='https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301') - doc.creation_info.add_creator(Tool('ScanCode')) - doc.creation_info.set_created_now() - - package = doc.package = Package(name='some/path', download_location=NoAssert()) - package.spdx_id = 'SPDXRef-Package' - package.cr_text = 'Some copyright' - package.set_checksum(Checksum(ChecksumAlgorithm.SHA1, 'SOME-SHA1')) - package.verif_code = 'SOME code' - package.license_declared = NoAssert() - package.conc_lics = NoAssert() - - file1 = File('./some/path/tofile') - file1.name = './some/path/tofile' - file1.spdx_id = 'SPDXRef-File' - file1.file_types = [FileType.OTHER] - file1.set_checksum(Checksum(ChecksumAlgorithm.SHA1, 'SOME-SHA1')) - file1.conc_lics = NoAssert() - file1.copyright = NoAssert() - - lic1 = License.from_identifier('LGPL-2.1-or-later') - file1.add_lics(lic1) - - package.add_lics_from_file(lic1) - doc.add_file(file1) - relationship = create_relationship(package.spdx_id, RelationshipType.CONTAINS, file1.spdx_id) - doc.add_relationship(relationship) - messages = ErrorMessages() - messages = doc.validate(messages) - assert not messages - - def test_document_multiple_packages(self): - doc = Document(Version(2, 1), License.from_identifier('CC0-1.0'), - 'Sample_Document-V2.1', spdx_id='SPDXRef-DOCUMENT', - namespace='https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301') - doc.creation_info.add_creator(Tool('ScanCode')) - doc.creation_info.set_created_now() - - package1 = Package(name='some/path1', download_location=NoAssert()) - package1.spdx_id = 'SPDXRef-Package1' - package1.cr_text = 'Some copyright' - package1.files_verified = False - package1.license_declared = NoAssert() - package1.conc_lics = NoAssert() - doc.add_package(package1) - - package2 = Package(name='some/path2', download_location=NoAssert()) - package2.spdx_id = 'SPDXRef-Package2' - package2.cr_text = 'Some copyright' - package2.files_verified = False - package2.license_declared = NoAssert() - package2.conc_lics = NoAssert() - doc.add_package(package2) - - assert len(doc.packages) == 2 - - def test_document_without_packages(self): - doc = Document(Version(2, 1), License.from_identifier("CC0-1.0"), - 'Sample_Document_V2.1', spdx_id='SPDXRef-DOCUMENT', - namespace='https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301') - doc.creation_info.add_creator(Tool('ScanCode')) - doc.creation_info.set_created_now() - - messages = doc.validate() - assert len(messages.messages) == 0 - -class TestWriters(TestCase): - maxDiff = None - - def _get_lgpl_doc(self, or_later=False): - doc = Document(Version(2, 1), License.from_identifier('CC0-1.0'), - 'Sample_Document-V2.1', spdx_id='SPDXRef-DOCUMENT', - namespace='https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301') - doc.creation_info.add_creator(Tool('ScanCode')) - doc.creation_info.set_created_now() - - package = doc.package = Package(name='some/path', download_location=NoAssert()) - package.spdx_id = 'SPDXRef-Package' - package.cr_text = 'Some copyright' - package.verif_code = 'SOME code' - package.set_checksum(Checksum(ChecksumAlgorithm.SHA1, 'SOME-SHA1')) - package.set_checksum(Checksum(ChecksumAlgorithm.SHA256, 'SOME-SHA256')) - package.license_declared = NoAssert() - package.conc_lics = NoAssert() - package.primary_package_purpose = PackagePurpose.FILE - package.release_date = datetime(2021, 1, 1, 12, 0, 0) - package.built_date = datetime(2021, 1, 1, 12, 0, 0) - package.valid_until_date = datetime(2022, 1, 1, 12, 0, 0) - - - file1 = File('./some/path/tofile') - file1.name = './some/path/tofile' - file1.spdx_id = 'SPDXRef-File' - file1.set_checksum(Checksum(ChecksumAlgorithm.SHA1, 'SOME-SHA1')) - file1.set_checksum(Checksum(ChecksumAlgorithm.SHA256, 'SOME-SHA256')) - file1.conc_lics = NoAssert() - file1.copyright = NoAssert() - file1.file_types = [FileType.OTHER, FileType.SOURCE] - - lic1 = License.from_identifier('LGPL-2.1-only') - if or_later: - lic1 = License.from_identifier('LGPL-2.1-or-later') - - file1.add_lics(lic1) - doc.add_file(file1) - - package.add_lics_from_file(lic1) - relationship = create_relationship(package.spdx_id, RelationshipType.CONTAINS, file1.spdx_id) - doc.add_relationship(relationship) - relationship = create_relationship(doc.spdx_id, RelationshipType.DESCRIBES, package.spdx_id) - doc.add_relationship(relationship) - return doc - - def _get_lgpl_multi_package_doc(self, or_later=False): - doc = Document(Version(2, 1), License.from_identifier('CC0-1.0'), - 'Sample_Document-V2.1', spdx_id='SPDXRef-DOCUMENT', - namespace='https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301') - doc.creation_info.add_creator(Tool('ScanCode')) - doc.creation_info.set_created_now() - - # This package does not have files analyzed - package1 = Package(name='some/path1', download_location=NoAssert()) - package1.spdx_id = 'SPDXRef-Package1' - package1.cr_text = 'Some copyright' - package1.files_analyzed = False - package1.license_declared = NoAssert() - package1.conc_lics = NoAssert() - doc.add_package(package1) - - # This one does, which is the default - package2 = Package(name='some/path2', download_location=NoAssert()) - package2.spdx_id = 'SPDXRef-Package2' - package2.cr_text = 'Some copyright' - package2.license_declared = NoAssert() - package2.conc_lics = NoAssert() - package2.verif_code = 'SOME code' - - # This one does, but per recommendation, we choose to make the - # verification code optional in this library - package3 = Package(name='some/path3', download_location=NoAssert()) - package3.spdx_id = 'SPDXRef-Package3' - package3.cr_text = 'Some copyright' - package3.license_declared = NoAssert() - package3.conc_lics = NoAssert() - - file1 = File('./some/path/tofile') - file1.name = './some/path/tofile' - file1.spdx_id = 'SPDXRef-File' - file1.set_checksum(Checksum(ChecksumAlgorithm.SHA1, 'SOME-SHA1')) - file1.conc_lics = NoAssert() - file1.copyright = NoAssert() - - lic1 = License.from_identifier('LGPL-2.1-only') - if or_later: - lic1 = License.from_identifier('LGPL-2.1-or-later') - - file1.add_lics(lic1) - - package2.add_lics_from_file(lic1) - package3.add_lics_from_file(lic1) - - doc.add_package(package2) - doc.add_package(package3) - doc.add_file(file1) - - relationship = create_relationship(doc.spdx_id, RelationshipType.DESCRIBES, package1.spdx_id) - doc.add_relationship(relationship) - relationship = create_relationship(doc.spdx_id, RelationshipType.DESCRIBES, package2.spdx_id) - doc.add_relationship(relationship) - relationship = create_relationship(doc.spdx_id, RelationshipType.DESCRIBES, package3.spdx_id) - doc.add_relationship(relationship) - relationship = create_relationship(package2.spdx_id, RelationshipType.CONTAINS, file1.spdx_id) - doc.add_relationship(relationship) - relationship = create_relationship(package3.spdx_id, RelationshipType.CONTAINS, file1.spdx_id) - doc.add_relationship(relationship) - - return doc - - def test_write_document_rdf_with_validate(self): - from spdx.writers.rdf import write_document - doc = self._get_lgpl_doc() - temp_dir = '' - try: - temp_dir = tempfile.mkdtemp(prefix='test_spdx') - result_file = os.path.join(temp_dir, 'spdx-simple.rdf') - with open(result_file, 'wb') as output: - write_document(doc, output, validate=True) - - expected_file = utils_test.get_test_loc( - 'doc_write/rdf-simple.json', - test_data_dir=utils_test.test_data_dir) - - utils_test.check_rdf_scan(expected_file, result_file, regen=False) - finally: - if temp_dir and os.path.exists(temp_dir): - shutil.rmtree(temp_dir) - - def test_write_document_rdf_with_or_later_with_validate(self): - from spdx.writers.rdf import write_document - doc = self._get_lgpl_doc(or_later=True) - - temp_dir = '' - try: - temp_dir = tempfile.mkdtemp(prefix='test_spdx') - result_file = os.path.join(temp_dir, 'spdx-simple-plus.rdf') - - # test proper! - with open(result_file, 'wb') as output: - write_document(doc, output, validate=True) - - expected_file = utils_test.get_test_loc( - 'doc_write/rdf-simple-plus.json', - test_data_dir=utils_test.test_data_dir) - - utils_test.check_rdf_scan(expected_file, result_file, regen=False) - finally: - if temp_dir and os.path.exists(temp_dir): - shutil.rmtree(temp_dir) - - def test_write_document_tv_with_validate(self): - from spdx.writers.tagvalue import write_document - doc = self._get_lgpl_doc() - - temp_dir = '' - try: - temp_dir = tempfile.mkdtemp(prefix='test_spdx') - result_file = os.path.join(temp_dir, 'spdx-simple.tv') - with open(result_file, 'w') as output: - write_document(doc, output, validate=True) - - expected_file = utils_test.get_test_loc( - 'doc_write/tv-simple.tv', - test_data_dir=utils_test.test_data_dir) - - utils_test.check_tv_scan(expected_file, result_file, regen=False) - finally: - if temp_dir and os.path.exists(temp_dir): - shutil.rmtree(temp_dir) - - def test_write_document_tv_with_or_later_with_validate(self): - from spdx.writers.tagvalue import write_document - - doc = self._get_lgpl_doc(or_later=True) - - temp_dir = '' - try: - temp_dir = tempfile.mkdtemp(prefix='test_spdx') - result_file = os.path.join(temp_dir, 'spdx-simple-plus.tv') - - # test proper! - with open(result_file, 'w') as output: - write_document(doc, output, validate=True) - - expected_file = utils_test.get_test_loc( - 'doc_write/tv-simple-plus.tv', - test_data_dir=utils_test.test_data_dir) - - utils_test.check_tv_scan(expected_file, result_file, regen=False) - finally: - if temp_dir and os.path.exists(temp_dir): - shutil.rmtree(temp_dir) - - def test_write_document_json_with_validate(self): - from spdx.writers.json import write_document - doc = self._get_lgpl_doc() - - temp_dir = '' - try: - temp_dir = tempfile.mkdtemp(prefix='test_spdx') - result_file = os.path.join(temp_dir, 'spdx-simple.json') - with open(result_file, 'w') as output: - write_document(doc, output, validate=True) - - expected_file = utils_test.get_test_loc( - 'doc_write/json-simple.json', - test_data_dir=utils_test.test_data_dir) - - utils_test.check_json_scan(expected_file, result_file, regen=False) - finally: - if temp_dir and os.path.exists(temp_dir): - shutil.rmtree(temp_dir) - - def test_write_document_json_with_or_later_with_validate(self): - from spdx.writers.json import write_document - doc = self._get_lgpl_doc(or_later=True) - - temp_dir = '' - try: - temp_dir = tempfile.mkdtemp(prefix='test_spdx') - result_file = os.path.join(temp_dir, 'spdx-simple-plus.json') - with open(result_file, 'w') as output: - write_document(doc, output, validate=True) - - expected_file = utils_test.get_test_loc( - 'doc_write/json-simple-plus.json', - test_data_dir=utils_test.test_data_dir) - - utils_test.check_json_scan(expected_file, result_file, regen=False) - finally: - if temp_dir and os.path.exists(temp_dir): - shutil.rmtree(temp_dir) - - def test_write_document_json_multi_package_with_validate(self): - from spdx.writers.json import write_document - doc = self._get_lgpl_multi_package_doc(or_later=True) - - temp_dir = '' - try: - temp_dir = tempfile.mkdtemp(prefix='test_spdx') - result_file = os.path.join(temp_dir, 'spdx-simple-multi-package.json') - with open(result_file, 'w') as output: - write_document(doc, output, validate=True) - - expected_file = utils_test.get_test_loc( - 'doc_write/json-simple-multi-package.json', - test_data_dir=utils_test.test_data_dir) - - utils_test.check_json_scan(expected_file, result_file, regen=False) - finally: - if temp_dir and os.path.exists(temp_dir): - shutil.rmtree(temp_dir) - - - def test_write_document_yaml_with_validate(self): - from spdx.writers.yaml import write_document - doc = self._get_lgpl_doc() - - temp_dir = '' - try: - temp_dir = tempfile.mkdtemp(prefix='test_spdx') - result_file = os.path.join(temp_dir, 'spdx-simple.yaml') - with open(result_file, 'w') as output: - write_document(doc, output, validate=True) - - expected_file = utils_test.get_test_loc( - 'doc_write/yaml-simple.yaml', - test_data_dir=utils_test.test_data_dir) - - utils_test.check_yaml_scan(expected_file, result_file, regen=False) - finally: - if temp_dir and os.path.exists(temp_dir): - shutil.rmtree(temp_dir) - - def test_write_document_yaml_with_or_later_with_validate(self): - from spdx.writers.yaml import write_document - doc = self._get_lgpl_doc(or_later=True) - - temp_dir = '' - try: - temp_dir = tempfile.mkdtemp(prefix='test_spdx') - result_file = os.path.join(temp_dir, 'spdx-simple-plus.yaml') - with open(result_file, 'w') as output: - write_document(doc, output, validate=True) - - expected_file = utils_test.get_test_loc( - 'doc_write/yaml-simple-plus.yaml', - test_data_dir=utils_test.test_data_dir) - - utils_test.check_yaml_scan(expected_file, result_file, regen=False) - finally: - if temp_dir and os.path.exists(temp_dir): - shutil.rmtree(temp_dir) - - def test_write_document_yaml_multi_package_with_validate(self): - from spdx.writers.yaml import write_document - doc = self._get_lgpl_multi_package_doc(or_later=True) - - temp_dir = '' - try: - temp_dir = tempfile.mkdtemp(prefix='test_spdx') - result_file = os.path.join(temp_dir, 'spdx-simple-multi-package.yaml') - with open(result_file, 'w') as output: - write_document(doc, output, validate=True) - - expected_file = utils_test.get_test_loc( - 'doc_write/yaml-simple-multi-package.yaml', - test_data_dir=utils_test.test_data_dir) - - utils_test.check_yaml_scan(expected_file, result_file, regen=False) - finally: - if temp_dir and os.path.exists(temp_dir): - shutil.rmtree(temp_dir) - - def test_write_document_xml_with_validate(self): - from spdx.writers.xml import write_document - doc = self._get_lgpl_doc() - - temp_dir = '' - try: - temp_dir = tempfile.mkdtemp(prefix='test_spdx') - result_file = os.path.join(temp_dir, 'spdx-simple.xml') - with open(result_file, 'w') as output: - write_document(doc, output, validate=True) - - expected_file = utils_test.get_test_loc( - 'doc_write/xml-simple.xml', - test_data_dir=utils_test.test_data_dir) - - utils_test.check_xml_scan(expected_file, result_file, regen=False) - finally: - if temp_dir and os.path.exists(temp_dir): - shutil.rmtree(temp_dir) - - def test_write_document_xml_with_or_later_with_validate(self): - from spdx.writers.xml import write_document - doc = self._get_lgpl_doc(or_later=True) - - temp_dir = '' - try: - temp_dir = tempfile.mkdtemp(prefix='test_spdx') - result_file = os.path.join(temp_dir, 'spdx-simple-plus.xml') - with open(result_file, 'w') as output: - write_document(doc, output, validate=True) - - expected_file = utils_test.get_test_loc( - 'doc_write/xml-simple-plus.xml', - test_data_dir=utils_test.test_data_dir) - - utils_test.check_xml_scan(expected_file, result_file, regen=False) - finally: - if temp_dir and os.path.exists(temp_dir): - shutil.rmtree(temp_dir) - - def test_write_document_xml_multi_package_with_validate(self): - from spdx.writers.xml import write_document - doc = self._get_lgpl_multi_package_doc(or_later=True) - - temp_dir = '' - try: - temp_dir = tempfile.mkdtemp(prefix='test_spdx') - result_file = os.path.join(temp_dir, 'spdx-simple-multi-package.xml') - with open(result_file, 'w') as output: - write_document(doc, output, validate=True) - - expected_file = utils_test.get_test_loc( - 'doc_write/xml-simple-multi-package.xml', - test_data_dir=utils_test.test_data_dir) - - utils_test.check_xml_scan(expected_file, result_file, regen=False) - finally: - if temp_dir and os.path.exists(temp_dir): - shutil.rmtree(temp_dir) - - def _get_mini_doc(self,): - doc = Document(Version(2, 1), License.from_identifier('CC0-1.0')) - doc.creation_info.set_created_now() - - return doc - - def test_write_document_tv_mini(self): - from spdx.writers.tagvalue import write_document - doc = self._get_mini_doc() - - temp_dir = '' - try: - temp_dir = tempfile.mkdtemp(prefix='test_spdx') - result_file = os.path.join(temp_dir, 'spdx-simple.tv') - with open(result_file, 'w') as output: - write_document(doc, output, validate=False) - expected_file = utils_test.get_test_loc('doc_write/tv-mini.tv') - utils_test.check_tv_scan(expected_file, result_file, regen=False) - finally: - if temp_dir and os.path.exists(temp_dir): - shutil.rmtree(temp_dir) - - def test_write_document_rdf_mini(self): - from spdx.writers.rdf import write_document - doc = self._get_mini_doc() - temp_dir = '' - try: - temp_dir = tempfile.mkdtemp(prefix='test_spdx') - result_file = os.path.join(temp_dir, 'spdx-simple.rdf') - with open(result_file, 'wb') as output: - write_document(doc, output, validate=False) - expected_file = utils_test.get_test_loc('doc_write/rdf-mini.json') - utils_test.check_rdf_scan(expected_file, result_file, regen=False) - finally: - if temp_dir and os.path.exists(temp_dir): - shutil.rmtree(temp_dir) - - -def create_relationship(spdx_element_id: str, relationship_type: RelationshipType, related_spdx_element: str) -> Relationship: - return Relationship(spdx_element_id + " " + relationship_type.name + " " + related_spdx_element) - - -class TestLicense(TestCase): - maxDiff = None - - def test_url(self): - lic = License(full_name='Apache License 1.0', identifier='Apache-1.0') - assert lic.url == 'http://spdx.org/licenses/Apache-1.0' - - def test_license_list(self): - assert LICENSE_MAP['Aladdin Free Public License'] == 'Aladdin' - assert LICENSE_MAP['Aladdin'] == 'Aladdin Free Public License' - assert LICENSE_MAP['MIT License'] == 'MIT' - assert LICENSE_MAP['MIT'] == 'MIT License' - assert LICENSE_MAP['BSD 4-Clause "Original" or "Old" License'] == 'BSD-4-Clause' - assert LICENSE_MAP['BSD-4-Clause'] == 'BSD 4-Clause "Original" or "Old" License' - - def test_from_full_name(self): - mit = License.from_full_name('MIT License') - assert mit.identifier == 'MIT' - assert mit.url == 'http://spdx.org/licenses/MIT' - - def test_from_identifier(self): - mit = License.from_identifier('MIT') - assert mit.full_name == 'MIT License' - assert mit.url == 'http://spdx.org/licenses/MIT' - - -class TestException(TestCase): - - def test_exception_list(self): - assert EXCEPTION_MAP['Linux Syscall Note'] == 'Linux-syscall-note' - assert EXCEPTION_MAP['Linux-syscall-note'] == 'Linux Syscall Note' - assert EXCEPTION_MAP['GCC Runtime Library exception 3.1'] == 'GCC-exception-3.1' - assert EXCEPTION_MAP['GCC-exception-3.1'] == 'GCC Runtime Library exception 3.1' diff --git a/tests/test_error_messages.py b/tests/test_error_messages.py deleted file mode 100644 index fa57f136a..000000000 --- a/tests/test_error_messages.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2021 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 spdx.parsers.loggers import ErrorMessages - - -def test_error_message_context(): - messages = ErrorMessages() - messages.push_context("package1") - messages.append("missing value: {0} {bar}", "foo", bar="bar") - messages.append("missing key") - messages.pop_context() - messages.append("lone message") - messages.push_context("package2") - messages.push_context("file1") - messages.append("more message") - assert messages.messages == [ - "package1: missing value: foo bar", - "package1: missing key", - "lone message", - "package2: file1: more message", - ] diff --git a/tests/test_jsonyamlxml_parser.py b/tests/test_jsonyamlxml_parser.py deleted file mode 100644 index 4198b8eff..000000000 --- a/tests/test_jsonyamlxml_parser.py +++ /dev/null @@ -1,72 +0,0 @@ - -# Copyright (c) Xavier Figueroa -# 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 collections import OrderedDict -import io -import json -from unittest import TestCase - -from spdx.parsers import jsonparser, yamlparser, xmlparser -from spdx.parsers.jsonyamlxmlbuilders import Builder -from spdx.parsers.loggers import StandardLogger - -from tests import utils_test -from tests.utils_test import TestParserUtils - - -class TestParser(TestCase): - maxDiff = None - - def check_document(self, document, expected_loc, regen=False): - result = TestParserUtils.to_dict(document) - - if regen: - with open(expected_loc, 'w') as o: - o.write(json.dumps(result, indent=2)) - - with io.open(expected_loc, encoding='utf-8') as ex: - expected = json.load(ex, object_pairs_hook=OrderedDict) - - assert result == expected - - def test_json_parser(self): - parser = jsonparser.Parser(Builder(), StandardLogger()) - test_file = utils_test.get_test_loc('formats/SPDXJsonExample.json') - with io.open(test_file, encoding='utf-8') as f: - document, _ = parser.parse(f) - expected_loc = utils_test.get_test_loc('doc_parse/expected.json') - self.check_document(document, expected_loc) - - def test_yaml_parser(self): - parser = yamlparser.Parser(Builder(), StandardLogger()) - test_file = utils_test.get_test_loc('formats/SPDXYamlExample.yaml') - with io.open(test_file, encoding='utf-8') as f: - document, _ = parser.parse(f) - expected_loc = utils_test.get_test_loc('doc_parse/expected.json') - self.check_document(document, expected_loc) - - def test_xml_parser(self): - parser = xmlparser.Parser(Builder(), StandardLogger()) - test_file = utils_test.get_test_loc('formats/SPDXXmlExample.xml') - with io.open(test_file, encoding='utf-8') as f: - document, _ = parser.parse(f) - expected_loc = utils_test.get_test_loc('doc_parse/expected.json') - self.check_document(document, expected_loc) - - def test_sbomyaml_parser(self): - parser = yamlparser.Parser(Builder(), StandardLogger()) - test_file = utils_test.get_test_loc('formats/SPDXSBOMExample.spdx.yml') - with io.open(test_file, encoding='utf-8') as f: - document, errors = parser.parse(f) - assert not errors - expected_loc = utils_test.get_test_loc('doc_parse/SBOMexpected.json') - self.check_document(document, expected_loc) diff --git a/tests/test_jsonyamlxml_writer.py b/tests/test_jsonyamlxml_writer.py deleted file mode 100644 index 78098a698..000000000 --- a/tests/test_jsonyamlxml_writer.py +++ /dev/null @@ -1,164 +0,0 @@ -import glob -import os -from datetime import datetime -from typing import List - -import pytest - -from spdx.checksum import Checksum, ChecksumAlgorithm -from spdx.document import Document -from spdx.file import File -from spdx.license import License -from spdx.package import Package, ExternalPackageRef, PackagePurpose -from spdx.parsers.parse_anything import parse_file -from spdx.relationship import Relationship -from spdx.snippet import Snippet -from spdx.utils import update_dict_item_with_new_item -from spdx.writers import write_anything -from tests.test_rdf_writer import minimal_document_with_package - -tested_formats: List[str] = ['yaml', 'xml', 'json'] - - -@pytest.fixture -def temporary_file_path() -> str: - temporary_file_path = "temp_test_writer_output" - yield temporary_file_path - file_with_ending = glob.glob(temporary_file_path + "*") - for file in file_with_ending: - os.remove(file) - - -@pytest.mark.parametrize("out_format", tested_formats) -def test_external_package_references(temporary_file_path: str, out_format: str) -> None: - document: Document = minimal_document_with_package() - package: Package = document.packages[0] - first_ref = ExternalPackageRef(category="PACKAGE-MANAGER") - second_ref = ExternalPackageRef(category="SECURITY") - package.add_pkg_ext_refs(first_ref) - package.add_pkg_ext_refs(second_ref) - - file_path_with_ending = temporary_file_path + "." + out_format - write_anything.write_file(document, file_path_with_ending, validate=False) - - parsed_document = parse_file(file_path_with_ending)[0] - - parsed_package: Package = parsed_document.packages[0] - assert len(parsed_package.pkg_ext_refs) is 2 - written_reference_categories = list(map(lambda x: x.category, parsed_package.pkg_ext_refs)) - assert first_ref.category in written_reference_categories - assert second_ref.category in written_reference_categories - - -@pytest.mark.parametrize("out_format", tested_formats) -def test_primary_package_purpose(temporary_file_path: str, out_format: str): - document: Document = minimal_document_with_package() - package: Package = document.packages[0] - package.primary_package_purpose = PackagePurpose.OPERATING_SYSTEM - - file_path_with_ending = temporary_file_path + "." + out_format - write_anything.write_file(document, file_path_with_ending, validate=False) - - parsed_document: Document = parse_file(file_path_with_ending)[0] - parsed_package: Package = parsed_document.packages[0] - - assert parsed_package.primary_package_purpose == PackagePurpose.OPERATING_SYSTEM - - -@pytest.mark.parametrize("out_format", tested_formats) -def test_release_built_valid_until_date(temporary_file_path: str, out_format: str): - document: Document = minimal_document_with_package() - package: Package = document.packages[0] - package.release_date = datetime(2021, 1, 1, 12, 0, 0) - package.built_date = datetime(2021, 1, 1, 12, 0, 0) - package.valid_until_date = datetime(2022, 1, 1, 12, 0, 0) - - file_path_with_ending = temporary_file_path + "." + out_format - write_anything.write_file(document, file_path_with_ending, validate=False) - - parsed_document: Document = parse_file(file_path_with_ending)[0] - parsed_package: Package = parsed_document.packages[0] - - assert parsed_package.release_date == package.release_date - assert parsed_package.built_date == package.built_date - assert parsed_package.valid_until_date == package.valid_until_date - - -@pytest.mark.parametrize("out_format", ['yaml', 'xml', 'json']) -def test_snippet_byte_range(temporary_file_path, out_format): - snippet = minimal_snippet() - parsed_snippet = write_and_parse_snippet(out_format, snippet, temporary_file_path) - - assert parsed_snippet.byte_range == (310, 420) - assert parsed_snippet.line_range is None - - -@pytest.mark.parametrize("out_format", ['yaml', 'xml', 'json']) -def test_snippet_ranges(temporary_file_path, out_format): - snippet = minimal_snippet() - snippet.line_range = (5, 23) - parsed_snippet = write_and_parse_snippet(out_format, snippet, temporary_file_path) - - assert parsed_snippet.byte_range == (310, 420) - assert parsed_snippet.line_range == (5, 23) - - -def minimal_snippet(): - snippet = Snippet() - snippet.spdx_id = "SPDXRef-Snippet" - snippet.snip_from_file_spdxid = "SPDXRef-File" - snippet.byte_range = (310, 420) - return snippet - - -def write_and_parse_snippet(out_format, snippet, temporary_file_path): - document: Document = minimal_document_with_package() - document.add_snippet(snippet) - file_path_with_ending = temporary_file_path + "." + out_format - write_anything.write_file(document, file_path_with_ending, validate=False) - parsed_document: Document = parse_file(file_path_with_ending)[0] - parsed_snippet: Snippet = parsed_document.snippet[0] - - return parsed_snippet - - -@pytest.mark.parametrize("out_format", ['yaml', 'xml', 'json']) -def test_files_without_package(temporary_file_path, out_format): - document: Document = minimal_document() - document.spdx_id = "SPDXRef-DOCUMENT" - file: File = minimal_file() - document.add_file(file) - describes_relationship: Relationship = Relationship("SPDXRef-DOCUMENT DESCRIBES SPDXRef-File") - document.add_relationship(relationship=describes_relationship) - - file_path_with_ending = temporary_file_path + "." + out_format - write_anything.write_file(document, file_path_with_ending, validate=False) - - parsed_document: Document = parse_file(file_path_with_ending)[0] - parsed_file: File = parsed_document.files[0] - - assert parsed_file.name == "Example File" - - -def minimal_document(): - document = Document(data_license=License.from_identifier('CC0-1.0')) - document.creation_info.set_created_now() - return document - - -def minimal_file(): - file = File(name="Example File", spdx_id="SPDXRef-File") - file.set_checksum(Checksum(ChecksumAlgorithm.SHA1, 'some-sha1-value')) - return file - - -@pytest.mark.parametrize("key,value,expected_length", - [("existing_key", "new_value", 2), ("existing_key", "existing_value", 1), - ("new_key", "new_value", 1)]) -def test_update_dict_item_with_new_item(key, value, expected_length): - current_state = {"existing_key": ["existing_value"]} - update_dict_item_with_new_item(current_state, key, value) - - assert key in current_state - assert value in current_state[key] - assert len(current_state[key]) == expected_length diff --git a/tests/test_package.py b/tests/test_package.py deleted file mode 100644 index 3b8325b02..000000000 --- a/tests/test_package.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 unittest -from unittest import TestCase - -from spdx.checksum import Checksum -from spdx.package import Package - - -class TestPackage(TestCase): - - @unittest.skip("https://github.com/spdx/tools-python/issues/296") - def test_calc_verif_code(self): - package = Package() - package.calc_verif_code() - - def test_package_with_non_sha1_check_sum(self): - package = Package() - package.set_checksum(Checksum("SHA256", '')) - - # Make sure that validation still works despite the checksum not being SHA1 - messages = [] - package.validate_checksums(messages) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_parse_anything.py b/tests/test_parse_anything.py deleted file mode 100644 index 6342153ec..000000000 --- a/tests/test_parse_anything.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2021 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. - -import os - -import pytest - -from spdx.parsers import parse_anything - -dirname = os.path.join(os.path.dirname(__file__), "data", "formats") -# Some rdf examples still cause issues -test_files = [os.path.join(dirname, fn) for fn in os.listdir(dirname) if not "RdfExample-v" in fn] - - -@pytest.mark.parametrize("test_file", test_files) -def test_parse_anything(test_file): - doc, error = parse_anything.parse_file(test_file) - - assert not error - - # test a few fields, the core of the tests are per parser - assert doc.name in ('Sample_Document-V2.1', 'xyz-0.1.0', 'SPDX-Tools-v2.0') - assert doc.comment in (None, 'This is a sample spreadsheet', 'Sample Comment', - 'This document was created using SPDX 2.0 using licenses from the web site.') diff --git a/tests/test_parsers_validation.py b/tests/test_parsers_validation.py deleted file mode 100644 index 4795fb6d5..000000000 --- a/tests/test_parsers_validation.py +++ /dev/null @@ -1,34 +0,0 @@ - -# Copyright (c) SPDX Python tools authors -# 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 - -from spdx.parsers import validations -from spdx import utils - - -class TestValidations(TestCase): - maxDiff = None - - def test_validate_pkg_cr_text_does_not_crash_on_None(self): - validations.validate_pkg_cr_text(None) - - def test_validate_pkg_cr_text_does_not_crash_on_NoAssert_or_SPDXNone(self): - validations.validate_pkg_cr_text(utils.NoAssert()) - validations.validate_pkg_cr_text(utils.SPDXNone()) - - def test_validate_file_cpyright_does_not_crash_on_None(self): - validations.validate_file_cpyright(None) - - def test_validate_file_cpyright_does_not_crash_on_NoAssert_or_SPDXNone(self): - validations.validate_file_cpyright(utils.NoAssert()) - validations.validate_file_cpyright(utils.SPDXNone()) diff --git a/tests/test_rdf_parser.py b/tests/test_rdf_parser.py deleted file mode 100644 index 7963781fa..000000000 --- a/tests/test_rdf_parser.py +++ /dev/null @@ -1,48 +0,0 @@ - -# Copyright (c) the SPDX tools authors -# 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 io -import json -import unittest -from collections import OrderedDict - -from spdx.parsers import rdf -from spdx.parsers.loggers import StandardLogger -from spdx.parsers.rdfbuilders import Builder as RDFBuilder - -from tests import utils_test -from tests.utils_test import TestParserUtils - - -class TestParser(unittest.TestCase): - maxDiff = None - - def test_rdf_parser(self, regen=False): - parser = rdf.Parser(RDFBuilder(), StandardLogger()) - test_file = utils_test.get_test_loc('formats/SPDXRdfExample.rdf', test_data_dir=utils_test.test_data_dir) - with io.open(test_file, 'rb') as f: - document, _ = parser.parse(f) - expected_loc = utils_test.get_test_loc('doc_parse/spdx-expected.json', test_data_dir=utils_test.test_data_dir) - self.check_document(document, expected_loc, regen=regen) - - def check_document(self, document, expected_loc, regen=False): - result = TestParserUtils.to_dict(document) - - if regen: - data = json.dumps(result, indent=2) - with io.open(expected_loc, 'w') as o: - o.write(data) - - with io.open(expected_loc, 'r', encoding='utf-8') as ex: - expected = json.load(ex, object_pairs_hook=OrderedDict) - - assert result == expected diff --git a/tests/test_rdf_writer.py b/tests/test_rdf_writer.py deleted file mode 100644 index 9153dbac6..000000000 --- a/tests/test_rdf_writer.py +++ /dev/null @@ -1,78 +0,0 @@ -import os - -import pytest -from rdflib import URIRef - -from spdx.document import Document -from spdx.license import License -from spdx.package import Package, ExternalPackageRef -from spdx.parsers.loggers import StandardLogger -from spdx.parsers.parse_anything import parse_file -from spdx.parsers.rdf import Parser -from spdx.parsers.rdfbuilders import Builder -from spdx.utils import NoAssert -from spdx.writers.rdf import Writer - - -@pytest.fixture -def temporary_file_path() -> str: - temporary_file_path = "temp_accept_provided_doc_node.rdf.xml" - yield temporary_file_path - os.remove(temporary_file_path) - - -# This test is really clunky since it's hard to isolate features of the rdf writer to test. Should be improved when -# that part is refactored. -def test_accept_provided_doc_node(temporary_file_path) -> None: - doc_node = URIRef("http://www.spdx.org/tools#SPDXRef-DOCUMENT") - document: Document = minimal_document_with_package() - - with open(temporary_file_path, "wb") as out: - writer = Writer(document, out) - writer.write(doc_node) - parser = Parser(Builder(), StandardLogger()) - with open(temporary_file_path, "r") as file: - parsed_document: Document = parser.parse(file)[0] - - # These properties are set automatically if no doc_node is provided. Instead, we provided an empty one - assert parsed_document.data_license is None - assert parsed_document.namespace is None - assert parsed_document.spdx_id is None - - -def test_external_package_references(temporary_file_path) -> None: - document: Document = minimal_document_with_package() - package: Package = document.packages[0] - first_ref = ExternalPackageRef(category="PACKAGE-MANAGER") - second_ref = ExternalPackageRef(category="SECURITY") - package.add_pkg_ext_refs(first_ref) - package.add_pkg_ext_refs(second_ref) - - # Not using write_anything here because we don't want to deal with validation - with open(temporary_file_path, "wb") as out: - writer = Writer(document, out) - writer.write() - - parsed_document = parse_file(temporary_file_path)[0] - parsed_package: Package = parsed_document.packages[0] - - assert len(parsed_package.pkg_ext_refs) is 2 - parsed_reference_categories = list(map(lambda x: x.category, parsed_package.pkg_ext_refs)) - assert first_ref.category in parsed_reference_categories - assert second_ref.category in parsed_reference_categories - - -def minimal_document_with_package() -> Document: - document = Document(data_license=License.from_identifier('CC0-1.0')) - document.creation_info.set_created_now() - package: Package = minimal_package() - document.add_package(package) - return document - - -def minimal_package() -> Package: - package = Package() - package.conc_lics = NoAssert() - package.license_declared = NoAssert() - package.add_lics_from_file(NoAssert()) - return package diff --git a/tests/test_tag_value_parser.py b/tests/test_tag_value_parser.py deleted file mode 100644 index 4008c3090..000000000 --- a/tests/test_tag_value_parser.py +++ /dev/null @@ -1,411 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 datetime import datetime -from unittest import TestCase - -import spdx -from spdx import utils -from spdx.package import PackagePurpose -from spdx.parsers.tagvalue import Parser -from spdx.parsers.lexers.tagvalue import Lexer -from spdx.parsers.tagvaluebuilders import Builder -from spdx.parsers.loggers import StandardLogger -from spdx.version import Version - -document_str = '\n'.join([ - 'SPDXVersion: SPDX-2.1', - 'DataLicense: CC0-1.0', - 'DocumentName: Sample_Document-V2.1', - 'SPDXID: SPDXRef-DOCUMENT', - 'DocumentComment: Sample Comment', - 'DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301' - ]) - -creation_str = '\n'.join([ - 'Creator: Person: Bob (bob@example.com)', - 'Creator: Organization: Acme.', - 'Created: 2010-02-03T00:00:00Z', - 'CreatorComment: Sample Comment' -]) - -review_str = '\n'.join([ - 'Reviewer: Person: Bob the Reviewer', - 'ReviewDate: 2010-02-10T00:00:00Z', - 'ReviewComment: Bob was Here.', - 'Reviewer: Person: Alice the Reviewer', - 'ReviewDate: 2011-02-10T00:00:00Z', - 'ReviewComment: Alice was also here.' -]) - -package_str = '\n'.join([ - 'PackageName: Test', - 'SPDXID: SPDXRef-Package', - 'PackageVersion: Version 0.9.2', - 'PackageDownloadLocation: http://example.com/test', - 'FilesAnalyzed: True', - 'PackageSummary: Test package', - 'PackageSourceInfo: Version 1.0 of test', - 'PackageFileName: test-1.0.zip', - 'PackageSupplier: Organization:ACME', - 'PackageOriginator: Organization:ACME', - 'PackageChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', - 'PackageVerificationCode: 4e3211c67a2d28fced849ee1bb76e7391b93feba (something.rdf, something.txt)', - 'PackageDescription: A package.', - 'PackageComment: Comment on the package.', - 'PackageCopyrightText: Copyright 2014 Acme Inc.', - 'PackageLicenseDeclared: Apache-2.0', - 'PackageLicenseConcluded: (LicenseRef-2.0 and Apache-2.0)', - 'PackageLicenseInfoFromFiles: Apache-1.0', - 'PackageLicenseInfoFromFiles: Apache-2.0', - 'PackageLicenseComments: License Comments', - 'ExternalRef: SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:', - 'ExternalRefComment: Some comment about the package.', - 'PrimaryPackagePurpose: OPERATING-SYSTEM', - 'BuiltDate: 2020-01-01T12:00:00Z', - 'ReleaseDate: 2021-01-01T12:00:00Z', - 'ValidUntilDate: 2022-01-01T12:00:00Z' -]) - -file_str = '\n'.join([ - 'FileName: testfile.java', - 'SPDXID: SPDXRef-File', - 'FileType: SOURCE', - 'FileType: TEXT', - 'FileChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', - 'LicenseConcluded: Apache-2.0', - 'LicenseInfoInFile: Apache-2.0', - 'FileCopyrightText: Copyright 2014 Acme Inc.', - 'ArtifactOfProjectName: AcmeTest', - 'ArtifactOfProjectHomePage: http://www.acme.org/', - 'ArtifactOfProjectURI: http://www.acme.org/', - 'FileComment: Very long file', - 'FileAttributionText: Acknowledgements that might be required to be communicated in some contexts.' - ]) - -unknown_tag_str = 'SomeUnknownTag: SomeUnknownValue' - -snippet_str = '\n'.join([ - 'SnippetSPDXID: SPDXRef-Snippet', - 'SnippetLicenseComments: Some lic comment.', - 'SnippetCopyrightText: Copyright 2008-2010 John Smith ', - 'SnippetComment: Some snippet comment.', - 'SnippetName: from linux kernel', - 'SnippetFromFileSPDXID: SPDXRef-DoapSource', - 'SnippetLicenseConcluded: Apache-2.0', - 'LicenseInfoInSnippet: Apache-2.0', - 'SnippetByteRange: 310:420', - 'SnippetLineRange: 5:23', -]) - -annotation_str = '\n'.join([ - 'Annotator: Person: Jane Doe()', - 'AnnotationDate: 2010-01-29T18:30:22Z', - 'AnnotationComment: Document level annotation', - 'AnnotationType: OTHER', - 'SPDXREF: SPDXRef-DOCUMENT' -]) - - -class TestLexer(TestCase): - maxDiff = None - - def setUp(self): - self.l = Lexer() - self.l.build() - - def test_document(self): - data = document_str - self.l.input(data) - self.token_assert_helper(self.l.token(), 'DOC_VERSION', 'SPDXVersion', 1) - self.token_assert_helper(self.l.token(), 'LINE', 'SPDX-2.1', 1) - self.token_assert_helper(self.l.token(), 'DOC_LICENSE', 'DataLicense', 2) - self.token_assert_helper(self.l.token(), 'LINE', 'CC0-1.0', 2) - self.token_assert_helper(self.l.token(), 'DOC_NAME', 'DocumentName', 3) - self.token_assert_helper(self.l.token(), 'LINE', 'Sample_Document-V2.1', - 3) - self.token_assert_helper(self.l.token(), 'SPDX_ID', 'SPDXID', 4) - self.token_assert_helper(self.l.token(), 'LINE', 'SPDXRef-DOCUMENT', 4) - self.token_assert_helper(self.l.token(), 'DOC_COMMENT', 'DocumentComment', 5) - self.token_assert_helper(self.l.token(), 'TEXT', 'Sample Comment', 5) - self.token_assert_helper(self.l.token(), 'DOC_NAMESPACE', - 'DocumentNamespace', 6) - self.token_assert_helper(self.l.token(), 'LINE', - 'https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301', - 6) - - def test_external_document_references(self): - data = ''' - ExternalDocumentRef:DocumentRef-spdx-tool-2.1 http://spdx.org/spdxdocs/spdx-tools-v2.1-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759 - ''' - self.l.input(data) - self.token_assert_helper(self.l.token(), 'EXT_DOC_REF', - 'ExternalDocumentRef', 2) - self.token_assert_helper(self.l.token(), 'DOC_REF_ID', - 'DocumentRef-spdx-tool-2.1', 2) - self.token_assert_helper(self.l.token(), 'DOC_URI', - 'http://spdx.org/spdxdocs/spdx-tools-v2.1-3F25' - '04E0-4F89-41D3-9A0C-0305E82C3301', 2) - self.token_assert_helper(self.l.token(), 'EXT_DOC_REF_CHKSUM', - 'SHA1: ' - 'd6a770ba38583ed4bb4525bd96e50461655d2759', 2) - - def test_creation_info(self): - data = creation_str - self.l.input(data) - self.token_assert_helper(self.l.token(), 'CREATOR', 'Creator', 1) - self.token_assert_helper(self.l.token(), 'PERSON_VALUE', "Person: Bob (bob@example.com)", 1) - self.token_assert_helper(self.l.token(), 'CREATOR', 'Creator', 2) - self.token_assert_helper(self.l.token(), 'ORG_VALUE', 'Organization: Acme.', 2) - self.token_assert_helper(self.l.token(), 'CREATED', 'Created', 3) - self.token_assert_helper(self.l.token(), 'DATE', '2010-02-03T00:00:00Z', 3) - self.token_assert_helper(self.l.token(), 'CREATOR_COMMENT', 'CreatorComment', 4) - self.token_assert_helper(self.l.token(), 'TEXT', 'Sample Comment', 4) - - def test_review_info(self): - data = review_str - self.l.input(data) - self.token_assert_helper(self.l.token(), 'REVIEWER', 'Reviewer', 1) - self.token_assert_helper(self.l.token(), 'PERSON_VALUE', "Person: Bob the Reviewer", 1) - self.token_assert_helper(self.l.token(), 'REVIEW_DATE', 'ReviewDate', 2) - self.token_assert_helper(self.l.token(), 'DATE', '2010-02-10T00:00:00Z', 2) - self.token_assert_helper(self.l.token(), 'REVIEW_COMMENT', 'ReviewComment', 3) - self.token_assert_helper(self.l.token(), 'TEXT', 'Bob was Here.', 3) - self.token_assert_helper(self.l.token(), 'REVIEWER', 'Reviewer', 4) - self.token_assert_helper(self.l.token(), 'PERSON_VALUE', "Person: Alice the Reviewer", 4) - self.token_assert_helper(self.l.token(), 'REVIEW_DATE', 'ReviewDate', 5) - self.token_assert_helper(self.l.token(), 'DATE', '2011-02-10T00:00:00Z', 5) - self.token_assert_helper(self.l.token(), 'REVIEW_COMMENT', 'ReviewComment', 6) - self.token_assert_helper(self.l.token(), 'TEXT', 'Alice was also here.', 6) - - def test_package(self): - data = package_str - self.l.input(data) - self.token_assert_helper(self.l.token(), 'PKG_NAME', 'PackageName', 1) - self.token_assert_helper(self.l.token(), 'LINE', 'Test', 1) - self.token_assert_helper(self.l.token(), 'SPDX_ID', 'SPDXID', 2) - self.token_assert_helper(self.l.token(), 'LINE', 'SPDXRef-Package', 2) - self.token_assert_helper(self.l.token(), 'PKG_VERSION', 'PackageVersion', 3) - self.token_assert_helper(self.l.token(), 'LINE', 'Version 0.9.2', 3) - self.token_assert_helper(self.l.token(), 'PKG_DOWN', 'PackageDownloadLocation', 4) - self.token_assert_helper(self.l.token(), 'LINE', 'http://example.com/test', 4) - self.token_assert_helper(self.l.token(), 'PKG_FILES_ANALYZED', 'FilesAnalyzed', 5) - self.token_assert_helper(self.l.token(), 'LINE', 'True', 5) - self.token_assert_helper(self.l.token(), 'PKG_SUM', 'PackageSummary', 6) - self.token_assert_helper(self.l.token(), 'TEXT', 'Test package', 6) - self.token_assert_helper(self.l.token(), 'PKG_SRC_INFO', 'PackageSourceInfo', 7) - self.token_assert_helper(self.l.token(), 'TEXT', 'Version 1.0 of test', 7) - self.token_assert_helper(self.l.token(), 'PKG_FILE_NAME', 'PackageFileName', 8) - self.token_assert_helper(self.l.token(), 'LINE', 'test-1.0.zip', 8) - self.token_assert_helper(self.l.token(), 'PKG_SUPPL', 'PackageSupplier', 9) - self.token_assert_helper(self.l.token(), 'ORG_VALUE', 'Organization:ACME', 9) - self.token_assert_helper(self.l.token(), 'PKG_ORIG', 'PackageOriginator', 10) - self.token_assert_helper(self.l.token(), 'ORG_VALUE', 'Organization:ACME', 10) - self.token_assert_helper(self.l.token(), 'PKG_CHKSUM', 'PackageChecksum', 11) - self.token_assert_helper(self.l.token(), 'CHKSUM', 'SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', 11) - self.token_assert_helper(self.l.token(), 'PKG_VERF_CODE', 'PackageVerificationCode', 12) - self.token_assert_helper(self.l.token(), 'LINE', '4e3211c67a2d28fced849ee1bb76e7391b93feba (something.rdf, something.txt)', 12) - self.token_assert_helper(self.l.token(), 'PKG_DESC', 'PackageDescription', 13) - self.token_assert_helper(self.l.token(), 'TEXT', 'A package.', 13) - self.token_assert_helper(self.l.token(), 'PKG_COMMENT', 'PackageComment', 14) - self.token_assert_helper(self.l.token(), 'TEXT', 'Comment on the package.', 14) - self.token_assert_helper(self.l.token(), 'PKG_CPY_TEXT', 'PackageCopyrightText', 15) - self.token_assert_helper(self.l.token(), 'TEXT', ' Copyright 2014 Acme Inc.', 15) - self.token_assert_helper(self.l.token(), 'PKG_LICS_DECL', 'PackageLicenseDeclared', 16) - self.token_assert_helper(self.l.token(), 'LINE', 'Apache-2.0', 16) - self.token_assert_helper(self.l.token(), 'PKG_LICS_CONC', 'PackageLicenseConcluded', 17) - self.token_assert_helper(self.l.token(), 'LINE', '(LicenseRef-2.0 and Apache-2.0)', 17) - self.token_assert_helper(self.l.token(), 'PKG_LICS_FFILE', 'PackageLicenseInfoFromFiles', 18) - self.token_assert_helper(self.l.token(), 'LINE', 'Apache-1.0', 18) - self.token_assert_helper(self.l.token(), 'PKG_LICS_FFILE', 'PackageLicenseInfoFromFiles', 19) - self.token_assert_helper(self.l.token(), 'LINE', 'Apache-2.0', 19) - self.token_assert_helper(self.l.token(), 'PKG_LICS_COMMENT', 'PackageLicenseComments', 20) - self.token_assert_helper(self.l.token(), 'TEXT', 'License Comments', 20) - self.token_assert_helper(self.l.token(), 'PKG_EXT_REF', 'ExternalRef', 21) - self.token_assert_helper(self.l.token(), 'LINE', 'SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:', 21) - self.token_assert_helper(self.l.token(), 'PKG_EXT_REF_COMMENT', 'ExternalRefComment', 22) - self.token_assert_helper(self.l.token(), 'TEXT', 'Some comment about the package.', 22) - self.token_assert_helper(self.l.token(), 'PRIMARY_PACKAGE_PURPOSE', 'PrimaryPackagePurpose', 23) - self.token_assert_helper(self.l.token(), 'OPERATING_SYSTEM', 'OPERATING-SYSTEM', 23) - self.token_assert_helper(self.l.token(), 'BUILT_DATE', 'BuiltDate', 24) - self.token_assert_helper(self.l.token(), 'DATE', '2020-01-01T12:00:00Z', 24) - self.token_assert_helper(self.l.token(), 'RELEASE_DATE', 'ReleaseDate', 25) - self.token_assert_helper(self.l.token(), 'DATE', '2021-01-01T12:00:00Z', 25) - self.token_assert_helper(self.l.token(), 'VALID_UNTIL_DATE', 'ValidUntilDate', 26) - self.token_assert_helper(self.l.token(), 'DATE', '2022-01-01T12:00:00Z', 26) - - - def test_unknown_tag(self): - data = unknown_tag_str - self.l.input(data) - self.token_assert_helper(self.l.token(), 'UNKNOWN_TAG', 'SomeUnknownTag', 1) - self.token_assert_helper(self.l.token(), 'LINE', 'SomeUnknownValue', 1) - - def test_snippet(self): - data = snippet_str - self.l.input(data) - self.token_assert_helper(self.l.token(), 'SNIPPET_SPDX_ID', 'SnippetSPDXID', 1) - self.token_assert_helper(self.l.token(), 'LINE', 'SPDXRef-Snippet', 1) - self.token_assert_helper(self.l.token(), 'SNIPPET_LICS_COMMENT', 'SnippetLicenseComments', 2) - self.token_assert_helper(self.l.token(), 'TEXT', 'Some lic comment.', 2) - self.token_assert_helper(self.l.token(), 'SNIPPET_CR_TEXT', 'SnippetCopyrightText', 3) - self.token_assert_helper(self.l.token(), 'TEXT', ' Copyright 2008-2010 John Smith ', 3) - self.token_assert_helper(self.l.token(), 'SNIPPET_COMMENT', 'SnippetComment', 4) - self.token_assert_helper(self.l.token(), 'TEXT', 'Some snippet comment.', 4) - self.token_assert_helper(self.l.token(), 'SNIPPET_NAME', 'SnippetName', 5) - self.token_assert_helper(self.l.token(), 'LINE', 'from linux kernel', 5) - self.token_assert_helper(self.l.token(), 'SNIPPET_FILE_SPDXID', - 'SnippetFromFileSPDXID', 6) - self.token_assert_helper(self.l.token(), 'LINE', 'SPDXRef-DoapSource', 6) - self.token_assert_helper(self.l.token(), 'SNIPPET_LICS_CONC', - 'SnippetLicenseConcluded', 7) - self.token_assert_helper(self.l.token(), 'LINE', 'Apache-2.0', 7) - self.token_assert_helper(self.l.token(), 'SNIPPET_LICS_INFO', - 'LicenseInfoInSnippet', 8) - self.token_assert_helper(self.l.token(), 'LINE', 'Apache-2.0', 8) - self.token_assert_helper(self.l.token(), 'SNIPPET_BYTE_RANGE', 'SnippetByteRange', 9) - self.token_assert_helper(self.l.token(), 'RANGE', '310:420', 9) - self.token_assert_helper(self.l.token(), 'SNIPPET_LINE_RANGE', 'SnippetLineRange', 10) - self.token_assert_helper(self.l.token(), 'RANGE', '5:23', 10) - - def test_annotation(self): - data = annotation_str - self.l.input(data) - self.token_assert_helper(self.l.token(), 'ANNOTATOR', 'Annotator', 1) - self.token_assert_helper(self.l.token(), 'PERSON_VALUE', 'Person: Jane Doe()', 1) - self.token_assert_helper(self.l.token(), 'ANNOTATION_DATE', 'AnnotationDate', 2) - self.token_assert_helper(self.l.token(), 'DATE', '2010-01-29T18:30:22Z', 2) - self.token_assert_helper(self.l.token(), 'ANNOTATION_COMMENT', 'AnnotationComment', 3) - self.token_assert_helper(self.l.token(), 'TEXT', 'Document level annotation', 3) - self.token_assert_helper(self.l.token(), 'ANNOTATION_TYPE', 'AnnotationType', 4) - self.token_assert_helper(self.l.token(), 'OTHER', 'OTHER', 4) - self.token_assert_helper(self.l.token(), 'ANNOTATION_SPDX_ID', 'SPDXREF', 5) - self.token_assert_helper(self.l.token(), 'LINE', 'SPDXRef-DOCUMENT', 5) - - def token_assert_helper(self, token, ttype, value, line): - assert token.type == ttype - assert token.value == value - assert token.lineno == line - - -class TestParser(TestCase): - maxDiff = None - complete_str = '{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}'.format(document_str, creation_str, review_str, package_str, - file_str, annotation_str, snippet_str) - - def setUp(self): - self.p = Parser(Builder(), StandardLogger()) - self.p.build() - - def test_doc(self): - document, error = self.p.parse(self.complete_str) - assert document is not None - assert not error - assert document.version == Version(major=2, minor=1) - assert document.data_license.identifier == 'CC0-1.0' - assert document.name == 'Sample_Document-V2.1' - assert document.spdx_id == 'SPDXRef-DOCUMENT' - assert document.comment == 'Sample Comment' - assert document.namespace == 'https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301' - - def test_creation_info(self): - document, error = self.p.parse(self.complete_str) - assert document is not None - assert not error - assert len(document.creation_info.creators) == 2 - assert document.creation_info.comment == 'Sample Comment' - assert (document.creation_info.created_iso_format == '2010-02-03T00:00:00Z') - - def test_review(self): - document, error = self.p.parse(self.complete_str) - assert document is not None - assert not error - assert len(document.reviews) == 2 - - def test_package(self): - document, error = self.p.parse(self.complete_str) - assert document is not None - assert not error - assert document.package.name == 'Test' - assert document.package.spdx_id == 'SPDXRef-Package' - assert document.package.version == 'Version 0.9.2' - assert len(document.package.licenses_from_files) == 2 - assert (document.package.conc_lics.identifier == 'LicenseRef-2.0 AND Apache-2.0') - assert document.package.files_analyzed is True - assert document.package.comment == 'Comment on the package.' - assert document.package.pkg_ext_refs[-1].category == 'SECURITY' - assert document.package.pkg_ext_refs[-1].pkg_ext_ref_type == 'cpe23Type' - assert document.package.pkg_ext_refs[-1].locator == 'cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:' - assert document.package.pkg_ext_refs[-1].comment == 'Some comment about the package.' - assert document.package.primary_package_purpose == PackagePurpose.OPERATING_SYSTEM - assert document.package.built_date == datetime(2020, 1, 1, 12, 0, 0) - assert document.package.release_date == datetime(2021, 1, 1, 12, 0, 0) - assert document.package.valid_until_date == datetime(2022, 1, 1, 12, 0, 0) - - def test_file(self): - document, error = self.p.parse(self.complete_str) - assert document is not None - assert not error - assert len(document.files) == 1 - spdx_file = document.files[0] - assert spdx_file.name == 'testfile.java' - assert spdx_file.spdx_id == 'SPDXRef-File' - assert spdx_file.file_types == [spdx.file.FileType.SOURCE, spdx.file.FileType.TEXT] - assert len(spdx_file.artifact_of_project_name) == 1 - assert len(spdx_file.artifact_of_project_home) == 1 - assert len(spdx_file.artifact_of_project_uri) == 1 - assert spdx_file.comment == 'Very long file' - assert spdx_file.attribution_text == 'Acknowledgements that might be required to be communicated in ' \ - 'some contexts.' - - def test_annotation(self): - document, error = self.p.parse(self.complete_str) - assert document is not None - assert not error - assert len(document.annotations) == 1 - assert document.annotations[-1].annotator.name == 'Jane Doe' - assert spdx.utils.datetime_iso_format(document.annotations[-1].annotation_date) == '2010-01-29T18:30:22Z' - assert document.annotations[-1].comment == 'Document level annotation' - assert document.annotations[-1].annotation_type == 'OTHER' - assert document.annotations[-1].spdx_id == 'SPDXRef-DOCUMENT' - - def test_unknown_tag(self): - try: - from StringIO import StringIO - except ImportError: - from io import StringIO - - saved_out = sys.stdout - sys.stdout = StringIO() - document, error = self.p.parse(unknown_tag_str) - self.assertEqual(sys.stdout.getvalue(), 'Found unknown tag : SomeUnknownTag at line: 1\n') - sys.stdout = saved_out - assert error - assert document is not None - - def test_snippet(self): - document, error = self.p.parse(self.complete_str) - assert document is not None - assert not error - assert len(document.snippet) == 1 - assert document.snippet[-1].spdx_id == 'SPDXRef-Snippet' - assert document.snippet[-1].name == 'from linux kernel' - assert document.snippet[-1].comment == 'Some snippet comment.' - assert document.snippet[-1].copyright == ' Copyright 2008-2010 John Smith ' - assert document.snippet[-1].license_comment == 'Some lic comment.' - assert document.snippet[-1].snip_from_file_spdxid == 'SPDXRef-DoapSource' - assert document.snippet[-1].conc_lics.identifier == 'Apache-2.0' - assert document.snippet[-1].licenses_in_snippet[-1].identifier == 'Apache-2.0' - assert document.snippet[-1].byte_range[0] == 310 - assert document.snippet[-1].byte_range[1] == 420 - assert document.snippet[-1].line_range[0] == 5 - assert document.snippet[-1].line_range[1] == 23 diff --git a/tests/test_write_anything.py b/tests/test_write_anything.py deleted file mode 100644 index 5f3c8b915..000000000 --- a/tests/test_write_anything.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright (c) 2021 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. - -import os - -import pytest - -from spdx.parsers import parse_anything -from spdx.writers import write_anything -from tests import utils_test - -dirname = os.path.join(os.path.dirname(__file__), "data", "formats") -test_files = [os.path.join(dirname, fn) for fn in os.listdir(dirname)] -test_files_json_yaml_xml_tag = [filename for filename in test_files if filename.endswith("json") - or filename.endswith("yaml") or filename.endswith("xml") or filename.endswith("tag")] -test_files_rdf = [filename for filename in test_files if filename.endswith("rdf")] -UNSTABLE_CONVERSIONS = { - "SPDXTagExample.tag-yaml", - "SPDXTagExample.tag-xml", - "SPDXTagExample.tag-json", - "SPDXXmlExample.xml-tag", - "SPDXJsonExample.json-tag", - "SPDXYamlExample.yaml-tag", - "SPDXRdfExample.rdf-rdf", - "SPDXYAMLExample-2.2.spdx.yaml-tag", - "SPDXJSONExample-v2.2.spdx.json-tag", - "SPDXXMLExample-v2.2.spdx.xml-tag", - "SPDXYAMLExample-2.3.spdx.yaml-tag", - "SPDXJSONExample-v2.3.spdx.json-tag", - "SPDXXMLExample-v2.3.spdx.xml-tag" -} - - -# Because the rdf-parser/ writer can't handle the mandatory field byte_range in snippets yet we can only test conversion -# from json, yaml, xml and tv to each other format and rdf to rdf. Otherwise, the jsonyamlxml- or tv-writer would add -# the initial value None for snippet_ranges which then leads to an error while parsing. -# https://github.com/spdx/tools-python/issues/274 - - -@pytest.mark.parametrize("out_format", ['yaml', 'xml', 'json', 'tag']) -@pytest.mark.parametrize("in_file", test_files_json_yaml_xml_tag, ids=lambda x: os.path.basename(x)) -def test_write_anything_json_yaml_xml_tv(in_file, out_format, tmpdir): - in_basename = os.path.basename(in_file) - write_anything_test(in_basename, in_file, out_format, tmpdir) - - -@pytest.mark.parametrize("out_format", ['rdf']) -@pytest.mark.parametrize("in_file", test_files_rdf, ids=lambda x: os.path.basename(x)) -def test_write_anything_rdf(in_file, out_format, tmpdir): - in_basename = os.path.basename(in_file) - write_anything_test(in_basename, in_file, out_format, tmpdir) - - -def write_anything_test(in_basename, in_file, out_format, tmpdir): - """This parses the in_file and writes it to the out_format, - then parses the written out_file again and checks if it is still the same as in_file.""" - doc_in, error_in = parse_anything.parse_file(in_file) - assert not error_in - result_in = utils_test.TestParserUtils.to_dict(doc_in) - - out_file_name = os.path.join(tmpdir, "test." + out_format) - write_anything.write_file(doc_in, out_file_name) - - doc_out, error_out = parse_anything.parse_file(out_file_name) - assert not error_out - result_out = utils_test.TestParserUtils.to_dict(doc_out) - - test = in_basename + "-" + out_format - if test not in UNSTABLE_CONVERSIONS: - assert result_in == result_out - - else: - # if this test fails, this means we are more stable \o/ - # in that case, please remove the test from UNSTABLE_CONVERSIONS list - assert result_out != result_in, test diff --git a/tests/testing_utils.py b/tests/testing_utils.py index 825276545..022e0de77 100644 --- a/tests/testing_utils.py +++ b/tests/testing_utils.py @@ -18,6 +18,7 @@ """Tools not exempt from being descended into in tracebacks""" + def make_decorator(func): """ Wraps a test decorator so as to properly replicate metadata @@ -60,8 +61,10 @@ def test_that_fails_by_passing(): you may want to use `assert_raises` instead. """ valid = ' or '.join([e.__name__ for e in exceptions]) + def decorate(func): name = func.__name__ + def newfunc(*arg, **kw): try: func(*arg, **kw) diff --git a/tests/utils_test.py b/tests/utils_test.py deleted file mode 100644 index 48b262b2c..000000000 --- a/tests/utils_test.py +++ /dev/null @@ -1,556 +0,0 @@ -# Copyright (c) the SPDX tools authors -# 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 io -import json -import ntpath -import os -import posixpath -import re -from collections import OrderedDict -from typing import List - -import xmltodict -import yaml - -import spdx -from spdx import utils -from spdx.relationship import Relationship -from spdx.utils import NoAssert - -test_data_dir = os.path.join(os.path.dirname(__file__), 'data') - - -def get_test_loc(test_path, test_data_dir=test_data_dir, debug=False, exists=True): - """ - Given a `test_path` relative to the `test_data_dir` directory, return the - location to a test file or directory for this path. No copy is done. - """ - if debug: - import inspect - caller = inspect.stack()[1][3] - print('\nget_test_loc,%(caller)s,"%(test_path)s","%(test_data_dir)s"' % locals()) - - assert test_path - assert test_data_dir - - if not os.path.exists(test_data_dir): - raise IOError("[Errno 2] No such directory: test_data_dir not found:" - " '%(test_data_dir)s'" % locals()) - - tpath = to_os_native_path(test_path) - test_loc = os.path.abspath(os.path.join(test_data_dir, tpath)) - - if exists and not os.path.exists(test_loc): - raise IOError("[Errno 2] No such file or directory: " - "test_path not found: '%(test_loc)s'" % locals()) - - return test_loc - - -def to_os_native_path(path): - """ - Normalize a path to use the native OS path separator. - """ - path = path.replace(posixpath.sep, os.path.sep) - path = path.replace(ntpath.sep, os.path.sep) - path = path.rstrip(os.path.sep) - return path - - -def strip_variable_text(rdf_text): - """ - Return rdf_text stripped from variable parts such as rdf nodeids - """ - - replace_nid = re.compile('rdf:nodeID="[^\"]*"').sub - rdf_text = replace_nid('', rdf_text) - - replace_creation = re.compile('.*', re.DOTALL).sub - rdf_text = replace_creation('', rdf_text) - - replace_pcc = re.compile('.*', re.DOTALL).sub - rdf_text = replace_pcc('', rdf_text) - return rdf_text - - -def load_and_clean_rdf(location): - """ - Return plain Python nested data for the SPDX RDF file at location suitable - for comparison. The file content is cleaned from variable parts such as - dates, generated UUIDs and versions - - NOTE: we use plain dicts to avoid ordering issues in XML. the SPDX tool and - lxml do not seem to return a consistent ordering that is needed for tests. - """ - with io.open(location, encoding='utf-8') as l: - content = l.read() - content = strip_variable_text(content) - data = xmltodict.parse(content, dict_constructor=dict) - return sort_nested(data) - - -def sort_nested(data): - """ - Return a new dict with any nested list sorted recursively. - """ - if isinstance(data, dict): - new_data = {} - for k, v in data.items(): - if isinstance(v, list): - v = sort_nested(v) - if isinstance(v, dict): - v = sort_nested(v) - new_data[k] = v - return new_data - elif isinstance(data, list): - new_data = [] - for v in sorted(data, key=lambda x: json.dumps(x, sort_keys=True)): - if isinstance(v, list): - v = sort_nested(v) - if isinstance(v, dict): - v = sort_nested(v) - new_data.append(v) - return new_data - - -def check_rdf_scan(expected_file, result_file, regen=False): - """ - Check that expected and result_file are equal. - Both are paths to SPDX RDF XML files, UTF-8 encoded. - """ - result = load_and_clean_rdf(result_file) - if regen: - expected = result - with io.open(expected_file, 'w', encoding='utf-8') as o: - json.dump(expected, o, indent=2) - else: - with io.open(expected_file, 'r', encoding='utf-8') as i: - expected = sort_nested(json.load(i)) - assert result == expected - - -def load_and_clean_tv(location): - """ - Return a mapping for the SPDX TV file at location suitable for comparison. - The file content is cleaned from variable parts such as dates, generated - UUIDs and versions - """ - with io.open(location, encoding='utf-8') as l: - content = l.read() - content = [l for l in content.splitlines(False) - if l and l.strip() and not l.startswith(('Creator: ', 'Created: ',))] - return '\n'.join(content) - - -def check_tv_scan(expected_file, result_file, regen=False): - """ - Check that expected and result_file are equal. - Both are paths to plain SPDX tv text files, UTF-8 encoded. - """ - result = load_and_clean_tv(result_file) - if regen: - with io.open(expected_file, 'w') as o: - o.write(result) - - expected = load_and_clean_tv(expected_file) - assert result == expected - - -def load_and_clean_json(location): - """ - Return plain Python nested data for the SPDX JSON file at location suitable - for comparison. The file content is cleaned from variable parts such as - dates, generated UUIDs and versions - """ - with io.open(location, encoding='utf-8') as l: - content = l.read() - data = json.loads(content) - - if 'creationInfo' in data: - del (data['creationInfo']) - - return sort_nested(data) - - -def check_json_scan(expected_file, result_file, regen=False): - """ - Check that expected_file and result_file are equal. - Both are paths to SPDX JSON files, UTF-8 encoded. - """ - result = load_and_clean_json(result_file) - if regen: - with io.open(expected_file, 'w', encoding='utf-8') as o: - o.write(result) - - expected = load_and_clean_json(expected_file) - assert result == expected - - -def load_and_clean_yaml(location): - """ - Return plain Python nested data for the SPDX YAML file at location suitable - for comparison. The file content is cleaned from variable parts such as - dates, generated UUIDs and versions - """ - with io.open(location, encoding='utf-8') as l: - content = l.read() - data = yaml.safe_load(content) - - if 'creationInfo' in data: - del (data['creationInfo']) - - return sort_nested(data) - - -def check_yaml_scan(expected_file, result_file, regen=False): - """ - Check that expected_file and result_file are equal. - Both are paths to SPDX YAML files, UTF-8 encoded. - """ - result = load_and_clean_yaml(result_file) - if regen: - with io.open(expected_file, 'w', encoding='utf-8') as o: - o.write(result) - - expected = load_and_clean_yaml(expected_file) - assert result == expected - - -def load_and_clean_xml(location): - """ - Return plain Python nested data for the SPDX XML file at location suitable - for comparison. The file content is cleaned from variable parts such as - dates, generated UUIDs and versions - """ - with io.open(location, encoding='utf-8') as l: - content = l.read() - data = xmltodict.parse(content, encoding='utf-8') - - if 'creationInfo' in data['Document']: - del (data['Document']['creationInfo']) - - return sort_nested(data) - - -def check_xml_scan(expected_file, result_file, regen=False): - """ - Check that expected_file and result_file are equal. - Both are paths to SPDX XML files, UTF-8 encoded. - """ - result = load_and_clean_xml(result_file) - if regen: - with io.open(expected_file, 'w', encoding='utf-8') as o: - o.write(result) - - expected = load_and_clean_xml(expected_file) - assert result == expected - - -class TestParserUtils(object): - """ - Helper class to represent SPDX Document models as Python types after parsing - to be compared to expected data from a JSON file. - """ - - @classmethod - def license_to_dict(cls, license_var): - """ - Represents spdx.license.License, spdx.license.LicenseConjunction or - spdx.license.LicenseDisjunction as a Python dictionary - """ - CONJ_SEP = re.compile(' AND | and ') - DISJ_SEP = re.compile(' OR | or ') - if license_var is None: - return None - license_dict = OrderedDict() - - if isinstance(license_var, NoAssert): - license_dict['type'] = 'Single' - license_dict['identifier'] = 'NOASSERTION' - license_dict['name'] = 'NOASSERTION' - return license_dict - elif isinstance(license_var, spdx.license.LicenseConjunction): - license_dict['type'] = 'Conjunction' - sep_regex = CONJ_SEP - elif isinstance(license_var, spdx.license.LicenseDisjunction): - license_dict['type'] = 'Disjunction' - sep_regex = DISJ_SEP - else: - license_dict['type'] = 'Single' - license_dict['identifier'] = license_var.identifier - license_dict['name'] = license_var.full_name - return license_dict - - license_dict['identifier'] = sorted(sep_regex.split(license_var.identifier)) - license_dict['name'] = sorted(sep_regex.split(license_var.full_name)) - - return license_dict - - @classmethod - def version_to_dict(cls, version): - """ - Represents spdx.version.Version as a Python dictionary - """ - return OrderedDict([ - ('major', int(version.major)), - ('minor', int(version.minor)) - ]) - - @classmethod - def entity_to_dict(cls, entity): - """ - Represents spdx.creationInfo.Creator subclasses as a dictionary - """ - if entity is None: - return None - entity_dict = OrderedDict(name=entity.name) - - if isinstance(entity, spdx.creationinfo.Tool): - entity_dict['type'] = 'Tool' - return entity_dict - - entity_dict['email'] = entity.email - entity_dict['type'] = 'Person' - - if isinstance(entity, spdx.creationinfo.Organization): - entity_dict['type'] = 'Organization' - return entity_dict - - return entity_dict - - @classmethod - def checksum_to_dict(cls, checksum): - """ - Represents spdx.checksum.Algorithm as a Python dictionary - """ - if checksum is None: - return None - return OrderedDict([ - ('identifier', checksum.identifier.name), - ('value', checksum.value)]) - - @classmethod - def package_to_dict(cls, package): - """ - Represents spdx.package.Package as a Python dictionary - """ - lics_from_files = [] - if package.are_files_analyzed: - lics_from_files = sorted(package.licenses_from_files, key=lambda lic: lic.identifier) - package_dict = OrderedDict([ - ('id', package.spdx_id), - ('name', package.name), - ('packageFileName', package.file_name), - ('summary', package.summary), - ('description', package.description), - ('versionInfo', package.version), - ('sourceInfo', package.source_info), - ('downloadLocation', str(package.download_location)), - ('homepage', package.homepage), - ('originator', cls.entity_to_dict(package.originator)), - ('supplier', cls.entity_to_dict(package.supplier)), - ('licenseConcluded', cls.license_to_dict(package.conc_lics)), - ('licenseDeclared', cls.license_to_dict(package.license_declared)), - ('copyrightText', str(package.cr_text)), - ('licenseComment', package.license_comment), - ('checksums', [cls.checksum_to_dict(checksum) for checksum in package.checksums.values()]), - ('licenseInfoFromFiles', [cls.license_to_dict(lic) for lic in lics_from_files]), - ('verificationCode', OrderedDict([ - ('value', package.verif_code), - ('excludedFilesNames', sorted(package.verif_exc_files))]) - ) - ]) - - if package.built_date: - package_dict['builtDate'] = utils.datetime_iso_format(package.built_date) - - if package.release_date: - package_dict['releaseDate'] = utils.datetime_iso_format(package.release_date) - - if package.valid_until_date: - package_dict['validUntilDate'] = utils.datetime_iso_format(package.valid_until_date) - - if package.primary_package_purpose: - package_dict['primaryPackagePurpose'] = package.primary_package_purpose.name.replace("_", "-") - - return package_dict - - @classmethod - def files_to_list(cls, files): - """ - Represents a list of spdx.file.File as a Python list of dictionaries - """ - files_list = [] - - for file in files: - lics_in_files = sorted(file.licenses_in_file, key=lambda lic: lic.identifier) - chk_sums = [] - for checksum in file.checksums.values(): - chk_sums.append(cls.checksum_to_dict(checksum)) - - file_dict = OrderedDict([ - ('id', file.spdx_id), - ('fileName', file.name), - ('fileTypes', [file_type.name for file_type in file.file_types]), - ('comment', file.comment), - ('licenseConcluded', cls.license_to_dict(file.conc_lics)), - ('copyrightText', file.copyright), - ('licenseComment', file.license_comment), - ('notice', file.notice), - ('checksums', chk_sums), - ('licenseInfoInFiles', [cls.license_to_dict(lic) for lic in lics_in_files]), - ('contributors', sorted(file.contributors)), - ('dependencies', sorted(file.dependencies)), - ('artifactOfProjectName', file.artifact_of_project_name), - ('artifactOfProjectHome', file.artifact_of_project_home), - ('artifactOfProjectURI', file.artifact_of_project_uri), - ]) - files_list.append(file_dict) - - return files_list - - @classmethod - def ext_document_references_to_list(cls, ext_doc_refs): - """ - Represents a list of spdx.document.ExternalDocumentRef as a Python list of dictionaries - """ - ext_doc_refs_list = [] - - for ext_doc_ref in ext_doc_refs: - ext_doc_ref_dict = OrderedDict([ - ('externalDocumentId', ext_doc_ref.external_document_id), - ('spdxDocument', ext_doc_ref.spdx_document_uri), - ('checksum', cls.checksum_to_dict(ext_doc_ref.checksum)), - ]) - ext_doc_refs_list.append(ext_doc_ref_dict) - - return ext_doc_refs_list - - @classmethod - def extracted_licenses_to_list(cls, extracted_licenses): - """ - Represents a list of spdx.document.ExtractedLicense as a Python list of dictionaries - """ - extracted_licenses_list = [] - - for extracted_license in extracted_licenses: - extracted_license_dict = OrderedDict([ - ('name', extracted_license.full_name), - ('identifier', extracted_license.identifier), - ('text', extracted_license.text), - ('comment', extracted_license.comment), - ('cross_refs', sorted(extracted_license.cross_ref)), - ]) - if extracted_license_dict not in extracted_licenses_list: - extracted_licenses_list.append(extracted_license_dict) - - return extracted_licenses_list - - @classmethod - def annotations_to_list(cls, annotations): - """ - Represents a list of spdx.annotation.Annotation as a Python list of dictionaries - """ - annotations_list = [] - - for annotation in annotations: - annotation_dict = OrderedDict([ - ('id', annotation.spdx_id), - ('comment', annotation.comment), - ('type', annotation.annotation_type), - ('annotator', cls.entity_to_dict(annotation.annotator)), - ('date', utils.datetime_iso_format(annotation.annotation_date)), - ]) - annotations_list.append(annotation_dict) - - return annotations_list - - @classmethod - def reviews_to_list(cls, reviews): - """ - Represents a list of spdx.review.Review as a Python list of dictionaries - """ - reviews_list = [] - - for review in reviews: - review_dict = OrderedDict([ - ('comment', review.comment), - ('reviewer', cls.entity_to_dict(review.reviewer)), - ('date', utils.datetime_iso_format(review.review_date)) - ]) - reviews_list.append(review_dict) - - return reviews_list - - @classmethod - def snippets_to_list(cls, snippets): - """ - Represents a list of spdx.snippet.Snippet as a Python list of dictionaries - """ - snippets_list = [] - - for snippet in snippets: - lics_from_snippet = sorted(snippet.licenses_in_snippet, key=lambda lic: lic.identifier) - snippet_dict = OrderedDict([ - ('id', snippet.spdx_id), - ('name', snippet.name), - ('comment', snippet.comment), - ('copyrightText', snippet.copyright), - ('licenseComments', snippet.license_comment), - ('snippetFromFile', snippet.snip_from_file_spdxid), - ('licenseConcluded', cls.license_to_dict(snippet.conc_lics)), - ('licenseInfoInSnippets', [cls.license_to_dict(lic) for lic in lics_from_snippet]), - ]) - snippets_list.append(snippet_dict) - - return snippets_list - - @classmethod - def relationships_to_dict_list(cls, relationships: List[Relationship]) -> List[OrderedDict]: - relationships_list = [] - for relationship in relationships: - relationship_dict = OrderedDict([ - ('spdx_element_id', relationship.spdx_element_id), - ('relationship_type', relationship.relationship_type), - ('related_spdx_element', relationship.related_spdx_element) - ]) - relationships_list.append(relationship_dict) - - return relationships_list - - @classmethod - def to_dict(cls, doc): - """ - Represents a SPDX Document (spdx.document.Document) as nested Python types - """ - creators = sorted(doc.creation_info.creators, key=lambda c: c.name) - return OrderedDict([ - ('id', doc.spdx_id), - ('specVersion', cls.version_to_dict(doc.version)), - ('documentNamespace', doc.namespace), - ('name', doc.name), - ('comment', doc.comment), - ('dataLicense', cls.license_to_dict(doc.data_license)), - ('licenseListVersion', cls.version_to_dict(doc.creation_info.license_list_version)), - ('creators', [cls.entity_to_dict(creator) for creator in creators]), - ('created', utils.datetime_iso_format(doc.creation_info.created)), - ('creatorComment', doc.creation_info.comment), - ('files', cls.files_to_list(sorted(doc.files))), - ('packages', [cls.package_to_dict(p) for p in doc.packages]), - ('externalDocumentRefs', cls.ext_document_references_to_list(sorted(doc.ext_document_references))), - ('extractedLicenses', cls.extracted_licenses_to_list(sorted(doc.extracted_licenses))), - ('annotations', cls.annotations_to_list(sorted(doc.annotations))), - ('reviews', cls.reviews_to_list(sorted(doc.reviews))), - ('snippets', cls.snippets_to_list(sorted(doc.snippet))), - ('relationships', cls.relationships_to_dict_list(doc.relationships)) - ]) From b919b41c0e7deea678049a26d035568e5b8a04b1 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Fri, 9 Dec 2022 11:44:34 +0100 Subject: [PATCH 024/362] [refactor] reformat readme Signed-off-by: Nicolaus Weidner --- README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 04a1431b4..a54d21bb0 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,15 @@ | [ ![Linux build status][1]][2] | [![macOS build status][3]][4] | [![Windows build status][5]][6] | [1]: https://travis-ci.org/spdx/tools-python.svg?branch=master + [2]: https://travis-ci.org/spdx/tools-python + [3]: https://circleci.com/gh/spdx/tools-python/tree/master.svg?style=shield&circle-token=36cca2dfa3639886fc34e22d92495a6773bdae6d + [4]: https://circleci.com/gh/spdx/tools-python/tree/master + [5]: https://ci.appveyor.com/api/projects/status/0bf9glha2yg9x8ef/branch/master?svg=true + [6]: https://ci.appveyor.com/project/spdx/tools-python/branch/master @@ -20,18 +25,15 @@ This library implements SPDX parsers, convertors, validators and handlers in Pyt - Issues: https://github.com/spdx/tools-python/issues - PyPI: https://pypi.python.org/pypi/spdx-tools - # 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. - # License [Apache-2.0](LICENSE) - # Features * API to create and manipulate SPDX v2.3 documents. @@ -47,12 +49,12 @@ This is the result of an initial GSoC contribution by @[ah450](https://github.co * Include specialized validation for SPDX v2.2.1(ISO 5962:2021) - # How to use ## Command-line usage: 1. **PARSER** (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`. @@ -62,6 +64,7 @@ If you are using a source distribution, try running: `pyspdxtools_parser --file 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 @@ -79,7 +82,6 @@ Example (if you are using a source distribution): `pyspdxtools_convertor -f rdf * For help use `pyspdxtools_convertor --help` - # Installation As always you should work in a virtualenv (venv). You can install a local clone @@ -87,7 +89,6 @@ of this repo with `yourenv/bin/pip install .` or install it from PyPI with `yourenv/bin/pip install spdx-tools`. Note that on Windows it would be `Scripts` instead of `bin`. - # Dependencies * PLY: https://pypi.python.org/pypi/ply/ used for parsing. @@ -96,7 +97,6 @@ instead of `bin`. * xmltodict: https://pypi.org/project/xmltodict/ for handling XML. * click: https://pypi.org/project/click/ for creating the CLI interface. - # Support * Submit issues, questions or feedback at https://github.com/spdx/tools-python/issues @@ -106,4 +106,5 @@ instead of `bin`. # Contributing -Contributions are very welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md) for instructions on how to contribute to the codebase. +Contributions are very welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md) for instructions on how to contribute to the +codebase. From 40a110ff69985231868fa97065dba33da751f690 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Fri, 9 Dec 2022 11:44:48 +0100 Subject: [PATCH 025/362] [issue-358] Add note regarding current state to readme Signed-off-by: Nicolaus Weidner --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index a54d21bb0..d467ea3b7 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,11 @@ [6]: https://ci.appveyor.com/project/spdx/tools-python/branch/master +# CURRENT STATE + +A major refactoring of large parts of the codebase is currently in progress. It is expected that functionality on `main` +is severely limited during this process. Please check out +the [latest release](https://github.com/spdx/tools-python/releases/tag/v0.7.0) if you are looking for a working version. # Information From 4bcee26785ee19f5ba77a8a6779a3abd7e951a05 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Fri, 9 Dec 2022 15:29:49 +0100 Subject: [PATCH 026/362] [review] Remove parser CLI tool code that will require major changes Signed-off-by: Nicolaus Weidner --- src/clitools/convertor.py | 5 +-- src/clitools/parser.py | 89 ++------------------------------------- 2 files changed, 5 insertions(+), 89 deletions(-) diff --git a/src/clitools/convertor.py b/src/clitools/convertor.py index 1f8e73e9f..2a9ecb0f5 100644 --- a/src/clitools/convertor.py +++ b/src/clitools/convertor.py @@ -35,7 +35,6 @@ def determine_infile_and_outfile(infile, outfile, src, from_, to): """ infile = src[0] outfile = src[1] - # infile = os.path.splitext(infile)[0] if from_ is not None: infile_path = os.path.splitext(infile)[0] infile = infile_path + "." + from_ @@ -91,8 +90,6 @@ def main(infile, outfile, src, from_, to, force): or use ' pyspdxtools_convertor --infile --outfile ' """ - raise NotImplementedError("Currently, conversion is not implemented") - try: infile, outfile = determine_infile_and_outfile(infile, outfile, src, from_, to) except ValueError as err: @@ -100,6 +97,8 @@ def main(infile, outfile, src, from_, to, force): print_help_msg(main) 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 diff --git a/src/clitools/parser.py b/src/clitools/parser.py index 5b7f7a9e3..e4340115b 100755 --- a/src/clitools/parser.py +++ b/src/clitools/parser.py @@ -29,94 +29,11 @@ def main(file, force): """ raise NotImplementedError("Currently, no parsers are implemented") - # Parse document and set as doc here + # Parse document # First one to implement is the Json parser: https://github.com/spdx/tools-python/issues/305 - print("doc comment: {0}".format(doc.comment)) - print("Creators:") - for c in doc.creation_info.creators: - print("\t{0}".format(c)) - print("Document review information:") - for review in doc.reviews: - print("\tReviewer: {0}".format(review.reviewer)) - print("\tDate: {0}".format(review.review_date)) - print("\tComment: {0}".format(review.comment)) - print("Creation comment: {0}".format(doc.creation_info.comment)) - for package in doc.packages: - print("Package Name: {0}".format(package.name)) - print("Package Version: {0}".format(package.version)) - print( - "Package Download Location: {0}".format(package.download_location) - ) - print("Package Homepage: {0}".format(package.homepage)) - for checksum in doc.package.checksums.values(): - print("Package Checksum: {0} {1}".format(checksum.identifier.name, checksum.value)) - print("Package Attribution Text: {0}".format(package.attribution_text)) - print("Package verification code: {0}".format(package.verif_code)) - print( - "Package excluded from verif: {0}".format( - ",".join(package.verif_exc_files) - ) - ) - print("Package license concluded: {0}".format(package.conc_lics)) - print("Package license declared: {0}".format(package.license_declared)) - print("Package licenses from files:") - for lics in package.licenses_from_files: - print("\t{0}".format(lics)) - print("Package Copyright text: {0}".format(package.cr_text)) - print("Package summary: {0}".format(package.summary)) - print("Package description: {0}".format(package.description)) - if len(package.pkg_ext_refs) > 0: - print("Package external references:") - for ref in package.pkg_ext_refs: - print(f"\tCategory: {ref.category}") - print(f"\tType: {ref.pkg_ext_ref_type}") - print(f"\tLocator: {ref.locator}") - if ref.comment: - print(f"\tComment: {ref.comment}") - if doc.files: - print("Files:") - for f in doc.files: - print("\tFile name: {0}".format(f.name)) - for file_type in f.file_types: - print("\tFile type: {0}".format(file_type.name)) - for file_checksum in f.checksums.values(): - print("\tFile Checksum: {0} {1}".format(file_checksum.identifier.name, file_checksum.value)) - print("\tFile license concluded: {0}".format(f.conc_lics)) - print( - "\tFile license info in file: {0}".format( - ",".join( - map(lambda l: l.identifier if not isinstance(l, (SpdxNone, SpdxNoAssertion)) else l.to_value(), - f.licenses_in_file)) - ) - ) - print( - "\tFile artifact of project name: {0}".format( - ",".join(f.artifact_of_project_name) - ) - ) - - if doc.extracted_licenses: - print("Document Extracted licenses:") - for lics in doc.extracted_licenses: - print("\tIdentifier: {0}".format(lics.identifier)) - print("\tName: {0}".format(lics.full_name)) - print("\tLicense Text: {0}".format(lics.text)) - if doc.annotations: - print("Annotations:") - for an in doc.annotations: - print("\tAnnotator: {0}".format(an.annotator)) - print("\tAnnotation Date: {0}".format(an.annotation_date)) - print("\tAnnotation Comment: {0}".format(an.comment)) - print("\tAnnotation Type: {0}".format(an.annotation_type)) - print("\tAnnotation SPDX Identifier: {0}".format(an.spdx_id)) - - if doc.relationships: - print("Relationships: ") - for relation in doc.relationships: - print("\tRelationship: {0}".format(relation.relationship)) - if relation.comment: - print("\tRelationship Comment: {0}".format(relation.comment)) + # 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__": From dbe17bbfbae06ffd708c816f1b1802a54f2d179a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 9 Dec 2022 17:35:42 +0100 Subject: [PATCH 027/362] [refactor] move comment to the end of extractedLicensingInfo constructor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit this makes sense as it is the only unconditionally optional parameter Signed-off-by: Armin Tänzer --- src/model/extracted_licensing_info.py | 6 +++--- tests/model/test_extracted_licensing_info.py | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/model/extracted_licensing_info.py b/src/model/extracted_licensing_info.py index df72fcfb7..10eaccb4e 100644 --- a/src/model/extracted_licensing_info.py +++ b/src/model/extracted_licensing_info.py @@ -20,11 +20,11 @@ class ExtractedLicensingInfo: license_id: Optional[str] = None extracted_text: Optional[str] = None license_name: Optional[str] = None - comment: Optional[str] = None cross_references: List[str] = field(default_factory=list) + comment: Optional[str] = None def __init__(self, license_id: Optional[str] = None, extracted_text: Optional[str] = None, - license_name: Optional[str] = None, comment: Optional[str] = None, - cross_references: List[str] = None): + license_name: Optional[str] = None, cross_references: List[str] = None, + comment: Optional[str] = None): cross_references = [] if cross_references is None else cross_references check_types_and_set_values(self, locals()) diff --git a/tests/model/test_extracted_licensing_info.py b/tests/model/test_extracted_licensing_info.py index 38a4aa600..1d83077a3 100644 --- a/tests/model/test_extracted_licensing_info.py +++ b/tests/model/test_extracted_licensing_info.py @@ -4,12 +4,12 @@ def test_correct_initialization(): - extracted_licensing_info = ExtractedLicensingInfo("id", "text", "name", "comment", ["reference"]) + extracted_licensing_info = ExtractedLicensingInfo("id", "text", "name", ["reference"], "comment") assert extracted_licensing_info.license_id == "id" assert extracted_licensing_info.extracted_text == "text" assert extracted_licensing_info.license_name == "name" - assert extracted_licensing_info.comment == "comment" assert extracted_licensing_info.cross_references == ["reference"] + assert extracted_licensing_info.comment == "comment" def test_wrong_type_in_license_id(): @@ -27,11 +27,11 @@ def test_wrong_type_in_license_name(): ExtractedLicensingInfo(license_name=42) -def test_wrong_type_in_comment(): +def test_wrong_type_in_cross_references(): with pytest.raises(TypeError): - ExtractedLicensingInfo(comment=42) + ExtractedLicensingInfo(cross_references=["ref", 42]) -def test_wrong_type_in_cross_references(): +def test_wrong_type_in_comment(): with pytest.raises(TypeError): - ExtractedLicensingInfo(cross_references=["ref", 42]) + ExtractedLicensingInfo(comment=42) From c329bc2be89f46dade78a08e82bfae147a4fcc93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Mon, 12 Dec 2022 10:47:12 +0100 Subject: [PATCH 028/362] [fix] remove leftover legacy files. Add NOASSERTION as possible value for extracted licensing info license name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/model/extracted_licensing_info.py | 7 +- tests/clitools/test_cli_convertor.py | 8 +- tests/test_checksum.py | 74 ---- tests/testing_utils.LICENSE | 502 -------------------------- tests/testing_utils.py | 81 ----- 5 files changed, 8 insertions(+), 664 deletions(-) delete mode 100644 tests/test_checksum.py delete mode 100644 tests/testing_utils.LICENSE delete mode 100644 tests/testing_utils.py diff --git a/src/model/extracted_licensing_info.py b/src/model/extracted_licensing_info.py index 10eaccb4e..659f996c8 100644 --- a/src/model/extracted_licensing_info.py +++ b/src/model/extracted_licensing_info.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. from dataclasses import field -from typing import Optional, List +from typing import Optional, List, Union +from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.typing.dataclass_with_properties import dataclass_with_properties from src.model.typing.type_checks import check_types_and_set_values @@ -19,12 +20,12 @@ class ExtractedLicensingInfo: license_id: Optional[str] = None extracted_text: Optional[str] = None - license_name: Optional[str] = None + license_name: Optional[Union[str, SpdxNoAssertion]] = None cross_references: List[str] = field(default_factory=list) comment: Optional[str] = None def __init__(self, license_id: Optional[str] = None, extracted_text: Optional[str] = None, - license_name: Optional[str] = None, cross_references: List[str] = None, + license_name: Optional[Union[str, SpdxNoAssertion]] = None, cross_references: List[str] = None, comment: Optional[str] = None): cross_references = [] if cross_references is None else cross_references check_types_and_set_values(self, locals()) diff --git a/tests/clitools/test_cli_convertor.py b/tests/clitools/test_cli_convertor.py index 3c0d25d89..d4ea5c760 100644 --- a/tests/clitools/test_cli_convertor.py +++ b/tests/clitools/test_cli_convertor.py @@ -9,11 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os from unittest import TestCase +import pytest + from src.clitools.convertor import determine_infile_and_outfile -from tests.testing_utils import raises class TestConvertor(TestCase): @@ -71,7 +71,6 @@ def test_determine_input_with_unknown_i_format_known_o_format(self): assert infile == expected_infile assert outfile == outfile_given - @raises(ValueError) def test_determine_input_with_invalid_arguments(self): infile_given = None outfile_given = None @@ -79,4 +78,5 @@ def test_determine_input_with_invalid_arguments(self): from_ = None to = None - infile, outfile = determine_infile_and_outfile(infile_given, outfile_given, src, from_, to) + with pytest.raises(ValueError): + determine_infile_and_outfile(infile_given, outfile_given, src, from_, to) diff --git a/tests/test_checksum.py b/tests/test_checksum.py deleted file mode 100644 index 03ff16b03..000000000 --- a/tests/test_checksum.py +++ /dev/null @@ -1,74 +0,0 @@ -# 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. -import pytest - -from spdx.checksum import ChecksumAlgorithm, Checksum - - -@pytest.mark.parametrize("algorithm,expected", - [("SHA1", "checksumAlgorithm_sha1"), ("SHA224", "checksumAlgorithm_sha224"), - ("SHA3_256", "checksumAlgorithm_sha3_256"), ("BLAKE2B_256", "checksumAlgorithm_blake2b256"), - ("MD5", "checksumAlgorithm_md5")]) -def test_checksum_to_rdf(algorithm, expected): - test_algorithm = ChecksumAlgorithm[algorithm] - rdf_algorithm = test_algorithm.algorithm_to_rdf_representation() - - assert rdf_algorithm == expected - - -@pytest.mark.parametrize("expected,rdf_algorithm", - [(ChecksumAlgorithm.SHA1, "checksumAlgorithm_sha1"), - (ChecksumAlgorithm.SHA224, "checksumAlgorithm_sha224"), - (ChecksumAlgorithm.SHA3_256, "checksumAlgorithm_sha3_256"), - (ChecksumAlgorithm.BLAKE2B_256, "checksumAlgorithm_blake2b256"), - (ChecksumAlgorithm.MD5, "checksumAlgorithm_md5")]) -def test_checksum_from_rdf(rdf_algorithm, expected): - algorithm = ChecksumAlgorithm.checksum_from_rdf(rdf_algorithm) - - assert algorithm == expected - - -@pytest.mark.parametrize("rdf_algorithm", - ["_checksumAlgorithm_sha1", "checksumAlgorithm_sha_224", "checksumAlgorithm_sha3256", - "checksumAlgorithm_blake2b 256", "checksumAlgorithm_blake2b-256", - "checksumAlgorithm_bblake2b 256"]) -def test_checksum_from_wrong_rdf(rdf_algorithm): - with pytest.raises(ValueError) as error: - ChecksumAlgorithm.checksum_from_rdf(rdf_algorithm) - - assert str(error.value).startswith("Invalid algorithm for checksum") - - -CHECKSUM_VALUE = "123Abc" - - -@pytest.mark.parametrize("checksum_string,expected", - [("SHA1: " + CHECKSUM_VALUE, Checksum(ChecksumAlgorithm.SHA1, CHECKSUM_VALUE)), - ("SHA3-256: " + CHECKSUM_VALUE, Checksum(ChecksumAlgorithm.SHA3_256, CHECKSUM_VALUE)), - ("ADLER32: " + CHECKSUM_VALUE, Checksum(ChecksumAlgorithm.ADLER32, CHECKSUM_VALUE)), - ("BLAKE3: " + CHECKSUM_VALUE, Checksum(ChecksumAlgorithm.BLAKE3, CHECKSUM_VALUE)), - ("BLAKE2b-256: " + CHECKSUM_VALUE, Checksum(ChecksumAlgorithm.BLAKE2B_256, CHECKSUM_VALUE)), - ("MD5: " + CHECKSUM_VALUE, Checksum(ChecksumAlgorithm.MD5, CHECKSUM_VALUE))]) -def test_checksum_from_string(checksum_string: str, expected: Checksum): - checksum: Checksum = Checksum.checksum_from_string(checksum_string) - assert checksum == expected - - -@pytest.mark.parametrize("checksum, expected", - [(Checksum(ChecksumAlgorithm.SHA1, CHECKSUM_VALUE), "SHA1: " + CHECKSUM_VALUE), - (Checksum(ChecksumAlgorithm.SHA3_256, CHECKSUM_VALUE), "SHA3-256: " + CHECKSUM_VALUE), - (Checksum(ChecksumAlgorithm.ADLER32, CHECKSUM_VALUE), "ADLER32: " + CHECKSUM_VALUE), - (Checksum(ChecksumAlgorithm.BLAKE3, CHECKSUM_VALUE), "BLAKE3: " + CHECKSUM_VALUE), - (Checksum(ChecksumAlgorithm.BLAKE2B_256, CHECKSUM_VALUE), "BLAKE2b-256: " + CHECKSUM_VALUE), - (Checksum(ChecksumAlgorithm.MD5, CHECKSUM_VALUE), "MD5: " + CHECKSUM_VALUE)]) -def test_checksum_to_tv(checksum: Checksum, expected: str): - checksum_string: str = checksum.to_tv() - assert checksum_string == expected diff --git a/tests/testing_utils.LICENSE b/tests/testing_utils.LICENSE deleted file mode 100644 index 2fb889408..000000000 --- a/tests/testing_utils.LICENSE +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/tests/testing_utils.py b/tests/testing_utils.py deleted file mode 100644 index 022e0de77..000000000 --- a/tests/testing_utils.py +++ /dev/null @@ -1,81 +0,0 @@ -# Subset of nose tools (the parts included have not been modified) -# Borrowed from: -# https://raw.githubusercontent.com/nose-devs/nose/7c26ad1e6b7d308cafa328ad34736d34028c122a/nose/tools/nontrivial.py -# Copyright (c) 2005-2009 Jason Pellerin and others. -# -# This program is free software; you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, -# Fifth Floor, Boston, MA 02110-1301 USA -# - -"""Tools not exempt from being descended into in tracebacks""" - - -def make_decorator(func): - """ - Wraps a test decorator so as to properly replicate metadata - of the decorated function, including nose's additional stuff - (namely, setup and teardown). - """ - def decorate(newfunc): - if hasattr(func, 'compat_func_name'): - name = func.compat_func_name - else: - name = func.__name__ - newfunc.__dict__ = func.__dict__ - newfunc.__doc__ = func.__doc__ - newfunc.__module__ = func.__module__ - if not hasattr(newfunc, 'compat_co_firstlineno'): - newfunc.compat_co_firstlineno = func.__code__.co_firstlineno - try: - newfunc.__name__ = name - except TypeError: - # can't set func name in 2.3 - newfunc.compat_func_name = name - return newfunc - return decorate - - -def raises(*exceptions): - """Test must raise one of expected exceptions to pass. - - Example use:: - - @raises(TypeError, ValueError) - def test_raises_type_error(): - raise TypeError("This test passes") - - @raises(Exception) - def test_that_fails_by_passing(): - pass - - If you want to test many assertions about exceptions in a single test, - you may want to use `assert_raises` instead. - """ - valid = ' or '.join([e.__name__ for e in exceptions]) - - def decorate(func): - name = func.__name__ - - def newfunc(*arg, **kw): - try: - func(*arg, **kw) - except exceptions: - pass - except: - raise - else: - message = "%s() did not raise %s" % (name, valid) - raise AssertionError(message) - newfunc = make_decorator(func)(newfunc) - return newfunc - return decorate - From adaaf1996f11e9ee485ae528ab9b43117136ec66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Mon, 28 Nov 2022 15:53:44 +0100 Subject: [PATCH 029/362] [issue-321] introduce dataclass with properties and typeguard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/model/dataclass_with_properties.py | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/model/dataclass_with_properties.py diff --git a/src/model/dataclass_with_properties.py b/src/model/dataclass_with_properties.py new file mode 100644 index 000000000..6d1c81a21 --- /dev/null +++ b/src/model/dataclass_with_properties.py @@ -0,0 +1,36 @@ +from dataclasses import dataclass + +from typeguard import typechecked + + +def dataclass_with_properties(cls): + """Decorator to generate a dataclass with properties out of the class' value:type list. + Their getters and setters will be subjected to the @typechecked decorator to ensure type conformity.""" + data_cls = dataclass(cls) + for field_name, field_type in data_cls.__annotations__.items(): + set_field = make_setter(field_name, field_type) + get_field = make_getter(field_name, field_type) + + setattr(data_cls, field_name, property(get_field, set_field)) + + return data_cls + + +def make_setter(field_name, field_type): + """helper method to avoid late binding when generating functions in a for loop""" + + @typechecked + def set_field(self, value: field_type): + setattr(self, f"_{field_name}", value) + + return set_field + + +def make_getter(field_name, field_type): + """helper method to avoid late binding when generating functions in a for loop""" + + @typechecked + def get_field(self) -> field_type: + return getattr(self, f"_{field_name}") + + return get_field From c5c20ae4cbaa196e1d48f886dc93257963de0c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 24 Nov 2022 12:53:57 +0100 Subject: [PATCH 030/362] [issue-307] create validation classes and tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- pyproject.toml | 2 +- src/model/dataclass_with_properties.py | 36 ----- src/validation/__init__.py | 0 src/validation/actor_validator.py | 34 +++++ src/validation/annotation_validator.py | 42 ++++++ src/validation/checksum_validator.py | 60 ++++++++ src/validation/creation_info_validator.py | 61 ++++++++ src/validation/document_validator.py | 60 ++++++++ .../external_document_ref_validator.py | 51 +++++++ .../external_package_ref_validator.py | 30 ++++ .../extracted_licensing_info_validator.py | 47 ++++++ src/validation/file_validator.py | 63 ++++++++ .../license_expression_validator.py | 27 ++++ src/validation/package_validator.py | 113 +++++++++++++++ .../package_verification_code_validator.py | 37 +++++ src/validation/relationship_validator.py | 42 ++++++ src/validation/snippet_validator.py | 76 ++++++++++ src/validation/spdx_id_validation.py | 79 +++++++++++ src/validation/uri_validators.py | 36 +++++ src/validation/validation_message.py | 34 +++++ tests/valid_defaults.py | 134 ++++++++++++++++++ tests/validation/__init__.py | 0 tests/validation/test_actor_validator.py | 33 +++++ tests/validation/test_annotation_validator.py | 39 +++++ tests/validation/test_checksum_validator.py | 94 ++++++++++++ .../test_creation_info_validator.py | 44 ++++++ tests/validation/test_document_validator.py | 20 +++ .../test_external_document_ref_validator.py | 40 ++++++ .../test_external_package_ref_validator.py | 34 +++++ ...test_extracted_licensing_info_validator.py | 39 +++++ tests/validation/test_file_validator.py | 40 ++++++ .../test_license_expression_validator.py | 14 ++ tests/validation/test_package_validator.py | 57 ++++++++ .../validation/test_relationship_validator.py | 61 ++++++++ tests/validation/test_snippet_validator.py | 66 +++++++++ tests/validation/test_spdx_id_validator.py | 1 + tests/validation/test_uri_validators.py | 99 +++++++++++++ 37 files changed, 1708 insertions(+), 37 deletions(-) delete mode 100644 src/model/dataclass_with_properties.py create mode 100644 src/validation/__init__.py create mode 100644 src/validation/actor_validator.py create mode 100644 src/validation/annotation_validator.py create mode 100644 src/validation/checksum_validator.py create mode 100644 src/validation/creation_info_validator.py create mode 100644 src/validation/document_validator.py create mode 100644 src/validation/external_document_ref_validator.py create mode 100644 src/validation/external_package_ref_validator.py create mode 100644 src/validation/extracted_licensing_info_validator.py create mode 100644 src/validation/file_validator.py create mode 100644 src/validation/license_expression_validator.py create mode 100644 src/validation/package_validator.py create mode 100644 src/validation/package_verification_code_validator.py create mode 100644 src/validation/relationship_validator.py create mode 100644 src/validation/snippet_validator.py create mode 100644 src/validation/spdx_id_validation.py create mode 100644 src/validation/uri_validators.py create mode 100644 src/validation/validation_message.py create mode 100644 tests/valid_defaults.py create mode 100644 tests/validation/__init__.py create mode 100644 tests/validation/test_actor_validator.py create mode 100644 tests/validation/test_annotation_validator.py create mode 100644 tests/validation/test_checksum_validator.py create mode 100644 tests/validation/test_creation_info_validator.py create mode 100644 tests/validation/test_document_validator.py create mode 100644 tests/validation/test_external_document_ref_validator.py create mode 100644 tests/validation/test_external_package_ref_validator.py create mode 100644 tests/validation/test_extracted_licensing_info_validator.py create mode 100644 tests/validation/test_file_validator.py create mode 100644 tests/validation/test_license_expression_validator.py create mode 100644 tests/validation/test_package_validator.py create mode 100644 tests/validation/test_relationship_validator.py create mode 100644 tests/validation/test_snippet_validator.py create mode 100644 tests/validation/test_spdx_id_validator.py create mode 100644 tests/validation/test_uri_validators.py diff --git a/pyproject.toml b/pyproject.toml index 17d43ba00..8177df578 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ ] urls = {Homepage = "https://github.com/spdx/tools-python"} requires-python = ">=3.7" -dependencies = ["ply", "rdflib", "click", "pyyaml", "xmltodict", "typeguard"] +dependencies = ["ply", "rdflib", "click", "pyyaml", "xmltodict", "typeguard", "uritools"] dynamic = ["version"] [project.optional-dependencies] diff --git a/src/model/dataclass_with_properties.py b/src/model/dataclass_with_properties.py deleted file mode 100644 index 6d1c81a21..000000000 --- a/src/model/dataclass_with_properties.py +++ /dev/null @@ -1,36 +0,0 @@ -from dataclasses import dataclass - -from typeguard import typechecked - - -def dataclass_with_properties(cls): - """Decorator to generate a dataclass with properties out of the class' value:type list. - Their getters and setters will be subjected to the @typechecked decorator to ensure type conformity.""" - data_cls = dataclass(cls) - for field_name, field_type in data_cls.__annotations__.items(): - set_field = make_setter(field_name, field_type) - get_field = make_getter(field_name, field_type) - - setattr(data_cls, field_name, property(get_field, set_field)) - - return data_cls - - -def make_setter(field_name, field_type): - """helper method to avoid late binding when generating functions in a for loop""" - - @typechecked - def set_field(self, value: field_type): - setattr(self, f"_{field_name}", value) - - return set_field - - -def make_getter(field_name, field_type): - """helper method to avoid late binding when generating functions in a for loop""" - - @typechecked - def get_field(self) -> field_type: - return getattr(self, f"_{field_name}") - - return get_field diff --git a/src/validation/__init__.py b/src/validation/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/validation/actor_validator.py b/src/validation/actor_validator.py new file mode 100644 index 000000000..fe4a1769a --- /dev/null +++ b/src/validation/actor_validator.py @@ -0,0 +1,34 @@ +from typing import List, Optional + +from src.model.actor import Actor, ActorType +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType + + +class ActorValidator: + spdx_version: str + parent_id: str + + # TODO: what is parent_id in the case of annotations? + def __init__(self, spdx_version: str, parent_id: Optional[str]): + self.spdx_version = spdx_version + self.parent_id = parent_id + + def validate_actors(self, actors: List[Actor]) -> List[ValidationMessage]: + validation_messages = [] + for actor in actors: + validation_messages.extend(self.validate_actor(actor)) + + return validation_messages + + def validate_actor(self, actor: Actor) -> List[ValidationMessage]: + validation_messages = [] + + if actor.actor_type == ActorType.TOOL and actor.email is not None: + validation_messages.append( + ValidationMessage( + f"email must be None if actor_type is TOOL, but is: {actor.email}", + ValidationContext(parent_id=self.parent_id, element_type=SpdxElementType.ACTOR, full_element=actor) + ) + ) + + return validation_messages diff --git a/src/validation/annotation_validator.py b/src/validation/annotation_validator.py new file mode 100644 index 000000000..e7a316efd --- /dev/null +++ b/src/validation/annotation_validator.py @@ -0,0 +1,42 @@ +from typing import List + +from src.model.annotation import Annotation +from src.model.document import Document +from src.validation.actor_validator import ActorValidator +from src.validation.spdx_id_validation import validate_spdx_id +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType + + +class AnnotationValidator: + spdx_version: str + document: Document + actor_validator: ActorValidator + + def __init__(self, spdx_version: str, document: Document): + self.spdx_version = spdx_version + self.document = document + self.actor_validator = ActorValidator(spdx_version, parent_id=None) + + def validate_annotations(self, annotations: List[Annotation]) -> List[ValidationMessage]: + validation_messages = [] + for annotation in annotations: + validation_messages.extend(self.validate_annotation(annotation)) + + return validation_messages + + def validate_annotation(self, annotation: Annotation) -> List[ValidationMessage]: + validation_messages = [] + document_spdx_id: str = self.document.creation_info.spdx_id + context = ValidationContext(element_type=SpdxElementType.ANNOTATION, + full_element=annotation) + + validation_messages.extend( + self.actor_validator.validate_actor(annotation.annotator) + ) + + messages: List[str] = validate_spdx_id(annotation.spdx_id, self.document, check_document=True) + for message in messages: + validation_messages.append(ValidationMessage(message, context)) + + return validation_messages + diff --git a/src/validation/checksum_validator.py b/src/validation/checksum_validator.py new file mode 100644 index 000000000..27f52f62c --- /dev/null +++ b/src/validation/checksum_validator.py @@ -0,0 +1,60 @@ +import re +from typing import List, Dict + +from src.model.checksum import Checksum, ChecksumAlgorithm +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType + +# in hexadecimal digits +algorithm_length: Dict = { + ChecksumAlgorithm.SHA1: "40", + ChecksumAlgorithm.SHA224: "56", + ChecksumAlgorithm.SHA256: "64", + ChecksumAlgorithm.SHA384: "96", + ChecksumAlgorithm.SHA512: "128", + ChecksumAlgorithm.SHA3_256: "64", + ChecksumAlgorithm.SHA3_384: "96", + ChecksumAlgorithm.SHA3_512: "128", + ChecksumAlgorithm.BLAKE2B_256: "64", + ChecksumAlgorithm.BLAKE2B_384: "96", + ChecksumAlgorithm.BLAKE2B_512: "128", + ChecksumAlgorithm.BLAKE3: "256,", # at least 256 bits + ChecksumAlgorithm.MD2: "32", + ChecksumAlgorithm.MD4: "32", + ChecksumAlgorithm.MD5: "32", + ChecksumAlgorithm.MD6: "0,512", # between 0 and 512 bits + ChecksumAlgorithm.ADLER32: "8", +} + + +class ChecksumValidator: + spdx_version: str + parent_id: str + + def __init__(self, spdx_version: str, parent_id: str): + self.spdx_version = spdx_version + self.parent_id = parent_id + + def validate_checksums(self, checksums: List[Checksum]) -> List[ValidationMessage]: + validation_messages = [] + for checksum in checksums: + validation_messages.extend(self.validate_checksum(checksum)) + + return validation_messages + + def validate_checksum(self, checksum: Checksum) -> List[ValidationMessage]: + validation_messages = [] + algorithm = checksum.algorithm + context = ValidationContext(parent_id=self.parent_id, element_type=SpdxElementType.CHECKSUM, full_element=checksum) + + if not re.match("^[0-9a-f]{" + algorithm_length[algorithm] + "}$", checksum.value): + if algorithm == ChecksumAlgorithm.BLAKE3: + length = "at least 256" + elif algorithm == ChecksumAlgorithm.MD6: + length = "between 0 and 512" + else: + length = algorithm_length[algorithm] + validation_messages.append( + ValidationMessage(f'value of {algorithm} must consist of {length} hexadecimal digits, but is: {checksum.value} (length: {len(checksum.value)} digits)', context) + ) + + return validation_messages diff --git a/src/validation/creation_info_validator.py b/src/validation/creation_info_validator.py new file mode 100644 index 000000000..9356c1fec --- /dev/null +++ b/src/validation/creation_info_validator.py @@ -0,0 +1,61 @@ +import re +from typing import List, Optional +from src.model.document import CreationInfo +from src.validation.actor_validator import ActorValidator +from src.validation.external_document_ref_validator import ExternalDocumentRefValidator +from src.validation.uri_validators import validate_uri +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType + + +class CreationInfoValidator: + spdx_version: str + + def __init__(self, spdx_version): + self.spdx_version = spdx_version + + def validate_creation_info(self, creation_info: CreationInfo) -> List[ValidationMessage]: + validation_messages: List[ValidationMessage] = [] + actor_validator = ActorValidator(self.spdx_version, creation_info.spdx_id) + external_document_ref_validator = ExternalDocumentRefValidator(self.spdx_version, creation_info.spdx_id) + + context = ValidationContext(spdx_id=creation_info.spdx_id, element_type=SpdxElementType.DOCUMENT) + + if not re.match(r"^SPDX-\d+.\d+$", creation_info.spdx_version): + validation_messages.append( + ValidationMessage( + f'spdx_version must be of the form "SPDX-[major].[minor]" but is: {creation_info.spdx_version}', + context + ) + ) + + if creation_info.spdx_id != "SPDXRef-DOCUMENT": + validation_messages.append( + ValidationMessage( + f'spdx_id must be SPDXRef-DOCUMENT, but is: {creation_info.spdx_id}', + context + ) + ) + + if creation_info.data_license != "CC0-1.0": + validation_messages.append( + ValidationMessage( + f'data_license must be "CC0-1.0", but is: {creation_info.data_license}', + context + ) + ) + + for message in validate_uri(creation_info.document_namespace): + validation_messages.append( + ValidationMessage( + 'document_namespace ' + message, context + ) + ) + + validation_messages.extend( + actor_validator.validate_actors(creation_info.creators) + ) + + validation_messages.extend( + external_document_ref_validator.validate_external_document_refs(creation_info.external_document_refs)) + + return validation_messages diff --git a/src/validation/document_validator.py b/src/validation/document_validator.py new file mode 100644 index 000000000..c31242f48 --- /dev/null +++ b/src/validation/document_validator.py @@ -0,0 +1,60 @@ +from typing import List + +from src.model.document import Document +from src.model.relationship import RelationshipType +from src.validation.annotation_validator import AnnotationValidator +from src.validation.creation_info_validator import CreationInfoValidator +from src.validation.extracted_licensing_info_validator import ExtractedLicensingInfoValidator +from src.validation.file_validator import FileValidator +from src.validation.package_validator import PackageValidator +from src.validation.relationship_validator import RelationshipValidator +from src.validation.snippet_validator import SnippetValidator +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType + + +class DocumentValidator: + spdx_version: str + creation_info_validator: CreationInfoValidator + snippet_validator: SnippetValidator + annotation_validator: AnnotationValidator + relationship_validator: RelationshipValidator + extracted_licensing_info_validator: ExtractedLicensingInfoValidator + + def __init__(self, spdx_version: str): + self.spdx_version = spdx_version + self.creation_info_validator = CreationInfoValidator(spdx_version) + self.extracted_licensing_info_validator = ExtractedLicensingInfoValidator(spdx_version) + + def validate_full_spdx_document(self, document: Document) -> List[ValidationMessage]: + package_validator = PackageValidator(self.spdx_version, document) + file_validator = FileValidator(self.spdx_version, document) + snippet_validator = SnippetValidator(self.spdx_version, document) + annotation_validator = AnnotationValidator(self.spdx_version, document) + relationship_validator = RelationshipValidator(self.spdx_version, document) + + validation_messages: List[ValidationMessage] = [] + + validation_messages.extend(self.creation_info_validator.validate_creation_info(document.creation_info)) + validation_messages.extend(package_validator.validate_packages(document.packages)) + validation_messages.extend(file_validator.validate_files(document.files)) + validation_messages.extend(snippet_validator.validate_snippets(document.snippets)) + validation_messages.extend(annotation_validator.validate_annotations(document.annotations)) + validation_messages.extend(relationship_validator.validate_relationships(document.relationships)) + validation_messages.extend(self.extracted_licensing_info_validator.validate_extracted_licensing_infos( + document.extracted_licensing_info)) + + # TODO: is this correct here? Also, make test for it + document_id = document.creation_info.spdx_id + document_describes_relationships = [relationship for relationship in document.relationships if + relationship.relationship_type == RelationshipType.DESCRIBES and relationship.spdx_element_id == document_id] + described_by_document_relationships = [relationship for relationship in document.relationships if + relationship.relationship_type == RelationshipType.DESCRIBED_BY and relationship.related_spdx_element_id == document_id] + + if not document_describes_relationships + described_by_document_relationships: + validation_messages.append( + ValidationMessage( + f'there must be at least one relationship "{document_id} DESCRIBES ..." or "... DESCRIBED_BY {document_id}"', + ValidationContext(spdx_id=document_id, + element_type=SpdxElementType.DOCUMENT))) + + return validation_messages diff --git a/src/validation/external_document_ref_validator.py b/src/validation/external_document_ref_validator.py new file mode 100644 index 000000000..2aa7b26ae --- /dev/null +++ b/src/validation/external_document_ref_validator.py @@ -0,0 +1,51 @@ +import re +from typing import List, Optional + +from src.model.external_document_ref import ExternalDocumentRef +from src.validation.checksum_validator import ChecksumValidator +from src.validation.uri_validators import validate_uri +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType + + +class ExternalDocumentRefValidator: + spdx_version: str + parent_id: str + checksum_validator: ChecksumValidator + + def __init__(self, spdx_version: str, parent_id: str): + self.spdx_version = spdx_version + self.parent_id = parent_id + self.checksum_validator = ChecksumValidator(spdx_version, parent_id) + + def validate_external_document_refs(self, external_document_refs: List[ExternalDocumentRef]) -> List[ValidationMessage]: + validation_messages = [] + for external_document_ref in external_document_refs: + validation_messages.extend(self.validate_external_document_ref(external_document_ref)) + + return validation_messages + + def validate_external_document_ref(self, external_document_ref: ExternalDocumentRef) -> List[ValidationMessage]: + validation_messages = [] + context = ValidationContext(parent_id=self.parent_id, element_type=SpdxElementType.EXTERNAL_DOCUMENT_REF, + full_element=external_document_ref) + + if not re.match(r"^DocumentRef-[\da-zA-Z.+-]+$", external_document_ref.document_ref_id): + validation_messages.append( + ValidationMessage( + f'document_ref_id must only contain letters, numbers, ".", "-" and "+" and must begin with "DocumentRef-", but is: {external_document_ref.document_ref_id}', + context + ) + ) + + for message in validate_uri(external_document_ref.document_uri): + validation_messages.append( + ValidationMessage( + 'document_uri ' + message, context + ) + ) + + validation_messages.extend( + self.checksum_validator.validate_checksum(external_document_ref.checksum) + ) + + return validation_messages diff --git a/src/validation/external_package_ref_validator.py b/src/validation/external_package_ref_validator.py new file mode 100644 index 000000000..6bfb7124f --- /dev/null +++ b/src/validation/external_package_ref_validator.py @@ -0,0 +1,30 @@ +from typing import List + +from src.model.package import ExternalPackageRef +from src.validation.checksum_validator import ChecksumValidator +from src.validation.validation_message import ValidationMessage +from src.validation.license_expression_validator import LicenseExpressionValidator + + +class ExternalPackageRefValidator: + spdx_version: str + parent_id: str + checksum_validator: ChecksumValidator + license_expression_validator: LicenseExpressionValidator + + def __init__(self, spdx_version: str, parent_id: str): + self.spdx_version = spdx_version + self.parent_id = parent_id + self.checksum_validator = ChecksumValidator(spdx_version, parent_id) + self.license_expression_validator = LicenseExpressionValidator(spdx_version) + + def validate_external_package_refs(self, external_package_refs: List[ExternalPackageRef]) -> List[ValidationMessage]: + validation_messages = [] + for external_package_ref in external_package_refs: + validation_messages.extend(self.validate_external_package_ref(external_package_ref)) + + return validation_messages + + def validate_external_package_ref(self, external_package_ref: ExternalPackageRef) -> List[ValidationMessage]: + # TODO: this is gonna be insane (Annex F) + return [] diff --git a/src/validation/extracted_licensing_info_validator.py b/src/validation/extracted_licensing_info_validator.py new file mode 100644 index 000000000..a05350f3f --- /dev/null +++ b/src/validation/extracted_licensing_info_validator.py @@ -0,0 +1,47 @@ +import re +from typing import List, Optional + +from src.model.extracted_licensing_info import ExtractedLicensingInfo +from src.validation.uri_validators import validate_url +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType + + +class ExtractedLicensingInfoValidator: + spdx_version: str + + def __init__(self, spdx_version): + self.spdx_version = spdx_version + + def validate_extracted_licensing_infos(self, extracted_licensing_infos: Optional[List[ExtractedLicensingInfo]]) -> List[ValidationMessage]: + if extracted_licensing_infos is None: + return [] + + validation_messages = [] + for extracted_licensing_info in extracted_licensing_infos: + validation_messages.extend(self.validate_extracted_licensing_info(extracted_licensing_info)) + + return validation_messages + + def validate_extracted_licensing_info(self, extracted_licensing_infos: ExtractedLicensingInfo) -> List[ValidationMessage]: + validation_messages: List[ValidationMessage] = [] + context = ValidationContext(element_type=SpdxElementType.EXTRACTED_LICENSING_INFO, full_element=extracted_licensing_infos) + + license_id: str = extracted_licensing_infos.license_id + if license_id and not re.match(r"^LicenseRef-[\da-zA-Z.-]+$", license_id): + validation_messages.append( + ValidationMessage(f'license_id must only contain letters, numbers, "." and "-" and must begin with "LicenseRef-", but is: {license_id}', + context) + ) + + if license_id and not extracted_licensing_infos.extracted_text: + validation_messages.append( + ValidationMessage('extracted_text must be provided if there is a license_id assigned', context) + ) + + for cross_reference in extracted_licensing_infos.cross_references: + for message in validate_url(cross_reference): + validation_messages.append( + ValidationMessage("cross_reference " + message, context) + ) + + return validation_messages diff --git a/src/validation/file_validator.py b/src/validation/file_validator.py new file mode 100644 index 000000000..34c20a9c7 --- /dev/null +++ b/src/validation/file_validator.py @@ -0,0 +1,63 @@ +from typing import List + +from src.model.checksum import ChecksumAlgorithm +from src.model.document import Document +from src.model.file import File +from src.validation.checksum_validator import ChecksumValidator +from src.validation.spdx_id_validation import is_valid_spdx_id, validate_spdx_id +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from src.validation.license_expression_validator import LicenseExpressionValidator + + +class FileValidator: + spdx_version: str + document: Document + license_expression_validator: LicenseExpressionValidator + + def __init__(self, spdx_version: str, document: Document): + self.spdx_version = spdx_version + self.document = document + self.license_expression_validator = LicenseExpressionValidator(spdx_version) + + def validate_files(self, files: List[File]) -> List[ValidationMessage]: + validation_messages = [] + for file in files: + validation_messages.extend(self.validate_file(file)) + + return validation_messages + + def validate_file(self, file: File) -> List[ValidationMessage]: + validation_messages = [] + checksum_validator = ChecksumValidator(self.spdx_version, file.spdx_id) + context = ValidationContext(spdx_id=file.spdx_id, element_type=SpdxElementType.FILE, full_element=file) + + for message in validate_spdx_id(file.spdx_id, self.document): + validation_messages.append(ValidationMessage(message, context)) + + if not file.name.startswith("./"): + validation_messages.append( + ValidationMessage( + f'file name must be a relative path to the file, starting with "./", but is: {file.name}', + context) + ) + + if ChecksumAlgorithm.SHA1 not in [checksum.algorithm for checksum in file.checksums]: + validation_messages.append( + ValidationMessage( + f'checksums must contain a SHA1 algorithm checksum, but is: {file.checksums}', + context) + ) + + validation_messages.extend( + checksum_validator.validate_checksums(file.checksums) + ) + + validation_messages.extend( + self.license_expression_validator.validate_license_expression(file.concluded_license) + ) + + validation_messages.extend( + self.license_expression_validator.validate_license_expressions(file.license_info_in_file) + ) + + return validation_messages diff --git a/src/validation/license_expression_validator.py b/src/validation/license_expression_validator.py new file mode 100644 index 000000000..4e4e42cba --- /dev/null +++ b/src/validation/license_expression_validator.py @@ -0,0 +1,27 @@ +from typing import List, Optional, Union + +from src.model.license_expression import LicenseExpression +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone +from src.validation.validation_message import ValidationMessage + + +class LicenseExpressionValidator: + spdx_version: str + + def __init__(self, spdx_version): + self.spdx_version = spdx_version + + def validate_license_expressions(self, license_expressions: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]]) -> List[ValidationMessage]: + if license_expressions in [SpdxNoAssertion(), SpdxNone(), None]: + return [] + + error_messages = [] + + for license_expression in license_expressions: + error_messages.extend(self.validate_license_expression(license_expression)) + + return error_messages + + def validate_license_expression(self, license_expression: LicenseExpression) -> List[ValidationMessage]: + return [] diff --git a/src/validation/package_validator.py b/src/validation/package_validator.py new file mode 100644 index 000000000..942b063ea --- /dev/null +++ b/src/validation/package_validator.py @@ -0,0 +1,113 @@ +from typing import List + +from src.model.document import Document +from src.model.package import Package +from src.model.relationship import RelationshipType +from src.validation.checksum_validator import ChecksumValidator +from src.validation.external_package_ref_validator import ExternalPackageRefValidator +from src.validation.license_expression_validator import LicenseExpressionValidator +from src.validation.package_verification_code_validator import PackageVerificationCodeValidator +from src.validation.spdx_id_validation import validate_spdx_id +from src.validation.uri_validators import validate_url, validate_package_download_location +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType + + +class PackageValidator: + spdx_version: str + document: Document + license_expression_validator: LicenseExpressionValidator + + def __init__(self, spdx_version: str, document: Document): + self.spdx_version = spdx_version + self.document = document + self.license_expression_validator = LicenseExpressionValidator(spdx_version) + + def validate_packages(self, packages: List[Package]) -> List[ValidationMessage]: + validation_messages: List[ValidationMessage] = [] + for package in packages: + validation_messages.extend(self.validate_package(package)) + + return validation_messages + + def validate_package(self, package: Package) -> List[ValidationMessage]: + checksum_validator = ChecksumValidator(self.spdx_version, parent_id=package.spdx_id) + verification_code_validator = PackageVerificationCodeValidator(self.spdx_version, package.spdx_id) + external_package_ref_validator = ExternalPackageRefValidator(self.spdx_version, package.spdx_id) + + validation_messages: List[ValidationMessage] = [] + context = ValidationContext(spdx_id=package.spdx_id, parent_id=self.document.creation_info.spdx_id, + element_type=SpdxElementType.PACKAGE, full_element=package) + + for message in validate_spdx_id(package.spdx_id, self.document): + validation_messages.append(ValidationMessage(message, context)) + + download_location = package.download_location + if isinstance(download_location, str): + for message in validate_package_download_location(download_location): + validation_messages.append(ValidationMessage("download_location " + message, context)) + + homepage = package.homepage + if isinstance(homepage, str): + for message in validate_url(homepage): + validation_messages.append(ValidationMessage("homepage " + message, context)) + + if package.verification_code: + if not package.files_analyzed: + validation_messages.append( + ValidationMessage( + f'verification_code must be None if files_analyzed is False, but is: {package.verification_code}', + context)) + else: + validation_messages.extend( + verification_code_validator.validate_verification_code(package.verification_code) + ) + + # TODO: make test for this + if not package.files_analyzed: + package_contains_relationships = [relationship for relationship in self.document.relationships if + relationship.relationship_type == RelationshipType.CONTAINS and relationship.spdx_element_id == package.spdx_id] + if package_contains_relationships: + validation_messages.append( + ValidationMessage( + f'package must contain no elements if files_analyzed is False, but found {package_contains_relationships}', + context) + ) + + contained_in_package_relationships = [relationship for relationship in self.document.relationships if + relationship.relationship_type == RelationshipType.CONTAINED_BY and relationship.related_spdx_element_id == package.spdx_id] + if contained_in_package_relationships: + validation_messages.append( + ValidationMessage( + f'package must contain no elements if files_analyzed is False, but found {package_contains_relationships}', + context) + ) + + validation_messages.extend( + checksum_validator.validate_checksums(package.checksums) + ) + + validation_messages.extend( + self.license_expression_validator.validate_license_expression(package.license_concluded) + ) + + if package.license_info_from_files: + if not package.files_analyzed: + validation_messages.append( + ValidationMessage( + f'license_info_from_files must be None if files_analyzed is False, but is: {package.license_info_from_files}', + context) + ) + else: + validation_messages.extend( + self.license_expression_validator.validate_license_expressions(package.license_info_from_files) + ) + + validation_messages.extend( + self.license_expression_validator.validate_license_expression(package.license_declared) + ) + + validation_messages.extend( + external_package_ref_validator.validate_external_package_refs(package.external_references) + ) + + return validation_messages diff --git a/src/validation/package_verification_code_validator.py b/src/validation/package_verification_code_validator.py new file mode 100644 index 000000000..3864d7d75 --- /dev/null +++ b/src/validation/package_verification_code_validator.py @@ -0,0 +1,37 @@ +import re +from typing import List + +from src.model.package import PackageVerificationCode +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType + + +class PackageVerificationCodeValidator: + spdx_version: str + parent_id: str + + def __init__(self, spdx_version: str, parent_id: str): + self.spdx_version = spdx_version + self.parent_id = parent_id + + # TODO: make test for this + def validate_verification_code(self, verification_code: PackageVerificationCode) -> List[ValidationMessage]: + validation_messages: List[ValidationMessage] = [] + context = ValidationContext(parent_id=self.parent_id, element_type=SpdxElementType.PACKAGE_VERIFICATION_CODE, + full_element=verification_code) + + for file in verification_code.excluded_files: + if not file.startswith("./"): + validation_messages.append( + ValidationMessage( + f'file name must be a relative path to the file, starting with "./", but is: {file}', context) + ) + + value = verification_code.value + if not re.match("^[0-9a-f]{40}$", value): + validation_messages.append( + ValidationMessage( + f'value of verification_code must consist of 40 hexadecimal digits, but is: {value} (length: {len(value)} digits)', + context) + ) + + return validation_messages diff --git a/src/validation/relationship_validator.py b/src/validation/relationship_validator.py new file mode 100644 index 000000000..b83319621 --- /dev/null +++ b/src/validation/relationship_validator.py @@ -0,0 +1,42 @@ +from typing import List + +from src.model.document import Document +from src.model.relationship import Relationship, RelationshipType +from src.validation.spdx_id_validation import validate_spdx_id +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType + + +class RelationshipValidator: + spdx_version: str + document: Document + + def __init__(self, spdx_version: str, document: Document): + self.spdx_version = spdx_version + self.document = document + + def validate_relationships(self, relationships: List[Relationship]) -> List[ValidationMessage]: + validation_messages = [] + for relationship in relationships: + validation_messages.extend(self.validate_relationship(relationship)) + + return validation_messages + + def validate_relationship(self, relationship: Relationship) -> List[ValidationMessage]: + validation_messages = [] + context = ValidationContext(element_type=SpdxElementType.RELATIONSHIP, + full_element=relationship) + + first_id: str = relationship.spdx_element_id + second_id: str = relationship.related_spdx_element_id + relationship_type: RelationshipType = relationship.relationship_type + + for spdx_id in [first_id, second_id]: + messages: List[str] = validate_spdx_id(spdx_id, self.document, check_document=True) + for message in messages: + validation_messages.append(ValidationMessage(message, context)) + + if self.spdx_version != "2.3": + if relationship_type == RelationshipType.SPECIFICATION_FOR or relationship_type == RelationshipType.REQUIREMENT_DESCRIPTION_FOR: + validation_messages.append(ValidationMessage(f'{relationship_type} is not supported for SPDX versions below 2.3', context)) + + return validation_messages diff --git a/src/validation/snippet_validator.py b/src/validation/snippet_validator.py new file mode 100644 index 000000000..0ed3e0899 --- /dev/null +++ b/src/validation/snippet_validator.py @@ -0,0 +1,76 @@ +from typing import List + +from src.model.document import Document +from src.model.snippet import Snippet +from src.validation.license_expression_validator import LicenseExpressionValidator +from src.validation.spdx_id_validation import validate_spdx_id +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType + + +class SnippetValidator: + spdx_version: str + document: Document + license_expression_validator: LicenseExpressionValidator + + def __init__(self, spdx_version: str, document: Document): + self.spdx_version = spdx_version + self.document = document + self.license_expression_validator = LicenseExpressionValidator(spdx_version) + + def validate_snippets(self, snippets: List[Snippet]) -> List[ValidationMessage]: + validation_messages = [] + for snippet in snippets: + validation_messages.extend(self.validate_snippet(snippet)) + + return validation_messages + + def validate_snippet(self, snippet: Snippet) -> List[ValidationMessage]: + validation_messages = [] + context = ValidationContext(spdx_id=snippet.spdx_id, element_type=SpdxElementType.SNIPPET, full_element=snippet) + + messages: List[str] = validate_spdx_id(snippet.spdx_id, self.document) + for message in messages: + validation_messages.append(ValidationMessage(message, context)) + + messages: List[str] = validate_spdx_id(snippet.file_spdx_id, self.document, check_files=True) + for message in messages: + validation_messages.append(ValidationMessage(message, context)) + + if snippet.byte_range[0] < 1: + validation_messages.append( + ValidationMessage( + f'byte_range values must be greater than or equal to 1, but is: {snippet.byte_range}', + context) + ) + + if snippet.byte_range[0] > snippet.byte_range[1]: + validation_messages.append( + ValidationMessage( + f'the first value of byte_range must be less than or equal to the second, but is: {snippet.byte_range}', + context) + ) + + if snippet.line_range: + if snippet.line_range[0] < 1: + validation_messages.append( + ValidationMessage( + f'line_range values must be greater than or equal to 1, but is: {snippet.line_range}', + context) + ) + + if snippet.line_range[0] > snippet.line_range[1]: + validation_messages.append( + ValidationMessage( + f'the first value of line_range must be less than or equal to the second, but is: {snippet.line_range}', + context) + ) + + validation_messages.extend( + self.license_expression_validator.validate_license_expression(snippet.concluded_license) + ) + + validation_messages.extend( + self.license_expression_validator.validate_license_expressions(snippet.license_info_in_snippet) + ) + + return validation_messages diff --git a/src/validation/spdx_id_validation.py b/src/validation/spdx_id_validation.py new file mode 100644 index 000000000..47e884431 --- /dev/null +++ b/src/validation/spdx_id_validation.py @@ -0,0 +1,79 @@ +import re +from typing import Optional, List, Tuple + +from src.model.document import Document +from src.model.file import File + + +def is_valid_spdx_id(spdx_id: str) -> bool: + return bool(re.match(r"^SPDXRef-[\da-zA-Z.-]+$", spdx_id)) + + +def is_valid_external_doc_ref_id(external_ref_id: str) -> bool: + return bool(re.match(r"^DocumentRef-[\da-zA-Z.+-]+$", external_ref_id)) + + +def is_spdx_id_present_in_files(spdx_id: str, files: List[File]) -> bool: + return spdx_id in [file.spdx_id for file in files] + + +def is_spdx_id_present_in_document(spdx_id: str, document: Document) -> bool: + all_spdx_ids_in_document: List[str] = get_list_of_all_spdx_ids(document) + + return spdx_id in all_spdx_ids_in_document + + +def get_list_of_all_spdx_ids(document: Document) -> List[str]: + all_spdx_ids_in_document: List[str] = [document.creation_info.spdx_id] + + all_spdx_ids_in_document.extend([package.spdx_id for package in document.packages]) + all_spdx_ids_in_document.extend([file.spdx_id for file in document.files]) + all_spdx_ids_in_document.extend([snippet.spdx_id for snippet in document.snippets]) + + return all_spdx_ids_in_document + + +def is_external_doc_ref_present_in_document(external_ref_id: str, document: Document) -> bool: + all_external_doc_ref_ids_in_document = [external_doc_ref.document_ref_id for external_doc_ref in + document.creation_info.external_document_refs] + + return external_ref_id in all_external_doc_ref_ids_in_document + + +def validate_spdx_id(spdx_id: str, document: Document, check_document: bool = False, check_files: bool = False) -> List[str]: + """ Test that the given spdx_id (and a potential DocumentRef to an external document) is valid + and, if it is a reference, actually exists in the document. Optionally checks files or the whole document + for the existence of the spdx_id (i.e. if it is used as a reference). Returns a list of validation messages, + and the external document ref part and id part of the provided spdx_id. """ + + validation_messages: List[str] = [] + split_id: List[str] = spdx_id.split(":") + + # # # invalid case # # # + if len(split_id) > 2: + return [f'spdx_id must not contain more than one colon in order to separate the external document reference id from the internal SPDX id, but is: {spdx_id}'] + + # # # case with external document ref prefix # # # + if len(split_id) == 2: + if not is_valid_external_doc_ref_id(split_id[0]): + validation_messages.append(f'the external document reference part of spdx_id must only contain letters, numbers, ".", "-" and "+" and must begin with "DocumentRef-", but is: {split_id[0]}') + if not is_valid_spdx_id(split_id[1]): + validation_messages.append(f'the internal SPDX id part of spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: {split_id[1]}') + if not is_external_doc_ref_present_in_document(split_id[0], document): + validation_messages.append(f'did not find the external document reference {split_id[0]} in the SPDX document') + + return validation_messages + + # # # "normal" case # # # + if not is_valid_spdx_id(spdx_id): + validation_messages.append(f'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: {spdx_id}') + + if check_document: + if not is_spdx_id_present_in_document(spdx_id, document): + validation_messages.append(f'did not find the referenced spdx_id {spdx_id} in the SPDX document') + + if check_files: + if not is_spdx_id_present_in_files(spdx_id, document.files): + validation_messages.append(f'did not find the referenced spdx_id {spdx_id} in the SPDX document\'s files') + + return validation_messages diff --git a/src/validation/uri_validators.py b/src/validation/uri_validators.py new file mode 100644 index 000000000..ba151a881 --- /dev/null +++ b/src/validation/uri_validators.py @@ -0,0 +1,36 @@ +import re +from typing import List + +from uritools import isabsuri, urisplit + +url_pattern = "(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/|ssh:\\/\\/|git:\\/\\/|svn:\\/\\/|sftp:\\/\\/|ftp:\\/\\/)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+){0,100}\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?" +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\\.\\-]+)" +download_location_pattern = ( + "^(((" + supported_download_repos + "\\+)?" + url_pattern + ")|" + git_pattern + "|" + bazaar_pattern + ")$") + + +def validate_url(url: str) -> List[str]: + if not re.match(url_pattern, url): + return [f'must be a valid URL, but is: {url}'] + + return [] + + +def validate_package_download_location(location: str) -> List[str]: + if not re.match(download_location_pattern, location): + return [f'must be a valid download location, but is: {location}'] + + return [] + + +def validate_uri(uri: str) -> List[str]: + if not isabsuri(uri): + return [f'must be a valid URI specified in RFC-3986, but is: {uri}'] + else: + split = urisplit(uri) + if split.scheme is None: + return [f'must have a URI scheme, but is: {uri}'] + + return [] diff --git a/src/validation/validation_message.py b/src/validation/validation_message.py new file mode 100644 index 000000000..b79840ecc --- /dev/null +++ b/src/validation/validation_message.py @@ -0,0 +1,34 @@ +from dataclasses import dataclass +from enum import Enum, auto +from typing import Optional, Any + + +class SpdxElementType(Enum): + PACKAGE_VERIFICATION_CODE = auto() + EXTERNAL_DOCUMENT_REF = auto() + CHECKSUM = auto() + EXTERNAL_PACKAGE_REF = auto() + ACTOR = auto() + DOCUMENT = auto() + CREATION_INFO = auto() + PACKAGE = auto() + FILE = auto() + SNIPPET = auto() + LICENSE = auto() + ANNOTATION = auto() + RELATIONSHIP = auto() + EXTRACTED_LICENSING_INFO = auto() + + +@dataclass(eq=True, frozen=True) +class ValidationContext: + spdx_id: Optional[str] = None # not every type has an id, or it might be missing + parent_id: Optional[str] = None # if a parent is known and has a valid id + element_type: Optional[SpdxElementType] = None + full_element: Any = None # can be any class of the data model + + +@dataclass(eq=True, frozen=True) +class ValidationMessage: + validation_message: str + context: ValidationContext diff --git a/tests/valid_defaults.py b/tests/valid_defaults.py new file mode 100644 index 000000000..9339070a5 --- /dev/null +++ b/tests/valid_defaults.py @@ -0,0 +1,134 @@ +from datetime import datetime + +from src.model.actor import Actor, ActorType +from src.model.annotation import AnnotationType, Annotation +from src.model.checksum import Checksum, ChecksumAlgorithm +from src.model.document import CreationInfo, Document +from src.model.external_document_ref import ExternalDocumentRef +from src.model.extracted_licensing_info import ExtractedLicensingInfo +from src.model.file import File +from src.model.package import Package, PackageVerificationCode, ExternalPackageRef, ExternalPackageRefCategory +from src.model.relationship import Relationship, RelationshipType +from src.model.snippet import Snippet +from src.model.spdx_none import SpdxNone + + +def get_actor(actor_type=ActorType.PERSON, name="person name", mail=None) -> Actor: + return Actor(actor_type, name, mail) + + +def get_annotation(spdx_id="SPDXRef-DOCUMENT", annotation_type=AnnotationType.OTHER, annotator=get_actor(), + annotation_date=datetime(2022, 1, 1), annotation_comment="annotation comment") -> Annotation: + return Annotation(spdx_id, annotation_type, annotator, annotation_date, annotation_comment) + + +def get_checksum(algorithm=ChecksumAlgorithm.SHA1, value="85ed0817af83a24ad8da68c2b5094de69833983c") -> Checksum: + return Checksum(algorithm, value) + + +def get_creation_info(spdx_version="SPDX-2.3", spdx_id="SPDXRef-DOCUMENT", name="document_name", + document_namespace="https://some.uri", + creators=None, created=datetime(2022, 1, 1), creator_comment=None, data_license="CC0-1.0", + external_document_refs=None, license_list_version=None, document_comment=None) -> CreationInfo: + if creators is None: + creators = [get_actor()] + + if external_document_refs is None: + external_document_refs = [] + + return CreationInfo(spdx_version, spdx_id, name, document_namespace, creators, created, creator_comment, + data_license, external_document_refs, license_list_version, document_comment) + + +def get_document(creation_info=get_creation_info(), packages=None, files=None, snippets=None, annotations=None, + relationships=None, extracted_licensing_info=None) -> Document: + if packages is None: + packages = [] + if files is None: + files = [] + if snippets is None: + snippets = [] + if annotations is None: + annotations = [] + if relationships is None: + relationships = [] + if extracted_licensing_info is None: + extracted_licensing_info = [] + + return Document(creation_info, packages, files, snippets, annotations, relationships, extracted_licensing_info) + + +def get_external_document_ref(document_ref_id="DocumentRef-idstring", document_uri="https://some.uri", + checksum=get_checksum()) -> ExternalDocumentRef: + return ExternalDocumentRef(document_ref_id, document_uri, checksum) + + +def get_extracted_licensing_info(license_id="LicenseRef-1", extracted_text="extracted text", + license_name="license name", cross_references=None, + comment=None, ) -> ExtractedLicensingInfo: + if cross_references is None: + cross_references = ["http://some.url"] + return ExtractedLicensingInfo(license_id, extracted_text, license_name, cross_references, comment) + + +def get_file(name="./file/name.py", spdx_id="SPDXRef-File", checksums=None, file_type=None, concluded_license=None, + license_info_in_file=None, license_comment=None, copyright_text=None, comment=None, notice=None, + contributors=None, attribution_texts=None): + if checksums is None: + checksums = [get_checksum()] + if contributors is None: + contributors = [] + if attribution_texts is None: + attribution_texts = [] + + return File(name, spdx_id, checksums, file_type, concluded_license, license_info_in_file, license_comment, + copyright_text, comment, notice, contributors, attribution_texts) + + +def get_package_verification_code(value="85ed0817af83a24ad8da68c2b5094de69833983c", + excluded_files=None) -> PackageVerificationCode: + if excluded_files is None: + excluded_files = [] + + return PackageVerificationCode(value, excluded_files) + + +def get_external_package_ref(category=ExternalPackageRefCategory.SECURITY, reference_type="cpe22Type", + locator="cpe:/o:canonical:ubuntu_linux:10.04:-:lts", + comment="external package ref comment") -> ExternalPackageRef: + return ExternalPackageRef(category, reference_type, locator, comment) + + +def get_package(spdx_id="SPDXRef-Package", name="package name", download_location=SpdxNone(), version=None, + file_name=None, supplier=None, originator=None, files_analyzed=False, verification_code=None, + checksums=None, homepage=None, source_info=None, license_concluded=None, license_info_from_files=None, + license_declared=None, license_comment=None, copyright_text=None, summary=None, description=None, + comment=None, external_references=None, attribution_texts=None, primary_package_purpose=None, + release_date=None, built_date=None, valid_until_date=None) -> Package: + if checksums is None: + checksums = [] + if external_references is None: + external_references = [] + if attribution_texts is None: + attribution_texts = [] + + return Package(spdx_id, name, download_location, version, file_name, supplier, originator, files_analyzed, + verification_code, checksums, homepage, source_info, license_concluded, license_info_from_files, + license_declared, license_comment, copyright_text, summary, description, comment, + external_references, attribution_texts, primary_package_purpose, release_date, built_date, + valid_until_date) + + +def get_relationship(spdx_element_id="SPDXRef-DOCUMENT", relationship_type=RelationshipType.DESCRIBES, + related_spdx_element_id="SPDXRef-File", comment=None) -> Relationship: + return Relationship(spdx_element_id, relationship_type, related_spdx_element_id, comment) + + +def get_snippet(spdx_id="SPDXRef-Snippet", file_spdx_id="SPDXRef-File", byte_range=(200, 400), line_range=None, + concluded_license=None, license_info_in_snippet=None, license_comment=None, copyright_text=None, + comment=None, name=None, attribution_texts=None) -> Snippet: + if attribution_texts is None: + attribution_texts = [] + + return Snippet(spdx_id, file_spdx_id, byte_range, line_range, concluded_license, license_info_in_snippet, + license_comment, copyright_text, comment, name, attribution_texts) diff --git a/tests/validation/__init__.py b/tests/validation/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/validation/test_actor_validator.py b/tests/validation/test_actor_validator.py new file mode 100644 index 000000000..36eead62a --- /dev/null +++ b/tests/validation/test_actor_validator.py @@ -0,0 +1,33 @@ +from typing import List + +import pytest + +from src.model.actor import ActorType, Actor +from src.validation.actor_validator import ActorValidator +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from tests.valid_defaults import get_actor + + +def test_correct_actor_person(): + actor_validator = ActorValidator("2.3", "SPDXRef-DOCUMENT") + + actor = Actor(ActorType.PERSON, "person name", "mail@mail.com") + validation_messages: List[ValidationMessage] = actor_validator.validate_actor(actor) + + assert validation_messages == [] + + +@pytest.mark.parametrize("actor, expected_message", + [(get_actor(actor_type=ActorType.TOOL, mail="mail@mail.com"), + 'email must be None if actor_type is TOOL, but is: mail@mail.com'), + ]) +def test_wrong_actor(actor, expected_message): + parent_id = "SPDXRef-DOCUMENT" + actor_validator = ActorValidator("2.3", parent_id) + validation_messages: List[ValidationMessage] = actor_validator.validate_actor(actor) + + expected = ValidationMessage(expected_message, + ValidationContext(parent_id=parent_id, element_type=SpdxElementType.ACTOR, + full_element=actor)) + + assert validation_messages == [expected] diff --git a/tests/validation/test_annotation_validator.py b/tests/validation/test_annotation_validator.py new file mode 100644 index 000000000..522841f4a --- /dev/null +++ b/tests/validation/test_annotation_validator.py @@ -0,0 +1,39 @@ +from datetime import datetime +from typing import List + +import pytest + +from src.model.annotation import Annotation, AnnotationType +from src.model.document import Document +from src.validation.annotation_validator import AnnotationValidator +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from tests.valid_defaults import get_actor, get_annotation, get_document, get_file + + +def test_correct_annotation(): + document: Document = get_document(files=[get_file(spdx_id="SPDXRef-File")]) + annotation_validator = AnnotationValidator("2.3", document) + + annotation = Annotation("SPDXRef-File", AnnotationType.OTHER, get_actor(), datetime(2022, 1, 1), "comment") + validation_messages: List[ValidationMessage] = annotation_validator.validate_annotation(annotation) + + assert validation_messages == [] + + +@pytest.mark.parametrize("annotation_id, file_id, expected_message", + [("SPDXRef-some_file", "SPDXRef-some_file", + 'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: SPDXRef-some_file'), + ("SPDXRef-File", "SPDXRef-hiddenFile", + 'did not find the referenced spdx_id SPDXRef-File in the SPDX document') + ]) +def test_wrong_annotation(annotation_id, file_id, expected_message): + annotation: Annotation = get_annotation(spdx_id=annotation_id) + document: Document = get_document(files=[get_file(spdx_id=file_id)]) + annotation_validator = AnnotationValidator("2.3", document) + validation_messages: List[ValidationMessage] = annotation_validator.validate_annotation(annotation) + + expected = ValidationMessage(expected_message, + ValidationContext(element_type=SpdxElementType.ANNOTATION, + full_element=annotation)) + + assert validation_messages == [expected] diff --git a/tests/validation/test_checksum_validator.py b/tests/validation/test_checksum_validator.py new file mode 100644 index 000000000..53388b85f --- /dev/null +++ b/tests/validation/test_checksum_validator.py @@ -0,0 +1,94 @@ +from typing import List + +import pytest + +from src.model.checksum import Checksum, ChecksumAlgorithm +from src.validation.checksum_validator import ChecksumValidator +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType + + +@pytest.mark.parametrize("checksum", + [Checksum(ChecksumAlgorithm.SHA1, "71c4025dd9897b364f3ebbb42c484ff43d00791c"), + Checksum(ChecksumAlgorithm.SHA224, + "9c9f4e27d957a123cc32d86afe33ae53b1184192cccb23b0f257f588"), + Checksum(ChecksumAlgorithm.SHA256, + "fbea580d286bbbbb41314430d58ba887716a74d7134119c5307cdc9f0c7a4299"), + Checksum(ChecksumAlgorithm.SHA384, + "73b4ad9a34e5f76cb2525ea6bb8b1dcf9ba79426b3295bd18bc6d148cba4fcc2ca3cf2630fd481b47caaac9127103933"), + Checksum(ChecksumAlgorithm.SHA512, + "c2aa8a5d297f5e888ce9a30d3745ccc5a628533449a9f98524de3d23695a268f394a67faf8ef370727c2946f1dbbec34aeb7ac10f15af43e7cb5547f1a464053"), + Checksum(ChecksumAlgorithm.SHA3_256, + "1e772489c042f49aeaae32b00fc5ef170a25afa741cffaafadde597d4d1727ce"), + Checksum(ChecksumAlgorithm.SHA3_384, + "dd9e30747551865b483bd76bd967384dce0e5670d1b1c3f701cffac7f49b1c46791253493835136b3aa5f679e364c166"), + Checksum(ChecksumAlgorithm.SHA3_512, + "906bca5580be8c95ae44f775363fb69968ad568898dfb03e0ff96cd9445a0b75f817b68e5c1e80ad624031f851cfddd3a101e1d111310266a5d46e2bc1ffbb36"), + Checksum(ChecksumAlgorithm.BLAKE2B_256, + "a0eb3ddfa5807780a562b9c313b2537f1e8dc621e9a524f8c1ffcf07a79e35c7"), + Checksum(ChecksumAlgorithm.BLAKE2B_384, + "902511afc8939c0193d87857f45a19eddfd7e0413b0f8701a3baaf1b025f882b45a8fbf623fa0ad79b64850ac7a4d0b2"), + Checksum(ChecksumAlgorithm.BLAKE2B_512, + "72c23b0160e1af3cb159f0cc96210c5e9aecc5a65d4618566776fa6117bf84929dcef56c7f8b087691c23000c945470842d90b5e8c4af74dce531ca8ebd8824c"), + Checksum(ChecksumAlgorithm.BLAKE3, + "a872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafed"), + Checksum(ChecksumAlgorithm.MD2, "af1eec2a1b18886c3f3cc244349d91d8"), + Checksum(ChecksumAlgorithm.MD4, "d4c41ce30a517d6ce9d79c8c17bb4b66"), + Checksum(ChecksumAlgorithm.MD5, "0d7f61beb7018b3924c6b8f96549fa39"), + Checksum(ChecksumAlgorithm.MD6, + "af1eec2a1b18886c3f3cc244349d91d8d4c41ce30a517d6ce9d79c8c17bb4b660d7f61beb7018b3924c6b8f96549fa39"), + Checksum(ChecksumAlgorithm.ADLER32, "02ec0130")]) +def test_correct_checksum(checksum): + checksum_validator = ChecksumValidator("2.3", "parent_id") + + validation_messages: List[ValidationMessage] = checksum_validator.validate_checksum(checksum) + + assert validation_messages == [] + + +@pytest.mark.parametrize("checksum, expected_message", + [(Checksum(ChecksumAlgorithm.SHA1, "af1eec2a1b18886c3f3cc244349d91d8"), + 'value of ChecksumAlgorithm.SHA1 must consist of 40 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + (Checksum(ChecksumAlgorithm.SHA224, "af1eec2a1b18886c3f3cc244349d91d8"), + 'value of ChecksumAlgorithm.SHA224 must consist of 56 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + (Checksum(ChecksumAlgorithm.SHA256, "af1eec2a1b18886c3f3cc244349d91d8"), + 'value of ChecksumAlgorithm.SHA256 must consist of 64 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + (Checksum(ChecksumAlgorithm.SHA384, "af1eec2a1b18886c3f3cc244349d91d8"), + 'value of ChecksumAlgorithm.SHA384 must consist of 96 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + (Checksum(ChecksumAlgorithm.SHA512, "af1eec2a1b18886c3f3cc244349d91d8"), + 'value of ChecksumAlgorithm.SHA512 must consist of 128 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + (Checksum(ChecksumAlgorithm.SHA3_256, "af1eec2a1b18886c3f3cc244349d91d8"), + 'value of ChecksumAlgorithm.SHA3_256 must consist of 64 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + (Checksum(ChecksumAlgorithm.SHA3_384, "af1eec2a1b18886c3f3cc244349d91d8"), + 'value of ChecksumAlgorithm.SHA3_384 must consist of 96 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + (Checksum(ChecksumAlgorithm.SHA3_512, "af1eec2a1b18886c3f3cc244349d91d8"), + 'value of ChecksumAlgorithm.SHA3_512 must consist of 128 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + (Checksum(ChecksumAlgorithm.BLAKE2B_256, "af1eec2a1b18886c3f3cc244349d91d8"), + 'value of ChecksumAlgorithm.BLAKE2B_256 must consist of 64 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + (Checksum(ChecksumAlgorithm.BLAKE2B_384, "af1eec2a1b18886c3f3cc244349d91d8"), + 'value of ChecksumAlgorithm.BLAKE2B_384 must consist of 96 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + (Checksum(ChecksumAlgorithm.BLAKE2B_512, "af1eec2a1b18886c3f3cc244349d91d8"), + 'value of ChecksumAlgorithm.BLAKE2B_512 must consist of 128 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + (Checksum(ChecksumAlgorithm.BLAKE3, "af1eec2a1b18886c3f3cc244349d91d8"), + 'value of ChecksumAlgorithm.BLAKE3 must consist of at least 256 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + (Checksum(ChecksumAlgorithm.MD2, "71c4025dd9897b364f3ebbb42c484ff43d00791c"), + 'value of ChecksumAlgorithm.MD2 must consist of 32 hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791c (length: 40 digits)'), + (Checksum(ChecksumAlgorithm.MD4, "71c4025dd9897b364f3ebbb42c484ff43d00791c"), + 'value of ChecksumAlgorithm.MD4 must consist of 32 hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791c (length: 40 digits)'), + (Checksum(ChecksumAlgorithm.MD5, "71c4025dd9897b364f3ebbb42c484ff43d00791c"), + 'value of ChecksumAlgorithm.MD5 must consist of 32 hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791c (length: 40 digits)'), + (Checksum(ChecksumAlgorithm.MD6, + "a872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafed5"), + 'value of ChecksumAlgorithm.MD6 must consist of between 0 and 512 hexadecimal digits, but is: a872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafed5 (length: 513 digits)'), + (Checksum(ChecksumAlgorithm.ADLER32, "af1eec2a1b18886c3f3cc244349d91d8"), + 'value of ChecksumAlgorithm.ADLER32 must consist of 8 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + ]) +def test_wrong_checksum(checksum, expected_message): + parent_id = "parent_id" + checksum_validator = ChecksumValidator("2.3", parent_id) + validation_messages: List[ValidationMessage] = checksum_validator.validate_checksum(checksum) + + expected = ValidationMessage(expected_message, + ValidationContext(parent_id=parent_id, element_type=SpdxElementType.CHECKSUM, + full_element=checksum)) + + assert validation_messages == [expected] diff --git a/tests/validation/test_creation_info_validator.py b/tests/validation/test_creation_info_validator.py new file mode 100644 index 000000000..571fed45c --- /dev/null +++ b/tests/validation/test_creation_info_validator.py @@ -0,0 +1,44 @@ +from datetime import datetime +from typing import List + +import pytest + +from src.model.document import CreationInfo +from src.model.version import Version +from src.validation.creation_info_validator import CreationInfoValidator +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from tests.valid_defaults import get_actor, get_external_document_ref, get_creation_info + + +def test_correct_creation_info(): + creation_info_validator = CreationInfoValidator("2.3") + + creation_info = CreationInfo("SPDX-2.3", "SPDXRef-DOCUMENT", "document name", "https://some.uri", + [get_actor(), get_actor()], datetime(2022, 1, 1), "creator_comment", + "CC0-1.0", [get_external_document_ref(), get_external_document_ref()], Version(6, 3), + "doc_comment") + validation_messages: List[ValidationMessage] = creation_info_validator.validate_creation_info(creation_info) + + assert validation_messages == [] + + +@pytest.mark.parametrize \ + ("creation_info_input, spdx_id, expected_message", + [(get_creation_info(spdx_version="version-2.3"), "SPDXRef-DOCUMENT", + 'spdx_version must be of the form "SPDX-[major].[minor]" but is: version-2.3'), + (get_creation_info(spdx_id="SPDXRef-doc"), "SPDXRef-doc", + 'spdx_id must be SPDXRef-DOCUMENT, but is: SPDXRef-doc'), + (get_creation_info(data_license="MIT"), "SPDXRef-DOCUMENT", + 'data_license must be "CC0-1.0", but is: MIT'), + (get_creation_info(document_namespace="some_namespace"), "SPDXRef-DOCUMENT", + 'document_namespace must be a valid URI specified in RFC-3986, but is: some_namespace'), + ]) +def test_wrong_creation_info(creation_info_input, expected_message, spdx_id): + creation_info_validator = CreationInfoValidator("2.3") + creation_info = creation_info_input + validation_messages: List[ValidationMessage] = creation_info_validator.validate_creation_info(creation_info) + + expected = ValidationMessage(expected_message, + ValidationContext(spdx_id, None, SpdxElementType.DOCUMENT)) + + assert validation_messages == [expected] diff --git a/tests/validation/test_document_validator.py b/tests/validation/test_document_validator.py new file mode 100644 index 000000000..7f16b192f --- /dev/null +++ b/tests/validation/test_document_validator.py @@ -0,0 +1,20 @@ +from typing import List +from unittest import mock + +from src.model.document import Document +from src.validation.document_validator import DocumentValidator +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from tests.valid_defaults import get_creation_info, get_package, get_file, get_snippet, get_annotation, \ + get_relationship, get_extracted_licensing_info + + +def test_correct_document(): + document_validator = DocumentValidator("2.3") + + document = Document(get_creation_info(), [get_package(), get_package()], [get_file(), get_file()], [get_snippet(), get_snippet()], [get_annotation(), get_annotation()], + [get_relationship(), get_relationship()], [get_extracted_licensing_info(), get_extracted_licensing_info()]) + validation_messages: List[ValidationMessage] = document_validator.validate_full_spdx_document(document) + + assert validation_messages == [] + +# TODO: some kind of super test is needed to test that all the subvalidations are correctly called diff --git a/tests/validation/test_external_document_ref_validator.py b/tests/validation/test_external_document_ref_validator.py new file mode 100644 index 000000000..d5de1f43d --- /dev/null +++ b/tests/validation/test_external_document_ref_validator.py @@ -0,0 +1,40 @@ +from typing import List + +import pytest + +from src.model.external_document_ref import ExternalDocumentRef +from src.validation.external_document_ref_validator import ExternalDocumentRefValidator +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from tests.valid_defaults import get_checksum, get_external_document_ref + + +def test_correct_external_document_ref(): + external_document_ref_validator = ExternalDocumentRefValidator("2.3", "parent_id") + + external_document_ref = ExternalDocumentRef("DocumentRef-id", "http://some.uri", get_checksum()) + validation_messages: List[ValidationMessage] = external_document_ref_validator.validate_external_document_ref( + external_document_ref) + + assert validation_messages == [] + + +@pytest.mark.parametrize("external_document_ref, expected_message", + [(get_external_document_ref(document_ref_id="SPDXRef-id"), + 'document_ref_id must only contain letters, numbers, ".", "-" and "+" and must begin with "DocumentRef-", but is: SPDXRef-id'), + (get_external_document_ref(document_ref_id="DocumentRef-some_id"), + 'document_ref_id must only contain letters, numbers, ".", "-" and "+" and must begin with "DocumentRef-", but is: DocumentRef-some_id'), + (get_external_document_ref(document_uri="some_uri"), + 'document_uri must be a valid URI specified in RFC-3986, but is: some_uri') + ]) +def test_wrong_external_document_ref(external_document_ref, expected_message): + parent_id = "SPDXRef-DOCUMENT" + external_document_ref_validator = ExternalDocumentRefValidator("2.3", parent_id) + validation_messages: List[ValidationMessage] = external_document_ref_validator.validate_external_document_ref( + external_document_ref) + + expected = ValidationMessage(expected_message, + ValidationContext(parent_id=parent_id, + element_type=SpdxElementType.EXTERNAL_DOCUMENT_REF, + full_element=external_document_ref)) + + assert validation_messages == [expected] diff --git a/tests/validation/test_external_package_ref_validator.py b/tests/validation/test_external_package_ref_validator.py new file mode 100644 index 000000000..7db8b2f76 --- /dev/null +++ b/tests/validation/test_external_package_ref_validator.py @@ -0,0 +1,34 @@ +from typing import List +import pytest +from src.model.package import ExternalPackageRef, ExternalPackageRefCategory +from src.validation.external_package_ref_validator import ExternalPackageRefValidator +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from tests.valid_defaults import get_external_package_ref + + +def test_correct_external_package_ref(): + external_package_ref_validator = ExternalPackageRefValidator("2.3", "SPDXRef-Package") + + external_package_ref = ExternalPackageRef(ExternalPackageRefCategory.OTHER, "swh", + "swh:1:cnt:94a9ed024d3859793618152ea559a168bbcbb5e2", "comment") + validation_messages: List[ValidationMessage] = external_package_ref_validator.validate_external_package_ref( + external_package_ref) + + assert validation_messages == [] + + +@pytest.mark.parametrize("external_package_ref, expected_message", + [(get_external_package_ref(), + 'TBD'), + ]) +@pytest.mark.skip("add tests once external package ref validation is implemented") +def wrong_external_package_ref(external_package_ref, expected_message): + parent_id = "SPDXRef-Package" + external_package_ref_validator = ExternalPackageRefValidator("2.3", parent_id) + validation_messages: List[ValidationMessage] = external_package_ref_validator.validate_external_package_ref(external_package_ref) + + expected = ValidationMessage(expected_message, + ValidationContext(parent_id=parent_id, element_type=SpdxElementType.EXTERNAL_PACKAGE_REF, + full_element=external_package_ref)) + + assert validation_messages == [expected] diff --git a/tests/validation/test_extracted_licensing_info_validator.py b/tests/validation/test_extracted_licensing_info_validator.py new file mode 100644 index 000000000..12c6968e1 --- /dev/null +++ b/tests/validation/test_extracted_licensing_info_validator.py @@ -0,0 +1,39 @@ +from typing import List + +import pytest + +from src.model.extracted_licensing_info import ExtractedLicensingInfo +from src.validation.extracted_licensing_info_validator import ExtractedLicensingInfoValidator +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from tests.valid_defaults import get_extracted_licensing_info + + +def test_correct_extracted_licensing_info(): + extracted_licensing_info_validator = ExtractedLicensingInfoValidator("2.3") + + extracted_licensing_info = ExtractedLicensingInfo("LicenseRef-1", "extracted text", "license name", ["http://some.url"], "comment") + validation_messages: List[ValidationMessage] = extracted_licensing_info_validator.validate_extracted_licensing_info( + extracted_licensing_info) + + assert validation_messages == [] + + +# TODO: tests for licenses not on the SPDX License list (i.e. they must provide id, name and cross-references) +@pytest.mark.parametrize("extracted_licensing_info, expected_message", + [(get_extracted_licensing_info(license_id="SPDXRef-wrong"), + 'license_id must only contain letters, numbers, "." and "-" and must begin with "LicenseRef-", but is: SPDXRef-wrong'), + (get_extracted_licensing_info(extracted_text=None), + 'extracted_text must be provided if there is a license_id assigned'), + (get_extracted_licensing_info(cross_references=["wrong_url"]), + 'cross_reference must be a valid URL, but is: wrong_url') + ]) +def test_wrong_extracted_licensing_info(extracted_licensing_info, expected_message): + extracted_licensing_info_validator = ExtractedLicensingInfoValidator("2.3") + validation_messages: List[ValidationMessage] = extracted_licensing_info_validator.validate_extracted_licensing_info(extracted_licensing_info) + + expected = ValidationMessage(expected_message, + ValidationContext(element_type=SpdxElementType.EXTRACTED_LICENSING_INFO, + full_element=extracted_licensing_info)) + + assert validation_messages == [expected] + diff --git a/tests/validation/test_file_validator.py b/tests/validation/test_file_validator.py new file mode 100644 index 000000000..d723cd8a0 --- /dev/null +++ b/tests/validation/test_file_validator.py @@ -0,0 +1,40 @@ +from typing import List +import pytest + +from src.model.checksum import Checksum, ChecksumAlgorithm +from src.model.file import File, FileType +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone +from src.validation.file_validator import FileValidator +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from tests.valid_defaults import get_checksum, get_file, get_document + + +def test_correct_file(): + file_validator = FileValidator("2.3", get_document()) + + file = File("./file/name.py", "SPDXRef-File", [get_checksum()], [FileType.OTHER, FileType.SPDX], SpdxNone(), SpdxNoAssertion(), + "comment on license", "copyright", "comment", "notice", ["contributor"], ["attribution"]) + validation_messages: List[ValidationMessage] = file_validator.validate_file(file) + + assert validation_messages == [] + + +@pytest.mark.parametrize("file_input, spdx_id, expected_message", + [(get_file(spdx_id="SPDXRef-some_file"), "SPDXRef-some_file", + 'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: SPDXRef-some_file'), + (get_file(name="wrong file name"), get_file().spdx_id, + 'file name must be a relative path to the file, starting with "./", but is: wrong file name'), + (get_file(checksums=[Checksum(ChecksumAlgorithm.MD2, "d4c41ce30a517d6ce9d79c8c17bb4b66")]), get_file().spdx_id, + f'checksums must contain a SHA1 algorithm checksum, but is: {[Checksum(ChecksumAlgorithm.MD2, "d4c41ce30a517d6ce9d79c8c17bb4b66")]}') + ]) +def test_wrong_file(file_input, spdx_id, expected_message): + file_validator = FileValidator("2.3", get_document()) + validation_messages: List[ValidationMessage] = file_validator.validate_file(file_input) + + expected = ValidationMessage(expected_message, + ValidationContext(spdx_id=spdx_id, + element_type=SpdxElementType.FILE, + full_element=file_input)) + + assert validation_messages == [expected] diff --git a/tests/validation/test_license_expression_validator.py b/tests/validation/test_license_expression_validator.py new file mode 100644 index 000000000..419ad804e --- /dev/null +++ b/tests/validation/test_license_expression_validator.py @@ -0,0 +1,14 @@ +from typing import List + +from src.model.license_expression import LicenseExpression +from src.validation.license_expression_validator import LicenseExpressionValidator +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType + + +def test_correct_license_expression(): + license_expression_validator = LicenseExpressionValidator("2.3") + + license_expression = LicenseExpression("LicenseRef-1") + validation_messages: List[ValidationMessage] = license_expression_validator.validate_license_expression(license_expression) + + assert validation_messages == [] diff --git a/tests/validation/test_package_validator.py b/tests/validation/test_package_validator.py new file mode 100644 index 000000000..eb47c5592 --- /dev/null +++ b/tests/validation/test_package_validator.py @@ -0,0 +1,57 @@ +from datetime import datetime +from typing import List + +import pytest + +from src.model.license_expression import LicenseExpression +from src.model.package import Package, PackagePurpose +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone +from src.validation.package_validator import PackageValidator +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from tests.valid_defaults import get_checksum, get_external_package_ref, get_actor, get_package_verification_code, \ + get_package, get_document + + +def test_correct_package(): + package_validator = PackageValidator("2.3", get_document()) + + package = Package("SPDXRef-Package", "package name", "www.download.com", "version", "file_name", SpdxNoAssertion(), + get_actor(), True, + get_package_verification_code(), [get_checksum()], "https://homepage.com", "source_info", None, + [LicenseExpression("expression")], + SpdxNone(), "comment on license", "copyright", "summary", "description", "comment", + [get_external_package_ref()], ["text"], PackagePurpose.OTHER, datetime(2022, 1, 1), None, None) + validation_messages: List[ValidationMessage] = package_validator.validate_package(package) + + assert validation_messages == [] + + +# TODO: is verification_code required if files_analyzed=True? +@pytest.mark.parametrize("package_input, expected_message", + [(get_package(spdx_id="SPDXRef-some_package"), + 'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: SPDXRef-some_package'), + (get_package(files_analyzed=False, verification_code=get_package_verification_code()), + f'verification_code must be None if files_analyzed is False, but is: {get_package_verification_code()}'), + (get_package(download_location="bad_download_location"), + "download_location must be a valid download location, but is: bad_download_location"), + (get_package(homepage="bad_url"), + 'homepage must be a valid URL, but is: bad_url'), + (get_package(files_analyzed=False, license_info_from_files=SpdxNone()), + 'license_info_from_files must be None if files_analyzed is False, but is: NONE'), + (get_package(files_analyzed=False, license_info_from_files=SpdxNoAssertion()), + 'license_info_from_files must be None if files_analyzed is False, but is: NOASSERTION'), + (get_package(files_analyzed=False, license_info_from_files=[LicenseExpression("some_license")]), + 'license_info_from_files must be None if files_analyzed is False, but is: [LicenseExpression(expression_string=\'some_license\')]') + ]) +def test_wrong_package(package_input, expected_message): + parent_id = "SPDXRef-DOCUMENT" + package_validator = PackageValidator("2.3", get_document()) + package = package_input + validation_messages: List[ValidationMessage] = package_validator.validate_package(package) + + expected = ValidationMessage(expected_message, + ValidationContext(spdx_id=package.spdx_id, parent_id=parent_id, element_type=SpdxElementType.PACKAGE, + full_element=package)) + + assert validation_messages == [expected] diff --git a/tests/validation/test_relationship_validator.py b/tests/validation/test_relationship_validator.py new file mode 100644 index 000000000..f8a7ff4d7 --- /dev/null +++ b/tests/validation/test_relationship_validator.py @@ -0,0 +1,61 @@ +from typing import List + +import pytest + +from src.model.document import Document +from src.model.relationship import Relationship, RelationshipType +from src.validation.relationship_validator import RelationshipValidator +from src.validation.validation_message import ValidationMessage, SpdxElementType, ValidationContext +from tests.valid_defaults import get_document, get_package, get_relationship, get_file + + +def test_correct_relationship(): + document: Document = get_document(packages=[get_package(spdx_id="SPDXRef-Package")]) + relationship_validator = RelationshipValidator("2.3", document) + + relationship = Relationship("SPDXRef-DOCUMENT", RelationshipType.AMENDS, "SPDXRef-Package", comment="comment") + validation_messages: List[ValidationMessage] = relationship_validator.validate_relationship(relationship) + + assert validation_messages == [] + + +@pytest.mark.parametrize("first_id, second_id, wrong_file_id, expected_message", + [("SPDXRef-some_file", "SPDXRef-File", "SPDXRef-some_file", + 'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: SPDXRef-some_file'), + ("SPDXRef-File", "SPDXRef-some_file", "SPDXRef-some_file", + 'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: SPDXRef-some_file'), + ("SPDXRef-unknownFile", "SPDXRef-hiddenFile", "SPDXRef-hiddenFile", + 'did not find the referenced spdx_id SPDXRef-unknownFile in the SPDX document'), + ("SPDXRef-hiddenFile", "SPDXRef-unknownFile", "SPDXRef-hiddenFile", + 'did not find the referenced spdx_id SPDXRef-unknownFile in the SPDX document'), + ]) +def test_wrong_relationship(first_id, second_id, wrong_file_id, expected_message): + relationship: Relationship = get_relationship(spdx_element_id=first_id, related_spdx_element_id=second_id) + document: Document = get_document(files=[get_file(spdx_id="SPDXRef-File"), get_file(spdx_id=wrong_file_id)]) + relationship_validator = RelationshipValidator("2.3", document) + validation_messages: List[ValidationMessage] = relationship_validator.validate_relationship(relationship) + + expected = ValidationMessage(expected_message, + ValidationContext(element_type=SpdxElementType.RELATIONSHIP, + full_element=relationship)) + + assert validation_messages == [expected] + + +@pytest.mark.parametrize("relationship, expected_message", + [(Relationship("SPDXRef-DOCUMENT", RelationshipType.SPECIFICATION_FOR, "SPDXRef-Package"), + "RelationshipType.SPECIFICATION_FOR is not supported for SPDX versions below 2.3"), + (Relationship("SPDXRef-DOCUMENT", RelationshipType.REQUIREMENT_DESCRIPTION_FOR, + "SPDXRef-Package"), + "RelationshipType.REQUIREMENT_DESCRIPTION_FOR is not supported for SPDX versions below 2.3")]) +def test_v2_3_only_types(relationship, expected_message): + document: Document = get_document(packages=[get_package(spdx_id="SPDXRef-Package")]) + relationship_validator = RelationshipValidator("2.2", document) + + validation_message: List[ValidationMessage] = relationship_validator.validate_relationship(relationship) + + expected = [ValidationMessage(expected_message, + ValidationContext(element_type=SpdxElementType.RELATIONSHIP, + full_element=relationship))] + + assert validation_message == expected diff --git a/tests/validation/test_snippet_validator.py b/tests/validation/test_snippet_validator.py new file mode 100644 index 000000000..b39791846 --- /dev/null +++ b/tests/validation/test_snippet_validator.py @@ -0,0 +1,66 @@ +from typing import List + +import pytest + +from src.model.document import Document +from src.model.license_expression import LicenseExpression +from src.model.snippet import Snippet +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.validation.snippet_validator import SnippetValidator +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from tests.valid_defaults import get_snippet, get_document, get_file + + +def test_correct_snippet(): + document: Document = get_document(files=[get_file(spdx_id="SPDXRef-File")]) + + snippet_validator = SnippetValidator("2.3", document) + + snippet = Snippet("SPDXRef-Snippet", "SPDXRef-File", (200, 400), (20, 40), LicenseExpression("some_license"), + SpdxNoAssertion(), "comment on license", + "copyright", "comment", "name", ["attribution"]) + validation_messages: List[ValidationMessage] = snippet_validator.validate_snippet(snippet) + + assert validation_messages == [] + + +@pytest.mark.parametrize("snippet_id, snippet_file_id, file_id, expected_message", + [("SPDXRef-some_snippet", "SPDXRef-File", "SPDXRef-File", + 'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: SPDXRef-some_snippet'), + ("SPDXRef-Snippet", "SPDXRef-some_file", "SPDXRef-some_file", + 'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: SPDXRef-some_file'), + ("SPDXRef-Snippet", "SPDXRef-File", "SPDXRef-hiddenFile", + 'did not find the referenced spdx_id SPDXRef-File in the SPDX document\'s files') + ]) +def test_wrong_spdx_ids(snippet_id, snippet_file_id, file_id, expected_message): + snippet_validator = SnippetValidator("2.3", get_document(files=[get_file(spdx_id=file_id)])) + snippet = get_snippet(spdx_id=snippet_id, file_spdx_id=snippet_file_id) + validation_messages: List[ValidationMessage] = snippet_validator.validate_snippet(snippet) + + expected = ValidationMessage(expected_message, + ValidationContext(spdx_id=snippet_id, element_type=SpdxElementType.SNIPPET, + full_element=snippet)) + + assert validation_messages == [expected] + + +@pytest.mark.parametrize("snippet_input, expected_message", + [(get_snippet(byte_range=(-12, 45)), + 'byte_range values must be greater than or equal to 1, but is: (-12, 45)'), + (get_snippet(byte_range=(45, 23)), + 'the first value of byte_range must be less than or equal to the second, but is: (45, 23)'), + (get_snippet(line_range=(-12, 45)), + 'line_range values must be greater than or equal to 1, but is: (-12, 45)'), + (get_snippet(line_range=(45, 23)), + 'the first value of line_range must be less than or equal to the second, but is: (45, 23)') + ]) +def test_wrong_ranges(snippet_input, expected_message): + snippet_validator = SnippetValidator("2.3", get_document(files=[get_file()])) + + validation_messages: List[ValidationMessage] = snippet_validator.validate_snippet(snippet_input) + + expected = ValidationMessage(expected_message, + ValidationContext(spdx_id=snippet_input.spdx_id, element_type=SpdxElementType.SNIPPET, + full_element=snippet_input)) + + assert validation_messages == [expected] diff --git a/tests/validation/test_spdx_id_validator.py b/tests/validation/test_spdx_id_validator.py new file mode 100644 index 000000000..3940605f8 --- /dev/null +++ b/tests/validation/test_spdx_id_validator.py @@ -0,0 +1 @@ +# TODO: add tests diff --git a/tests/validation/test_uri_validators.py b/tests/validation/test_uri_validators.py new file mode 100644 index 000000000..f1065c3b3 --- /dev/null +++ b/tests/validation/test_uri_validators.py @@ -0,0 +1,99 @@ + +from uritools import isuri, isabsuri +import pytest + +from src.validation.uri_validators import validate_url, validate_package_download_location, validate_uri + + +@pytest.mark.parametrize("input_value", ['https://some.url', "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"]) +def test_valid_url(input_value): + assert validate_url(input_value) == [] + + +# TODO: more negative examples +@pytest.mark.parametrize("input_value", [':::::', ]) +def test_invalid_url(input_value): + assert validate_url(input_value) == [f'must be a valid URL, but is: {input_value}'] + + +@pytest.mark.parametrize("input_value", ["http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz", + "git://git.myproject.org/MyProject", + "git+https://git.myproject.org/MyProject.git", + "git+http://git.myproject.org/MyProject", + "git+ssh://git.myproject.org/MyProject.git", + "git+git://git.myproject.org/MyProject", + "git+git@git.myproject.org:MyProject", + "git://git.myproject.org/MyProject#src/somefile.c", + "git+https://git.myproject.org/MyProject#src/Class.java", + "git://git.myproject.org/MyProject.git@master", + "git+https://git.myproject.org/MyProject.git@v1.0", + "git://git.myproject.org/MyProject.git@da39a3ee5e6b4b0d3255bfef95601890afd80709", + "git+https://git.myproject.org/MyProject.git@master#/src/MyClass.cpp", + "git+https://git.myproject.org/MyProject@da39a3ee5e6b4b0d3255bfef95601890afd80709#lib/variable.rb", + "hg+http://hg.myproject.org/MyProject", + "hg+https://hg.myproject.org/MyProject", + "hg+ssh://hg.myproject.org/MyProject", + "hg+https://hg.myproject.org/MyProject#src/somefile.c", + "hg+https://hg.myproject.org/MyProject#src/Class.java", + "hg+https://hg.myproject.org/MyProject@da39a3ee5e6b", + "hg+https://hg.myproject.org/MyProject@2019", + "hg+https://hg.myproject.org/MyProject@v1.0", + "hg+https://hg.myproject.org/MyProject@special_feature", + "hg+https://hg.myproject.org/MyProject@master#/src/MyClass.cpp", + "hg+https://hg.myproject.org/MyProject@da39a3ee5e6b#lib/variable.rb", + "svn://svn.myproject.org/svn/MyProject", + "svn+svn://svn.myproject.org/svn/MyProject", + "svn+http://svn.myproject.org/svn/MyProject/trunk", + "svn+https://svn.myproject.org/svn/MyProject/trunk", + "svn+https://svn.myproject.org/MyProject#src/somefile.c", + "svn+https://svn.myproject.org/MyProject#src/Class.java", + "svn+https://svn.myproject.org/MyProject/trunk#src/somefile.c", + "svn+https://svn.myproject.org/MyProject/trunk/src/somefile.c", + "svn+https://svn.myproject.org/svn/MyProject/trunk@2019", + "svn+https://svn.myproject.org/MyProject@123#/src/MyClass.cpp", + "svn+https://svn.myproject.org/MyProject/trunk@1234#lib/variable/variable.rb", + "bzr+https://bzr.myproject.org/MyProject/trunk", + "bzr+http://bzr.myproject.org/MyProject/trunk", + "bzr+sftp://myproject.org/MyProject/trunk", + "bzr+ssh://myproject.org/MyProject/trunk", + "bzr+ftp://myproject.org/MyProject/trunk", + "bzr+lp:MyProject", + "bzr+https://bzr.myproject.org/MyProject/trunk#src/somefile.c", + "bzr+https://bzr.myproject.org/MyProject/trunk#src/Class.java", + "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", + ]) +def test_valid_package_download_location(input_value): + assert validate_package_download_location(input_value) == [] + + +# TODO: more negative examples +@pytest.mark.parametrize("input_value", [':::::', ]) +def test_invalid_package_download_location(input_value): + assert validate_package_download_location(input_value) == [f'must be a valid download location, but is: {input_value}'] + + +@pytest.mark.parametrize("input_value", ['https://some.uri', "http:////some", "https://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82...", + 'h://someweirdtest^?', "https://some.uri that goes on!?"]) +def test_valid_uri(input_value): + message = validate_uri(input_value) + + assert message == [] + + +@pytest.mark.parametrize("input_value", ["/invalid/uri", "http//uri", "http://some#uri", "some/uri", 'some weird test']) +def test_invalid_uri(input_value): + message = validate_uri(input_value) + + assert message == [f'must be a valid URI specified in RFC-3986, but is: {input_value}'] + + +@pytest.mark.parametrize("input_value", ['://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82...']) +@pytest.mark.skip("validate_uri() seems to invalidate URIs without scheme, so it does not run into this case. But I'm not sure yet if this covers all scheme-less examples.") +def test_uri_without_scheme(input_value): + message = validate_uri(input_value) + + assert message == [f'must have a URI scheme, but is: {input_value}'] + From 7e3e0bad7ad396f95b289c422cfc8074a81c2b83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 15 Dec 2022 14:26:19 +0100 Subject: [PATCH 031/362] [review] smaller fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/validation/actor_validator.py | 1 - src/validation/annotation_validator.py | 10 ++--- src/validation/checksum_validator.py | 7 ++- src/validation/creation_info_validator.py | 9 ++-- src/validation/document_validator.py | 1 - .../external_document_ref_validator.py | 10 +++-- .../external_package_ref_validator.py | 7 +-- .../extracted_licensing_info_validator.py | 19 ++++---- src/validation/file_validator.py | 6 +-- .../license_expression_validator.py | 4 +- src/validation/package_validator.py | 18 ++++---- .../package_verification_code_validator.py | 4 +- src/validation/relationship_validator.py | 5 ++- src/validation/snippet_validator.py | 10 ++--- ...id_validation.py => spdx_id_validators.py} | 30 ++++++++----- src/validation/uri_validators.py | 12 ++--- src/validation/validation_message.py | 4 +- tests/valid_defaults.py | 31 +------------ tests/validation/test_actor_validator.py | 6 +-- tests/validation/test_annotation_validator.py | 10 ++--- tests/validation/test_checksum_validator.py | 38 ++++++++-------- .../test_creation_info_validator.py | 11 +++-- tests/validation/test_document_validator.py | 13 +++--- .../test_external_document_ref_validator.py | 24 +--------- .../test_external_package_ref_validator.py | 20 +++++---- ...test_extracted_licensing_info_validator.py | 19 ++++---- tests/validation/test_file_validator.py | 19 ++++---- .../test_license_expression_validator.py | 7 +-- tests/validation/test_package_validator.py | 24 ++++------ .../validation/test_relationship_validator.py | 19 ++++---- tests/validation/test_snippet_validator.py | 32 +++----------- tests/validation/test_spdx_id_validator.py | 1 - tests/validation/test_spdx_id_validators.py | 1 + tests/validation/test_uri_validators.py | 44 ++++++++++--------- 34 files changed, 206 insertions(+), 270 deletions(-) rename src/validation/{spdx_id_validation.py => spdx_id_validators.py} (64%) delete mode 100644 tests/validation/test_spdx_id_validator.py create mode 100644 tests/validation/test_spdx_id_validators.py diff --git a/src/validation/actor_validator.py b/src/validation/actor_validator.py index fe4a1769a..21cfa7e5e 100644 --- a/src/validation/actor_validator.py +++ b/src/validation/actor_validator.py @@ -8,7 +8,6 @@ class ActorValidator: spdx_version: str parent_id: str - # TODO: what is parent_id in the case of annotations? def __init__(self, spdx_version: str, parent_id: Optional[str]): self.spdx_version = spdx_version self.parent_id = parent_id diff --git a/src/validation/annotation_validator.py b/src/validation/annotation_validator.py index e7a316efd..3a6197e87 100644 --- a/src/validation/annotation_validator.py +++ b/src/validation/annotation_validator.py @@ -3,7 +3,7 @@ from src.model.annotation import Annotation from src.model.document import Document from src.validation.actor_validator import ActorValidator -from src.validation.spdx_id_validation import validate_spdx_id +from src.validation.spdx_id_validators import validate_spdx_id from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType @@ -15,7 +15,7 @@ class AnnotationValidator: def __init__(self, spdx_version: str, document: Document): self.spdx_version = spdx_version self.document = document - self.actor_validator = ActorValidator(spdx_version, parent_id=None) + self.actor_validator = ActorValidator(spdx_version, parent_id="annotation") def validate_annotations(self, annotations: List[Annotation]) -> List[ValidationMessage]: validation_messages = [] @@ -26,17 +26,13 @@ def validate_annotations(self, annotations: List[Annotation]) -> List[Validation def validate_annotation(self, annotation: Annotation) -> List[ValidationMessage]: validation_messages = [] - document_spdx_id: str = self.document.creation_info.spdx_id context = ValidationContext(element_type=SpdxElementType.ANNOTATION, full_element=annotation) - validation_messages.extend( - self.actor_validator.validate_actor(annotation.annotator) - ) + validation_messages.extend(self.actor_validator.validate_actor(annotation.annotator)) messages: List[str] = validate_spdx_id(annotation.spdx_id, self.document, check_document=True) for message in messages: validation_messages.append(ValidationMessage(message, context)) return validation_messages - diff --git a/src/validation/checksum_validator.py b/src/validation/checksum_validator.py index 27f52f62c..ffed999b5 100644 --- a/src/validation/checksum_validator.py +++ b/src/validation/checksum_validator.py @@ -44,7 +44,8 @@ def validate_checksums(self, checksums: List[Checksum]) -> List[ValidationMessag def validate_checksum(self, checksum: Checksum) -> List[ValidationMessage]: validation_messages = [] algorithm = checksum.algorithm - context = ValidationContext(parent_id=self.parent_id, element_type=SpdxElementType.CHECKSUM, full_element=checksum) + context = ValidationContext(parent_id=self.parent_id, element_type=SpdxElementType.CHECKSUM, + full_element=checksum) if not re.match("^[0-9a-f]{" + algorithm_length[algorithm] + "}$", checksum.value): if algorithm == ChecksumAlgorithm.BLAKE3: @@ -54,7 +55,9 @@ def validate_checksum(self, checksum: Checksum) -> List[ValidationMessage]: else: length = algorithm_length[algorithm] validation_messages.append( - ValidationMessage(f'value of {algorithm} must consist of {length} hexadecimal digits, but is: {checksum.value} (length: {len(checksum.value)} digits)', context) + ValidationMessage( + f"value of {algorithm} must consist of {length} hexadecimal digits, but is: {checksum.value} (length: {len(checksum.value)} digits)", + context) ) return validation_messages diff --git a/src/validation/creation_info_validator.py b/src/validation/creation_info_validator.py index 9356c1fec..63dc44eac 100644 --- a/src/validation/creation_info_validator.py +++ b/src/validation/creation_info_validator.py @@ -1,5 +1,6 @@ import re -from typing import List, Optional +from typing import List + from src.model.document import CreationInfo from src.validation.actor_validator import ActorValidator from src.validation.external_document_ref_validator import ExternalDocumentRefValidator @@ -10,7 +11,7 @@ class CreationInfoValidator: spdx_version: str - def __init__(self, spdx_version): + def __init__(self, spdx_version: str): self.spdx_version = spdx_version def validate_creation_info(self, creation_info: CreationInfo) -> List[ValidationMessage]: @@ -31,7 +32,7 @@ def validate_creation_info(self, creation_info: CreationInfo) -> List[Validation if creation_info.spdx_id != "SPDXRef-DOCUMENT": validation_messages.append( ValidationMessage( - f'spdx_id must be SPDXRef-DOCUMENT, but is: {creation_info.spdx_id}', + f'spdx_id must be "SPDXRef-DOCUMENT", but is: {creation_info.spdx_id}', context ) ) @@ -47,7 +48,7 @@ def validate_creation_info(self, creation_info: CreationInfo) -> List[Validation for message in validate_uri(creation_info.document_namespace): validation_messages.append( ValidationMessage( - 'document_namespace ' + message, context + "document_namespace " + message, context ) ) diff --git a/src/validation/document_validator.py b/src/validation/document_validator.py index c31242f48..4f370fe39 100644 --- a/src/validation/document_validator.py +++ b/src/validation/document_validator.py @@ -43,7 +43,6 @@ def validate_full_spdx_document(self, document: Document) -> List[ValidationMess validation_messages.extend(self.extracted_licensing_info_validator.validate_extracted_licensing_infos( document.extracted_licensing_info)) - # TODO: is this correct here? Also, make test for it document_id = document.creation_info.spdx_id document_describes_relationships = [relationship for relationship in document.relationships if relationship.relationship_type == RelationshipType.DESCRIBES and relationship.spdx_element_id == document_id] diff --git a/src/validation/external_document_ref_validator.py b/src/validation/external_document_ref_validator.py index 2aa7b26ae..3a02242be 100644 --- a/src/validation/external_document_ref_validator.py +++ b/src/validation/external_document_ref_validator.py @@ -1,8 +1,9 @@ import re -from typing import List, Optional +from typing import List from src.model.external_document_ref import ExternalDocumentRef from src.validation.checksum_validator import ChecksumValidator +from src.validation.spdx_id_validators import is_valid_external_doc_ref_id from src.validation.uri_validators import validate_uri from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType @@ -17,7 +18,8 @@ def __init__(self, spdx_version: str, parent_id: str): self.parent_id = parent_id self.checksum_validator = ChecksumValidator(spdx_version, parent_id) - def validate_external_document_refs(self, external_document_refs: List[ExternalDocumentRef]) -> List[ValidationMessage]: + def validate_external_document_refs(self, external_document_refs: List[ExternalDocumentRef]) -> List[ + ValidationMessage]: validation_messages = [] for external_document_ref in external_document_refs: validation_messages.extend(self.validate_external_document_ref(external_document_ref)) @@ -29,7 +31,7 @@ def validate_external_document_ref(self, external_document_ref: ExternalDocument context = ValidationContext(parent_id=self.parent_id, element_type=SpdxElementType.EXTERNAL_DOCUMENT_REF, full_element=external_document_ref) - if not re.match(r"^DocumentRef-[\da-zA-Z.+-]+$", external_document_ref.document_ref_id): + if not is_valid_external_doc_ref_id(external_document_ref.document_ref_id): validation_messages.append( ValidationMessage( f'document_ref_id must only contain letters, numbers, ".", "-" and "+" and must begin with "DocumentRef-", but is: {external_document_ref.document_ref_id}', @@ -40,7 +42,7 @@ def validate_external_document_ref(self, external_document_ref: ExternalDocument for message in validate_uri(external_document_ref.document_uri): validation_messages.append( ValidationMessage( - 'document_uri ' + message, context + "document_uri " + message, context ) ) diff --git a/src/validation/external_package_ref_validator.py b/src/validation/external_package_ref_validator.py index 6bfb7124f..201d9d3bd 100644 --- a/src/validation/external_package_ref_validator.py +++ b/src/validation/external_package_ref_validator.py @@ -2,8 +2,8 @@ from src.model.package import ExternalPackageRef from src.validation.checksum_validator import ChecksumValidator -from src.validation.validation_message import ValidationMessage from src.validation.license_expression_validator import LicenseExpressionValidator +from src.validation.validation_message import ValidationMessage class ExternalPackageRefValidator: @@ -18,7 +18,8 @@ def __init__(self, spdx_version: str, parent_id: str): self.checksum_validator = ChecksumValidator(spdx_version, parent_id) self.license_expression_validator = LicenseExpressionValidator(spdx_version) - def validate_external_package_refs(self, external_package_refs: List[ExternalPackageRef]) -> List[ValidationMessage]: + def validate_external_package_refs(self, external_package_refs: List[ExternalPackageRef]) -> List[ + ValidationMessage]: validation_messages = [] for external_package_ref in external_package_refs: validation_messages.extend(self.validate_external_package_ref(external_package_ref)) @@ -26,5 +27,5 @@ def validate_external_package_refs(self, external_package_refs: List[ExternalPac return validation_messages def validate_external_package_ref(self, external_package_ref: ExternalPackageRef) -> List[ValidationMessage]: - # TODO: this is gonna be insane (Annex F) + # TODO: https://github.com/spdx/tools-python/issues/373 return [] diff --git a/src/validation/extracted_licensing_info_validator.py b/src/validation/extracted_licensing_info_validator.py index a05350f3f..d614fa4b5 100644 --- a/src/validation/extracted_licensing_info_validator.py +++ b/src/validation/extracted_licensing_info_validator.py @@ -12,30 +12,31 @@ class ExtractedLicensingInfoValidator: def __init__(self, spdx_version): self.spdx_version = spdx_version - def validate_extracted_licensing_infos(self, extracted_licensing_infos: Optional[List[ExtractedLicensingInfo]]) -> List[ValidationMessage]: - if extracted_licensing_infos is None: - return [] - + def validate_extracted_licensing_infos(self, extracted_licensing_infos: Optional[List[ExtractedLicensingInfo]]) -> \ + List[ValidationMessage]: validation_messages = [] for extracted_licensing_info in extracted_licensing_infos: validation_messages.extend(self.validate_extracted_licensing_info(extracted_licensing_info)) return validation_messages - def validate_extracted_licensing_info(self, extracted_licensing_infos: ExtractedLicensingInfo) -> List[ValidationMessage]: + def validate_extracted_licensing_info(self, extracted_licensing_infos: ExtractedLicensingInfo) -> List[ + ValidationMessage]: validation_messages: List[ValidationMessage] = [] - context = ValidationContext(element_type=SpdxElementType.EXTRACTED_LICENSING_INFO, full_element=extracted_licensing_infos) + context = ValidationContext(element_type=SpdxElementType.EXTRACTED_LICENSING_INFO, + full_element=extracted_licensing_infos) license_id: str = extracted_licensing_infos.license_id if license_id and not re.match(r"^LicenseRef-[\da-zA-Z.-]+$", license_id): validation_messages.append( - ValidationMessage(f'license_id must only contain letters, numbers, "." and "-" and must begin with "LicenseRef-", but is: {license_id}', - context) + ValidationMessage( + f'license_id must only contain letters, numbers, "." and "-" and must begin with "LicenseRef-", but is: {license_id}', + context) ) if license_id and not extracted_licensing_infos.extracted_text: validation_messages.append( - ValidationMessage('extracted_text must be provided if there is a license_id assigned', context) + ValidationMessage("extracted_text must be provided if there is a license_id assigned", context) ) for cross_reference in extracted_licensing_infos.cross_references: diff --git a/src/validation/file_validator.py b/src/validation/file_validator.py index 34c20a9c7..2a313a317 100644 --- a/src/validation/file_validator.py +++ b/src/validation/file_validator.py @@ -4,9 +4,9 @@ from src.model.document import Document from src.model.file import File from src.validation.checksum_validator import ChecksumValidator -from src.validation.spdx_id_validation import is_valid_spdx_id, validate_spdx_id -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from src.validation.license_expression_validator import LicenseExpressionValidator +from src.validation.spdx_id_validators import validate_spdx_id +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType class FileValidator: @@ -44,7 +44,7 @@ def validate_file(self, file: File) -> List[ValidationMessage]: if ChecksumAlgorithm.SHA1 not in [checksum.algorithm for checksum in file.checksums]: validation_messages.append( ValidationMessage( - f'checksums must contain a SHA1 algorithm checksum, but is: {file.checksums}', + f"checksums must contain a SHA1 algorithm checksum, but only contains: {[checksum.algorithm for checksum in file.checksums]}", context) ) diff --git a/src/validation/license_expression_validator.py b/src/validation/license_expression_validator.py index 4e4e42cba..95d36771b 100644 --- a/src/validation/license_expression_validator.py +++ b/src/validation/license_expression_validator.py @@ -12,7 +12,8 @@ class LicenseExpressionValidator: def __init__(self, spdx_version): self.spdx_version = spdx_version - def validate_license_expressions(self, license_expressions: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]]) -> List[ValidationMessage]: + def validate_license_expressions(self, license_expressions: Optional[ + Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]]) -> List[ValidationMessage]: if license_expressions in [SpdxNoAssertion(), SpdxNone(), None]: return [] @@ -24,4 +25,5 @@ def validate_license_expressions(self, license_expressions: Optional[Union[List[ return error_messages def validate_license_expression(self, license_expression: LicenseExpression) -> List[ValidationMessage]: + # TODO: implement this once we have a better license expression model: https://github.com/spdx/tools-python/issues/374 return [] diff --git a/src/validation/package_validator.py b/src/validation/package_validator.py index 942b063ea..6d2cd9017 100644 --- a/src/validation/package_validator.py +++ b/src/validation/package_validator.py @@ -7,8 +7,8 @@ from src.validation.external_package_ref_validator import ExternalPackageRefValidator from src.validation.license_expression_validator import LicenseExpressionValidator from src.validation.package_verification_code_validator import PackageVerificationCodeValidator -from src.validation.spdx_id_validation import validate_spdx_id -from src.validation.uri_validators import validate_url, validate_package_download_location +from src.validation.spdx_id_validators import validate_spdx_id +from src.validation.uri_validators import validate_url, validate_download_location from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType @@ -30,7 +30,7 @@ def validate_packages(self, packages: List[Package]) -> List[ValidationMessage]: return validation_messages def validate_package(self, package: Package) -> List[ValidationMessage]: - checksum_validator = ChecksumValidator(self.spdx_version, parent_id=package.spdx_id) + checksum_validator = ChecksumValidator(self.spdx_version, package.spdx_id) verification_code_validator = PackageVerificationCodeValidator(self.spdx_version, package.spdx_id) external_package_ref_validator = ExternalPackageRefValidator(self.spdx_version, package.spdx_id) @@ -43,8 +43,8 @@ def validate_package(self, package: Package) -> List[ValidationMessage]: download_location = package.download_location if isinstance(download_location, str): - for message in validate_package_download_location(download_location): - validation_messages.append(ValidationMessage("download_location " + message, context)) + for message in validate_download_location(download_location): + validation_messages.append(ValidationMessage("package download_location " + message, context)) homepage = package.homepage if isinstance(homepage, str): @@ -55,7 +55,7 @@ def validate_package(self, package: Package) -> List[ValidationMessage]: if not package.files_analyzed: validation_messages.append( ValidationMessage( - f'verification_code must be None if files_analyzed is False, but is: {package.verification_code}', + f"verification_code must be None if files_analyzed is False, but is: {package.verification_code}", context)) else: validation_messages.extend( @@ -69,7 +69,7 @@ def validate_package(self, package: Package) -> List[ValidationMessage]: if package_contains_relationships: validation_messages.append( ValidationMessage( - f'package must contain no elements if files_analyzed is False, but found {package_contains_relationships}', + f"package must contain no elements if files_analyzed is False, but found {package_contains_relationships}", context) ) @@ -78,7 +78,7 @@ def validate_package(self, package: Package) -> List[ValidationMessage]: if contained_in_package_relationships: validation_messages.append( ValidationMessage( - f'package must contain no elements if files_analyzed is False, but found {package_contains_relationships}', + f"package must contain no elements if files_analyzed is False, but found {package_contains_relationships}", context) ) @@ -94,7 +94,7 @@ def validate_package(self, package: Package) -> List[ValidationMessage]: if not package.files_analyzed: validation_messages.append( ValidationMessage( - f'license_info_from_files must be None if files_analyzed is False, but is: {package.license_info_from_files}', + f"license_info_from_files must be None if files_analyzed is False, but is: {package.license_info_from_files}", context) ) else: diff --git a/src/validation/package_verification_code_validator.py b/src/validation/package_verification_code_validator.py index 3864d7d75..422cf8e19 100644 --- a/src/validation/package_verification_code_validator.py +++ b/src/validation/package_verification_code_validator.py @@ -26,11 +26,11 @@ def validate_verification_code(self, verification_code: PackageVerificationCode) f'file name must be a relative path to the file, starting with "./", but is: {file}', context) ) - value = verification_code.value + value: str = verification_code.value if not re.match("^[0-9a-f]{40}$", value): validation_messages.append( ValidationMessage( - f'value of verification_code must consist of 40 hexadecimal digits, but is: {value} (length: {len(value)} digits)', + f"value of verification_code must consist of 40 hexadecimal digits, but is: {value} (length: {len(value)} digits)", context) ) diff --git a/src/validation/relationship_validator.py b/src/validation/relationship_validator.py index b83319621..d7a0ef88f 100644 --- a/src/validation/relationship_validator.py +++ b/src/validation/relationship_validator.py @@ -2,7 +2,7 @@ from src.model.document import Document from src.model.relationship import Relationship, RelationshipType -from src.validation.spdx_id_validation import validate_spdx_id +from src.validation.spdx_id_validators import validate_spdx_id from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType @@ -37,6 +37,7 @@ def validate_relationship(self, relationship: Relationship) -> List[ValidationMe if self.spdx_version != "2.3": if relationship_type == RelationshipType.SPECIFICATION_FOR or relationship_type == RelationshipType.REQUIREMENT_DESCRIPTION_FOR: - validation_messages.append(ValidationMessage(f'{relationship_type} is not supported for SPDX versions below 2.3', context)) + validation_messages.append( + ValidationMessage(f"{relationship_type} is not supported for SPDX versions below 2.3", context)) return validation_messages diff --git a/src/validation/snippet_validator.py b/src/validation/snippet_validator.py index 0ed3e0899..6dd87a84a 100644 --- a/src/validation/snippet_validator.py +++ b/src/validation/snippet_validator.py @@ -3,7 +3,7 @@ from src.model.document import Document from src.model.snippet import Snippet from src.validation.license_expression_validator import LicenseExpressionValidator -from src.validation.spdx_id_validation import validate_spdx_id +from src.validation.spdx_id_validators import validate_spdx_id from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType @@ -39,14 +39,14 @@ def validate_snippet(self, snippet: Snippet) -> List[ValidationMessage]: if snippet.byte_range[0] < 1: validation_messages.append( ValidationMessage( - f'byte_range values must be greater than or equal to 1, but is: {snippet.byte_range}', + f"byte_range values must be greater than or equal to 1, but is: {snippet.byte_range}", context) ) if snippet.byte_range[0] > snippet.byte_range[1]: validation_messages.append( ValidationMessage( - f'the first value of byte_range must be less than or equal to the second, but is: {snippet.byte_range}', + f"the first value of byte_range must be less than or equal to the second, but is: {snippet.byte_range}", context) ) @@ -54,14 +54,14 @@ def validate_snippet(self, snippet: Snippet) -> List[ValidationMessage]: if snippet.line_range[0] < 1: validation_messages.append( ValidationMessage( - f'line_range values must be greater than or equal to 1, but is: {snippet.line_range}', + f"line_range values must be greater than or equal to 1, but is: {snippet.line_range}", context) ) if snippet.line_range[0] > snippet.line_range[1]: validation_messages.append( ValidationMessage( - f'the first value of line_range must be less than or equal to the second, but is: {snippet.line_range}', + f"the first value of line_range must be less than or equal to the second, but is: {snippet.line_range}", context) ) diff --git a/src/validation/spdx_id_validation.py b/src/validation/spdx_id_validators.py similarity index 64% rename from src/validation/spdx_id_validation.py rename to src/validation/spdx_id_validators.py index 47e884431..189033c3a 100644 --- a/src/validation/spdx_id_validation.py +++ b/src/validation/spdx_id_validators.py @@ -1,11 +1,11 @@ import re -from typing import Optional, List, Tuple +from typing import List from src.model.document import Document from src.model.file import File -def is_valid_spdx_id(spdx_id: str) -> bool: +def is_valid_internal_spdx_id(spdx_id: str) -> bool: return bool(re.match(r"^SPDXRef-[\da-zA-Z.-]+$", spdx_id)) @@ -40,7 +40,8 @@ def is_external_doc_ref_present_in_document(external_ref_id: str, document: Docu return external_ref_id in all_external_doc_ref_ids_in_document -def validate_spdx_id(spdx_id: str, document: Document, check_document: bool = False, check_files: bool = False) -> List[str]: +def validate_spdx_id(spdx_id: str, document: Document, check_document: bool = False, check_files: bool = False) -> List[ + str]: """ Test that the given spdx_id (and a potential DocumentRef to an external document) is valid and, if it is a reference, actually exists in the document. Optionally checks files or the whole document for the existence of the spdx_id (i.e. if it is used as a reference). Returns a list of validation messages, @@ -51,29 +52,34 @@ def validate_spdx_id(spdx_id: str, document: Document, check_document: bool = Fa # # # invalid case # # # if len(split_id) > 2: - return [f'spdx_id must not contain more than one colon in order to separate the external document reference id from the internal SPDX id, but is: {spdx_id}'] + return [ + f"spdx_id must not contain more than one colon in order to separate the external document reference id from the internal SPDX id, but is: {spdx_id}"] # # # case with external document ref prefix # # # if len(split_id) == 2: if not is_valid_external_doc_ref_id(split_id[0]): - validation_messages.append(f'the external document reference part of spdx_id must only contain letters, numbers, ".", "-" and "+" and must begin with "DocumentRef-", but is: {split_id[0]}') - if not is_valid_spdx_id(split_id[1]): - validation_messages.append(f'the internal SPDX id part of spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: {split_id[1]}') + validation_messages.append( + f'the external document reference part of spdx_id must only contain letters, numbers, ".", "-" and "+" and must begin with "DocumentRef-", but is: {split_id[0]}') + if not is_valid_internal_spdx_id(split_id[1]): + validation_messages.append( + f'the internal SPDX id part of spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: {split_id[1]}') if not is_external_doc_ref_present_in_document(split_id[0], document): - validation_messages.append(f'did not find the external document reference {split_id[0]} in the SPDX document') + validation_messages.append( + f"did not find the external document reference {split_id[0]} in the SPDX document") return validation_messages # # # "normal" case # # # - if not is_valid_spdx_id(spdx_id): - validation_messages.append(f'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: {spdx_id}') + if not is_valid_internal_spdx_id(spdx_id): + validation_messages.append( + f'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: {spdx_id}') if check_document: if not is_spdx_id_present_in_document(spdx_id, document): - validation_messages.append(f'did not find the referenced spdx_id {spdx_id} in the SPDX document') + validation_messages.append(f"did not find the referenced spdx_id {spdx_id} in the SPDX document") if check_files: if not is_spdx_id_present_in_files(spdx_id, document.files): - validation_messages.append(f'did not find the referenced spdx_id {spdx_id} in the SPDX document\'s files') + validation_messages.append(f"did not find the referenced spdx_id {spdx_id} in the SPDX document's files") return validation_messages diff --git a/src/validation/uri_validators.py b/src/validation/uri_validators.py index ba151a881..c28915b8b 100644 --- a/src/validation/uri_validators.py +++ b/src/validation/uri_validators.py @@ -8,29 +8,29 @@ git_pattern = "(git\\+git@[a-zA-Z0-9\\.\\-]+:[a-zA-Z0-9/\\\\.@\\-]+)" bazaar_pattern = "(bzr\\+lp:[a-zA-Z0-9\\.\\-]+)" download_location_pattern = ( - "^(((" + supported_download_repos + "\\+)?" + url_pattern + ")|" + git_pattern + "|" + bazaar_pattern + ")$") + "^(((" + supported_download_repos + "\\+)?" + url_pattern + ")|" + git_pattern + "|" + bazaar_pattern + ")$") def validate_url(url: str) -> List[str]: if not re.match(url_pattern, url): - return [f'must be a valid URL, but is: {url}'] + return [f"must be a valid URL, but is: {url}"] return [] -def validate_package_download_location(location: str) -> List[str]: +def validate_download_location(location: str) -> List[str]: if not re.match(download_location_pattern, location): - return [f'must be a valid download location, but is: {location}'] + return [f"must be a valid download location according to the specification, but is: {location}"] return [] def validate_uri(uri: str) -> List[str]: if not isabsuri(uri): - return [f'must be a valid URI specified in RFC-3986, but is: {uri}'] + return [f"must be a valid URI specified in RFC-3986, but is: {uri}"] else: split = urisplit(uri) if split.scheme is None: - return [f'must have a URI scheme, but is: {uri}'] + return [f"must have a URI scheme, but is: {uri}"] return [] diff --git a/src/validation/validation_message.py b/src/validation/validation_message.py index b79840ecc..c90e7a71e 100644 --- a/src/validation/validation_message.py +++ b/src/validation/validation_message.py @@ -20,7 +20,7 @@ class SpdxElementType(Enum): EXTRACTED_LICENSING_INFO = auto() -@dataclass(eq=True, frozen=True) +@dataclass(frozen=True) class ValidationContext: spdx_id: Optional[str] = None # not every type has an id, or it might be missing parent_id: Optional[str] = None # if a parent is known and has a valid id @@ -28,7 +28,7 @@ class ValidationContext: full_element: Any = None # can be any class of the data model -@dataclass(eq=True, frozen=True) +@dataclass(frozen=True) class ValidationMessage: validation_message: str context: ValidationContext diff --git a/tests/valid_defaults.py b/tests/valid_defaults.py index 9339070a5..114c03ad4 100644 --- a/tests/valid_defaults.py +++ b/tests/valid_defaults.py @@ -33,27 +33,12 @@ def get_creation_info(spdx_version="SPDX-2.3", spdx_id="SPDXRef-DOCUMENT", name= if creators is None: creators = [get_actor()] - if external_document_refs is None: - external_document_refs = [] - return CreationInfo(spdx_version, spdx_id, name, document_namespace, creators, created, creator_comment, data_license, external_document_refs, license_list_version, document_comment) def get_document(creation_info=get_creation_info(), packages=None, files=None, snippets=None, annotations=None, relationships=None, extracted_licensing_info=None) -> Document: - if packages is None: - packages = [] - if files is None: - files = [] - if snippets is None: - snippets = [] - if annotations is None: - annotations = [] - if relationships is None: - relationships = [] - if extracted_licensing_info is None: - extracted_licensing_info = [] return Document(creation_info, packages, files, snippets, annotations, relationships, extracted_licensing_info) @@ -73,13 +58,9 @@ def get_extracted_licensing_info(license_id="LicenseRef-1", extracted_text="extr def get_file(name="./file/name.py", spdx_id="SPDXRef-File", checksums=None, file_type=None, concluded_license=None, license_info_in_file=None, license_comment=None, copyright_text=None, comment=None, notice=None, - contributors=None, attribution_texts=None): + contributors=None, attribution_texts=None) -> File: if checksums is None: checksums = [get_checksum()] - if contributors is None: - contributors = [] - if attribution_texts is None: - attribution_texts = [] return File(name, spdx_id, checksums, file_type, concluded_license, license_info_in_file, license_comment, copyright_text, comment, notice, contributors, attribution_texts) @@ -87,8 +68,6 @@ def get_file(name="./file/name.py", spdx_id="SPDXRef-File", checksums=None, file def get_package_verification_code(value="85ed0817af83a24ad8da68c2b5094de69833983c", excluded_files=None) -> PackageVerificationCode: - if excluded_files is None: - excluded_files = [] return PackageVerificationCode(value, excluded_files) @@ -105,12 +84,6 @@ def get_package(spdx_id="SPDXRef-Package", name="package name", download_locatio license_declared=None, license_comment=None, copyright_text=None, summary=None, description=None, comment=None, external_references=None, attribution_texts=None, primary_package_purpose=None, release_date=None, built_date=None, valid_until_date=None) -> Package: - if checksums is None: - checksums = [] - if external_references is None: - external_references = [] - if attribution_texts is None: - attribution_texts = [] return Package(spdx_id, name, download_location, version, file_name, supplier, originator, files_analyzed, verification_code, checksums, homepage, source_info, license_concluded, license_info_from_files, @@ -127,8 +100,6 @@ def get_relationship(spdx_element_id="SPDXRef-DOCUMENT", relationship_type=Relat def get_snippet(spdx_id="SPDXRef-Snippet", file_spdx_id="SPDXRef-File", byte_range=(200, 400), line_range=None, concluded_license=None, license_info_in_snippet=None, license_comment=None, copyright_text=None, comment=None, name=None, attribution_texts=None) -> Snippet: - if attribution_texts is None: - attribution_texts = [] return Snippet(spdx_id, file_spdx_id, byte_range, line_range, concluded_license, license_info_in_snippet, license_comment, copyright_text, comment, name, attribution_texts) diff --git a/tests/validation/test_actor_validator.py b/tests/validation/test_actor_validator.py index 36eead62a..6eee0151b 100644 --- a/tests/validation/test_actor_validator.py +++ b/tests/validation/test_actor_validator.py @@ -8,7 +8,7 @@ from tests.valid_defaults import get_actor -def test_correct_actor_person(): +def test_valid_actor_person(): actor_validator = ActorValidator("2.3", "SPDXRef-DOCUMENT") actor = Actor(ActorType.PERSON, "person name", "mail@mail.com") @@ -19,9 +19,9 @@ def test_correct_actor_person(): @pytest.mark.parametrize("actor, expected_message", [(get_actor(actor_type=ActorType.TOOL, mail="mail@mail.com"), - 'email must be None if actor_type is TOOL, but is: mail@mail.com'), + "email must be None if actor_type is TOOL, but is: mail@mail.com"), ]) -def test_wrong_actor(actor, expected_message): +def test_invalid_actor(actor, expected_message): parent_id = "SPDXRef-DOCUMENT" actor_validator = ActorValidator("2.3", parent_id) validation_messages: List[ValidationMessage] = actor_validator.validate_actor(actor) diff --git a/tests/validation/test_annotation_validator.py b/tests/validation/test_annotation_validator.py index 522841f4a..1367bd773 100644 --- a/tests/validation/test_annotation_validator.py +++ b/tests/validation/test_annotation_validator.py @@ -10,7 +10,7 @@ from tests.valid_defaults import get_actor, get_annotation, get_document, get_file -def test_correct_annotation(): +def test_valid_annotation(): document: Document = get_document(files=[get_file(spdx_id="SPDXRef-File")]) annotation_validator = AnnotationValidator("2.3", document) @@ -21,12 +21,10 @@ def test_correct_annotation(): @pytest.mark.parametrize("annotation_id, file_id, expected_message", - [("SPDXRef-some_file", "SPDXRef-some_file", - 'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: SPDXRef-some_file'), - ("SPDXRef-File", "SPDXRef-hiddenFile", - 'did not find the referenced spdx_id SPDXRef-File in the SPDX document') + [("SPDXRef-File", "SPDXRef-hiddenFile", + "did not find the referenced spdx_id SPDXRef-File in the SPDX document") ]) -def test_wrong_annotation(annotation_id, file_id, expected_message): +def test_invalid_annotation(annotation_id, file_id, expected_message): annotation: Annotation = get_annotation(spdx_id=annotation_id) document: Document = get_document(files=[get_file(spdx_id=file_id)]) annotation_validator = AnnotationValidator("2.3", document) diff --git a/tests/validation/test_checksum_validator.py b/tests/validation/test_checksum_validator.py index 53388b85f..8ebc0d2b3 100644 --- a/tests/validation/test_checksum_validator.py +++ b/tests/validation/test_checksum_validator.py @@ -37,7 +37,7 @@ Checksum(ChecksumAlgorithm.MD6, "af1eec2a1b18886c3f3cc244349d91d8d4c41ce30a517d6ce9d79c8c17bb4b660d7f61beb7018b3924c6b8f96549fa39"), Checksum(ChecksumAlgorithm.ADLER32, "02ec0130")]) -def test_correct_checksum(checksum): +def test_valid_checksum(checksum): checksum_validator = ChecksumValidator("2.3", "parent_id") validation_messages: List[ValidationMessage] = checksum_validator.validate_checksum(checksum) @@ -47,42 +47,42 @@ def test_correct_checksum(checksum): @pytest.mark.parametrize("checksum, expected_message", [(Checksum(ChecksumAlgorithm.SHA1, "af1eec2a1b18886c3f3cc244349d91d8"), - 'value of ChecksumAlgorithm.SHA1 must consist of 40 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + "value of ChecksumAlgorithm.SHA1 must consist of 40 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.SHA224, "af1eec2a1b18886c3f3cc244349d91d8"), - 'value of ChecksumAlgorithm.SHA224 must consist of 56 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + "value of ChecksumAlgorithm.SHA224 must consist of 56 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.SHA256, "af1eec2a1b18886c3f3cc244349d91d8"), - 'value of ChecksumAlgorithm.SHA256 must consist of 64 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + "value of ChecksumAlgorithm.SHA256 must consist of 64 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.SHA384, "af1eec2a1b18886c3f3cc244349d91d8"), - 'value of ChecksumAlgorithm.SHA384 must consist of 96 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + "value of ChecksumAlgorithm.SHA384 must consist of 96 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.SHA512, "af1eec2a1b18886c3f3cc244349d91d8"), - 'value of ChecksumAlgorithm.SHA512 must consist of 128 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + "value of ChecksumAlgorithm.SHA512 must consist of 128 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.SHA3_256, "af1eec2a1b18886c3f3cc244349d91d8"), - 'value of ChecksumAlgorithm.SHA3_256 must consist of 64 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + "value of ChecksumAlgorithm.SHA3_256 must consist of 64 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.SHA3_384, "af1eec2a1b18886c3f3cc244349d91d8"), - 'value of ChecksumAlgorithm.SHA3_384 must consist of 96 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + "value of ChecksumAlgorithm.SHA3_384 must consist of 96 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.SHA3_512, "af1eec2a1b18886c3f3cc244349d91d8"), - 'value of ChecksumAlgorithm.SHA3_512 must consist of 128 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + "value of ChecksumAlgorithm.SHA3_512 must consist of 128 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.BLAKE2B_256, "af1eec2a1b18886c3f3cc244349d91d8"), - 'value of ChecksumAlgorithm.BLAKE2B_256 must consist of 64 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + "value of ChecksumAlgorithm.BLAKE2B_256 must consist of 64 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.BLAKE2B_384, "af1eec2a1b18886c3f3cc244349d91d8"), - 'value of ChecksumAlgorithm.BLAKE2B_384 must consist of 96 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + "value of ChecksumAlgorithm.BLAKE2B_384 must consist of 96 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.BLAKE2B_512, "af1eec2a1b18886c3f3cc244349d91d8"), - 'value of ChecksumAlgorithm.BLAKE2B_512 must consist of 128 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + "value of ChecksumAlgorithm.BLAKE2B_512 must consist of 128 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.BLAKE3, "af1eec2a1b18886c3f3cc244349d91d8"), - 'value of ChecksumAlgorithm.BLAKE3 must consist of at least 256 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + "value of ChecksumAlgorithm.BLAKE3 must consist of at least 256 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.MD2, "71c4025dd9897b364f3ebbb42c484ff43d00791c"), - 'value of ChecksumAlgorithm.MD2 must consist of 32 hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791c (length: 40 digits)'), + "value of ChecksumAlgorithm.MD2 must consist of 32 hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791c (length: 40 digits)"), (Checksum(ChecksumAlgorithm.MD4, "71c4025dd9897b364f3ebbb42c484ff43d00791c"), - 'value of ChecksumAlgorithm.MD4 must consist of 32 hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791c (length: 40 digits)'), + "value of ChecksumAlgorithm.MD4 must consist of 32 hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791c (length: 40 digits)"), (Checksum(ChecksumAlgorithm.MD5, "71c4025dd9897b364f3ebbb42c484ff43d00791c"), - 'value of ChecksumAlgorithm.MD5 must consist of 32 hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791c (length: 40 digits)'), + "value of ChecksumAlgorithm.MD5 must consist of 32 hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791c (length: 40 digits)"), (Checksum(ChecksumAlgorithm.MD6, "a872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafed5"), - 'value of ChecksumAlgorithm.MD6 must consist of between 0 and 512 hexadecimal digits, but is: a872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafed5 (length: 513 digits)'), + "value of ChecksumAlgorithm.MD6 must consist of between 0 and 512 hexadecimal digits, but is: a872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafed5 (length: 513 digits)"), (Checksum(ChecksumAlgorithm.ADLER32, "af1eec2a1b18886c3f3cc244349d91d8"), - 'value of ChecksumAlgorithm.ADLER32 must consist of 8 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)'), + "value of ChecksumAlgorithm.ADLER32 must consist of 8 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), ]) -def test_wrong_checksum(checksum, expected_message): +def test_invalid_checksum(checksum, expected_message): parent_id = "parent_id" checksum_validator = ChecksumValidator("2.3", parent_id) validation_messages: List[ValidationMessage] = checksum_validator.validate_checksum(checksum) diff --git a/tests/validation/test_creation_info_validator.py b/tests/validation/test_creation_info_validator.py index 571fed45c..2adac8f5d 100644 --- a/tests/validation/test_creation_info_validator.py +++ b/tests/validation/test_creation_info_validator.py @@ -10,7 +10,7 @@ from tests.valid_defaults import get_actor, get_external_document_ref, get_creation_info -def test_correct_creation_info(): +def test_valid_creation_info(): creation_info_validator = CreationInfoValidator("2.3") creation_info = CreationInfo("SPDX-2.3", "SPDXRef-DOCUMENT", "document name", "https://some.uri", @@ -27,16 +27,15 @@ def test_correct_creation_info(): [(get_creation_info(spdx_version="version-2.3"), "SPDXRef-DOCUMENT", 'spdx_version must be of the form "SPDX-[major].[minor]" but is: version-2.3'), (get_creation_info(spdx_id="SPDXRef-doc"), "SPDXRef-doc", - 'spdx_id must be SPDXRef-DOCUMENT, but is: SPDXRef-doc'), + 'spdx_id must be "SPDXRef-DOCUMENT", but is: SPDXRef-doc'), (get_creation_info(data_license="MIT"), "SPDXRef-DOCUMENT", 'data_license must be "CC0-1.0", but is: MIT'), (get_creation_info(document_namespace="some_namespace"), "SPDXRef-DOCUMENT", - 'document_namespace must be a valid URI specified in RFC-3986, but is: some_namespace'), + "document_namespace must be a valid URI specified in RFC-3986, but is: some_namespace"), ]) -def test_wrong_creation_info(creation_info_input, expected_message, spdx_id): +def test_invalid_creation_info(creation_info_input, expected_message, spdx_id): creation_info_validator = CreationInfoValidator("2.3") - creation_info = creation_info_input - validation_messages: List[ValidationMessage] = creation_info_validator.validate_creation_info(creation_info) + validation_messages: List[ValidationMessage] = creation_info_validator.validate_creation_info(creation_info_input) expected = ValidationMessage(expected_message, ValidationContext(spdx_id, None, SpdxElementType.DOCUMENT)) diff --git a/tests/validation/test_document_validator.py b/tests/validation/test_document_validator.py index 7f16b192f..7a10f7e6a 100644 --- a/tests/validation/test_document_validator.py +++ b/tests/validation/test_document_validator.py @@ -1,20 +1,21 @@ from typing import List -from unittest import mock from src.model.document import Document from src.validation.document_validator import DocumentValidator -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from src.validation.validation_message import ValidationMessage from tests.valid_defaults import get_creation_info, get_package, get_file, get_snippet, get_annotation, \ get_relationship, get_extracted_licensing_info -def test_correct_document(): +def test_valid_document(): document_validator = DocumentValidator("2.3") - document = Document(get_creation_info(), [get_package(), get_package()], [get_file(), get_file()], [get_snippet(), get_snippet()], [get_annotation(), get_annotation()], - [get_relationship(), get_relationship()], [get_extracted_licensing_info(), get_extracted_licensing_info()]) + document = Document(get_creation_info(), [get_package(), get_package()], [get_file(), get_file()], + [get_snippet(), get_snippet()], [get_annotation(), get_annotation()], + [get_relationship(), get_relationship()], + [get_extracted_licensing_info(), get_extracted_licensing_info()]) validation_messages: List[ValidationMessage] = document_validator.validate_full_spdx_document(document) assert validation_messages == [] -# TODO: some kind of super test is needed to test that all the subvalidations are correctly called +# TODO: https://github.com/spdx/tools-python/issues/375 diff --git a/tests/validation/test_external_document_ref_validator.py b/tests/validation/test_external_document_ref_validator.py index d5de1f43d..4bf7993ef 100644 --- a/tests/validation/test_external_document_ref_validator.py +++ b/tests/validation/test_external_document_ref_validator.py @@ -8,7 +8,7 @@ from tests.valid_defaults import get_checksum, get_external_document_ref -def test_correct_external_document_ref(): +def test_valid_external_document_ref(): external_document_ref_validator = ExternalDocumentRefValidator("2.3", "parent_id") external_document_ref = ExternalDocumentRef("DocumentRef-id", "http://some.uri", get_checksum()) @@ -16,25 +16,3 @@ def test_correct_external_document_ref(): external_document_ref) assert validation_messages == [] - - -@pytest.mark.parametrize("external_document_ref, expected_message", - [(get_external_document_ref(document_ref_id="SPDXRef-id"), - 'document_ref_id must only contain letters, numbers, ".", "-" and "+" and must begin with "DocumentRef-", but is: SPDXRef-id'), - (get_external_document_ref(document_ref_id="DocumentRef-some_id"), - 'document_ref_id must only contain letters, numbers, ".", "-" and "+" and must begin with "DocumentRef-", but is: DocumentRef-some_id'), - (get_external_document_ref(document_uri="some_uri"), - 'document_uri must be a valid URI specified in RFC-3986, but is: some_uri') - ]) -def test_wrong_external_document_ref(external_document_ref, expected_message): - parent_id = "SPDXRef-DOCUMENT" - external_document_ref_validator = ExternalDocumentRefValidator("2.3", parent_id) - validation_messages: List[ValidationMessage] = external_document_ref_validator.validate_external_document_ref( - external_document_ref) - - expected = ValidationMessage(expected_message, - ValidationContext(parent_id=parent_id, - element_type=SpdxElementType.EXTERNAL_DOCUMENT_REF, - full_element=external_document_ref)) - - assert validation_messages == [expected] diff --git a/tests/validation/test_external_package_ref_validator.py b/tests/validation/test_external_package_ref_validator.py index 7db8b2f76..95d88557a 100644 --- a/tests/validation/test_external_package_ref_validator.py +++ b/tests/validation/test_external_package_ref_validator.py @@ -1,34 +1,38 @@ from typing import List + import pytest + from src.model.package import ExternalPackageRef, ExternalPackageRefCategory from src.validation.external_package_ref_validator import ExternalPackageRefValidator from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.valid_defaults import get_external_package_ref -def test_correct_external_package_ref(): +def test_valid_external_package_ref(): external_package_ref_validator = ExternalPackageRefValidator("2.3", "SPDXRef-Package") - external_package_ref = ExternalPackageRef(ExternalPackageRefCategory.OTHER, "swh", + external_package_ref = ExternalPackageRef(ExternalPackageRefCategory.OTHER, "swh", "swh:1:cnt:94a9ed024d3859793618152ea559a168bbcbb5e2", "comment") validation_messages: List[ValidationMessage] = external_package_ref_validator.validate_external_package_ref( external_package_ref) assert validation_messages == [] - + @pytest.mark.parametrize("external_package_ref, expected_message", [(get_external_package_ref(), - 'TBD'), + "TBD"), ]) -@pytest.mark.skip("add tests once external package ref validation is implemented") -def wrong_external_package_ref(external_package_ref, expected_message): +@pytest.mark.skip("add tests once external package ref validation is implemented: https://github.com/spdx/tools-python/issues/373") +def test_invalid_external_package_ref(external_package_ref, expected_message): parent_id = "SPDXRef-Package" external_package_ref_validator = ExternalPackageRefValidator("2.3", parent_id) - validation_messages: List[ValidationMessage] = external_package_ref_validator.validate_external_package_ref(external_package_ref) + validation_messages: List[ValidationMessage] = external_package_ref_validator.validate_external_package_ref( + external_package_ref) expected = ValidationMessage(expected_message, - ValidationContext(parent_id=parent_id, element_type=SpdxElementType.EXTERNAL_PACKAGE_REF, + ValidationContext(parent_id=parent_id, + element_type=SpdxElementType.EXTERNAL_PACKAGE_REF, full_element=external_package_ref)) assert validation_messages == [expected] diff --git a/tests/validation/test_extracted_licensing_info_validator.py b/tests/validation/test_extracted_licensing_info_validator.py index 12c6968e1..5201f05e3 100644 --- a/tests/validation/test_extracted_licensing_info_validator.py +++ b/tests/validation/test_extracted_licensing_info_validator.py @@ -8,10 +8,11 @@ from tests.valid_defaults import get_extracted_licensing_info -def test_correct_extracted_licensing_info(): +def test_valid_extracted_licensing_info(): extracted_licensing_info_validator = ExtractedLicensingInfoValidator("2.3") - extracted_licensing_info = ExtractedLicensingInfo("LicenseRef-1", "extracted text", "license name", ["http://some.url"], "comment") + extracted_licensing_info = ExtractedLicensingInfo("LicenseRef-1", "extracted text", "license name", + ["http://some.url"], "comment") validation_messages: List[ValidationMessage] = extracted_licensing_info_validator.validate_extracted_licensing_info( extracted_licensing_info) @@ -20,20 +21,18 @@ def test_correct_extracted_licensing_info(): # TODO: tests for licenses not on the SPDX License list (i.e. they must provide id, name and cross-references) @pytest.mark.parametrize("extracted_licensing_info, expected_message", - [(get_extracted_licensing_info(license_id="SPDXRef-wrong"), - 'license_id must only contain letters, numbers, "." and "-" and must begin with "LicenseRef-", but is: SPDXRef-wrong'), - (get_extracted_licensing_info(extracted_text=None), + [(get_extracted_licensing_info(extracted_text=None), 'extracted_text must be provided if there is a license_id assigned'), - (get_extracted_licensing_info(cross_references=["wrong_url"]), - 'cross_reference must be a valid URL, but is: wrong_url') + (get_extracted_licensing_info(cross_references=["invalid_url"]), + 'cross_reference must be a valid URL, but is: invalid_url') ]) -def test_wrong_extracted_licensing_info(extracted_licensing_info, expected_message): +def test_invalid_extracted_licensing_info(extracted_licensing_info, expected_message): extracted_licensing_info_validator = ExtractedLicensingInfoValidator("2.3") - validation_messages: List[ValidationMessage] = extracted_licensing_info_validator.validate_extracted_licensing_info(extracted_licensing_info) + validation_messages: List[ValidationMessage] = extracted_licensing_info_validator.validate_extracted_licensing_info( + extracted_licensing_info) expected = ValidationMessage(expected_message, ValidationContext(element_type=SpdxElementType.EXTRACTED_LICENSING_INFO, full_element=extracted_licensing_info)) assert validation_messages == [expected] - diff --git a/tests/validation/test_file_validator.py b/tests/validation/test_file_validator.py index d723cd8a0..b3bb86cae 100644 --- a/tests/validation/test_file_validator.py +++ b/tests/validation/test_file_validator.py @@ -1,4 +1,5 @@ from typing import List + import pytest from src.model.checksum import Checksum, ChecksumAlgorithm @@ -10,10 +11,11 @@ from tests.valid_defaults import get_checksum, get_file, get_document -def test_correct_file(): +def test_valid_file(): file_validator = FileValidator("2.3", get_document()) - file = File("./file/name.py", "SPDXRef-File", [get_checksum()], [FileType.OTHER, FileType.SPDX], SpdxNone(), SpdxNoAssertion(), + file = File("./file/name.py", "SPDXRef-File", [get_checksum()], [FileType.OTHER, FileType.SPDX], SpdxNone(), + SpdxNoAssertion(), "comment on license", "copyright", "comment", "notice", ["contributor"], ["attribution"]) validation_messages: List[ValidationMessage] = file_validator.validate_file(file) @@ -21,14 +23,13 @@ def test_correct_file(): @pytest.mark.parametrize("file_input, spdx_id, expected_message", - [(get_file(spdx_id="SPDXRef-some_file"), "SPDXRef-some_file", - 'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: SPDXRef-some_file'), - (get_file(name="wrong file name"), get_file().spdx_id, - 'file name must be a relative path to the file, starting with "./", but is: wrong file name'), - (get_file(checksums=[Checksum(ChecksumAlgorithm.MD2, "d4c41ce30a517d6ce9d79c8c17bb4b66")]), get_file().spdx_id, - f'checksums must contain a SHA1 algorithm checksum, but is: {[Checksum(ChecksumAlgorithm.MD2, "d4c41ce30a517d6ce9d79c8c17bb4b66")]}') + [(get_file(name="invalid file name"), get_file().spdx_id, + 'file name must be a relative path to the file, starting with "./", but is: invalid file name'), + (get_file(checksums=[Checksum(ChecksumAlgorithm.MD2, "d4c41ce30a517d6ce9d79c8c17bb4b66")]), + get_file().spdx_id, + f'checksums must contain a SHA1 algorithm checksum, but only contains: []') ]) -def test_wrong_file(file_input, spdx_id, expected_message): +def test_invalid_file(file_input, spdx_id, expected_message): file_validator = FileValidator("2.3", get_document()) validation_messages: List[ValidationMessage] = file_validator.validate_file(file_input) diff --git a/tests/validation/test_license_expression_validator.py b/tests/validation/test_license_expression_validator.py index 419ad804e..51cde45bc 100644 --- a/tests/validation/test_license_expression_validator.py +++ b/tests/validation/test_license_expression_validator.py @@ -2,13 +2,14 @@ from src.model.license_expression import LicenseExpression from src.validation.license_expression_validator import LicenseExpressionValidator -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from src.validation.validation_message import ValidationMessage -def test_correct_license_expression(): +def test_valid_license_expression(): license_expression_validator = LicenseExpressionValidator("2.3") license_expression = LicenseExpression("LicenseRef-1") - validation_messages: List[ValidationMessage] = license_expression_validator.validate_license_expression(license_expression) + validation_messages: List[ValidationMessage] = license_expression_validator.validate_license_expression( + license_expression) assert validation_messages == [] diff --git a/tests/validation/test_package_validator.py b/tests/validation/test_package_validator.py index eb47c5592..9a4ec8e19 100644 --- a/tests/validation/test_package_validator.py +++ b/tests/validation/test_package_validator.py @@ -13,7 +13,7 @@ get_package, get_document -def test_correct_package(): +def test_valid_package(): package_validator = PackageValidator("2.3", get_document()) package = Package("SPDXRef-Package", "package name", "www.download.com", "version", "file_name", SpdxNoAssertion(), @@ -29,29 +29,23 @@ def test_correct_package(): # TODO: is verification_code required if files_analyzed=True? @pytest.mark.parametrize("package_input, expected_message", - [(get_package(spdx_id="SPDXRef-some_package"), - 'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: SPDXRef-some_package'), - (get_package(files_analyzed=False, verification_code=get_package_verification_code()), + [(get_package(files_analyzed=False, verification_code=get_package_verification_code()), f'verification_code must be None if files_analyzed is False, but is: {get_package_verification_code()}'), - (get_package(download_location="bad_download_location"), - "download_location must be a valid download location, but is: bad_download_location"), - (get_package(homepage="bad_url"), - 'homepage must be a valid URL, but is: bad_url'), (get_package(files_analyzed=False, license_info_from_files=SpdxNone()), 'license_info_from_files must be None if files_analyzed is False, but is: NONE'), (get_package(files_analyzed=False, license_info_from_files=SpdxNoAssertion()), 'license_info_from_files must be None if files_analyzed is False, but is: NOASSERTION'), - (get_package(files_analyzed=False, license_info_from_files=[LicenseExpression("some_license")]), + (get_package(files_analyzed=False, + license_info_from_files=[LicenseExpression("some_license")]), 'license_info_from_files must be None if files_analyzed is False, but is: [LicenseExpression(expression_string=\'some_license\')]') ]) -def test_wrong_package(package_input, expected_message): - parent_id = "SPDXRef-DOCUMENT" +def test_invalid_package(package_input, expected_message): package_validator = PackageValidator("2.3", get_document()) - package = package_input - validation_messages: List[ValidationMessage] = package_validator.validate_package(package) + validation_messages: List[ValidationMessage] = package_validator.validate_package(package_input) expected = ValidationMessage(expected_message, - ValidationContext(spdx_id=package.spdx_id, parent_id=parent_id, element_type=SpdxElementType.PACKAGE, - full_element=package)) + ValidationContext(spdx_id=package_input.spdx_id, parent_id="SPDXRef-DOCUMENT", + element_type=SpdxElementType.PACKAGE, + full_element=package_input)) assert validation_messages == [expected] diff --git a/tests/validation/test_relationship_validator.py b/tests/validation/test_relationship_validator.py index f8a7ff4d7..7388e9ee0 100644 --- a/tests/validation/test_relationship_validator.py +++ b/tests/validation/test_relationship_validator.py @@ -9,7 +9,7 @@ from tests.valid_defaults import get_document, get_package, get_relationship, get_file -def test_correct_relationship(): +def test_valid_relationship(): document: Document = get_document(packages=[get_package(spdx_id="SPDXRef-Package")]) relationship_validator = RelationshipValidator("2.3", document) @@ -19,19 +19,16 @@ def test_correct_relationship(): assert validation_messages == [] -@pytest.mark.parametrize("first_id, second_id, wrong_file_id, expected_message", - [("SPDXRef-some_file", "SPDXRef-File", "SPDXRef-some_file", - 'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: SPDXRef-some_file'), - ("SPDXRef-File", "SPDXRef-some_file", "SPDXRef-some_file", - 'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: SPDXRef-some_file'), - ("SPDXRef-unknownFile", "SPDXRef-hiddenFile", "SPDXRef-hiddenFile", +@pytest.mark.parametrize("spdx_element_id, related_spdx_element_id, expected_message", + [("SPDXRef-unknownFile", "SPDXRef-File", 'did not find the referenced spdx_id SPDXRef-unknownFile in the SPDX document'), - ("SPDXRef-hiddenFile", "SPDXRef-unknownFile", "SPDXRef-hiddenFile", + ("SPDXRef-File", "SPDXRef-unknownFile", 'did not find the referenced spdx_id SPDXRef-unknownFile in the SPDX document'), ]) -def test_wrong_relationship(first_id, second_id, wrong_file_id, expected_message): - relationship: Relationship = get_relationship(spdx_element_id=first_id, related_spdx_element_id=second_id) - document: Document = get_document(files=[get_file(spdx_id="SPDXRef-File"), get_file(spdx_id=wrong_file_id)]) +def test_unknown_spdx_id(spdx_element_id, related_spdx_element_id, expected_message): + relationship: Relationship = get_relationship(spdx_element_id=spdx_element_id, + related_spdx_element_id=related_spdx_element_id) + document: Document = get_document(files=[get_file(spdx_id="SPDXRef-File")]) relationship_validator = RelationshipValidator("2.3", document) validation_messages: List[ValidationMessage] = relationship_validator.validate_relationship(relationship) diff --git a/tests/validation/test_snippet_validator.py b/tests/validation/test_snippet_validator.py index b39791846..1b3b1dbc7 100644 --- a/tests/validation/test_snippet_validator.py +++ b/tests/validation/test_snippet_validator.py @@ -11,7 +11,7 @@ from tests.valid_defaults import get_snippet, get_document, get_file -def test_correct_snippet(): +def test_valid_snippet(): document: Document = get_document(files=[get_file(spdx_id="SPDXRef-File")]) snippet_validator = SnippetValidator("2.3", document) @@ -24,37 +24,17 @@ def test_correct_snippet(): assert validation_messages == [] -@pytest.mark.parametrize("snippet_id, snippet_file_id, file_id, expected_message", - [("SPDXRef-some_snippet", "SPDXRef-File", "SPDXRef-File", - 'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: SPDXRef-some_snippet'), - ("SPDXRef-Snippet", "SPDXRef-some_file", "SPDXRef-some_file", - 'spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: SPDXRef-some_file'), - ("SPDXRef-Snippet", "SPDXRef-File", "SPDXRef-hiddenFile", - 'did not find the referenced spdx_id SPDXRef-File in the SPDX document\'s files') - ]) -def test_wrong_spdx_ids(snippet_id, snippet_file_id, file_id, expected_message): - snippet_validator = SnippetValidator("2.3", get_document(files=[get_file(spdx_id=file_id)])) - snippet = get_snippet(spdx_id=snippet_id, file_spdx_id=snippet_file_id) - validation_messages: List[ValidationMessage] = snippet_validator.validate_snippet(snippet) - - expected = ValidationMessage(expected_message, - ValidationContext(spdx_id=snippet_id, element_type=SpdxElementType.SNIPPET, - full_element=snippet)) - - assert validation_messages == [expected] - - @pytest.mark.parametrize("snippet_input, expected_message", [(get_snippet(byte_range=(-12, 45)), - 'byte_range values must be greater than or equal to 1, but is: (-12, 45)'), + "byte_range values must be greater than or equal to 1, but is: (-12, 45)"), (get_snippet(byte_range=(45, 23)), - 'the first value of byte_range must be less than or equal to the second, but is: (45, 23)'), + "the first value of byte_range must be less than or equal to the second, but is: (45, 23)"), (get_snippet(line_range=(-12, 45)), - 'line_range values must be greater than or equal to 1, but is: (-12, 45)'), + "line_range values must be greater than or equal to 1, but is: (-12, 45)"), (get_snippet(line_range=(45, 23)), - 'the first value of line_range must be less than or equal to the second, but is: (45, 23)') + "the first value of line_range must be less than or equal to the second, but is: (45, 23)") ]) -def test_wrong_ranges(snippet_input, expected_message): +def test_invalid_ranges(snippet_input, expected_message): snippet_validator = SnippetValidator("2.3", get_document(files=[get_file()])) validation_messages: List[ValidationMessage] = snippet_validator.validate_snippet(snippet_input) diff --git a/tests/validation/test_spdx_id_validator.py b/tests/validation/test_spdx_id_validator.py deleted file mode 100644 index 3940605f8..000000000 --- a/tests/validation/test_spdx_id_validator.py +++ /dev/null @@ -1 +0,0 @@ -# TODO: add tests diff --git a/tests/validation/test_spdx_id_validators.py b/tests/validation/test_spdx_id_validators.py new file mode 100644 index 000000000..69692be70 --- /dev/null +++ b/tests/validation/test_spdx_id_validators.py @@ -0,0 +1 @@ +# TODO: https://github.com/spdx/tools-python/issues/376 diff --git a/tests/validation/test_uri_validators.py b/tests/validation/test_uri_validators.py index f1065c3b3..ca1403493 100644 --- a/tests/validation/test_uri_validators.py +++ b/tests/validation/test_uri_validators.py @@ -1,20 +1,19 @@ - -from uritools import isuri, isabsuri import pytest -from src.validation.uri_validators import validate_url, validate_package_download_location, validate_uri +from src.validation.uri_validators import validate_url, validate_download_location, validate_uri -@pytest.mark.parametrize("input_value", ['https://some.url', "https://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82...", +@pytest.mark.parametrize("input_value", ["https://some.url", + "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"]) def test_valid_url(input_value): assert validate_url(input_value) == [] -# TODO: more negative examples -@pytest.mark.parametrize("input_value", [':::::', ]) +# TODO: more negative examples: https://github.com/spdx/tools-python/issues/377 +@pytest.mark.parametrize("input_value", [":::::", ]) def test_invalid_url(input_value): - assert validate_url(input_value) == [f'must be a valid URL, but is: {input_value}'] + assert validate_url(input_value) == [f"must be a valid URL, but is: {input_value}"] @pytest.mark.parametrize("input_value", ["http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz", @@ -66,34 +65,37 @@ def test_invalid_url(input_value): "bzr+https://bzr.myproject.org/MyProject/trunk@2019#src/somefile.c", ]) def test_valid_package_download_location(input_value): - assert validate_package_download_location(input_value) == [] + assert validate_download_location(input_value) == [] -# TODO: more negative examples -@pytest.mark.parametrize("input_value", [':::::', ]) +# TODO: more negative examples: https://github.com/spdx/tools-python/issues/377 +@pytest.mark.parametrize("input_value", [":::::", ]) def test_invalid_package_download_location(input_value): - assert validate_package_download_location(input_value) == [f'must be a valid download location, but is: {input_value}'] + assert validate_download_location(input_value) == [ + f"must be a valid download location according to the specification, but is: {input_value}"] -@pytest.mark.parametrize("input_value", ['https://some.uri', "http:////some", "https://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82...", - 'h://someweirdtest^?', "https://some.uri that goes on!?"]) +@pytest.mark.parametrize("input_value", ["https://some.uri", "http:////some", + "https://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82...", + "h://someweirdtest^?", "https://some.uri that goes on!?"]) def test_valid_uri(input_value): message = validate_uri(input_value) assert message == [] - -@pytest.mark.parametrize("input_value", ["/invalid/uri", "http//uri", "http://some#uri", "some/uri", 'some weird test']) + +@pytest.mark.parametrize("input_value", ["/invalid/uri", "http//uri", "http://some#uri", "some/uri", "some weird test"]) def test_invalid_uri(input_value): message = validate_uri(input_value) - - assert message == [f'must be a valid URI specified in RFC-3986, but is: {input_value}'] + assert message == [f"must be a valid URI specified in RFC-3986, but is: {input_value}"] -@pytest.mark.parametrize("input_value", ['://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82...']) -@pytest.mark.skip("validate_uri() seems to invalidate URIs without scheme, so it does not run into this case. But I'm not sure yet if this covers all scheme-less examples.") + +@pytest.mark.parametrize("input_value", ["://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82..."]) +@pytest.mark.skip( + "validate_uri() seems to invalidate URIs without scheme, so it does not run into this case. But I'm not sure yet if this covers all scheme-less examples." + "https://github.com/spdx/tools-python/issues/377") def test_uri_without_scheme(input_value): message = validate_uri(input_value) - assert message == [f'must have a URI scheme, but is: {input_value}'] - + assert message == [f"must have a URI scheme, but is: {input_value}"] From dc4243a82d8cf5a3cf8c6b327dab25b600888d5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 21 Dec 2022 16:41:02 +0100 Subject: [PATCH 032/362] [review] remove class structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/validation/actor_validator.py | 37 ++-- src/validation/annotation_validator.py | 39 ++-- src/validation/checksum_validator.py | 63 +++--- src/validation/creation_info_validator.py | 73 +++---- src/validation/document_validator.py | 76 +++----- .../external_document_ref_validator.py | 59 +++--- .../external_package_ref_validator.py | 31 +-- .../extracted_licensing_info_validator.py | 68 +++---- src/validation/file_validator.py | 71 +++---- .../license_expression_validator.py | 27 ++- src/validation/package_validator.py | 180 ++++++++---------- .../package_verification_code_validator.py | 44 ++--- src/validation/relationship_validator.py | 49 ++--- src/validation/snippet_validator.py | 86 ++++----- tests/validation/test_actor_validator.py | 9 +- tests/validation/test_annotation_validator.py | 8 +- tests/validation/test_checksum_validator.py | 9 +- .../test_creation_info_validator.py | 12 +- tests/validation/test_document_validator.py | 6 +- .../test_external_document_ref_validator.py | 6 +- .../test_external_package_ref_validator.py | 10 +- ...test_extracted_licensing_info_validator.py | 11 +- tests/validation/test_file_validator.py | 9 +- .../test_license_expression_validator.py | 7 +- tests/validation/test_package_validator.py | 9 +- .../validation/test_relationship_validator.py | 11 +- tests/validation/test_snippet_validator.py | 10 +- 27 files changed, 411 insertions(+), 609 deletions(-) diff --git a/src/validation/actor_validator.py b/src/validation/actor_validator.py index 21cfa7e5e..f08d2231e 100644 --- a/src/validation/actor_validator.py +++ b/src/validation/actor_validator.py @@ -1,33 +1,26 @@ -from typing import List, Optional +from typing import List from src.model.actor import Actor, ActorType from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -class ActorValidator: - spdx_version: str - parent_id: str +def validate_actors(actors: List[Actor], parent_id: str) -> List[ValidationMessage]: + validation_messages = [] + for actor in actors: + validation_messages.extend(validate_actor(actor, parent_id)) - def __init__(self, spdx_version: str, parent_id: Optional[str]): - self.spdx_version = spdx_version - self.parent_id = parent_id + return validation_messages - def validate_actors(self, actors: List[Actor]) -> List[ValidationMessage]: - validation_messages = [] - for actor in actors: - validation_messages.extend(self.validate_actor(actor)) - return validation_messages +def validate_actor(actor: Actor, parent_id: str) -> List[ValidationMessage]: + validation_messages = [] - def validate_actor(self, actor: Actor) -> List[ValidationMessage]: - validation_messages = [] - - if actor.actor_type == ActorType.TOOL and actor.email is not None: - validation_messages.append( - ValidationMessage( - f"email must be None if actor_type is TOOL, but is: {actor.email}", - ValidationContext(parent_id=self.parent_id, element_type=SpdxElementType.ACTOR, full_element=actor) - ) + if actor.actor_type == ActorType.TOOL and actor.email is not None: + validation_messages.append( + ValidationMessage( + f"email must be None if actor_type is TOOL, but is: {actor.email}", + ValidationContext(parent_id=parent_id, element_type=SpdxElementType.ACTOR, full_element=actor) ) + ) - return validation_messages + return validation_messages diff --git a/src/validation/annotation_validator.py b/src/validation/annotation_validator.py index 3a6197e87..c806273c7 100644 --- a/src/validation/annotation_validator.py +++ b/src/validation/annotation_validator.py @@ -2,37 +2,28 @@ from src.model.annotation import Annotation from src.model.document import Document -from src.validation.actor_validator import ActorValidator +from src.validation.actor_validator import validate_actor from src.validation.spdx_id_validators import validate_spdx_id from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -class AnnotationValidator: - spdx_version: str - document: Document - actor_validator: ActorValidator +def validate_annotations(annotations: List[Annotation], document: Document) -> List[ValidationMessage]: + validation_messages = [] + for annotation in annotations: + validation_messages.extend(validate_annotation(annotation, document)) - def __init__(self, spdx_version: str, document: Document): - self.spdx_version = spdx_version - self.document = document - self.actor_validator = ActorValidator(spdx_version, parent_id="annotation") + return validation_messages - def validate_annotations(self, annotations: List[Annotation]) -> List[ValidationMessage]: - validation_messages = [] - for annotation in annotations: - validation_messages.extend(self.validate_annotation(annotation)) - return validation_messages +def validate_annotation(annotation: Annotation, document: Document) -> List[ValidationMessage]: + validation_messages = [] + context = ValidationContext(element_type=SpdxElementType.ANNOTATION, + full_element=annotation) - def validate_annotation(self, annotation: Annotation) -> List[ValidationMessage]: - validation_messages = [] - context = ValidationContext(element_type=SpdxElementType.ANNOTATION, - full_element=annotation) + validation_messages.extend(validate_actor(annotation.annotator, "annotation")) - validation_messages.extend(self.actor_validator.validate_actor(annotation.annotator)) + messages: List[str] = validate_spdx_id(annotation.spdx_id, document, check_document=True) + for message in messages: + validation_messages.append(ValidationMessage(message, context)) - messages: List[str] = validate_spdx_id(annotation.spdx_id, self.document, check_document=True) - for message in messages: - validation_messages.append(ValidationMessage(message, context)) - - return validation_messages + return validation_messages diff --git a/src/validation/checksum_validator.py b/src/validation/checksum_validator.py index ffed999b5..0773a1a63 100644 --- a/src/validation/checksum_validator.py +++ b/src/validation/checksum_validator.py @@ -26,38 +26,31 @@ } -class ChecksumValidator: - spdx_version: str - parent_id: str - - def __init__(self, spdx_version: str, parent_id: str): - self.spdx_version = spdx_version - self.parent_id = parent_id - - def validate_checksums(self, checksums: List[Checksum]) -> List[ValidationMessage]: - validation_messages = [] - for checksum in checksums: - validation_messages.extend(self.validate_checksum(checksum)) - - return validation_messages - - def validate_checksum(self, checksum: Checksum) -> List[ValidationMessage]: - validation_messages = [] - algorithm = checksum.algorithm - context = ValidationContext(parent_id=self.parent_id, element_type=SpdxElementType.CHECKSUM, - full_element=checksum) - - if not re.match("^[0-9a-f]{" + algorithm_length[algorithm] + "}$", checksum.value): - if algorithm == ChecksumAlgorithm.BLAKE3: - length = "at least 256" - elif algorithm == ChecksumAlgorithm.MD6: - length = "between 0 and 512" - else: - length = algorithm_length[algorithm] - validation_messages.append( - ValidationMessage( - f"value of {algorithm} must consist of {length} hexadecimal digits, but is: {checksum.value} (length: {len(checksum.value)} digits)", - context) - ) - - return validation_messages +def validate_checksums(checksums: List[Checksum], parent_id: str) -> List[ValidationMessage]: + validation_messages = [] + for checksum in checksums: + validation_messages.extend(validate_checksum(checksum, parent_id)) + + return validation_messages + + +def validate_checksum(checksum: Checksum, parent_id: str) -> List[ValidationMessage]: + validation_messages = [] + algorithm = checksum.algorithm + context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.CHECKSUM, + full_element=checksum) + + if not re.match("^[0-9a-f]{" + algorithm_length[algorithm] + "}$", checksum.value): + if algorithm == ChecksumAlgorithm.BLAKE3: + length = "at least 256" + elif algorithm == ChecksumAlgorithm.MD6: + length = "between 0 and 512" + else: + length = algorithm_length[algorithm] + validation_messages.append( + ValidationMessage( + f"value of {algorithm} must consist of {length} hexadecimal digits, but is: {checksum.value} (length: {len(checksum.value)} digits)", + context) + ) + + return validation_messages diff --git a/src/validation/creation_info_validator.py b/src/validation/creation_info_validator.py index 63dc44eac..b9f59552c 100644 --- a/src/validation/creation_info_validator.py +++ b/src/validation/creation_info_validator.py @@ -2,61 +2,50 @@ from typing import List from src.model.document import CreationInfo -from src.validation.actor_validator import ActorValidator -from src.validation.external_document_ref_validator import ExternalDocumentRefValidator +from src.validation.actor_validator import validate_actors +from src.validation.external_document_ref_validator import validate_external_document_refs from src.validation.uri_validators import validate_uri from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -class CreationInfoValidator: - spdx_version: str +def validate_creation_info(creation_info: CreationInfo) -> List[ValidationMessage]: + validation_messages: List[ValidationMessage] = [] - def __init__(self, spdx_version: str): - self.spdx_version = spdx_version + context = ValidationContext(spdx_id=creation_info.spdx_id, element_type=SpdxElementType.DOCUMENT) - def validate_creation_info(self, creation_info: CreationInfo) -> List[ValidationMessage]: - validation_messages: List[ValidationMessage] = [] - actor_validator = ActorValidator(self.spdx_version, creation_info.spdx_id) - external_document_ref_validator = ExternalDocumentRefValidator(self.spdx_version, creation_info.spdx_id) - - context = ValidationContext(spdx_id=creation_info.spdx_id, element_type=SpdxElementType.DOCUMENT) - - if not re.match(r"^SPDX-\d+.\d+$", creation_info.spdx_version): - validation_messages.append( - ValidationMessage( - f'spdx_version must be of the form "SPDX-[major].[minor]" but is: {creation_info.spdx_version}', - context - ) + if not re.match(r"^SPDX-\d+.\d+$", creation_info.spdx_version): + validation_messages.append( + ValidationMessage( + f'spdx_version must be of the form "SPDX-[major].[minor]" but is: {creation_info.spdx_version}', + context ) + ) - if creation_info.spdx_id != "SPDXRef-DOCUMENT": - validation_messages.append( - ValidationMessage( - f'spdx_id must be "SPDXRef-DOCUMENT", but is: {creation_info.spdx_id}', - context - ) + if creation_info.spdx_id != "SPDXRef-DOCUMENT": + validation_messages.append( + ValidationMessage( + f'spdx_id must be "SPDXRef-DOCUMENT", but is: {creation_info.spdx_id}', + context ) + ) - if creation_info.data_license != "CC0-1.0": - validation_messages.append( - ValidationMessage( - f'data_license must be "CC0-1.0", but is: {creation_info.data_license}', - context - ) + if creation_info.data_license != "CC0-1.0": + validation_messages.append( + ValidationMessage( + f'data_license must be "CC0-1.0", but is: {creation_info.data_license}', + context ) + ) - for message in validate_uri(creation_info.document_namespace): - validation_messages.append( - ValidationMessage( - "document_namespace " + message, context - ) + for message in validate_uri(creation_info.document_namespace): + validation_messages.append( + ValidationMessage( + "document_namespace " + message, context ) - - validation_messages.extend( - actor_validator.validate_actors(creation_info.creators) ) - validation_messages.extend( - external_document_ref_validator.validate_external_document_refs(creation_info.external_document_refs)) + validation_messages.extend(validate_actors(creation_info.creators, creation_info.spdx_id)) + + validation_messages.extend(validate_external_document_refs(creation_info.external_document_refs, creation_info.spdx_id)) - return validation_messages + return validation_messages diff --git a/src/validation/document_validator.py b/src/validation/document_validator.py index 4f370fe39..b10255a1b 100644 --- a/src/validation/document_validator.py +++ b/src/validation/document_validator.py @@ -2,58 +2,38 @@ from src.model.document import Document from src.model.relationship import RelationshipType -from src.validation.annotation_validator import AnnotationValidator -from src.validation.creation_info_validator import CreationInfoValidator -from src.validation.extracted_licensing_info_validator import ExtractedLicensingInfoValidator -from src.validation.file_validator import FileValidator -from src.validation.package_validator import PackageValidator -from src.validation.relationship_validator import RelationshipValidator -from src.validation.snippet_validator import SnippetValidator +from src.validation.annotation_validator import validate_annotations +from src.validation.creation_info_validator import validate_creation_info +from src.validation.extracted_licensing_info_validator import validate_extracted_licensing_infos +from src.validation.file_validator import validate_files +from src.validation.package_validator import validate_packages +from src.validation.relationship_validator import validate_relationships +from src.validation.snippet_validator import validate_snippets from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -class DocumentValidator: - spdx_version: str - creation_info_validator: CreationInfoValidator - snippet_validator: SnippetValidator - annotation_validator: AnnotationValidator - relationship_validator: RelationshipValidator - extracted_licensing_info_validator: ExtractedLicensingInfoValidator +def validate_full_spdx_document(document: Document, spdx_version: str) -> List[ValidationMessage]: + validation_messages: List[ValidationMessage] = [] - def __init__(self, spdx_version: str): - self.spdx_version = spdx_version - self.creation_info_validator = CreationInfoValidator(spdx_version) - self.extracted_licensing_info_validator = ExtractedLicensingInfoValidator(spdx_version) + validation_messages.extend(validate_creation_info(document.creation_info)) + validation_messages.extend(validate_packages(document.packages, document)) + validation_messages.extend(validate_files(document.files, document)) + validation_messages.extend(validate_snippets(document.snippets, document)) + validation_messages.extend(validate_annotations(document.annotations, document)) + validation_messages.extend(validate_relationships(document.relationships, document, spdx_version)) + validation_messages.extend(validate_extracted_licensing_infos(document.extracted_licensing_info)) - def validate_full_spdx_document(self, document: Document) -> List[ValidationMessage]: - package_validator = PackageValidator(self.spdx_version, document) - file_validator = FileValidator(self.spdx_version, document) - snippet_validator = SnippetValidator(self.spdx_version, document) - annotation_validator = AnnotationValidator(self.spdx_version, document) - relationship_validator = RelationshipValidator(self.spdx_version, document) + document_id = document.creation_info.spdx_id + document_describes_relationships = [relationship for relationship in document.relationships if + relationship.relationship_type == RelationshipType.DESCRIBES and relationship.spdx_element_id == document_id] + described_by_document_relationships = [relationship for relationship in document.relationships if + relationship.relationship_type == RelationshipType.DESCRIBED_BY and relationship.related_spdx_element_id == document_id] - validation_messages: List[ValidationMessage] = [] + if not document_describes_relationships + described_by_document_relationships: + validation_messages.append( + ValidationMessage( + f'there must be at least one relationship "{document_id} DESCRIBES ..." or "... DESCRIBED_BY {document_id}"', + ValidationContext(spdx_id=document_id, + element_type=SpdxElementType.DOCUMENT))) - validation_messages.extend(self.creation_info_validator.validate_creation_info(document.creation_info)) - validation_messages.extend(package_validator.validate_packages(document.packages)) - validation_messages.extend(file_validator.validate_files(document.files)) - validation_messages.extend(snippet_validator.validate_snippets(document.snippets)) - validation_messages.extend(annotation_validator.validate_annotations(document.annotations)) - validation_messages.extend(relationship_validator.validate_relationships(document.relationships)) - validation_messages.extend(self.extracted_licensing_info_validator.validate_extracted_licensing_infos( - document.extracted_licensing_info)) - - document_id = document.creation_info.spdx_id - document_describes_relationships = [relationship for relationship in document.relationships if - relationship.relationship_type == RelationshipType.DESCRIBES and relationship.spdx_element_id == document_id] - described_by_document_relationships = [relationship for relationship in document.relationships if - relationship.relationship_type == RelationshipType.DESCRIBED_BY and relationship.related_spdx_element_id == document_id] - - if not document_describes_relationships + described_by_document_relationships: - validation_messages.append( - ValidationMessage( - f'there must be at least one relationship "{document_id} DESCRIBES ..." or "... DESCRIBED_BY {document_id}"', - ValidationContext(spdx_id=document_id, - element_type=SpdxElementType.DOCUMENT))) - - return validation_messages + return validation_messages diff --git a/src/validation/external_document_ref_validator.py b/src/validation/external_document_ref_validator.py index 3a02242be..af4377a5c 100644 --- a/src/validation/external_document_ref_validator.py +++ b/src/validation/external_document_ref_validator.py @@ -2,52 +2,41 @@ from typing import List from src.model.external_document_ref import ExternalDocumentRef -from src.validation.checksum_validator import ChecksumValidator +from src.validation.checksum_validator import validate_checksum from src.validation.spdx_id_validators import is_valid_external_doc_ref_id from src.validation.uri_validators import validate_uri from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -class ExternalDocumentRefValidator: - spdx_version: str - parent_id: str - checksum_validator: ChecksumValidator +def validate_external_document_refs(external_document_refs: List[ExternalDocumentRef], parent_id: str) -> List[ + ValidationMessage]: + validation_messages = [] + for external_document_ref in external_document_refs: + validation_messages.extend(validate_external_document_ref(external_document_ref, parent_id)) - def __init__(self, spdx_version: str, parent_id: str): - self.spdx_version = spdx_version - self.parent_id = parent_id - self.checksum_validator = ChecksumValidator(spdx_version, parent_id) + return validation_messages - def validate_external_document_refs(self, external_document_refs: List[ExternalDocumentRef]) -> List[ - ValidationMessage]: - validation_messages = [] - for external_document_ref in external_document_refs: - validation_messages.extend(self.validate_external_document_ref(external_document_ref)) - return validation_messages +def validate_external_document_ref(external_document_ref: ExternalDocumentRef, parent_id: str) -> List[ValidationMessage]: + validation_messages = [] + context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.EXTERNAL_DOCUMENT_REF, + full_element=external_document_ref) - def validate_external_document_ref(self, external_document_ref: ExternalDocumentRef) -> List[ValidationMessage]: - validation_messages = [] - context = ValidationContext(parent_id=self.parent_id, element_type=SpdxElementType.EXTERNAL_DOCUMENT_REF, - full_element=external_document_ref) - - if not is_valid_external_doc_ref_id(external_document_ref.document_ref_id): - validation_messages.append( - ValidationMessage( - f'document_ref_id must only contain letters, numbers, ".", "-" and "+" and must begin with "DocumentRef-", but is: {external_document_ref.document_ref_id}', - context - ) + if not is_valid_external_doc_ref_id(external_document_ref.document_ref_id): + validation_messages.append( + ValidationMessage( + f'document_ref_id must only contain letters, numbers, ".", "-" and "+" and must begin with "DocumentRef-", but is: {external_document_ref.document_ref_id}', + context ) + ) - for message in validate_uri(external_document_ref.document_uri): - validation_messages.append( - ValidationMessage( - "document_uri " + message, context - ) + for message in validate_uri(external_document_ref.document_uri): + validation_messages.append( + ValidationMessage( + "document_uri " + message, context ) - - validation_messages.extend( - self.checksum_validator.validate_checksum(external_document_ref.checksum) ) - return validation_messages + validation_messages.extend(validate_checksum(external_document_ref.checksum, parent_id)) + + return validation_messages diff --git a/src/validation/external_package_ref_validator.py b/src/validation/external_package_ref_validator.py index 201d9d3bd..2fee1f001 100644 --- a/src/validation/external_package_ref_validator.py +++ b/src/validation/external_package_ref_validator.py @@ -1,31 +1,18 @@ from typing import List from src.model.package import ExternalPackageRef -from src.validation.checksum_validator import ChecksumValidator -from src.validation.license_expression_validator import LicenseExpressionValidator from src.validation.validation_message import ValidationMessage -class ExternalPackageRefValidator: - spdx_version: str - parent_id: str - checksum_validator: ChecksumValidator - license_expression_validator: LicenseExpressionValidator +def validate_external_package_refs(external_package_refs: List[ExternalPackageRef], parent_id: str) -> List[ + ValidationMessage]: + validation_messages = [] + for external_package_ref in external_package_refs: + validation_messages.extend(validate_external_package_ref(external_package_ref, parent_id)) - def __init__(self, spdx_version: str, parent_id: str): - self.spdx_version = spdx_version - self.parent_id = parent_id - self.checksum_validator = ChecksumValidator(spdx_version, parent_id) - self.license_expression_validator = LicenseExpressionValidator(spdx_version) + return validation_messages - def validate_external_package_refs(self, external_package_refs: List[ExternalPackageRef]) -> List[ - ValidationMessage]: - validation_messages = [] - for external_package_ref in external_package_refs: - validation_messages.extend(self.validate_external_package_ref(external_package_ref)) - return validation_messages - - def validate_external_package_ref(self, external_package_ref: ExternalPackageRef) -> List[ValidationMessage]: - # TODO: https://github.com/spdx/tools-python/issues/373 - return [] +def validate_external_package_ref(external_package_ref: ExternalPackageRef, parent_id: str) -> List[ValidationMessage]: + # TODO: https://github.com/spdx/tools-python/issues/373 + return [] diff --git a/src/validation/extracted_licensing_info_validator.py b/src/validation/extracted_licensing_info_validator.py index d614fa4b5..5d7b5f4aa 100644 --- a/src/validation/extracted_licensing_info_validator.py +++ b/src/validation/extracted_licensing_info_validator.py @@ -6,43 +6,37 @@ from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -class ExtractedLicensingInfoValidator: - spdx_version: str - - def __init__(self, spdx_version): - self.spdx_version = spdx_version - - def validate_extracted_licensing_infos(self, extracted_licensing_infos: Optional[List[ExtractedLicensingInfo]]) -> \ - List[ValidationMessage]: - validation_messages = [] - for extracted_licensing_info in extracted_licensing_infos: - validation_messages.extend(self.validate_extracted_licensing_info(extracted_licensing_info)) - - return validation_messages - - def validate_extracted_licensing_info(self, extracted_licensing_infos: ExtractedLicensingInfo) -> List[ - ValidationMessage]: - validation_messages: List[ValidationMessage] = [] - context = ValidationContext(element_type=SpdxElementType.EXTRACTED_LICENSING_INFO, - full_element=extracted_licensing_infos) - - license_id: str = extracted_licensing_infos.license_id - if license_id and not re.match(r"^LicenseRef-[\da-zA-Z.-]+$", license_id): +def validate_extracted_licensing_infos(extracted_licensing_infos: Optional[List[ExtractedLicensingInfo]]) -> List[ValidationMessage]: + validation_messages = [] + for extracted_licensing_info in extracted_licensing_infos: + validation_messages.extend(validate_extracted_licensing_info(extracted_licensing_info)) + + return validation_messages + + +def validate_extracted_licensing_info(extracted_licensing_infos: ExtractedLicensingInfo) -> List[ + ValidationMessage]: + validation_messages: List[ValidationMessage] = [] + context = ValidationContext(element_type=SpdxElementType.EXTRACTED_LICENSING_INFO, + full_element=extracted_licensing_infos) + + license_id: str = extracted_licensing_infos.license_id + if license_id and not re.match(r"^LicenseRef-[\da-zA-Z.-]+$", license_id): + validation_messages.append( + ValidationMessage( + f'license_id must only contain letters, numbers, "." and "-" and must begin with "LicenseRef-", but is: {license_id}', + context) + ) + + if license_id and not extracted_licensing_infos.extracted_text: + validation_messages.append( + ValidationMessage("extracted_text must be provided if there is a license_id assigned", context) + ) + + for cross_reference in extracted_licensing_infos.cross_references: + for message in validate_url(cross_reference): validation_messages.append( - ValidationMessage( - f'license_id must only contain letters, numbers, "." and "-" and must begin with "LicenseRef-", but is: {license_id}', - context) + ValidationMessage("cross_reference " + message, context) ) - if license_id and not extracted_licensing_infos.extracted_text: - validation_messages.append( - ValidationMessage("extracted_text must be provided if there is a license_id assigned", context) - ) - - for cross_reference in extracted_licensing_infos.cross_references: - for message in validate_url(cross_reference): - validation_messages.append( - ValidationMessage("cross_reference " + message, context) - ) - - return validation_messages + return validation_messages diff --git a/src/validation/file_validator.py b/src/validation/file_validator.py index 2a313a317..97c2d307f 100644 --- a/src/validation/file_validator.py +++ b/src/validation/file_validator.py @@ -3,61 +3,44 @@ from src.model.checksum import ChecksumAlgorithm from src.model.document import Document from src.model.file import File -from src.validation.checksum_validator import ChecksumValidator -from src.validation.license_expression_validator import LicenseExpressionValidator +from src.validation.checksum_validator import validate_checksums +from src.validation.license_expression_validator import validate_license_expressions, validate_license_expression from src.validation.spdx_id_validators import validate_spdx_id from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -class FileValidator: - spdx_version: str - document: Document - license_expression_validator: LicenseExpressionValidator +def validate_files(files: List[File], document: Document) -> List[ValidationMessage]: + validation_messages = [] + for file in files: + validation_messages.extend(validate_file(file, document)) - def __init__(self, spdx_version: str, document: Document): - self.spdx_version = spdx_version - self.document = document - self.license_expression_validator = LicenseExpressionValidator(spdx_version) + return validation_messages - def validate_files(self, files: List[File]) -> List[ValidationMessage]: - validation_messages = [] - for file in files: - validation_messages.extend(self.validate_file(file)) - return validation_messages +def validate_file(file: File, document: Document) -> List[ValidationMessage]: + validation_messages = [] + context = ValidationContext(spdx_id=file.spdx_id, element_type=SpdxElementType.FILE, full_element=file) - def validate_file(self, file: File) -> List[ValidationMessage]: - validation_messages = [] - checksum_validator = ChecksumValidator(self.spdx_version, file.spdx_id) - context = ValidationContext(spdx_id=file.spdx_id, element_type=SpdxElementType.FILE, full_element=file) + for message in validate_spdx_id(file.spdx_id, document): + validation_messages.append(ValidationMessage(message, context)) - for message in validate_spdx_id(file.spdx_id, self.document): - validation_messages.append(ValidationMessage(message, context)) - - if not file.name.startswith("./"): - validation_messages.append( - ValidationMessage( - f'file name must be a relative path to the file, starting with "./", but is: {file.name}', - context) - ) - - if ChecksumAlgorithm.SHA1 not in [checksum.algorithm for checksum in file.checksums]: - validation_messages.append( - ValidationMessage( - f"checksums must contain a SHA1 algorithm checksum, but only contains: {[checksum.algorithm for checksum in file.checksums]}", - context) - ) - - validation_messages.extend( - checksum_validator.validate_checksums(file.checksums) + if not file.name.startswith("./"): + validation_messages.append( + ValidationMessage( + f'file name must be a relative path to the file, starting with "./", but is: {file.name}', context) ) - validation_messages.extend( - self.license_expression_validator.validate_license_expression(file.concluded_license) + if ChecksumAlgorithm.SHA1 not in [checksum.algorithm for checksum in file.checksums]: + validation_messages.append( + ValidationMessage( + f"checksums must contain a SHA1 algorithm checksum, but only contains: {[checksum.algorithm for checksum in file.checksums]}", + context) ) - validation_messages.extend( - self.license_expression_validator.validate_license_expressions(file.license_info_in_file) - ) + validation_messages.extend(validate_checksums(file.checksums, file.spdx_id)) + + validation_messages.extend(validate_license_expression(file.concluded_license)) + + validation_messages.extend(validate_license_expressions(file.license_info_in_file)) - return validation_messages + return validation_messages diff --git a/src/validation/license_expression_validator.py b/src/validation/license_expression_validator.py index 95d36771b..1d2b9c5ac 100644 --- a/src/validation/license_expression_validator.py +++ b/src/validation/license_expression_validator.py @@ -6,24 +6,19 @@ from src.validation.validation_message import ValidationMessage -class LicenseExpressionValidator: - spdx_version: str - - def __init__(self, spdx_version): - self.spdx_version = spdx_version +def validate_license_expressions(license_expressions: Optional[ + Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]]) -> List[ValidationMessage]: + if license_expressions in [SpdxNoAssertion(), SpdxNone(), None]: + return [] - def validate_license_expressions(self, license_expressions: Optional[ - Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]]) -> List[ValidationMessage]: - if license_expressions in [SpdxNoAssertion(), SpdxNone(), None]: - return [] + error_messages = [] - error_messages = [] + for license_expression in license_expressions: + error_messages.extend(validate_license_expression(license_expression)) - for license_expression in license_expressions: - error_messages.extend(self.validate_license_expression(license_expression)) + return error_messages - return error_messages - def validate_license_expression(self, license_expression: LicenseExpression) -> List[ValidationMessage]: - # TODO: implement this once we have a better license expression model: https://github.com/spdx/tools-python/issues/374 - return [] +def validate_license_expression(license_expression: LicenseExpression) -> List[ValidationMessage]: + # TODO: implement this once we have a better license expression model: https://github.com/spdx/tools-python/issues/374 + return [] diff --git a/src/validation/package_validator.py b/src/validation/package_validator.py index 6d2cd9017..65cb4c3a2 100644 --- a/src/validation/package_validator.py +++ b/src/validation/package_validator.py @@ -3,111 +3,87 @@ from src.model.document import Document from src.model.package import Package from src.model.relationship import RelationshipType -from src.validation.checksum_validator import ChecksumValidator -from src.validation.external_package_ref_validator import ExternalPackageRefValidator -from src.validation.license_expression_validator import LicenseExpressionValidator -from src.validation.package_verification_code_validator import PackageVerificationCodeValidator +from src.validation.checksum_validator import validate_checksums +from src.validation.external_package_ref_validator import validate_external_package_refs +from src.validation.license_expression_validator import validate_license_expression, validate_license_expressions +from src.validation.package_verification_code_validator import validate_verification_code from src.validation.spdx_id_validators import validate_spdx_id from src.validation.uri_validators import validate_url, validate_download_location from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -class PackageValidator: - spdx_version: str - document: Document - license_expression_validator: LicenseExpressionValidator - - def __init__(self, spdx_version: str, document: Document): - self.spdx_version = spdx_version - self.document = document - self.license_expression_validator = LicenseExpressionValidator(spdx_version) - - def validate_packages(self, packages: List[Package]) -> List[ValidationMessage]: - validation_messages: List[ValidationMessage] = [] - for package in packages: - validation_messages.extend(self.validate_package(package)) - - return validation_messages - - def validate_package(self, package: Package) -> List[ValidationMessage]: - checksum_validator = ChecksumValidator(self.spdx_version, package.spdx_id) - verification_code_validator = PackageVerificationCodeValidator(self.spdx_version, package.spdx_id) - external_package_ref_validator = ExternalPackageRefValidator(self.spdx_version, package.spdx_id) - - validation_messages: List[ValidationMessage] = [] - context = ValidationContext(spdx_id=package.spdx_id, parent_id=self.document.creation_info.spdx_id, - element_type=SpdxElementType.PACKAGE, full_element=package) - - for message in validate_spdx_id(package.spdx_id, self.document): - validation_messages.append(ValidationMessage(message, context)) - - download_location = package.download_location - if isinstance(download_location, str): - for message in validate_download_location(download_location): - validation_messages.append(ValidationMessage("package download_location " + message, context)) - - homepage = package.homepage - if isinstance(homepage, str): - for message in validate_url(homepage): - validation_messages.append(ValidationMessage("homepage " + message, context)) - - if package.verification_code: - if not package.files_analyzed: - validation_messages.append( - ValidationMessage( - f"verification_code must be None if files_analyzed is False, but is: {package.verification_code}", - context)) - else: - validation_messages.extend( - verification_code_validator.validate_verification_code(package.verification_code) - ) - - # TODO: make test for this +def validate_packages(packages: List[Package], document: Document) -> List[ValidationMessage]: + validation_messages: List[ValidationMessage] = [] + for package in packages: + validation_messages.extend(validate_package(package, document)) + + return validation_messages + + +def validate_package(package: Package, document: Document) -> List[ValidationMessage]: + + validation_messages: List[ValidationMessage] = [] + context = ValidationContext(spdx_id=package.spdx_id, parent_id=document.creation_info.spdx_id, + element_type=SpdxElementType.PACKAGE, full_element=package) + + for message in validate_spdx_id(package.spdx_id, document): + validation_messages.append(ValidationMessage(message, context)) + + download_location = package.download_location + if isinstance(download_location, str): + for message in validate_download_location(download_location): + validation_messages.append(ValidationMessage("package download_location " + message, context)) + + homepage = package.homepage + if isinstance(homepage, str): + for message in validate_url(homepage): + validation_messages.append(ValidationMessage("homepage " + message, context)) + + if package.verification_code: + if not package.files_analyzed: + validation_messages.append( + ValidationMessage( + f"verification_code must be None if files_analyzed is False, but is: {package.verification_code}", + context)) + else: + validation_messages.extend(validate_verification_code(package.verification_code, package.spdx_id)) + + # TODO: make test for this + if not package.files_analyzed: + package_contains_relationships = [relationship for relationship in document.relationships if + relationship.relationship_type == RelationshipType.CONTAINS and relationship.spdx_element_id == package.spdx_id] + if package_contains_relationships: + validation_messages.append( + ValidationMessage( + f"package must contain no elements if files_analyzed is False, but found {package_contains_relationships}", + context) + ) + + contained_in_package_relationships = [relationship for relationship in document.relationships if + relationship.relationship_type == RelationshipType.CONTAINED_BY and relationship.related_spdx_element_id == package.spdx_id] + if contained_in_package_relationships: + validation_messages.append( + ValidationMessage( + f"package must contain no elements if files_analyzed is False, but found {package_contains_relationships}", + context) + ) + + validation_messages.extend(validate_checksums(package.checksums, package.spdx_id)) + + validation_messages.extend(validate_license_expression(package.license_concluded)) + + if package.license_info_from_files: if not package.files_analyzed: - package_contains_relationships = [relationship for relationship in self.document.relationships if - relationship.relationship_type == RelationshipType.CONTAINS and relationship.spdx_element_id == package.spdx_id] - if package_contains_relationships: - validation_messages.append( - ValidationMessage( - f"package must contain no elements if files_analyzed is False, but found {package_contains_relationships}", - context) - ) - - contained_in_package_relationships = [relationship for relationship in self.document.relationships if - relationship.relationship_type == RelationshipType.CONTAINED_BY and relationship.related_spdx_element_id == package.spdx_id] - if contained_in_package_relationships: - validation_messages.append( - ValidationMessage( - f"package must contain no elements if files_analyzed is False, but found {package_contains_relationships}", - context) - ) - - validation_messages.extend( - checksum_validator.validate_checksums(package.checksums) - ) - - validation_messages.extend( - self.license_expression_validator.validate_license_expression(package.license_concluded) - ) - - if package.license_info_from_files: - if not package.files_analyzed: - validation_messages.append( - ValidationMessage( - f"license_info_from_files must be None if files_analyzed is False, but is: {package.license_info_from_files}", - context) - ) - else: - validation_messages.extend( - self.license_expression_validator.validate_license_expressions(package.license_info_from_files) - ) - - validation_messages.extend( - self.license_expression_validator.validate_license_expression(package.license_declared) - ) - - validation_messages.extend( - external_package_ref_validator.validate_external_package_refs(package.external_references) - ) - - return validation_messages + validation_messages.append( + ValidationMessage( + f"license_info_from_files must be None if files_analyzed is False, but is: {package.license_info_from_files}", + context) + ) + else: + validation_messages.extend(validate_license_expressions(package.license_info_from_files)) + + validation_messages.extend(validate_license_expression(package.license_declared)) + + validation_messages.extend(validate_external_package_refs(package.external_references, package.spdx_id)) + + return validation_messages diff --git a/src/validation/package_verification_code_validator.py b/src/validation/package_verification_code_validator.py index 422cf8e19..bd2d7076e 100644 --- a/src/validation/package_verification_code_validator.py +++ b/src/validation/package_verification_code_validator.py @@ -5,33 +5,25 @@ from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -class PackageVerificationCodeValidator: - spdx_version: str - parent_id: str - - def __init__(self, spdx_version: str, parent_id: str): - self.spdx_version = spdx_version - self.parent_id = parent_id - - # TODO: make test for this - def validate_verification_code(self, verification_code: PackageVerificationCode) -> List[ValidationMessage]: - validation_messages: List[ValidationMessage] = [] - context = ValidationContext(parent_id=self.parent_id, element_type=SpdxElementType.PACKAGE_VERIFICATION_CODE, - full_element=verification_code) - - for file in verification_code.excluded_files: - if not file.startswith("./"): - validation_messages.append( - ValidationMessage( - f'file name must be a relative path to the file, starting with "./", but is: {file}', context) - ) - - value: str = verification_code.value - if not re.match("^[0-9a-f]{40}$", value): +# TODO: make test for this +def validate_verification_code(verification_code: PackageVerificationCode, parent_id: str) -> List[ValidationMessage]: + validation_messages: List[ValidationMessage] = [] + context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.PACKAGE_VERIFICATION_CODE, + full_element=verification_code) + + for file in verification_code.excluded_files: + if not file.startswith("./"): validation_messages.append( ValidationMessage( - f"value of verification_code must consist of 40 hexadecimal digits, but is: {value} (length: {len(value)} digits)", - context) + f'file name must be a relative path to the file, starting with "./", but is: {file}', context) ) - return validation_messages + value: str = verification_code.value + if not re.match("^[0-9a-f]{40}$", value): + validation_messages.append( + ValidationMessage( + f"value of verification_code must consist of 40 hexadecimal digits, but is: {value} (length: {len(value)} digits)", + context) + ) + + return validation_messages diff --git a/src/validation/relationship_validator.py b/src/validation/relationship_validator.py index d7a0ef88f..a1ce9ea77 100644 --- a/src/validation/relationship_validator.py +++ b/src/validation/relationship_validator.py @@ -6,38 +6,31 @@ from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -class RelationshipValidator: - spdx_version: str - document: Document +def validate_relationships(relationships: List[Relationship], document: Document, spdx_version: str) -> List[ValidationMessage]: + validation_messages = [] + for relationship in relationships: + validation_messages.extend(validate_relationship(relationship, document, spdx_version)) - def __init__(self, spdx_version: str, document: Document): - self.spdx_version = spdx_version - self.document = document + return validation_messages - def validate_relationships(self, relationships: List[Relationship]) -> List[ValidationMessage]: - validation_messages = [] - for relationship in relationships: - validation_messages.extend(self.validate_relationship(relationship)) - return validation_messages +def validate_relationship(relationship: Relationship, document: Document, spdx_version: str) -> List[ValidationMessage]: + validation_messages = [] + context = ValidationContext(element_type=SpdxElementType.RELATIONSHIP, + full_element=relationship) - def validate_relationship(self, relationship: Relationship) -> List[ValidationMessage]: - validation_messages = [] - context = ValidationContext(element_type=SpdxElementType.RELATIONSHIP, - full_element=relationship) + first_id: str = relationship.spdx_element_id + second_id: str = relationship.related_spdx_element_id + relationship_type: RelationshipType = relationship.relationship_type - first_id: str = relationship.spdx_element_id - second_id: str = relationship.related_spdx_element_id - relationship_type: RelationshipType = relationship.relationship_type + for spdx_id in [first_id, second_id]: + messages: List[str] = validate_spdx_id(spdx_id, document, check_document=True) + for message in messages: + validation_messages.append(ValidationMessage(message, context)) - for spdx_id in [first_id, second_id]: - messages: List[str] = validate_spdx_id(spdx_id, self.document, check_document=True) - for message in messages: - validation_messages.append(ValidationMessage(message, context)) + if spdx_version != "2.3": + if relationship_type == RelationshipType.SPECIFICATION_FOR or relationship_type == RelationshipType.REQUIREMENT_DESCRIPTION_FOR: + validation_messages.append( + ValidationMessage(f"{relationship_type} is not supported for SPDX versions below 2.3", context)) - if self.spdx_version != "2.3": - if relationship_type == RelationshipType.SPECIFICATION_FOR or relationship_type == RelationshipType.REQUIREMENT_DESCRIPTION_FOR: - validation_messages.append( - ValidationMessage(f"{relationship_type} is not supported for SPDX versions below 2.3", context)) - - return validation_messages + return validation_messages diff --git a/src/validation/snippet_validator.py b/src/validation/snippet_validator.py index 6dd87a84a..045320984 100644 --- a/src/validation/snippet_validator.py +++ b/src/validation/snippet_validator.py @@ -2,75 +2,63 @@ from src.model.document import Document from src.model.snippet import Snippet -from src.validation.license_expression_validator import LicenseExpressionValidator +from src.validation.license_expression_validator import validate_license_expression, \ + validate_license_expressions from src.validation.spdx_id_validators import validate_spdx_id from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -class SnippetValidator: - spdx_version: str - document: Document - license_expression_validator: LicenseExpressionValidator +def validate_snippets(snippets: List[Snippet], document: Document) -> List[ValidationMessage]: + validation_messages = [] + for snippet in snippets: + validation_messages.extend(validate_snippet(snippet, document)) - def __init__(self, spdx_version: str, document: Document): - self.spdx_version = spdx_version - self.document = document - self.license_expression_validator = LicenseExpressionValidator(spdx_version) + return validation_messages - def validate_snippets(self, snippets: List[Snippet]) -> List[ValidationMessage]: - validation_messages = [] - for snippet in snippets: - validation_messages.extend(self.validate_snippet(snippet)) - return validation_messages +def validate_snippet(snippet: Snippet, document: Document) -> List[ValidationMessage]: + validation_messages = [] + context = ValidationContext(spdx_id=snippet.spdx_id, element_type=SpdxElementType.SNIPPET, full_element=snippet) - def validate_snippet(self, snippet: Snippet) -> List[ValidationMessage]: - validation_messages = [] - context = ValidationContext(spdx_id=snippet.spdx_id, element_type=SpdxElementType.SNIPPET, full_element=snippet) + messages: List[str] = validate_spdx_id(snippet.spdx_id, document) + for message in messages: + validation_messages.append(ValidationMessage(message, context)) - messages: List[str] = validate_spdx_id(snippet.spdx_id, self.document) - for message in messages: - validation_messages.append(ValidationMessage(message, context)) + messages: List[str] = validate_spdx_id(snippet.file_spdx_id, document, check_files=True) + for message in messages: + validation_messages.append(ValidationMessage(message, context)) - messages: List[str] = validate_spdx_id(snippet.file_spdx_id, self.document, check_files=True) - for message in messages: - validation_messages.append(ValidationMessage(message, context)) + if snippet.byte_range[0] < 1: + validation_messages.append( + ValidationMessage( + f"byte_range values must be greater than or equal to 1, but is: {snippet.byte_range}", + context) + ) + + if snippet.byte_range[0] > snippet.byte_range[1]: + validation_messages.append( + ValidationMessage( + f"the first value of byte_range must be less than or equal to the second, but is: {snippet.byte_range}", + context) + ) - if snippet.byte_range[0] < 1: + if snippet.line_range: + if snippet.line_range[0] < 1: validation_messages.append( ValidationMessage( - f"byte_range values must be greater than or equal to 1, but is: {snippet.byte_range}", + f"line_range values must be greater than or equal to 1, but is: {snippet.line_range}", context) ) - if snippet.byte_range[0] > snippet.byte_range[1]: + if snippet.line_range[0] > snippet.line_range[1]: validation_messages.append( ValidationMessage( - f"the first value of byte_range must be less than or equal to the second, but is: {snippet.byte_range}", + f"the first value of line_range must be less than or equal to the second, but is: {snippet.line_range}", context) ) - if snippet.line_range: - if snippet.line_range[0] < 1: - validation_messages.append( - ValidationMessage( - f"line_range values must be greater than or equal to 1, but is: {snippet.line_range}", - context) - ) - - if snippet.line_range[0] > snippet.line_range[1]: - validation_messages.append( - ValidationMessage( - f"the first value of line_range must be less than or equal to the second, but is: {snippet.line_range}", - context) - ) + validation_messages.extend(validate_license_expression(snippet.concluded_license)) - validation_messages.extend( - self.license_expression_validator.validate_license_expression(snippet.concluded_license) - ) - - validation_messages.extend( - self.license_expression_validator.validate_license_expressions(snippet.license_info_in_snippet) - ) + validation_messages.extend(validate_license_expressions(snippet.license_info_in_snippet)) - return validation_messages + return validation_messages diff --git a/tests/validation/test_actor_validator.py b/tests/validation/test_actor_validator.py index 6eee0151b..29a37e825 100644 --- a/tests/validation/test_actor_validator.py +++ b/tests/validation/test_actor_validator.py @@ -3,16 +3,14 @@ import pytest from src.model.actor import ActorType, Actor -from src.validation.actor_validator import ActorValidator +from src.validation.actor_validator import validate_actor from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.valid_defaults import get_actor def test_valid_actor_person(): - actor_validator = ActorValidator("2.3", "SPDXRef-DOCUMENT") - actor = Actor(ActorType.PERSON, "person name", "mail@mail.com") - validation_messages: List[ValidationMessage] = actor_validator.validate_actor(actor) + validation_messages: List[ValidationMessage] = validate_actor(actor, "SPDXRef-DOCUMENT") assert validation_messages == [] @@ -23,8 +21,7 @@ def test_valid_actor_person(): ]) def test_invalid_actor(actor, expected_message): parent_id = "SPDXRef-DOCUMENT" - actor_validator = ActorValidator("2.3", parent_id) - validation_messages: List[ValidationMessage] = actor_validator.validate_actor(actor) + validation_messages: List[ValidationMessage] = validate_actor(actor, parent_id) expected = ValidationMessage(expected_message, ValidationContext(parent_id=parent_id, element_type=SpdxElementType.ACTOR, diff --git a/tests/validation/test_annotation_validator.py b/tests/validation/test_annotation_validator.py index 1367bd773..8071f6790 100644 --- a/tests/validation/test_annotation_validator.py +++ b/tests/validation/test_annotation_validator.py @@ -5,17 +5,16 @@ from src.model.annotation import Annotation, AnnotationType from src.model.document import Document -from src.validation.annotation_validator import AnnotationValidator +from src.validation.annotation_validator import validate_annotation from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.valid_defaults import get_actor, get_annotation, get_document, get_file def test_valid_annotation(): document: Document = get_document(files=[get_file(spdx_id="SPDXRef-File")]) - annotation_validator = AnnotationValidator("2.3", document) annotation = Annotation("SPDXRef-File", AnnotationType.OTHER, get_actor(), datetime(2022, 1, 1), "comment") - validation_messages: List[ValidationMessage] = annotation_validator.validate_annotation(annotation) + validation_messages: List[ValidationMessage] = validate_annotation(annotation, document) assert validation_messages == [] @@ -27,8 +26,7 @@ def test_valid_annotation(): def test_invalid_annotation(annotation_id, file_id, expected_message): annotation: Annotation = get_annotation(spdx_id=annotation_id) document: Document = get_document(files=[get_file(spdx_id=file_id)]) - annotation_validator = AnnotationValidator("2.3", document) - validation_messages: List[ValidationMessage] = annotation_validator.validate_annotation(annotation) + validation_messages: List[ValidationMessage] = validate_annotation(annotation, document) expected = ValidationMessage(expected_message, ValidationContext(element_type=SpdxElementType.ANNOTATION, diff --git a/tests/validation/test_checksum_validator.py b/tests/validation/test_checksum_validator.py index 8ebc0d2b3..461d14305 100644 --- a/tests/validation/test_checksum_validator.py +++ b/tests/validation/test_checksum_validator.py @@ -3,7 +3,7 @@ import pytest from src.model.checksum import Checksum, ChecksumAlgorithm -from src.validation.checksum_validator import ChecksumValidator +from src.validation.checksum_validator import validate_checksum from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType @@ -38,9 +38,7 @@ "af1eec2a1b18886c3f3cc244349d91d8d4c41ce30a517d6ce9d79c8c17bb4b660d7f61beb7018b3924c6b8f96549fa39"), Checksum(ChecksumAlgorithm.ADLER32, "02ec0130")]) def test_valid_checksum(checksum): - checksum_validator = ChecksumValidator("2.3", "parent_id") - - validation_messages: List[ValidationMessage] = checksum_validator.validate_checksum(checksum) + validation_messages: List[ValidationMessage] = validate_checksum(checksum, "parent_id") assert validation_messages == [] @@ -84,8 +82,7 @@ def test_valid_checksum(checksum): ]) def test_invalid_checksum(checksum, expected_message): parent_id = "parent_id" - checksum_validator = ChecksumValidator("2.3", parent_id) - validation_messages: List[ValidationMessage] = checksum_validator.validate_checksum(checksum) + validation_messages: List[ValidationMessage] = validate_checksum(checksum, parent_id) expected = ValidationMessage(expected_message, ValidationContext(parent_id=parent_id, element_type=SpdxElementType.CHECKSUM, diff --git a/tests/validation/test_creation_info_validator.py b/tests/validation/test_creation_info_validator.py index 2adac8f5d..58ffb4132 100644 --- a/tests/validation/test_creation_info_validator.py +++ b/tests/validation/test_creation_info_validator.py @@ -5,19 +5,17 @@ from src.model.document import CreationInfo from src.model.version import Version -from src.validation.creation_info_validator import CreationInfoValidator +from src.validation.creation_info_validator import validate_creation_info from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.valid_defaults import get_actor, get_external_document_ref, get_creation_info def test_valid_creation_info(): - creation_info_validator = CreationInfoValidator("2.3") - creation_info = CreationInfo("SPDX-2.3", "SPDXRef-DOCUMENT", "document name", "https://some.uri", [get_actor(), get_actor()], datetime(2022, 1, 1), "creator_comment", "CC0-1.0", [get_external_document_ref(), get_external_document_ref()], Version(6, 3), "doc_comment") - validation_messages: List[ValidationMessage] = creation_info_validator.validate_creation_info(creation_info) + validation_messages: List[ValidationMessage] = validate_creation_info(creation_info) assert validation_messages == [] @@ -34,10 +32,8 @@ def test_valid_creation_info(): "document_namespace must be a valid URI specified in RFC-3986, but is: some_namespace"), ]) def test_invalid_creation_info(creation_info_input, expected_message, spdx_id): - creation_info_validator = CreationInfoValidator("2.3") - validation_messages: List[ValidationMessage] = creation_info_validator.validate_creation_info(creation_info_input) + validation_messages: List[ValidationMessage] = validate_creation_info(creation_info_input) - expected = ValidationMessage(expected_message, - ValidationContext(spdx_id, None, SpdxElementType.DOCUMENT)) + expected = ValidationMessage(expected_message, ValidationContext(spdx_id, None, SpdxElementType.DOCUMENT)) assert validation_messages == [expected] diff --git a/tests/validation/test_document_validator.py b/tests/validation/test_document_validator.py index 7a10f7e6a..fc8fec5ca 100644 --- a/tests/validation/test_document_validator.py +++ b/tests/validation/test_document_validator.py @@ -1,20 +1,18 @@ from typing import List from src.model.document import Document -from src.validation.document_validator import DocumentValidator +from src.validation.document_validator import validate_full_spdx_document from src.validation.validation_message import ValidationMessage from tests.valid_defaults import get_creation_info, get_package, get_file, get_snippet, get_annotation, \ get_relationship, get_extracted_licensing_info def test_valid_document(): - document_validator = DocumentValidator("2.3") - document = Document(get_creation_info(), [get_package(), get_package()], [get_file(), get_file()], [get_snippet(), get_snippet()], [get_annotation(), get_annotation()], [get_relationship(), get_relationship()], [get_extracted_licensing_info(), get_extracted_licensing_info()]) - validation_messages: List[ValidationMessage] = document_validator.validate_full_spdx_document(document) + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, "2.3") assert validation_messages == [] diff --git a/tests/validation/test_external_document_ref_validator.py b/tests/validation/test_external_document_ref_validator.py index 4bf7993ef..95976c00c 100644 --- a/tests/validation/test_external_document_ref_validator.py +++ b/tests/validation/test_external_document_ref_validator.py @@ -3,16 +3,14 @@ import pytest from src.model.external_document_ref import ExternalDocumentRef -from src.validation.external_document_ref_validator import ExternalDocumentRefValidator +from src.validation.external_document_ref_validator import validate_external_document_ref from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.valid_defaults import get_checksum, get_external_document_ref def test_valid_external_document_ref(): - external_document_ref_validator = ExternalDocumentRefValidator("2.3", "parent_id") external_document_ref = ExternalDocumentRef("DocumentRef-id", "http://some.uri", get_checksum()) - validation_messages: List[ValidationMessage] = external_document_ref_validator.validate_external_document_ref( - external_document_ref) + validation_messages: List[ValidationMessage] = validate_external_document_ref(external_document_ref, "parent_id") assert validation_messages == [] diff --git a/tests/validation/test_external_package_ref_validator.py b/tests/validation/test_external_package_ref_validator.py index 95d88557a..09b478f7d 100644 --- a/tests/validation/test_external_package_ref_validator.py +++ b/tests/validation/test_external_package_ref_validator.py @@ -3,18 +3,16 @@ import pytest from src.model.package import ExternalPackageRef, ExternalPackageRefCategory -from src.validation.external_package_ref_validator import ExternalPackageRefValidator +from src.validation.external_package_ref_validator import validate_external_package_ref from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.valid_defaults import get_external_package_ref def test_valid_external_package_ref(): - external_package_ref_validator = ExternalPackageRefValidator("2.3", "SPDXRef-Package") external_package_ref = ExternalPackageRef(ExternalPackageRefCategory.OTHER, "swh", "swh:1:cnt:94a9ed024d3859793618152ea559a168bbcbb5e2", "comment") - validation_messages: List[ValidationMessage] = external_package_ref_validator.validate_external_package_ref( - external_package_ref) + validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, "parent_id") assert validation_messages == [] @@ -26,9 +24,7 @@ def test_valid_external_package_ref(): @pytest.mark.skip("add tests once external package ref validation is implemented: https://github.com/spdx/tools-python/issues/373") def test_invalid_external_package_ref(external_package_ref, expected_message): parent_id = "SPDXRef-Package" - external_package_ref_validator = ExternalPackageRefValidator("2.3", parent_id) - validation_messages: List[ValidationMessage] = external_package_ref_validator.validate_external_package_ref( - external_package_ref) + validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, parent_id) expected = ValidationMessage(expected_message, ValidationContext(parent_id=parent_id, diff --git a/tests/validation/test_extracted_licensing_info_validator.py b/tests/validation/test_extracted_licensing_info_validator.py index 5201f05e3..eae417d2a 100644 --- a/tests/validation/test_extracted_licensing_info_validator.py +++ b/tests/validation/test_extracted_licensing_info_validator.py @@ -3,18 +3,15 @@ import pytest from src.model.extracted_licensing_info import ExtractedLicensingInfo -from src.validation.extracted_licensing_info_validator import ExtractedLicensingInfoValidator +from src.validation.extracted_licensing_info_validator import validate_extracted_licensing_info from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.valid_defaults import get_extracted_licensing_info def test_valid_extracted_licensing_info(): - extracted_licensing_info_validator = ExtractedLicensingInfoValidator("2.3") - extracted_licensing_info = ExtractedLicensingInfo("LicenseRef-1", "extracted text", "license name", ["http://some.url"], "comment") - validation_messages: List[ValidationMessage] = extracted_licensing_info_validator.validate_extracted_licensing_info( - extracted_licensing_info) + validation_messages: List[ValidationMessage] = validate_extracted_licensing_info(extracted_licensing_info) assert validation_messages == [] @@ -27,9 +24,7 @@ def test_valid_extracted_licensing_info(): 'cross_reference must be a valid URL, but is: invalid_url') ]) def test_invalid_extracted_licensing_info(extracted_licensing_info, expected_message): - extracted_licensing_info_validator = ExtractedLicensingInfoValidator("2.3") - validation_messages: List[ValidationMessage] = extracted_licensing_info_validator.validate_extracted_licensing_info( - extracted_licensing_info) + validation_messages: List[ValidationMessage] = validate_extracted_licensing_info(extracted_licensing_info) expected = ValidationMessage(expected_message, ValidationContext(element_type=SpdxElementType.EXTRACTED_LICENSING_INFO, diff --git a/tests/validation/test_file_validator.py b/tests/validation/test_file_validator.py index b3bb86cae..329f90387 100644 --- a/tests/validation/test_file_validator.py +++ b/tests/validation/test_file_validator.py @@ -6,18 +6,16 @@ from src.model.file import File, FileType from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone -from src.validation.file_validator import FileValidator +from src.validation.file_validator import validate_file from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.valid_defaults import get_checksum, get_file, get_document def test_valid_file(): - file_validator = FileValidator("2.3", get_document()) - file = File("./file/name.py", "SPDXRef-File", [get_checksum()], [FileType.OTHER, FileType.SPDX], SpdxNone(), SpdxNoAssertion(), "comment on license", "copyright", "comment", "notice", ["contributor"], ["attribution"]) - validation_messages: List[ValidationMessage] = file_validator.validate_file(file) + validation_messages: List[ValidationMessage] = validate_file(file, get_document()) assert validation_messages == [] @@ -30,8 +28,7 @@ def test_valid_file(): f'checksums must contain a SHA1 algorithm checksum, but only contains: []') ]) def test_invalid_file(file_input, spdx_id, expected_message): - file_validator = FileValidator("2.3", get_document()) - validation_messages: List[ValidationMessage] = file_validator.validate_file(file_input) + validation_messages: List[ValidationMessage] = validate_file(file_input, get_document()) expected = ValidationMessage(expected_message, ValidationContext(spdx_id=spdx_id, diff --git a/tests/validation/test_license_expression_validator.py b/tests/validation/test_license_expression_validator.py index 51cde45bc..d917be7d5 100644 --- a/tests/validation/test_license_expression_validator.py +++ b/tests/validation/test_license_expression_validator.py @@ -1,15 +1,12 @@ from typing import List from src.model.license_expression import LicenseExpression -from src.validation.license_expression_validator import LicenseExpressionValidator +from src.validation.license_expression_validator import validate_license_expression from src.validation.validation_message import ValidationMessage def test_valid_license_expression(): - license_expression_validator = LicenseExpressionValidator("2.3") - license_expression = LicenseExpression("LicenseRef-1") - validation_messages: List[ValidationMessage] = license_expression_validator.validate_license_expression( - license_expression) + validation_messages: List[ValidationMessage] = validate_license_expression(license_expression) assert validation_messages == [] diff --git a/tests/validation/test_package_validator.py b/tests/validation/test_package_validator.py index 9a4ec8e19..85b4d3e45 100644 --- a/tests/validation/test_package_validator.py +++ b/tests/validation/test_package_validator.py @@ -7,22 +7,20 @@ from src.model.package import Package, PackagePurpose from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone -from src.validation.package_validator import PackageValidator +from src.validation.package_validator import validate_package from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.valid_defaults import get_checksum, get_external_package_ref, get_actor, get_package_verification_code, \ get_package, get_document def test_valid_package(): - package_validator = PackageValidator("2.3", get_document()) - package = Package("SPDXRef-Package", "package name", "www.download.com", "version", "file_name", SpdxNoAssertion(), get_actor(), True, get_package_verification_code(), [get_checksum()], "https://homepage.com", "source_info", None, [LicenseExpression("expression")], SpdxNone(), "comment on license", "copyright", "summary", "description", "comment", [get_external_package_ref()], ["text"], PackagePurpose.OTHER, datetime(2022, 1, 1), None, None) - validation_messages: List[ValidationMessage] = package_validator.validate_package(package) + validation_messages: List[ValidationMessage] = validate_package(package, get_document()) assert validation_messages == [] @@ -40,8 +38,7 @@ def test_valid_package(): 'license_info_from_files must be None if files_analyzed is False, but is: [LicenseExpression(expression_string=\'some_license\')]') ]) def test_invalid_package(package_input, expected_message): - package_validator = PackageValidator("2.3", get_document()) - validation_messages: List[ValidationMessage] = package_validator.validate_package(package_input) + validation_messages: List[ValidationMessage] = validate_package(package_input, get_document()) expected = ValidationMessage(expected_message, ValidationContext(spdx_id=package_input.spdx_id, parent_id="SPDXRef-DOCUMENT", diff --git a/tests/validation/test_relationship_validator.py b/tests/validation/test_relationship_validator.py index 7388e9ee0..d53d9fee3 100644 --- a/tests/validation/test_relationship_validator.py +++ b/tests/validation/test_relationship_validator.py @@ -4,17 +4,16 @@ from src.model.document import Document from src.model.relationship import Relationship, RelationshipType -from src.validation.relationship_validator import RelationshipValidator +from src.validation.relationship_validator import validate_relationship from src.validation.validation_message import ValidationMessage, SpdxElementType, ValidationContext from tests.valid_defaults import get_document, get_package, get_relationship, get_file def test_valid_relationship(): document: Document = get_document(packages=[get_package(spdx_id="SPDXRef-Package")]) - relationship_validator = RelationshipValidator("2.3", document) relationship = Relationship("SPDXRef-DOCUMENT", RelationshipType.AMENDS, "SPDXRef-Package", comment="comment") - validation_messages: List[ValidationMessage] = relationship_validator.validate_relationship(relationship) + validation_messages: List[ValidationMessage] = validate_relationship(relationship, document, "2.3") assert validation_messages == [] @@ -29,8 +28,7 @@ def test_unknown_spdx_id(spdx_element_id, related_spdx_element_id, expected_mess relationship: Relationship = get_relationship(spdx_element_id=spdx_element_id, related_spdx_element_id=related_spdx_element_id) document: Document = get_document(files=[get_file(spdx_id="SPDXRef-File")]) - relationship_validator = RelationshipValidator("2.3", document) - validation_messages: List[ValidationMessage] = relationship_validator.validate_relationship(relationship) + validation_messages: List[ValidationMessage] = validate_relationship(relationship, document, "2.3") expected = ValidationMessage(expected_message, ValidationContext(element_type=SpdxElementType.RELATIONSHIP, @@ -47,9 +45,8 @@ def test_unknown_spdx_id(spdx_element_id, related_spdx_element_id, expected_mess "RelationshipType.REQUIREMENT_DESCRIPTION_FOR is not supported for SPDX versions below 2.3")]) def test_v2_3_only_types(relationship, expected_message): document: Document = get_document(packages=[get_package(spdx_id="SPDXRef-Package")]) - relationship_validator = RelationshipValidator("2.2", document) - validation_message: List[ValidationMessage] = relationship_validator.validate_relationship(relationship) + validation_message: List[ValidationMessage] = validate_relationship(relationship, document, "2.2") expected = [ValidationMessage(expected_message, ValidationContext(element_type=SpdxElementType.RELATIONSHIP, diff --git a/tests/validation/test_snippet_validator.py b/tests/validation/test_snippet_validator.py index 1b3b1dbc7..2ab8f97d7 100644 --- a/tests/validation/test_snippet_validator.py +++ b/tests/validation/test_snippet_validator.py @@ -6,7 +6,7 @@ from src.model.license_expression import LicenseExpression from src.model.snippet import Snippet from src.model.spdx_no_assertion import SpdxNoAssertion -from src.validation.snippet_validator import SnippetValidator +from src.validation.snippet_validator import validate_snippet from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.valid_defaults import get_snippet, get_document, get_file @@ -14,12 +14,10 @@ def test_valid_snippet(): document: Document = get_document(files=[get_file(spdx_id="SPDXRef-File")]) - snippet_validator = SnippetValidator("2.3", document) - snippet = Snippet("SPDXRef-Snippet", "SPDXRef-File", (200, 400), (20, 40), LicenseExpression("some_license"), SpdxNoAssertion(), "comment on license", "copyright", "comment", "name", ["attribution"]) - validation_messages: List[ValidationMessage] = snippet_validator.validate_snippet(snippet) + validation_messages: List[ValidationMessage] = validate_snippet(snippet, document) assert validation_messages == [] @@ -35,9 +33,7 @@ def test_valid_snippet(): "the first value of line_range must be less than or equal to the second, but is: (45, 23)") ]) def test_invalid_ranges(snippet_input, expected_message): - snippet_validator = SnippetValidator("2.3", get_document(files=[get_file()])) - - validation_messages: List[ValidationMessage] = snippet_validator.validate_snippet(snippet_input) + validation_messages: List[ValidationMessage] = validate_snippet(snippet_input, get_document(files=[get_file()])) expected = ValidationMessage(expected_message, ValidationContext(spdx_id=snippet_input.spdx_id, element_type=SpdxElementType.SNIPPET, From 9db4a4e938dbc4e9d06df6b5a553aa478b51b154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 22 Dec 2022 11:06:27 +0100 Subject: [PATCH 033/362] [review] add methods to validate packages, files and snippets individually MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/validation/file_validator.py | 14 ++++-- src/validation/package_validator.py | 51 ++++++++++++---------- src/validation/snippet_validator.py | 14 ++++-- tests/validation/test_file_validator.py | 6 +-- tests/validation/test_package_validator.py | 6 +-- tests/validation/test_snippet_validator.py | 7 +-- 6 files changed, 61 insertions(+), 37 deletions(-) diff --git a/src/validation/file_validator.py b/src/validation/file_validator.py index 97c2d307f..60216adab 100644 --- a/src/validation/file_validator.py +++ b/src/validation/file_validator.py @@ -12,18 +12,26 @@ def validate_files(files: List[File], document: Document) -> List[ValidationMessage]: validation_messages = [] for file in files: - validation_messages.extend(validate_file(file, document)) + validation_messages.extend(validate_file_within_document(file, document)) return validation_messages -def validate_file(file: File, document: Document) -> List[ValidationMessage]: - validation_messages = [] +def validate_file_within_document(file: File, document: Document) -> List[ValidationMessage]: + validation_messages: List[ValidationMessage] = [] context = ValidationContext(spdx_id=file.spdx_id, element_type=SpdxElementType.FILE, full_element=file) for message in validate_spdx_id(file.spdx_id, document): validation_messages.append(ValidationMessage(message, context)) + validation_messages.extend(validate_file(file, context)) + + return validation_messages + + +def validate_file(file: File, context: ValidationContext) -> List[ValidationMessage]: + validation_messages = [] + if not file.name.startswith("./"): validation_messages.append( ValidationMessage( diff --git a/src/validation/package_validator.py b/src/validation/package_validator.py index 65cb4c3a2..544b3a03c 100644 --- a/src/validation/package_validator.py +++ b/src/validation/package_validator.py @@ -15,13 +15,12 @@ def validate_packages(packages: List[Package], document: Document) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] for package in packages: - validation_messages.extend(validate_package(package, document)) + validation_messages.extend(validate_package_within_document(package, document)) return validation_messages -def validate_package(package: Package, document: Document) -> List[ValidationMessage]: - +def validate_package_within_document(package: Package, document: Document) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] context = ValidationContext(spdx_id=package.spdx_id, parent_id=document.creation_info.spdx_id, element_type=SpdxElementType.PACKAGE, full_element=package) @@ -29,25 +28,6 @@ def validate_package(package: Package, document: Document) -> List[ValidationMes for message in validate_spdx_id(package.spdx_id, document): validation_messages.append(ValidationMessage(message, context)) - download_location = package.download_location - if isinstance(download_location, str): - for message in validate_download_location(download_location): - validation_messages.append(ValidationMessage("package download_location " + message, context)) - - homepage = package.homepage - if isinstance(homepage, str): - for message in validate_url(homepage): - validation_messages.append(ValidationMessage("homepage " + message, context)) - - if package.verification_code: - if not package.files_analyzed: - validation_messages.append( - ValidationMessage( - f"verification_code must be None if files_analyzed is False, but is: {package.verification_code}", - context)) - else: - validation_messages.extend(validate_verification_code(package.verification_code, package.spdx_id)) - # TODO: make test for this if not package.files_analyzed: package_contains_relationships = [relationship for relationship in document.relationships if @@ -68,6 +48,33 @@ def validate_package(package: Package, document: Document) -> List[ValidationMes context) ) + validation_messages.extend(validate_package(package, context)) + + return validation_messages + + +def validate_package(package: Package, context: ValidationContext) -> List[ValidationMessage]: + validation_messages: List[ValidationMessage] = [] + + download_location = package.download_location + if isinstance(download_location, str): + for message in validate_download_location(download_location): + validation_messages.append(ValidationMessage("package download_location " + message, context)) + + homepage = package.homepage + if isinstance(homepage, str): + for message in validate_url(homepage): + validation_messages.append(ValidationMessage("homepage " + message, context)) + + if package.verification_code: + if not package.files_analyzed: + validation_messages.append( + ValidationMessage( + f"verification_code must be None if files_analyzed is False, but is: {package.verification_code}", + context)) + else: + validation_messages.extend(validate_verification_code(package.verification_code, package.spdx_id)) + validation_messages.extend(validate_checksums(package.checksums, package.spdx_id)) validation_messages.extend(validate_license_expression(package.license_concluded)) diff --git a/src/validation/snippet_validator.py b/src/validation/snippet_validator.py index 045320984..9b20199b9 100644 --- a/src/validation/snippet_validator.py +++ b/src/validation/snippet_validator.py @@ -11,13 +11,13 @@ def validate_snippets(snippets: List[Snippet], document: Document) -> List[ValidationMessage]: validation_messages = [] for snippet in snippets: - validation_messages.extend(validate_snippet(snippet, document)) + validation_messages.extend(validate_snippet_within_document(snippet, document)) return validation_messages -def validate_snippet(snippet: Snippet, document: Document) -> List[ValidationMessage]: - validation_messages = [] +def validate_snippet_within_document(snippet: Snippet, document: Document) -> List[ValidationMessage]: + validation_messages: List[ValidationMessage] = [] context = ValidationContext(spdx_id=snippet.spdx_id, element_type=SpdxElementType.SNIPPET, full_element=snippet) messages: List[str] = validate_spdx_id(snippet.spdx_id, document) @@ -28,6 +28,14 @@ def validate_snippet(snippet: Snippet, document: Document) -> List[ValidationMes for message in messages: validation_messages.append(ValidationMessage(message, context)) + validation_messages.extend(validate_snippet(snippet, context)) + + return validation_messages + + +def validate_snippet(snippet: Snippet, context: ValidationContext) -> List[ValidationMessage]: + validation_messages = [] + if snippet.byte_range[0] < 1: validation_messages.append( ValidationMessage( diff --git a/tests/validation/test_file_validator.py b/tests/validation/test_file_validator.py index 329f90387..22c1bb76a 100644 --- a/tests/validation/test_file_validator.py +++ b/tests/validation/test_file_validator.py @@ -6,7 +6,7 @@ from src.model.file import File, FileType from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone -from src.validation.file_validator import validate_file +from src.validation.file_validator import validate_file_within_document from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.valid_defaults import get_checksum, get_file, get_document @@ -15,7 +15,7 @@ def test_valid_file(): file = File("./file/name.py", "SPDXRef-File", [get_checksum()], [FileType.OTHER, FileType.SPDX], SpdxNone(), SpdxNoAssertion(), "comment on license", "copyright", "comment", "notice", ["contributor"], ["attribution"]) - validation_messages: List[ValidationMessage] = validate_file(file, get_document()) + validation_messages: List[ValidationMessage] = validate_file_within_document(file, get_document()) assert validation_messages == [] @@ -28,7 +28,7 @@ def test_valid_file(): f'checksums must contain a SHA1 algorithm checksum, but only contains: []') ]) def test_invalid_file(file_input, spdx_id, expected_message): - validation_messages: List[ValidationMessage] = validate_file(file_input, get_document()) + validation_messages: List[ValidationMessage] = validate_file_within_document(file_input, get_document()) expected = ValidationMessage(expected_message, ValidationContext(spdx_id=spdx_id, diff --git a/tests/validation/test_package_validator.py b/tests/validation/test_package_validator.py index 85b4d3e45..3b37317c8 100644 --- a/tests/validation/test_package_validator.py +++ b/tests/validation/test_package_validator.py @@ -7,7 +7,7 @@ from src.model.package import Package, PackagePurpose from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone -from src.validation.package_validator import validate_package +from src.validation.package_validator import validate_package_within_document from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.valid_defaults import get_checksum, get_external_package_ref, get_actor, get_package_verification_code, \ get_package, get_document @@ -20,7 +20,7 @@ def test_valid_package(): [LicenseExpression("expression")], SpdxNone(), "comment on license", "copyright", "summary", "description", "comment", [get_external_package_ref()], ["text"], PackagePurpose.OTHER, datetime(2022, 1, 1), None, None) - validation_messages: List[ValidationMessage] = validate_package(package, get_document()) + validation_messages: List[ValidationMessage] = validate_package_within_document(package, get_document()) assert validation_messages == [] @@ -38,7 +38,7 @@ def test_valid_package(): 'license_info_from_files must be None if files_analyzed is False, but is: [LicenseExpression(expression_string=\'some_license\')]') ]) def test_invalid_package(package_input, expected_message): - validation_messages: List[ValidationMessage] = validate_package(package_input, get_document()) + validation_messages: List[ValidationMessage] = validate_package_within_document(package_input, get_document()) expected = ValidationMessage(expected_message, ValidationContext(spdx_id=package_input.spdx_id, parent_id="SPDXRef-DOCUMENT", diff --git a/tests/validation/test_snippet_validator.py b/tests/validation/test_snippet_validator.py index 2ab8f97d7..b94a148b4 100644 --- a/tests/validation/test_snippet_validator.py +++ b/tests/validation/test_snippet_validator.py @@ -6,7 +6,7 @@ from src.model.license_expression import LicenseExpression from src.model.snippet import Snippet from src.model.spdx_no_assertion import SpdxNoAssertion -from src.validation.snippet_validator import validate_snippet +from src.validation.snippet_validator import validate_snippet_within_document from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.valid_defaults import get_snippet, get_document, get_file @@ -17,7 +17,7 @@ def test_valid_snippet(): snippet = Snippet("SPDXRef-Snippet", "SPDXRef-File", (200, 400), (20, 40), LicenseExpression("some_license"), SpdxNoAssertion(), "comment on license", "copyright", "comment", "name", ["attribution"]) - validation_messages: List[ValidationMessage] = validate_snippet(snippet, document) + validation_messages: List[ValidationMessage] = validate_snippet_within_document(snippet, document) assert validation_messages == [] @@ -33,7 +33,8 @@ def test_valid_snippet(): "the first value of line_range must be less than or equal to the second, but is: (45, 23)") ]) def test_invalid_ranges(snippet_input, expected_message): - validation_messages: List[ValidationMessage] = validate_snippet(snippet_input, get_document(files=[get_file()])) + validation_messages: List[ValidationMessage] = validate_snippet_within_document(snippet_input, + get_document(files=[get_file()])) expected = ValidationMessage(expected_message, ValidationContext(spdx_id=snippet_input.spdx_id, element_type=SpdxElementType.SNIPPET, From ce4035ad5adb148a21d5f34db3269542f8d4fdb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 28 Dec 2022 10:46:22 +0100 Subject: [PATCH 034/362] [review] make Document and ValidationContext optional in validation calls of packages, files and snippets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/validation/file_validator.py | 19 +++++++++++++------ src/validation/package_validator.py | 16 +++++++++++----- src/validation/snippet_validator.py | 19 +++++++++++++------ tests/validation/test_file_validator.py | 1 + tests/validation/test_snippet_validator.py | 4 +++- 5 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/validation/file_validator.py b/src/validation/file_validator.py index 60216adab..77c45c59e 100644 --- a/src/validation/file_validator.py +++ b/src/validation/file_validator.py @@ -1,4 +1,4 @@ -from typing import List +from typing import List, Optional from src.model.checksum import ChecksumAlgorithm from src.model.document import Document @@ -9,17 +9,22 @@ from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_files(files: List[File], document: Document) -> List[ValidationMessage]: +def validate_files(files: List[File], document: Optional[Document] = None) -> List[ValidationMessage]: validation_messages = [] - for file in files: - validation_messages.extend(validate_file_within_document(file, document)) + if document: + for file in files: + validation_messages.extend(validate_file_within_document(file, document)) + else: + for file in files: + validation_messages.extend(validate_file(file)) return validation_messages def validate_file_within_document(file: File, document: Document) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] - context = ValidationContext(spdx_id=file.spdx_id, element_type=SpdxElementType.FILE, full_element=file) + context = ValidationContext(spdx_id=file.spdx_id, parent_id=document.creation_info.spdx_id, + element_type=SpdxElementType.FILE, full_element=file) for message in validate_spdx_id(file.spdx_id, document): validation_messages.append(ValidationMessage(message, context)) @@ -29,8 +34,10 @@ def validate_file_within_document(file: File, document: Document) -> List[Valida return validation_messages -def validate_file(file: File, context: ValidationContext) -> List[ValidationMessage]: +def validate_file(file: File, context: Optional[ValidationContext] = None) -> List[ValidationMessage]: validation_messages = [] + if not context: + context = ValidationContext(spdx_id=file.spdx_id, element_type=SpdxElementType.FILE, full_element=file) if not file.name.startswith("./"): validation_messages.append( diff --git a/src/validation/package_validator.py b/src/validation/package_validator.py index 544b3a03c..8cce5b921 100644 --- a/src/validation/package_validator.py +++ b/src/validation/package_validator.py @@ -1,4 +1,4 @@ -from typing import List +from typing import List, Optional from src.model.document import Document from src.model.package import Package @@ -12,10 +12,14 @@ from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_packages(packages: List[Package], document: Document) -> List[ValidationMessage]: +def validate_packages(packages: List[Package], document: Optional[Document] = None) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] - for package in packages: - validation_messages.extend(validate_package_within_document(package, document)) + if document: + for package in packages: + validation_messages.extend(validate_package_within_document(package, document)) + else: + for package in packages: + validation_messages.extend(validate_package(package)) return validation_messages @@ -53,8 +57,10 @@ def validate_package_within_document(package: Package, document: Document) -> Li return validation_messages -def validate_package(package: Package, context: ValidationContext) -> List[ValidationMessage]: +def validate_package(package: Package, context: Optional[ValidationContext] = None) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] + if not context: + context = ValidationContext(spdx_id=package.spdx_id, element_type=SpdxElementType.PACKAGE, full_element=package) download_location = package.download_location if isinstance(download_location, str): diff --git a/src/validation/snippet_validator.py b/src/validation/snippet_validator.py index 9b20199b9..715355932 100644 --- a/src/validation/snippet_validator.py +++ b/src/validation/snippet_validator.py @@ -1,4 +1,4 @@ -from typing import List +from typing import List, Optional from src.model.document import Document from src.model.snippet import Snippet @@ -8,17 +8,22 @@ from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_snippets(snippets: List[Snippet], document: Document) -> List[ValidationMessage]: +def validate_snippets(snippets: List[Snippet], document: Optional[Document] = None) -> List[ValidationMessage]: validation_messages = [] - for snippet in snippets: - validation_messages.extend(validate_snippet_within_document(snippet, document)) + if document: + for snippet in snippets: + validation_messages.extend(validate_snippet_within_document(snippet, document)) + else: + for snippet in snippets: + validation_messages.extend(validate_snippet(snippet)) return validation_messages def validate_snippet_within_document(snippet: Snippet, document: Document) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] - context = ValidationContext(spdx_id=snippet.spdx_id, element_type=SpdxElementType.SNIPPET, full_element=snippet) + context = ValidationContext(spdx_id=snippet.spdx_id, parent_id=document.creation_info.spdx_id, + element_type=SpdxElementType.SNIPPET, full_element=snippet) messages: List[str] = validate_spdx_id(snippet.spdx_id, document) for message in messages: @@ -33,8 +38,10 @@ def validate_snippet_within_document(snippet: Snippet, document: Document) -> Li return validation_messages -def validate_snippet(snippet: Snippet, context: ValidationContext) -> List[ValidationMessage]: +def validate_snippet(snippet: Snippet, context: Optional[ValidationContext] = None) -> List[ValidationMessage]: validation_messages = [] + if not context: + context = ValidationContext(spdx_id=snippet.spdx_id, element_type=SpdxElementType.SNIPPET, full_element=snippet) if snippet.byte_range[0] < 1: validation_messages.append( diff --git a/tests/validation/test_file_validator.py b/tests/validation/test_file_validator.py index 22c1bb76a..60831f331 100644 --- a/tests/validation/test_file_validator.py +++ b/tests/validation/test_file_validator.py @@ -32,6 +32,7 @@ def test_invalid_file(file_input, spdx_id, expected_message): expected = ValidationMessage(expected_message, ValidationContext(spdx_id=spdx_id, + parent_id=get_document().creation_info.spdx_id, element_type=SpdxElementType.FILE, full_element=file_input)) diff --git a/tests/validation/test_snippet_validator.py b/tests/validation/test_snippet_validator.py index b94a148b4..60670bdc3 100644 --- a/tests/validation/test_snippet_validator.py +++ b/tests/validation/test_snippet_validator.py @@ -37,7 +37,9 @@ def test_invalid_ranges(snippet_input, expected_message): get_document(files=[get_file()])) expected = ValidationMessage(expected_message, - ValidationContext(spdx_id=snippet_input.spdx_id, element_type=SpdxElementType.SNIPPET, + ValidationContext(spdx_id=snippet_input.spdx_id, + parent_id=get_document().creation_info.spdx_id, + element_type=SpdxElementType.SNIPPET, full_element=snippet_input)) assert validation_messages == [expected] From a75ecba00c614aa49aaefce114de405c7c19972f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 28 Dec 2022 11:07:49 +0100 Subject: [PATCH 035/362] [review] refactoring and small additions, add copyright MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/validation/actor_validator.py | 11 +++++++ src/validation/annotation_validator.py | 11 +++++++ src/validation/checksum_validator.py | 11 +++++++ src/validation/creation_info_validator.py | 11 +++++++ src/validation/document_validator.py | 11 +++++++ .../external_document_ref_validator.py | 12 +++++++- .../external_package_ref_validator.py | 11 +++++++ .../extracted_licensing_info_validator.py | 11 +++++++ src/validation/file_validator.py | 11 +++++++ .../license_expression_validator.py | 11 +++++++ src/validation/package_validator.py | 30 ++++++++++++++----- .../package_verification_code_validator.py | 13 +++++++- src/validation/relationship_validator.py | 11 +++++++ src/validation/snippet_validator.py | 11 +++++++ src/validation/spdx_id_validators.py | 11 +++++++ src/validation/uri_validators.py | 11 +++++++ src/validation/validation_message.py | 11 +++++++ tests/valid_defaults.py | 11 +++++++ tests/validation/test_actor_validator.py | 11 +++++++ tests/validation/test_annotation_validator.py | 11 +++++++ tests/validation/test_checksum_validator.py | 11 +++++++ .../test_creation_info_validator.py | 11 +++++++ tests/validation/test_document_validator.py | 11 +++++++ .../test_external_document_ref_validator.py | 11 +++++++ .../test_external_package_ref_validator.py | 11 +++++++ ...test_extracted_licensing_info_validator.py | 11 +++++++ tests/validation/test_file_validator.py | 11 +++++++ .../test_license_expression_validator.py | 11 +++++++ tests/validation/test_package_validator.py | 12 +++++++- .../validation/test_relationship_validator.py | 11 +++++++ tests/validation/test_snippet_validator.py | 11 +++++++ tests/validation/test_spdx_id_validators.py | 11 +++++++ tests/validation/test_uri_validators.py | 11 +++++++ 33 files changed, 375 insertions(+), 11 deletions(-) diff --git a/src/validation/actor_validator.py b/src/validation/actor_validator.py index f08d2231e..99f82223b 100644 --- a/src/validation/actor_validator.py +++ b/src/validation/actor_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List from src.model.actor import Actor, ActorType diff --git a/src/validation/annotation_validator.py b/src/validation/annotation_validator.py index c806273c7..89eea269f 100644 --- a/src/validation/annotation_validator.py +++ b/src/validation/annotation_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List from src.model.annotation import Annotation diff --git a/src/validation/checksum_validator.py b/src/validation/checksum_validator.py index 0773a1a63..12119f787 100644 --- a/src/validation/checksum_validator.py +++ b/src/validation/checksum_validator.py @@ -1,3 +1,14 @@ +# 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. + import re from typing import List, Dict diff --git a/src/validation/creation_info_validator.py b/src/validation/creation_info_validator.py index b9f59552c..5f7a42783 100644 --- a/src/validation/creation_info_validator.py +++ b/src/validation/creation_info_validator.py @@ -1,3 +1,14 @@ +# 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. + import re from typing import List diff --git a/src/validation/document_validator.py b/src/validation/document_validator.py index b10255a1b..a0a151e21 100644 --- a/src/validation/document_validator.py +++ b/src/validation/document_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List from src.model.document import Document diff --git a/src/validation/external_document_ref_validator.py b/src/validation/external_document_ref_validator.py index af4377a5c..5a10db208 100644 --- a/src/validation/external_document_ref_validator.py +++ b/src/validation/external_document_ref_validator.py @@ -1,4 +1,14 @@ -import re +# 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 typing import List from src.model.external_document_ref import ExternalDocumentRef diff --git a/src/validation/external_package_ref_validator.py b/src/validation/external_package_ref_validator.py index 2fee1f001..29aaf51b9 100644 --- a/src/validation/external_package_ref_validator.py +++ b/src/validation/external_package_ref_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List from src.model.package import ExternalPackageRef diff --git a/src/validation/extracted_licensing_info_validator.py b/src/validation/extracted_licensing_info_validator.py index 5d7b5f4aa..7a94b3aec 100644 --- a/src/validation/extracted_licensing_info_validator.py +++ b/src/validation/extracted_licensing_info_validator.py @@ -1,3 +1,14 @@ +# 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. + import re from typing import List, Optional diff --git a/src/validation/file_validator.py b/src/validation/file_validator.py index 77c45c59e..54560de07 100644 --- a/src/validation/file_validator.py +++ b/src/validation/file_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List, Optional from src.model.checksum import ChecksumAlgorithm diff --git a/src/validation/license_expression_validator.py b/src/validation/license_expression_validator.py index 1d2b9c5ac..a478fe68b 100644 --- a/src/validation/license_expression_validator.py +++ b/src/validation/license_expression_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List, Optional, Union from src.model.license_expression import LicenseExpression diff --git a/src/validation/package_validator.py b/src/validation/package_validator.py index 8cce5b921..a6686a5b1 100644 --- a/src/validation/package_validator.py +++ b/src/validation/package_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List, Optional from src.model.document import Document @@ -32,7 +43,7 @@ def validate_package_within_document(package: Package, document: Document) -> Li for message in validate_spdx_id(package.spdx_id, document): validation_messages.append(ValidationMessage(message, context)) - # TODO: make test for this + # TODO: make test for this (https://github.com/spdx/tools-python/issues/386) if not package.files_analyzed: package_contains_relationships = [relationship for relationship in document.relationships if relationship.relationship_type == RelationshipType.CONTAINS and relationship.spdx_element_id == package.spdx_id] @@ -48,7 +59,7 @@ def validate_package_within_document(package: Package, document: Document) -> Li if contained_in_package_relationships: validation_messages.append( ValidationMessage( - f"package must contain no elements if files_analyzed is False, but found {package_contains_relationships}", + f"package must contain no elements if files_analyzed is False, but found {contained_in_package_relationships}", context) ) @@ -72,28 +83,31 @@ def validate_package(package: Package, context: Optional[ValidationContext] = No for message in validate_url(homepage): validation_messages.append(ValidationMessage("homepage " + message, context)) - if package.verification_code: + # TODO: is verification_code required if files_analyzed=True? (https://github.com/spdx/tools-python/issues/386) + verification_code = package.verification_code + if verification_code: if not package.files_analyzed: validation_messages.append( ValidationMessage( - f"verification_code must be None if files_analyzed is False, but is: {package.verification_code}", + f"verification_code must be None if files_analyzed is False, but is: {verification_code}", context)) else: - validation_messages.extend(validate_verification_code(package.verification_code, package.spdx_id)) + validation_messages.extend(validate_verification_code(verification_code, package.spdx_id)) validation_messages.extend(validate_checksums(package.checksums, package.spdx_id)) validation_messages.extend(validate_license_expression(package.license_concluded)) - if package.license_info_from_files: + license_info_from_files = package.license_info_from_files + if license_info_from_files: if not package.files_analyzed: validation_messages.append( ValidationMessage( - f"license_info_from_files must be None if files_analyzed is False, but is: {package.license_info_from_files}", + f"license_info_from_files must be None if files_analyzed is False, but is: {license_info_from_files}", context) ) else: - validation_messages.extend(validate_license_expressions(package.license_info_from_files)) + validation_messages.extend(validate_license_expressions(license_info_from_files)) validation_messages.extend(validate_license_expression(package.license_declared)) diff --git a/src/validation/package_verification_code_validator.py b/src/validation/package_verification_code_validator.py index bd2d7076e..ccf216fb3 100644 --- a/src/validation/package_verification_code_validator.py +++ b/src/validation/package_verification_code_validator.py @@ -1,3 +1,14 @@ +# 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. + import re from typing import List @@ -5,7 +16,7 @@ from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -# TODO: make test for this +# TODO: make test for this (https://github.com/spdx/tools-python/issues/386) def validate_verification_code(verification_code: PackageVerificationCode, parent_id: str) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.PACKAGE_VERIFICATION_CODE, diff --git a/src/validation/relationship_validator.py b/src/validation/relationship_validator.py index a1ce9ea77..a9292253d 100644 --- a/src/validation/relationship_validator.py +++ b/src/validation/relationship_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List from src.model.document import Document diff --git a/src/validation/snippet_validator.py b/src/validation/snippet_validator.py index 715355932..1b127addb 100644 --- a/src/validation/snippet_validator.py +++ b/src/validation/snippet_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List, Optional from src.model.document import Document diff --git a/src/validation/spdx_id_validators.py b/src/validation/spdx_id_validators.py index 189033c3a..30e90be78 100644 --- a/src/validation/spdx_id_validators.py +++ b/src/validation/spdx_id_validators.py @@ -1,3 +1,14 @@ +# 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. + import re from typing import List diff --git a/src/validation/uri_validators.py b/src/validation/uri_validators.py index c28915b8b..a43ef2e9a 100644 --- a/src/validation/uri_validators.py +++ b/src/validation/uri_validators.py @@ -1,3 +1,14 @@ +# 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. + import re from typing import List diff --git a/src/validation/validation_message.py b/src/validation/validation_message.py index c90e7a71e..1407ffcc7 100644 --- a/src/validation/validation_message.py +++ b/src/validation/validation_message.py @@ -1,3 +1,14 @@ +# 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 dataclasses import dataclass from enum import Enum, auto from typing import Optional, Any diff --git a/tests/valid_defaults.py b/tests/valid_defaults.py index 114c03ad4..a800193f0 100644 --- a/tests/valid_defaults.py +++ b/tests/valid_defaults.py @@ -1,3 +1,14 @@ +# 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 datetime import datetime from src.model.actor import Actor, ActorType diff --git a/tests/validation/test_actor_validator.py b/tests/validation/test_actor_validator.py index 29a37e825..4215fe7e5 100644 --- a/tests/validation/test_actor_validator.py +++ b/tests/validation/test_actor_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List import pytest diff --git a/tests/validation/test_annotation_validator.py b/tests/validation/test_annotation_validator.py index 8071f6790..885693d8b 100644 --- a/tests/validation/test_annotation_validator.py +++ b/tests/validation/test_annotation_validator.py @@ -1,3 +1,14 @@ +# 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 datetime import datetime from typing import List diff --git a/tests/validation/test_checksum_validator.py b/tests/validation/test_checksum_validator.py index 461d14305..661fb110a 100644 --- a/tests/validation/test_checksum_validator.py +++ b/tests/validation/test_checksum_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List import pytest diff --git a/tests/validation/test_creation_info_validator.py b/tests/validation/test_creation_info_validator.py index 58ffb4132..fb9cc86e9 100644 --- a/tests/validation/test_creation_info_validator.py +++ b/tests/validation/test_creation_info_validator.py @@ -1,3 +1,14 @@ +# 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 datetime import datetime from typing import List diff --git a/tests/validation/test_document_validator.py b/tests/validation/test_document_validator.py index fc8fec5ca..be66477c0 100644 --- a/tests/validation/test_document_validator.py +++ b/tests/validation/test_document_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List from src.model.document import Document diff --git a/tests/validation/test_external_document_ref_validator.py b/tests/validation/test_external_document_ref_validator.py index 95976c00c..1c2e5cf52 100644 --- a/tests/validation/test_external_document_ref_validator.py +++ b/tests/validation/test_external_document_ref_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List import pytest diff --git a/tests/validation/test_external_package_ref_validator.py b/tests/validation/test_external_package_ref_validator.py index 09b478f7d..7b94e84e6 100644 --- a/tests/validation/test_external_package_ref_validator.py +++ b/tests/validation/test_external_package_ref_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List import pytest diff --git a/tests/validation/test_extracted_licensing_info_validator.py b/tests/validation/test_extracted_licensing_info_validator.py index eae417d2a..25047cca5 100644 --- a/tests/validation/test_extracted_licensing_info_validator.py +++ b/tests/validation/test_extracted_licensing_info_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List import pytest diff --git a/tests/validation/test_file_validator.py b/tests/validation/test_file_validator.py index 60831f331..fda40ab36 100644 --- a/tests/validation/test_file_validator.py +++ b/tests/validation/test_file_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List import pytest diff --git a/tests/validation/test_license_expression_validator.py b/tests/validation/test_license_expression_validator.py index d917be7d5..a8f334a3f 100644 --- a/tests/validation/test_license_expression_validator.py +++ b/tests/validation/test_license_expression_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List from src.model.license_expression import LicenseExpression diff --git a/tests/validation/test_package_validator.py b/tests/validation/test_package_validator.py index 3b37317c8..8c81c6739 100644 --- a/tests/validation/test_package_validator.py +++ b/tests/validation/test_package_validator.py @@ -1,3 +1,14 @@ +# 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 datetime import datetime from typing import List @@ -25,7 +36,6 @@ def test_valid_package(): assert validation_messages == [] -# TODO: is verification_code required if files_analyzed=True? @pytest.mark.parametrize("package_input, expected_message", [(get_package(files_analyzed=False, verification_code=get_package_verification_code()), f'verification_code must be None if files_analyzed is False, but is: {get_package_verification_code()}'), diff --git a/tests/validation/test_relationship_validator.py b/tests/validation/test_relationship_validator.py index d53d9fee3..1d56bd6af 100644 --- a/tests/validation/test_relationship_validator.py +++ b/tests/validation/test_relationship_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List import pytest diff --git a/tests/validation/test_snippet_validator.py b/tests/validation/test_snippet_validator.py index 60670bdc3..9ceb675f6 100644 --- a/tests/validation/test_snippet_validator.py +++ b/tests/validation/test_snippet_validator.py @@ -1,3 +1,14 @@ +# 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 typing import List import pytest diff --git a/tests/validation/test_spdx_id_validators.py b/tests/validation/test_spdx_id_validators.py index 69692be70..5358305de 100644 --- a/tests/validation/test_spdx_id_validators.py +++ b/tests/validation/test_spdx_id_validators.py @@ -1 +1,12 @@ +# 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. + # TODO: https://github.com/spdx/tools-python/issues/376 diff --git a/tests/validation/test_uri_validators.py b/tests/validation/test_uri_validators.py index ca1403493..704dbbea0 100644 --- a/tests/validation/test_uri_validators.py +++ b/tests/validation/test_uri_validators.py @@ -1,3 +1,14 @@ +# 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. + import pytest from src.validation.uri_validators import validate_url, validate_download_location, validate_uri From b785cd5bf9da435d3bfae51719b534e028179d8c Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 25 Nov 2022 10:29:08 +0100 Subject: [PATCH 036/362] [issue-305] add new parser Signed-off-by: Meret Behrens --- src/model/typing/constructor_type_errors.py | 3 + src/parser/__init__.py | 0 src/parser/error.py | 21 ++ src/parser/json/__init__.py | 0 src/parser/json/actor_parser.py | 60 +++++ src/parser/json/annotation_parser.py | 156 ++++++++++++ src/parser/json/checksum_parser.py | 58 +++++ src/parser/json/creation_info_parser.py | 141 +++++++++++ src/parser/json/dict_parsing_functions.py | 17 ++ src/parser/json/extracted_licensing_parser.py | 60 +++++ src/parser/json/file_parser.py | 114 +++++++++ src/parser/json/json_parser.py | 104 ++++++++ src/parser/json/license_expression_parser.py | 27 ++ src/parser/json/package_parser.py | 238 ++++++++++++++++++ src/parser/json/relationship_parser.py | 223 ++++++++++++++++ src/parser/json/snippet_parser.py | 136 ++++++++++ src/parser/logger.py | 31 +++ tests/parser/__init__.py | 0 tests/parser/test_actor_parser.py | 35 +++ tests/parser/test_annotation_parser.py | 119 +++++++++ tests/parser/test_checksum_parser.py | 43 ++++ tests/parser/test_creation_info_parser.py | 61 +++++ .../test_extracted_licensing_info_parser.py | 69 +++++ tests/parser/test_file_parser.py | 127 ++++++++++ tests/parser/test_json_parser.py | 56 +++++ .../parser/test_license_expression_parser.py | 41 +++ tests/parser/test_package_parser.py | 204 +++++++++++++++ tests/parser/test_relationship_parser.py | 137 ++++++++++ tests/parser/test_snippet_parser.py | 139 ++++++++++ 29 files changed, 2420 insertions(+) create mode 100644 src/parser/__init__.py create mode 100644 src/parser/error.py create mode 100644 src/parser/json/__init__.py create mode 100644 src/parser/json/actor_parser.py create mode 100644 src/parser/json/annotation_parser.py create mode 100644 src/parser/json/checksum_parser.py create mode 100644 src/parser/json/creation_info_parser.py create mode 100644 src/parser/json/dict_parsing_functions.py create mode 100644 src/parser/json/extracted_licensing_parser.py create mode 100644 src/parser/json/file_parser.py create mode 100644 src/parser/json/json_parser.py create mode 100644 src/parser/json/license_expression_parser.py create mode 100644 src/parser/json/package_parser.py create mode 100644 src/parser/json/relationship_parser.py create mode 100644 src/parser/json/snippet_parser.py create mode 100644 src/parser/logger.py create mode 100644 tests/parser/__init__.py create mode 100644 tests/parser/test_actor_parser.py create mode 100644 tests/parser/test_annotation_parser.py create mode 100644 tests/parser/test_checksum_parser.py create mode 100644 tests/parser/test_creation_info_parser.py create mode 100644 tests/parser/test_extracted_licensing_info_parser.py create mode 100644 tests/parser/test_file_parser.py create mode 100644 tests/parser/test_json_parser.py create mode 100644 tests/parser/test_license_expression_parser.py create mode 100644 tests/parser/test_package_parser.py create mode 100644 tests/parser/test_relationship_parser.py create mode 100644 tests/parser/test_snippet_parser.py diff --git a/src/model/typing/constructor_type_errors.py b/src/model/typing/constructor_type_errors.py index cd02944ba..2f9143a9e 100644 --- a/src/model/typing/constructor_type_errors.py +++ b/src/model/typing/constructor_type_errors.py @@ -10,3 +10,6 @@ class ConstructorTypeErrors(TypeError): def __init__(self, messages: List[str]): self.messages = messages + + def get_messages(self): + return self.messages diff --git a/src/parser/__init__.py b/src/parser/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/parser/error.py b/src/parser/error.py new file mode 100644 index 000000000..c48b716a9 --- /dev/null +++ b/src/parser/error.py @@ -0,0 +1,21 @@ +# 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 typing import List + + +class SPDXParsingError(Exception): + messages: List[str] + + def __init__(self, messages: List[str]): + self.messages = messages + + def get_messages(self): + return self.messages diff --git a/src/parser/json/__init__.py b/src/parser/json/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/parser/json/actor_parser.py b/src/parser/json/actor_parser.py new file mode 100644 index 000000000..04386c5b4 --- /dev/null +++ b/src/parser/json/actor_parser.py @@ -0,0 +1,60 @@ +# 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. +import re +from typing import Union, Pattern, Match + +from src.model.actor import Actor, ActorType +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.typing.constructor_type_errors import ConstructorTypeErrors +from src.parser.error import SPDXParsingError + + +class ActorParser: + def parse_actor_or_no_assertion(self, actor_or_no_assertion: str) -> Union[SpdxNoAssertion, Actor]: + if actor_or_no_assertion == SpdxNoAssertion.__str__: + return SpdxNoAssertion() + else: + return self.parse_actor(actor_or_no_assertion) + + @staticmethod + def parse_actor(actor: str) -> Actor: + tool_re: Pattern = re.compile(r"Tool:\s*(.+)", re.UNICODE) + person_re: Pattern = re.compile(r"Person:\s*(([^(])+)(\((.*)\))?", re.UNICODE) + org_re: Pattern = re.compile(r"Organization:\s*(([^(])+)(\((.*)\))?", re.UNICODE) + tool_match: Match = tool_re.match(actor) + person_match: Match = person_re.match(actor) + org_match: Match = org_re.match(actor) + + if tool_match: + name: str = tool_match.group(1).strip() + try: + creator = Actor(ActorType.TOOL, name=name) + except ConstructorTypeErrors as err: + raise SPDXParsingError(err.get_messages()) + elif person_match: + name: str = person_match.group(1).strip() + email: str = person_match.group(4).strip() if person_match.group(4) else None + try: + creator = Actor(ActorType.PERSON, name=name, email=email) + except ConstructorTypeErrors as err: + raise SPDXParsingError(err.get_messages()) + elif org_match: + name: str = org_match.group(1).strip() + email: str = org_match.group(4).strip() if org_match.group(4) else None + try: + creator = Actor(ActorType.ORGANIZATION, name=name, email=email) + except ConstructorTypeErrors as err: + raise SPDXParsingError(err.get_messages()) + + else: + raise SPDXParsingError([f"Actor {actor} doesn't match any of person, organization or tool."]) + + return creator diff --git a/src/parser/json/annotation_parser.py b/src/parser/json/annotation_parser.py new file mode 100644 index 000000000..8d548a87b --- /dev/null +++ b/src/parser/json/annotation_parser.py @@ -0,0 +1,156 @@ +# 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 datetime import datetime +from typing import Dict, Optional, List + +from src.model.actor import Actor +from src.model.annotation import Annotation, AnnotationType +from src.model.typing.constructor_type_errors import ConstructorTypeErrors +from src.parser.error import SPDXParsingError +from src.parser.json.actor_parser import ActorParser +from src.parser.json.dict_parsing_functions import datetime_from_str +from src.parser.logger import Logger + + +class AnnotationParser: + logger: Logger + actor_parser: ActorParser + + def __init__(self): + self.logger = Logger() + self.actor_parser = ActorParser() + + def parse_all_annotations(self, input_doc_dict: Dict) -> List[Annotation]: + annotations_list = [] + doc_spdx_id: str = input_doc_dict.get("SPDXID") + document_annotations: List[Dict] = input_doc_dict.get("annotations") + if document_annotations: + try: + annotations_list.extend(self.parse_annotations(document_annotations, spdx_id=doc_spdx_id)) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + + reviews: List[Dict] = input_doc_dict.get("revieweds") + if reviews: + for review in reviews: + try: + review_annotation: Annotation = self.parse_review(review, spdx_id=doc_spdx_id) + if review_annotation: + annotations_list.append(review_annotation) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + packages: List[Dict] = input_doc_dict.get("packages") + if packages: + for package in packages: + package_spdx_id: str = package.get("SPDXID") + package_annotations: List[Dict] = package.get("annotations") + if package_annotations: + try: + annotations_list.extend(self.parse_annotations(package_annotations, spdx_id=package_spdx_id)) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + files: List[Dict] = input_doc_dict.get("files") + if files: + for file in files: + file_spdx_id: str = file.get("SPDXID") + file_annotations:List[Dict] = file.get("annotations") + if file_annotations: + try: + annotations_list.extend(self.parse_annotations(file_annotations, spdx_id=file_spdx_id)) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + + snippets: List[Dict] = input_doc_dict.get("snippets") + if snippets: + for snippet in snippets: + snippet_spdx_id: str = snippet.get("SPDXID") + snippet_annotations: List[Dict] = snippet.get("annotations") + if snippet_annotations: + try: + annotations_list.extend(self.parse_annotations(snippet_annotations, spdx_id=snippet_spdx_id)) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + + if self.logger.has_messages(): + raise SPDXParsingError(self.logger.get_messages()) + return annotations_list + + def parse_annotations(self, annotations_dict_list: List[Dict], spdx_id: Optional[str] = None) -> List[Annotation]: + logger = Logger() + annotations_list = [] + for annotation_dict in annotations_dict_list: + try: + annotation: Annotation = self.parse_annotation(annotation_dict, spdx_id=spdx_id) + annotations_list.append(annotation) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + if logger.has_messages(): + raise SPDXParsingError(logger.get_messages()) + + return annotations_list + + def parse_annotation(self, annotation: Dict, spdx_id: Optional[str] = None) -> Annotation: + logger = Logger() + spdx_id: str = annotation.get("SPDXID") or spdx_id + try: + annotation_type: Optional[AnnotationType] = self.parse_annotation_type(annotation.get("annotationType")) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + annotation_type = None + try: + annotator: Optional[Actor] = self.actor_parser.parse_actor(annotation.get("annotator")) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + annotator = None + try: + annotation_date: Optional[datetime] = datetime_from_str(annotation.get("annotationDate")) + except TypeError: + logger.append("ValueError while parsing annotationDate.") + annotation_date = None + annotation_comment: str = annotation.get("comment") + if logger.has_messages(): + raise SPDXParsingError([f"Error while parsing annotation: {logger.get_messages()}"]) + try: + annotation = Annotation(spdx_id, annotation_type, annotator, annotation_date, annotation_comment) + except ConstructorTypeErrors as err: + raise SPDXParsingError([f"Error while constructing annotation: {err.get_messages()}"]) + return annotation + + @staticmethod + def parse_annotation_type(annotation_type: str) -> AnnotationType: + try: + return AnnotationType[annotation_type] + except KeyError: + raise SPDXParsingError([f"Invalid annotation type: {annotation_type}"]) + + + def parse_review(self, review_dict: Dict, spdx_id: str) -> Annotation: + logger = Logger() + try: + annotator: Optional[Actor] = self.actor_parser.parse_actor(review_dict.get("reviewer")) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + annotator = None + try: + annotation_date: Optional[datetime] = datetime_from_str(review_dict.get("reviewDate")) + except TypeError: + logger.append("ValueError while parsing reviewDate.") + annotation_date = None + annotation_type = AnnotationType.REVIEW + comment: str = review_dict.get("comment") + if logger.has_messages(): + raise SPDXParsingError([f"Error while parsing review: {logger.get_messages()}"]) + + try: + return Annotation(spdx_id=spdx_id, annotator=annotator, annotation_date=annotation_date, + annotation_type=annotation_type, annotation_comment=comment) + except ConstructorTypeErrors as err: + raise SPDXParsingError([f"Error while constructing review: {err.get_messages()}"]) diff --git a/src/parser/json/checksum_parser.py b/src/parser/json/checksum_parser.py new file mode 100644 index 000000000..4f449e829 --- /dev/null +++ b/src/parser/json/checksum_parser.py @@ -0,0 +1,58 @@ +# 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 typing import Dict, List + +from src.model.checksum import Checksum, ChecksumAlgorithm +from src.model.typing.constructor_type_errors import ConstructorTypeErrors +from src.parser.error import SPDXParsingError +from src.parser.json.dict_parsing_functions import transform_json_str_to_enum_name +from src.parser.logger import Logger + + +class ChecksumParser: + auxiliary_logger: Logger + + def __init__(self): + self.auxiliary_logger = Logger() + + def parse_checksums(self, checksum_dicts_list: List[Dict]) -> List[Checksum]: + if not checksum_dicts_list: + raise SPDXParsingError([f"No checksums provided, checksums are mandatory for files."]) + + checksum_list = [] + for checksum_dict in checksum_dicts_list: + try: + checksum_list.append(self.parse_checksum(checksum_dict)) + except SPDXParsingError as err: + self.auxiliary_logger.append_all(err.get_messages()) + continue + if self.auxiliary_logger.has_messages(): + raise SPDXParsingError(self.auxiliary_logger.get_messages()) + + return checksum_list + + @staticmethod + def parse_checksum(checksum_dict: Dict) -> Checksum: + logger = Logger() + algorithm = transform_json_str_to_enum_name(checksum_dict.get("algorithm")) + try: + checksum_algorithm = ChecksumAlgorithm[algorithm] + except KeyError: + logger.append(f"Algorithm {algorithm} not valid for checksum.") + checksum_algorithm = None + checksum_value = checksum_dict.get("checksumValue") + if logger.has_messages(): + raise SPDXParsingError([f"Error while parsing checksum: {logger.get_messages()}"]) + try: + checksum = Checksum(algorithm=checksum_algorithm, value=checksum_value) + except ConstructorTypeErrors as err: + raise SPDXParsingError([f"Error while constructing checksum: {err.get_messages()}"]) + return checksum diff --git a/src/parser/json/creation_info_parser.py b/src/parser/json/creation_info_parser.py new file mode 100644 index 000000000..934998992 --- /dev/null +++ b/src/parser/json/creation_info_parser.py @@ -0,0 +1,141 @@ +# 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 datetime import datetime +from typing import Dict, Optional, List, Union + +from src.model.actor import Actor +from src.model.checksum import Checksum +from src.model.document import CreationInfo +from src.model.external_document_ref import ExternalDocumentRef +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.typing.constructor_type_errors import ConstructorTypeErrors +from src.model.version import Version +from src.parser.error import SPDXParsingError +from src.parser.json.actor_parser import ActorParser +from src.parser.json.checksum_parser import ChecksumParser +from src.parser.json.dict_parsing_functions import datetime_from_str, parse_optional_field +from src.parser.logger import Logger + + +class CreationInfoParser: + logger: Logger + actor_parser: ActorParser + checksum_parser: ChecksumParser + + def __init__(self): + self.logger = Logger() + self.actor_parser = ActorParser() + self.checksum_parser = ChecksumParser() + + def parse_creation_info(self, doc_dict: Dict) -> CreationInfo: + logger = Logger() + spdx_version: str = doc_dict.get("spdxVersion") + spdx_id: str = doc_dict.get("SPDXID") + name: str = doc_dict.get("name") + document_namespace: str = doc_dict.get("documentNamespace") + creation_info_dict: Dict = doc_dict.get("creationInfo") + + # There are nested required properties. If creationInfo is not set, we cannot continue parsing. + if creation_info_dict is None: + logger.append("CreationInfo is not valid.") + raise SPDXParsingError([f"Error while parsing doc {name}: {logger.get_messages()}"]) + try: + list_of_creators: List[str] = creation_info_dict.get("creators") + creators: List[Actor] = self.parse_creators(list_of_creators) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + creators = [] + try: + created: Optional[datetime] = datetime_from_str(creation_info_dict.get("created")) + except ValueError: + logger.append("Error while parsing created") + created = None + + creator_comment: Optional[str] = creation_info_dict.get("comment") + data_license: str = doc_dict.get("dataLicense") + try: + external_document_refs: List[ExternalDocumentRef] = parse_optional_field( + doc_dict.get("externalDocumentRefs"), + self.parse_external_document_refs) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + external_document_refs = [] + try: + license_list_version: Optional[Version] = parse_optional_field(creation_info_dict.get("licenseListVersion"), + self.parse_version) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + license_list_version = None + document_comment: Optional[str] = doc_dict.get("comment") + if logger.has_messages(): + raise SPDXParsingError([f"Error while parsing doc {name}: {logger.get_messages()}"]) + try: + creation_info = CreationInfo(spdx_version=spdx_version, spdx_id=spdx_id, name=name, + document_namespace=document_namespace, creators=creators, created=created, + license_list_version=license_list_version, document_comment=document_comment, + creator_comment=creator_comment, data_license=data_license, + external_document_refs=external_document_refs) + except ConstructorTypeErrors as err: + raise SPDXParsingError([f"Error while parsing doc {name}: {err.get_messages()}"]) + + return creation_info + + def parse_creators(self, creators_dict_list: List[str]) -> List[Actor]: + logger = Logger() + creators_list = [] + for creator_dict in creators_dict_list: + try: + creator: Union[Actor, SpdxNoAssertion] = self.actor_parser.parse_actor_or_no_assert(creator_dict) + creators_list.append(creator) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + if logger.has_messages(): + raise SPDXParsingError(logger.get_messages()) + return creators_list + + @staticmethod + def parse_version(version_str: str) -> Version: + try: + return Version.from_string(version_str) + except ValueError as err: + raise SPDXParsingError([f"Error while parsing version {version_str}: {err.args[0]}"]) + + def parse_external_document_refs(self, external_document_refs_dict: List[Dict]) -> List[ExternalDocumentRef]: + logger = Logger() + external_document_refs = [] + for external_ref_dict in external_document_refs_dict: + try: + external_doc_ref: ExternalDocumentRef = self.parse_external_doc_ref(external_ref_dict) + external_document_refs.append(external_doc_ref) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + if logger.has_messages(): + raise SPDXParsingError(logger.get_messages()) + return external_document_refs + + def parse_external_doc_ref(self, external_doc_ref_dict: Dict) -> ExternalDocumentRef: + logger = Logger() + try: + checksum: Optional[Checksum] = self.checksum_parser.parse_checksum(external_doc_ref_dict.get("checksum")) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + checksum = None + external_document_id: str = external_doc_ref_dict.get("externalDocumentId") + spdx_document: str = external_doc_ref_dict.get("spdxDocument") + if logger.has_messages(): + raise SPDXParsingError([f"Error while parsing external_doc_ref: {logger.get_messages()}"]) + try: + external_doc_ref = ExternalDocumentRef(document_ref_id=external_document_id, document_uri=spdx_document, + checksum=checksum) + except ConstructorTypeErrors as err: + raise SPDXParsingError([f"Error while constructing ExternalDocumentRef: {err.get_messages()}"]) + + return external_doc_ref diff --git a/src/parser/json/dict_parsing_functions.py b/src/parser/json/dict_parsing_functions.py new file mode 100644 index 000000000..c3a548b79 --- /dev/null +++ b/src/parser/json/dict_parsing_functions.py @@ -0,0 +1,17 @@ +from datetime import datetime +from typing import Any, Callable + + +def parse_optional_field(field: Any, method_to_parse:Callable=lambda x: x, default=None): + if not field: + return default + return method_to_parse(field) + + +def datetime_from_str(created: str) -> datetime: + date = datetime.strptime(created, "%Y-%m-%dT%H:%M:%SZ") + return date + + +def transform_json_str_to_enum_name(json_str: str) -> str: + return json_str.replace("-","_").upper() diff --git a/src/parser/json/extracted_licensing_parser.py b/src/parser/json/extracted_licensing_parser.py new file mode 100644 index 000000000..1f6171b60 --- /dev/null +++ b/src/parser/json/extracted_licensing_parser.py @@ -0,0 +1,60 @@ +# 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 typing import Dict, List, Optional, Union + +from src.model.extracted_licensing_info import ExtractedLicensingInfo +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.typing.constructor_type_errors import ConstructorTypeErrors +from src.parser.error import SPDXParsingError +from src.parser.json.dict_parsing_functions import parse_optional_field +from src.parser.logger import Logger + + +class ExtractedLicensingInfoParser: + logger: Logger + + def __init__(self): + self.logger = Logger() + + def parse_extracted_licensing_infos(self, extracted_licensing_info_dicts: List[Dict]) -> List[ + ExtractedLicensingInfo]: + extracted_licensing_info_list = [] + for extracted_licensing_info_dict in extracted_licensing_info_dicts: + try: + extracted_licensing_info_list.append(self.parse_extracted_licensing_info(extracted_licensing_info_dict)) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + if self.logger.has_messages(): + raise SPDXParsingError(self.logger.get_messages()) + return extracted_licensing_info_list + + def parse_extracted_licensing_info(self, extracted_licensing_info_dict: Dict) -> ExtractedLicensingInfo: + license_id: Optional[str] = extracted_licensing_info_dict.get("licenseId") + extracted_text: Optional[str] = extracted_licensing_info_dict.get("extractedText") + license_name: Optional[Union[str, SpdxNoAssertion]] = parse_optional_field( + extracted_licensing_info_dict.get("name"), self.parse_extracted_licensing_info_name) + cross_references: List[str] = extracted_licensing_info_dict.get("seeAlsos") + comment: str = extracted_licensing_info_dict.get("comment") + + try: + extracted_licensing_info_dict = ExtractedLicensingInfo(license_id=license_id, extracted_text=extracted_text, + comment=comment, license_name=license_name, + cross_references=cross_references) + except ConstructorTypeErrors as err: + raise SPDXParsingError([f"Error while constructing ExtractedLicensingInfo : {err.get_messages()}"]) + return extracted_licensing_info_dict + + @staticmethod + def parse_extracted_licensing_info_name(extracted_licensing_info_name_or_no_assertion) -> Union[str, SpdxNoAssertion]: + if extracted_licensing_info_name_or_no_assertion == SpdxNoAssertion().__str__(): + return SpdxNoAssertion() + else: + return extracted_licensing_info_name_or_no_assertion diff --git a/src/parser/json/file_parser.py b/src/parser/json/file_parser.py new file mode 100644 index 000000000..0be022734 --- /dev/null +++ b/src/parser/json/file_parser.py @@ -0,0 +1,114 @@ +# 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 typing import Dict, List, Optional, Union + +from src.model.checksum import Checksum +from src.model.file import File, FileType +from src.model.license_expression import LicenseExpression +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone +from src.model.typing.constructor_type_errors import ConstructorTypeErrors +from src.parser.error import SPDXParsingError +from src.parser.json.checksum_parser import ChecksumParser +from src.parser.json.dict_parsing_functions import parse_optional_field +from src.parser.json.license_expression_parser import LicenseExpressionParser +from src.parser.logger import Logger + + +class FileParser: + logger: Logger + checksum_parser: ChecksumParser + license_expression_parser: LicenseExpressionParser + + def __init__(self): + self.logger = Logger() + self.checksum_parser = ChecksumParser() + self.license_expression_parser = LicenseExpressionParser() + + def parse_files(self, file_dict_list) -> List[File]: + file_list = [] + for file_dict in file_dict_list: + try: + file: File = self.parse_file(file_dict) + file_list.append(file) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + continue + if self.logger.has_messages(): + raise SPDXParsingError(self.logger.get_messages()) + return file_list + + def parse_file(self, file_dict: Dict) -> Optional[File]: + logger = Logger() + name: str = file_dict.get("fileName") + spdx_id: str = file_dict.get("SPDXID") + checksums_list: List[Dict] = file_dict.get("checksums") + try: + checksums: List[Checksum] = self.checksum_parser.parse_checksums(checksums_list) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + checksums = [] + + attribution_texts: Optional[str] = file_dict.get("attributionTexts") + comment: Optional[str] = file_dict.get("comment") + copyright_text: Optional[str] = file_dict.get("copyrightText") + file_contributors: List[str] = file_dict.get("fileContributors") + try: + file_types: List[FileType] = parse_optional_field(file_dict.get("fileTypes"), self.parse_file_types) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + file_types = [] + license_comments: Optional[str] = file_dict.get("licenseComments") + try: + license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_optional_field( + file_dict.get("licenseConcluded"), + self.license_expression_parser.parse_license_expression) + except ConstructorTypeErrors as err: + logger.append_all(err.get_messages()) + license_concluded = None + try: + license_info_in_files: Optional[ + Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_optional_field( + file_dict.get("licenseInfoInFiles"), + self.license_expression_parser.parse_license_expression) + except ConstructorTypeErrors as err: + logger.append_all(err.get_messages()) + license_info_in_files = None + notice_text: Optional[str] = file_dict.get("noticeText") + + if logger.has_messages(): + raise SPDXParsingError([f"Error while parsing file {name}: {logger.get_messages()}"]) + + try: + file = File(name=name, spdx_id=spdx_id, checksums=checksums, attribution_texts=attribution_texts, + comment=comment, copyright_text=copyright_text, + file_type=file_types, contributors=file_contributors, license_comment=license_comments, + concluded_license=license_concluded, license_info_in_file=license_info_in_files, + notice=notice_text) + except ConstructorTypeErrors as error: + raise SPDXParsingError([f"Error while constructing file {name}: {error.get_messages()}"]) + + return file + + @staticmethod + def parse_file_types(file_types_list: List[str]) -> List[FileType]: + logger = Logger() + file_types = [] + for file_type in file_types_list: + try: + file_type = FileType[file_type] + except KeyError: + logger.append(f"FileType {file_type} is not valid.") + continue + file_types.append(file_type) + if logger.has_messages(): + raise SPDXParsingError([f"Error while parsing file_types: {logger.get_messages()}"]) + return file_types diff --git a/src/parser/json/json_parser.py b/src/parser/json/json_parser.py new file mode 100644 index 000000000..2105de5e3 --- /dev/null +++ b/src/parser/json/json_parser.py @@ -0,0 +1,104 @@ +# 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. +import json +from json import JSONDecodeError + +from src.model.document import Document, CreationInfo +from src.model.typing.constructor_type_errors import ConstructorTypeErrors +from src.parser.json.annotation_parser import AnnotationParser +from src.parser.json.creation_info_parser import CreationInfoParser +from src.parser.error import SPDXParsingError +from src.parser.json.dict_parsing_functions import parse_optional_field +from src.parser.json.extracted_licensing_parser import ExtractedLicensingInfoParser +from src.parser.json.file_parser import FileParser +from src.parser.logger import Logger +from src.parser.json.package_parser import PackageParser +from src.parser.json.relationship_parser import RelationshipParser +from src.parser.json.snippet_parser import SnippetParser + + +class JsonParser: + logger: Logger + creation_info_parser: CreationInfoParser + package_parser: PackageParser + file_parser: FileParser + snippet_parser: SnippetParser + extracted_licenses_parser: ExtractedLicensingInfoParser + relationship_parser: RelationshipParser + annotation_parser: AnnotationParser + + def __init__(self): + self.logger = Logger() + self.creation_info_parser = CreationInfoParser() + self.package_parser = PackageParser() + self.file_parser = FileParser() + self.snippet_parser = SnippetParser() + self.extracted_licenses_parser = ExtractedLicensingInfoParser() + self.relationship_parser = RelationshipParser() + self.annotation_parser = AnnotationParser() + + def parse(self, filename: str) -> Document: + try: + with open(filename) as file: + input_doc_as_dict = json.load(file) + except FileNotFoundError: + self.logger.append(f"File {filename} not found.") + raise SPDXParsingError(self.logger.get_messages()) + except JSONDecodeError: + self.logger.append(f"File {filename} is not a valid JSON file.") + raise SPDXParsingError(self.logger.get_messages()) + + creation_info: CreationInfo = self.creation_info_parser.parse_creation_info(input_doc_as_dict) + + try: + packages = parse_optional_field(input_doc_as_dict.get("packages"), self.package_parser.parse_packages, default=[]) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + packages = None + try: + files = parse_optional_field(input_doc_as_dict.get("files"), self.file_parser.parse_files) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + files = None + + try: + annotations = self.annotation_parser.parse_all_annotations(input_doc_as_dict) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + annotations = None + + try: + snippets = self.snippet_parser.parse_snippets(input_doc_as_dict.get("snippets")) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + snippets = None + try: + relationships = self.relationship_parser.parse_all_relationships(input_doc_as_dict) + # documentDescribes(Document), hasFiles(Package), relationships, fileDependencies (File), artifactOf(File) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + relationships = None + + try: + extracted_licensing_info = parse_optional_field(input_doc_as_dict.get("hasExtractedLicensingInfos"), + self.extracted_licenses_parser.parse_extracted_licensing_infos) + except ConstructorTypeErrors as err: + self.logger.append_all(err.get_messages()) + extracted_licensing_info = None + if self.logger.has_messages(): + raise SPDXParsingError(self.logger.get_messages()) + + document: Document = Document(creation_info=creation_info, packages=packages, files=files, + annotations=annotations, + snippets=snippets, relationships=relationships, + extracted_licensing_info=extracted_licensing_info) + + return document diff --git a/src/parser/json/license_expression_parser.py b/src/parser/json/license_expression_parser.py new file mode 100644 index 000000000..eb08ead24 --- /dev/null +++ b/src/parser/json/license_expression_parser.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 typing import Union, List + +from src.model.license_expression import LicenseExpression +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone + + +class LicenseExpressionParser: + def parse_license_expression(self, license_expression: Union[str, List[str]]) -> Union[LicenseExpression, SpdxNoAssertion, SpdxNone, List[LicenseExpression]]: + if license_expression == SpdxNone().__str__(): + return SpdxNone() + if license_expression == SpdxNoAssertion().__str__(): + return SpdxNoAssertion() + elif isinstance(license_expression, str): + return LicenseExpression(license_expression) + elif isinstance(license_expression, list): + return list(map(self.parse_license_expression, license_expression)) diff --git a/src/parser/json/package_parser.py b/src/parser/json/package_parser.py new file mode 100644 index 000000000..12845c8eb --- /dev/null +++ b/src/parser/json/package_parser.py @@ -0,0 +1,238 @@ +# 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 datetime import datetime +from typing import Dict, List, Optional, Union + +from src.model.actor import Actor +from src.model.checksum import Checksum +from src.model.license_expression import LicenseExpression +from src.model.package import Package, ExternalPackageRef, PackageVerificationCode, PackagePurpose, \ + ExternalPackageRefCategory +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone +from src.model.typing.constructor_type_errors import ConstructorTypeErrors +from src.parser.error import SPDXParsingError +from src.parser.json.actor_parser import ActorParser +from src.parser.json.checksum_parser import ChecksumParser +from src.parser.json.dict_parsing_functions import datetime_from_str, parse_optional_field, \ + transform_json_str_to_enum_name +from src.parser.json.license_expression_parser import LicenseExpressionParser +from src.parser.logger import Logger + + +class PackageParser: + logger: Logger + actor_parser: ActorParser + checksum_parser: ChecksumParser + license_expression_parser: LicenseExpressionParser + + def __init__(self): + self.actor_parser = ActorParser() + self.checksum_parser = ChecksumParser() + self.license_expression_parser = LicenseExpressionParser() + self.logger = Logger() + + def parse_packages(self, packages_dict_list: List[Dict]) -> List[Package]: + packages_list = [] + for package_dict in packages_dict_list: + try: + package: Package = self.parse_package(package_dict) + packages_list.append(package) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + continue + if self.logger.has_messages(): + raise SPDXParsingError(self.logger.get_messages()) + + return packages_list + + def parse_package(self, package_dict: Dict) -> Package: + logger = Logger() + name: str = package_dict.get("name") + spdx_id: str = package_dict.get("SPDXID") + attribution_texts: List[str] = package_dict.get("attributionTexts") + try: + built_date: Optional[datetime] = parse_optional_field(package_dict.get("builtDate"), datetime_from_str) + except ValueError: + logger.append("ValueError while parsing builtDate.") + built_date = None + try: + checksums: List[Checksum] = parse_optional_field(package_dict.get("checksums"), + self.checksum_parser.parse_checksums, default=[]) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + checksums = [] + comment: Optional[str] = package_dict.get("comment") + copyright_text: Optional[str] = package_dict.get("copyrightText") + description: Optional[str] = package_dict.get("description") + download_location: Union[str, SpdxNoAssertion, SpdxNone] = self.parse_download_location( + package_dict.get("downloadLocation")) + try: + external_refs: List[ExternalPackageRef] = parse_optional_field(package_dict.get("externalRefs"), + self.parse_external_refs) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + external_refs = [] + files_analyzed: Optional[bool] = parse_optional_field(package_dict.get("filesAnalyzed"), default=True) + homepage: Optional[str] = package_dict.get("homepage") + license_comments: Optional[str] = package_dict.get("licenseComments") + try: + license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_optional_field( + package_dict.get("licenseConcluded"), + self.license_expression_parser.parse_license_expression) + except ConstructorTypeErrors as err: + logger.append_all(err.get_messages()) + license_concluded = None + try: + license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_optional_field( + package_dict.get("licenseDeclared"), + self.license_expression_parser.parse_license_expression) + except ConstructorTypeErrors as err: + logger.append_all(err.get_messages()) + license_declared = None + try: + license_info_from_file: Optional[ + Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_optional_field( + package_dict.get("licenseInfoFromFiles"), + self.license_expression_parser.parse_license_expression) + except ConstructorTypeErrors as err: + logger.append_all(err.get_messages()) + license_info_from_file = None + try: + originator: Optional[Union[Actor, SpdxNoAssertion]] = parse_optional_field(package_dict.get("originator"), + self.actor_parser.parse_actor_or_no_assert) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + originator = None + package_file_name: Optional[str] = package_dict.get("packageFileName") + try: + package_verification_code: Optional[PackageVerificationCode] = parse_optional_field( + package_dict.get("packageVerificationCode"), self.parse_package_verification_code) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + package_verification_code = None + + try: + primary_package_purpose: Optional[PackagePurpose] = parse_optional_field( + package_dict.get("primaryPackagePurpose"), self.parse_primary_package_purpose) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + primary_package_purpose = None + + try: + release_date: Optional[datetime] = parse_optional_field(package_dict.get("releaseDate"), datetime_from_str) + except ValueError: + logger.append("ValueError while parsing releaseDate.") + release_date = None + source_info: Optional[str] = package_dict.get("sourceInfo") + summary: Optional[str] = package_dict.get("summary") + try: + supplier: Optional[Union[Actor, SpdxNoAssertion]] = parse_optional_field(package_dict.get("supplier"), + self.actor_parser.parse_actor_or_no_assert) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + supplier = None + try: + valid_until_date: Optional[datetime] = parse_optional_field(package_dict.get("validUntilDate"), + datetime_from_str) + except ValueError: + logger.append("ValueError while parsing validUntilDate.") + valid_until_date = None + + version_info: Optional[str] = package_dict.get("versionInfo") + if logger.has_messages(): + raise SPDXParsingError([f"Error while parsing Package {name}: {logger.get_messages()}"]) + + try: + package = Package(spdx_id=spdx_id, name=name, download_location=download_location, version=version_info, + file_name=package_file_name, supplier=supplier, originator=originator, + files_analyzed=files_analyzed, + verification_code=package_verification_code, checksums=checksums, homepage=homepage, + source_info=source_info, + license_concluded=license_concluded, license_info_from_files=license_info_from_file, + license_declared=license_declared, + license_comment=license_comments, copyright_text=copyright_text, summary=summary, + description=description, + comment=comment, external_references=external_refs, attribution_texts=attribution_texts, + primary_package_purpose=primary_package_purpose, + release_date=release_date, built_date=built_date, valid_until_date=valid_until_date) + + except ConstructorTypeErrors as err: + raise SPDXParsingError([f"Error while constructing Package {name}: {err.get_messages()}"]) + + return package + + def parse_external_refs(self, external_ref_dicts: List[Dict]) -> List[ExternalPackageRef]: + external_refs = [] + for external_ref_dict in external_ref_dicts: + try: + external_ref: ExternalPackageRef = self.parse_external_ref(external_ref_dict) + external_refs.append(external_ref) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + return external_refs + + def parse_external_ref(self, external_ref_dict: Dict) -> ExternalPackageRef: + logger = Logger() + try: + ref_category = self.parse_external_ref_category(external_ref_dict.get("referenceCategory")) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + ref_category = None + ref_locator = external_ref_dict.get("referenceLocator") + ref_type = external_ref_dict.get("referenceType") + comment = external_ref_dict.get("comment") + if logger.has_messages(): + raise SPDXParsingError([f"Error while parsing external ref: {logger.get_messages()}"]) + try: + external_ref = ExternalPackageRef(category=ref_category, reference_type=ref_type, locator=ref_locator, + comment=comment) + except ConstructorTypeErrors as err: + raise SPDXParsingError([f"Error while constructing external ref: {err.get_messages()}"]) + + return external_ref + + @staticmethod + def parse_external_ref_category(external_ref_category_str: str) -> ExternalPackageRefCategory: + try: + external_ref_category = ExternalPackageRefCategory[ + transform_json_str_to_enum_name(external_ref_category_str)] + except KeyError: + raise SPDXParsingError([f"Category {external_ref_category_str} not valid for externalPackageRef."]) + + return external_ref_category + + @staticmethod + def parse_package_verification_code(verification_code_dict: Dict) -> PackageVerificationCode: + excluded_files: List[str] = verification_code_dict.get("packageVerificationCodeExcludedFiles") + verification_code_value: str = verification_code_dict.get("packageVerificationCodeValue") + try: + package_verification_code = PackageVerificationCode(value=verification_code_value, + excluded_files=excluded_files) + except ConstructorTypeErrors as err: + raise SPDXParsingError([f"Error while parsing package verification code: {err.get_messages()}"]) + + return package_verification_code + + @staticmethod + def parse_primary_package_purpose(primary_package_purpose: str) -> PackagePurpose: + try: + return PackagePurpose[transform_json_str_to_enum_name(primary_package_purpose)] + except KeyError: + raise SPDXParsingError([f"Invalid primaryPackagePurpose: {primary_package_purpose}"]) + + @staticmethod + def parse_download_location(download_location: str) -> Union[str, SpdxNoAssertion, SpdxNone]: + if download_location == SpdxNone().__str__(): + return SpdxNone() + if download_location == SpdxNoAssertion().__str__(): + return SpdxNoAssertion() + return download_location diff --git a/src/parser/json/relationship_parser.py b/src/parser/json/relationship_parser.py new file mode 100644 index 000000000..2c9ff6dd0 --- /dev/null +++ b/src/parser/json/relationship_parser.py @@ -0,0 +1,223 @@ +# 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 typing import Dict, List, Optional + +from src.model.relationship import Relationship, RelationshipType +from src.model.typing.constructor_type_errors import ConstructorTypeErrors +from src.parser.error import SPDXParsingError +from src.parser.json.dict_parsing_functions import transform_json_str_to_enum_name +from src.parser.logger import Logger + + +class RelationshipParser: + logger: Logger + + def __init__(self): + self.logger = Logger() + + def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]: + relationships_list = [] + relationships_dicts: List[Dict] = input_doc_dict.get("relationships") + if relationships_dicts: + try: + relationships = self.parse_relationships(relationship_dicts=relationships_dicts) + relationships_list.extend(relationships) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + + document_describes: List[str] = input_doc_dict.get("documentDescribes") + doc_spdx_id: str = input_doc_dict.get("SPDXID") + if document_describes: + try: + describes_relationships = self.parse_document_describes(doc_spdx_id=doc_spdx_id, + described_spdx_ids=document_describes, + created_relationships=relationships_list) + relationships_list.extend(describes_relationships) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + + package_dicts: List[Dict] = input_doc_dict.get("packages") + if package_dicts: + try: + contains_relationships = self.parse_has_files(package_dicts=package_dicts, + created_relationships=relationships_list) + relationships_list.extend(contains_relationships) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + + file_dicts: List[Dict] = input_doc_dict.get("files") + if file_dicts: + # not implemented yet, deal with deprecated fields in file + try: + dependency_relationships = self.parse_file_dependencies(file_dicts=file_dicts) + relationships_list.extend(dependency_relationships) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + generated_relationships = self.parse_artifact_of(file_dicts=file_dicts) + + if self.logger.has_messages(): + raise SPDXParsingError(self.logger.get_messages()) + + return relationships_list + + def parse_relationships(self, relationship_dicts: List[Dict]) -> List[Relationship]: + logger = Logger() + relationship_list = [] + for relationship_dict in relationship_dicts: + try: + relationship_list.append(self.parse_relationship(relationship_dict)) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + if logger.has_messages(): + raise SPDXParsingError(logger.has_messages()) + return relationship_list + + def parse_relationship(self, relationship_dict: Dict) -> Relationship: + logger = Logger() + spdx_element_id: str = relationship_dict.get("spdxElementId") + related_spdx_element: str = relationship_dict.get("relatedSpdxElement") + try: + relationship_type: Optional[RelationshipType] = self.parse_relationship_type( + relationship_dict.get("relationshipType")) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + relationship_type = None + relationship_comment: str = relationship_dict.get("comment") + if logger.has_messages(): + raise SPDXParsingError([f"Error while parsing relationship: {logger.get_messages()}"]) + try: + relationship = Relationship(spdx_element_id=spdx_element_id, + relationship_type=relationship_type, + related_spdx_element_id=related_spdx_element, comment=relationship_comment) + except ConstructorTypeErrors as err: + raise SPDXParsingError([f"Error while constructing relationship: {err.get_messages()}"]) + return relationship + + @staticmethod + def parse_relationship_type(relationship_type_str: str) -> RelationshipType: + try: + relationship_type = RelationshipType[transform_json_str_to_enum_name(relationship_type_str)] + except KeyError: + raise SPDXParsingError([f"RelationshipType {relationship_type_str} is not valid."]) + except AttributeError: + raise SPDXParsingError([f"RelationshipType must be str, not {type(relationship_type_str).__name__}."]) + return relationship_type + + def parse_document_describes(self, doc_spdx_id: str, described_spdx_ids: List[str], + created_relationships: List[Relationship]) -> List[Relationship]: + logger = Logger() + describes_relationships = [] + for spdx_id in described_spdx_ids: + try: + describes_relationship = Relationship(spdx_element_id=doc_spdx_id, + relationship_type=RelationshipType.DESCRIBES, + related_spdx_element_id=spdx_id) + except ConstructorTypeErrors as err: + logger.append(err.get_messages()) + continue + if not self.check_if_relationship_exists(describes_relationship, created_relationships): + describes_relationships.append(describes_relationship) + if logger.has_messages(): + raise SPDXParsingError([f"Error while creating describes_relationship : {logger.get_messages()}"]) + + return describes_relationships + + def parse_has_files(self, package_dicts: List[Dict], created_relationships: List[Relationship]) -> List[ + Relationship]: + logger = Logger() + contains_relationships = [] + for package in package_dicts: + package_spdx_id = package.get("SPDXID") + contained_files = package.get("hasFiles") + if not contained_files: + continue + for file_spdx_id in contained_files: + try: + contains_relationship = Relationship(spdx_element_id=package_spdx_id, + relationship_type=RelationshipType.CONTAINS, + related_spdx_element_id=file_spdx_id) + except ConstructorTypeErrors as err: + logger.append(err.get_messages()) + continue + if not self.check_if_relationship_exists(relationship=contains_relationship, + created_relationships=created_relationships): + contains_relationships.append(contains_relationship) + if logger.has_messages(): + raise SPDXParsingError([f"Error while creating describes_relationship : {logger.get_messages()}"]) + + return contains_relationships + + def check_if_relationship_exists(self, relationship: Relationship, + created_relationships: List[Relationship]) -> bool: + created_relationships_without_comment: List[Relationship] = self.ignore_any_comments_in_relationship_list( + created_relationships) + if relationship in created_relationships_without_comment: + return True + relationship_converted: Relationship = self.convert_relationship(relationship) + if relationship_converted in created_relationships_without_comment: + return True + + return False + + @staticmethod + def ignore_any_comments_in_relationship_list(created_relationships: List[Relationship]) -> List[Relationship]: + relationships_without_comment = [Relationship(relationship_type=relationship.relationship_type, + related_spdx_element_id=relationship.related_spdx_element_id, + spdx_element_id=relationship.spdx_element_id) for relationship in + created_relationships] + return relationships_without_comment + + @staticmethod + def convert_relationship(relationship: Relationship) -> Relationship: + if relationship.relationship_type == RelationshipType.DESCRIBES: + return Relationship(related_spdx_element_id=relationship.spdx_element_id, + spdx_element_id=relationship.related_spdx_element_id, + relationship_type=RelationshipType.DESCRIBED_BY, comment=relationship.comment) + if relationship.relationship_type == RelationshipType.DESCRIBED_BY: + return Relationship(related_spdx_element_id=relationship.spdx_element_id, + spdx_element_id=relationship.related_spdx_element_id, + relationship_type=RelationshipType.DESCRIBES, comment=relationship.comment) + if relationship.relationship_type == RelationshipType.CONTAINS: + return Relationship(related_spdx_element_id=relationship.spdx_element_id, + spdx_element_id=relationship.related_spdx_element_id, + relationship_type=RelationshipType.CONTAINED_BY, comment=relationship.comment) + if relationship.relationship_type == RelationshipType.CONTAINED_BY: + return Relationship(related_spdx_element_id=relationship.spdx_element_id, + spdx_element_id=relationship.related_spdx_element_id, + relationship_type=RelationshipType.CONTAINS, comment=relationship.comment) + + @staticmethod + def parse_file_dependencies(file_dicts: List[Dict]) -> List[Relationship]: + logger = Logger() + dependency_relationships = [] + for file in file_dicts: + file_spdx_id: str = file.get("SPDXID") + dependency_of: List[str] = file.get("fileDependencies") + if not dependency_of: + continue + for dependency in dependency_of: + try: + dependency_relationship = Relationship(spdx_element_id=dependency, + relationship_type=RelationshipType.DEPENDENCY_OF, + related_spdx_element_id=file_spdx_id) + except ConstructorTypeErrors as err: + logger.append_all(err.get_messages()) + continue + dependency_relationships.append(dependency_relationship) + if logger.has_messages(): + raise SPDXParsingError([f"Error while creating dependency relationships: {logger.get_messages()}"]) + return dependency_relationships + + @staticmethod + def parse_artifact_of(file_dicts: List[Dict]) -> List[Relationship]: + generated_relationships = [] + # TODO: artifactOfs is deprecated and should be converted to an external package and a generated from relationship + return generated_relationships diff --git a/src/parser/json/snippet_parser.py b/src/parser/json/snippet_parser.py new file mode 100644 index 000000000..5655d0543 --- /dev/null +++ b/src/parser/json/snippet_parser.py @@ -0,0 +1,136 @@ +# 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 auto, Enum +from typing import Dict, Tuple, List, Optional, Union + +from src.model.license_expression import LicenseExpression +from src.model.snippet import Snippet +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone +from src.model.typing.constructor_type_errors import ConstructorTypeErrors +from src.parser.error import SPDXParsingError +from src.parser.json.dict_parsing_functions import parse_optional_field + +from src.parser.json.license_expression_parser import LicenseExpressionParser +from src.parser.logger import Logger + + +class RangeType(Enum): + BYTE = auto() + LINE = auto() + + +class SnippetParser: + logger: Logger + license_expression_parser = LicenseExpressionParser + + def __init__(self): + self.logger = Logger() + self.license_expression_parser = LicenseExpressionParser() + + def parse_snippets(self, snippet_dicts_list: List[Dict]) -> List[Snippet]: + snippets_list = [] + for snippet_dict in snippet_dicts_list: + try: + snippets_list.append(self.parse_snippet(snippet_dict)) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + if self.logger.has_messages(): + raise SPDXParsingError(self.logger.get_messages()) + + return snippets_list + + def parse_snippet(self, snippet_dict: Dict) -> Snippet: + logger = Logger() + spdx_id: str = snippet_dict.get("SPDXID") + file_spdx_id: str = snippet_dict.get("snippetFromFile") + name: Optional[str] = snippet_dict.get("name") + try: + ranges: Dict = self.parse_ranges(snippet_dict.get("ranges")) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + ranges = {} + byte_range: Tuple[int, int] = ranges.get(RangeType.BYTE) + + line_range: Optional[Tuple[int, int]] = ranges.get(RangeType.LINE) + attribution_texts: List[str] = snippet_dict.get("attributionTexts") + comment: Optional[str] = snippet_dict.get("comment") + copyright_text: Optional[str] = snippet_dict.get("copyrightText") + license_comment: Optional[str] = snippet_dict.get("licenseComments") + try: + concluded_license: Optional[Union[ + LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_optional_field( + snippet_dict.get("licenseConcluded"), self.license_expression_parser.parse_license_expression) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + concluded_license = None + try: + license_info: Optional[Union[List[ + LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_optional_field( + snippet_dict.get("licenseInfoInSnippets"), self.license_expression_parser.parse_license_expression) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + license_info = None + if logger.has_messages(): + raise SPDXParsingError([f"Error while parsing snippet: {logger.get_messages()}"]) + try: + snippet = Snippet(spdx_id=spdx_id, name=name, byte_range=byte_range, file_spdx_id=file_spdx_id, + line_range=line_range, attribution_texts=attribution_texts, comment=comment, + copyright_text=copyright_text, license_comment=license_comment, + concluded_license=concluded_license, + license_info_in_snippet=license_info) + except ConstructorTypeErrors as err: + raise SPDXParsingError([f"Error while parsing snippet: {err.get_messages()}"]) + return snippet + + def parse_ranges(self, ranges_from_snippet: List[Dict]) -> Dict: + if not ranges_from_snippet: + raise SPDXParsingError([f"No ranges dict provided."]) + logger = Logger() + ranges = {} + for range_dict in ranges_from_snippet: + try: + range_type: RangeType = self.validate_range_and_get_type(range_dict) + start_end_tuple: Tuple[int, int] = SnippetParser.get_start_end_tuple(range_dict, range_type) + ranges[range_type] = start_end_tuple + except ValueError as error: + logger.append(error.args[0]) + if logger.has_messages(): + raise SPDXParsingError([f"Error while parsing ranges_dict: {logger.get_messages()}"]) + return ranges + + @staticmethod + def get_start_end_tuple(range_dict: Dict, range_type: RangeType) -> Tuple[int, int]: + end_pointer: Dict = range_dict["endPointer"] + start_pointer: Dict = range_dict["startPointer"] + if range_type == RangeType.BYTE: + start: int = start_pointer["offset"] + end: int = end_pointer["offset"] + else: + start: int = start_pointer["lineNumber"] + end: int = end_pointer["lineNumber"] + return start, end + + def validate_range_and_get_type(self, range_dict: Dict) -> RangeType: + if ("startPointer" not in range_dict) or ("endPointer" not in range_dict): + raise ValueError("Start-/ Endpointer missing in ranges_dict.") + start_pointer_type = self.validate_pointer_and_get_type(range_dict["startPointer"]) + end_pointer_type = self.validate_pointer_and_get_type(range_dict["endPointer"]) + if start_pointer_type != end_pointer_type: + raise ValueError("Type of startpointer is not the same as type of endpointer.") + return start_pointer_type + + @staticmethod + def validate_pointer_and_get_type(pointer: Dict) -> RangeType: + if ("offset" in pointer and "lineNumber" in pointer) or ( + "offset" not in pointer and "lineNumber" not in pointer): + raise ValueError("Couldn't determine type of pointer.") + return RangeType.BYTE if "offset" in pointer else RangeType.LINE diff --git a/src/parser/logger.py b/src/parser/logger.py new file mode 100644 index 000000000..cbf4b5d90 --- /dev/null +++ b/src/parser/logger.py @@ -0,0 +1,31 @@ +# 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 typing import List + + +class Logger: + messages: List[str] + + def __init__(self): + self.messages = [] + + def append(self, message: str): + self.messages.append(message) + + def append_all(self, messages_to_append: List[str]): + for message in messages_to_append: + self.messages.append(message) + + def has_messages(self): + return bool(self.messages) + + def get_messages(self): + return list(self.messages) diff --git a/tests/parser/__init__.py b/tests/parser/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/parser/test_actor_parser.py b/tests/parser/test_actor_parser.py new file mode 100644 index 000000000..7dcc6b908 --- /dev/null +++ b/tests/parser/test_actor_parser.py @@ -0,0 +1,35 @@ +# 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. +import pytest + +from src.model.actor import ActorType +from src.parser.error import SPDXParsingError +from src.parser.json.actor_parser import ActorParser + + +def test_actor_parser(): + actor_parser = ActorParser() + actor_string = "Person: Jane Doe (jane.doe@example.com)" + + actor = actor_parser.parse_actor(actor_string) + + assert actor.actor_type == ActorType.PERSON + assert actor.name == "Jane Doe" + assert actor.email == "jane.doe@example.com" + +def test_invalid_actor(): + actor_parser = ActorParser() + actor_string = "Perso: Jane Doe (jane.doe@example.com)" + + with pytest.raises(SPDXParsingError) as err: + _ = actor_parser.parse_actor(actor_string) + assert err.typename == 'SPDXParsingError' + assert err.value.messages[0] == "Actor Perso: Jane Doe (jane.doe@example.com) doesn't match any of person, organization or tool." diff --git a/tests/parser/test_annotation_parser.py b/tests/parser/test_annotation_parser.py new file mode 100644 index 000000000..2fc12f17c --- /dev/null +++ b/tests/parser/test_annotation_parser.py @@ -0,0 +1,119 @@ +# 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. +import datetime + +import pytest + +from src.model.actor import Actor, ActorType +from src.model.annotation import AnnotationType, Annotation +from src.parser.error import SPDXParsingError +from src.parser.json.annotation_parser import AnnotationParser + + +def test_annotation_parser(): + annotation_parser = AnnotationParser() + annotation_dict = { + "annotationDate": "2010-01-29T18:30:22Z", + "annotationType": "OTHER", + "annotator": "Person: Jane Doe ()", + "comment": "Document level annotation" + } + + annotation = annotation_parser.parse_annotation(annotation_dict, spdx_id="SPDXRef-DOCUMENT") + + assert annotation.annotator == Actor(ActorType.PERSON, name="Jane Doe") + assert annotation.annotation_type == AnnotationType.OTHER + assert annotation.annotation_date == datetime.datetime(2010, 1, 29, 18, 30, 22) + assert annotation.annotation_comment == "Document level annotation" + + +def test_parse_all_annotations(): + annotation_parser = AnnotationParser() + doc_dict = { + "SPDXID": "SPDXRef-DOCUMENT", + "packages": [ + {"SPDXID": "SPDXRef-Package", + "annotations": [ + {"annotationDate": "2010-01-29T18:30:22Z", + "annotationType": "OTHER", + "annotator": "Person: Jane Doe ()", + "comment": "Package level annotation"} + ]}], + "files": [ + {"SPDXID": "SPDXRef-File", + "annotations": [ + {"annotationDate": "2010-01-29T18:30:22Z", + "annotationType": "OTHER", + "annotator": "Person: Jane Doe ()", + "comment": "File level annotation"} + ]} + ], + "snippets": [ + {"SPDXID": "SPDXRef-Snippet", + "annotations": [ + {"annotationDate": "2010-01-29T18:30:22Z", + "annotationType": "OTHER", + "annotator": "Person: Jane Doe ()", + "comment": "Snippet level annotation"} + ]}], + "revieweds": + [{ + "reviewDate": "2010-01-29T18:30:22Z", + "reviewer": "Person: Jane Doe ()", + "comment": "Review annotation" + }] + } + + annotations = annotation_parser.parse_all_annotations(input_doc_dict=doc_dict) + + assert len(annotations) == 4 + assert annotations == [Annotation(spdx_id='SPDXRef-DOCUMENT', + annotation_type=AnnotationType.REVIEW, + annotator=Actor(actor_type=ActorType.PERSON, + name='Jane Doe', + email=None), + annotation_date=datetime.datetime(2010, 1, 29, 18, 30, 22), + annotation_comment='Review annotation'), + Annotation(spdx_id='SPDXRef-Package', + annotation_type=AnnotationType.OTHER, + annotator=Actor(actor_type=ActorType.PERSON, + name='Jane Doe', + email=None), + annotation_date=datetime.datetime(2010, 1, 29, 18, 30, 22), + annotation_comment='Package level annotation'), + Annotation(spdx_id='SPDXRef-File', + annotation_type=AnnotationType.OTHER, + annotator=Actor(actor_type=ActorType.PERSON, + name='Jane Doe', + email=None), + annotation_date=datetime.datetime(2010, 1, 29, 18, 30, 22), + annotation_comment='File level annotation'), + Annotation(spdx_id='SPDXRef-Snippet', + annotation_type=AnnotationType.OTHER, + annotator=Actor(actor_type=ActorType.PERSON, + name='Jane Doe', + email=None), + annotation_date=datetime.datetime(2010, 1, 29, 18, 30, 22), + annotation_comment='Snippet level annotation')] + + +def test_parse_incomplete_annotation(): + annotation_parser = AnnotationParser() + annotation_dict = { + "annotator": "Person: Jane Doe ()" + } + + with pytest.raises(SPDXParsingError) as err: + _ = annotation_parser.parse_annotation(annotation_dict) + + assert err.type == SPDXParsingError + assert err.value.messages == ["Error while parsing annotation: ['Invalid annotation type: None', " + "'ValueError while parsing annotationDate.']"] diff --git a/tests/parser/test_checksum_parser.py b/tests/parser/test_checksum_parser.py new file mode 100644 index 000000000..035ff8de8 --- /dev/null +++ b/tests/parser/test_checksum_parser.py @@ -0,0 +1,43 @@ +# 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. +import pytest + +from src.model.checksum import ChecksumAlgorithm +from src.parser.error import SPDXParsingError +from src.parser.json.checksum_parser import ChecksumParser + + +def test_checksum_parser(): + checksum_parser = ChecksumParser() + checksum_dict = { + "algorithm": "SHA1", + "checksumValue": "d6a770ba38583ed4bb4525bd96e50461655d2759" + } + + checksum = checksum_parser.parse_checksum(checksum_dict) + + assert checksum.value == "d6a770ba38583ed4bb4525bd96e50461655d2759" + assert checksum.algorithm == ChecksumAlgorithm.SHA1 + + +def test_invalid_checksum(): + checksum_parser = ChecksumParser() + checksum_dict = { + "algorithm": "SHA", + "checksumValue": "d6a770ba38583ed4bb4525bd96e50461655d2759" + } + + with pytest.raises(SPDXParsingError) as err: + _ = checksum_parser.parse_checksum(checksum_dict) + + assert err.typename == 'SPDXParsingError' + assert err.value.messages[0] == "Error while parsing checksum: ['Algorithm SHA not valid for checksum.']" + diff --git a/tests/parser/test_creation_info_parser.py b/tests/parser/test_creation_info_parser.py new file mode 100644 index 000000000..877c13607 --- /dev/null +++ b/tests/parser/test_creation_info_parser.py @@ -0,0 +1,61 @@ +# 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 datetime import datetime + +import pytest + +from src.model.actor import Actor, ActorType +from src.model.version import Version +from src.parser.error import SPDXParsingError +from src.parser.json.creation_info_parser import CreationInfoParser + + +def test_creation_info_parser(): + creation_info_parser = CreationInfoParser() + doc_dict = { + "spdxVersion": "2.3", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "Example Document", + "dataLicense": "CC0-1.0", + "documentNamespace": "namespace", + "creationInfo": { + "created": "2010-01-29T18:30:22Z", + "creators": ["Tool: LicenseFind-1.0", "Organization: ExampleCodeInspect ()", "Person: Jane Doe ()"], + "licenseListVersion": "3.7", + "comment": "Some comment." + } + } + creation_info = creation_info_parser.parse_creation_info(doc_dict) + + assert creation_info.spdx_version == "2.3" + assert creation_info.spdx_id == "SPDXRef-DOCUMENT" + assert creation_info.name == "Example Document" + assert creation_info.document_namespace == "namespace" + assert creation_info.created == datetime(2010, 1, 29, 18, 30, 22) + assert creation_info.creators == [Actor(ActorType.TOOL, "LicenseFind-1.0"), + Actor(ActorType.ORGANIZATION, "ExampleCodeInspect"), + Actor(ActorType.PERSON, "Jane Doe")] + assert creation_info.license_list_version == Version(3, 7) + + +def test_parse_incomplete_creation_info(): + creation_info_parser = CreationInfoParser() + doc_dict = { + "spdxVersion": "2.3", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "Example Document" + } + + with pytest.raises(SPDXParsingError) as err: + _ = creation_info_parser.parse_creation_info(doc_dict) + + assert err.type == SPDXParsingError + assert err.value.messages == ["Error while parsing doc Example Document: ['CreationInfo is not valid.']"] diff --git a/tests/parser/test_extracted_licensing_info_parser.py b/tests/parser/test_extracted_licensing_info_parser.py new file mode 100644 index 000000000..1a6d532f1 --- /dev/null +++ b/tests/parser/test_extracted_licensing_info_parser.py @@ -0,0 +1,69 @@ +# 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. +import pytest + +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.parser.error import SPDXParsingError +from src.parser.json.extracted_licensing_parser import ExtractedLicensingInfoParser + + +def test_extracted_licensing_info_parser(): + extracted_licensing_info_parser = ExtractedLicensingInfoParser() + + extracted_licensing_infos_dict = { + + "licenseId": "LicenseRef-Beerware-4.2", + "comment": "The beerware license has a couple of other standard variants.", + "extractedText": "\"THE BEER-WARE LICENSE\" (Revision 42):\nphk@FreeBSD.ORG wrote this file. As long as you retain this notice you\ncan do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp", + "name": "Beer-Ware License (Version 42)", + "seeAlsos": ["http://people.freebsd.org/~phk/"] + + } + + extracted_licensing_info = extracted_licensing_info_parser.parse_extracted_licensing_info( + extracted_licensing_infos_dict) + + assert extracted_licensing_info.license_id == "LicenseRef-Beerware-4.2" + assert extracted_licensing_info.comment == "The beerware license has a couple of other standard variants." + assert extracted_licensing_info.extracted_text == "\"THE BEER-WARE LICENSE\" (Revision 42):\nphk@FreeBSD.ORG wrote this file. As long as you retain this notice you\ncan do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp" + assert extracted_licensing_info.license_name == "Beer-Ware License (Version 42)" + assert extracted_licensing_info.cross_references == ["http://people.freebsd.org/~phk/"] + + +def test_parse_invalid_extracted_licensing_info(): + extracted_licensing_info_parser = ExtractedLicensingInfoParser() + + extracted_licensing_infos_dict = { + "licenseId": "LicenseRef-Beerware-4.2", + "comment": 56, + "extractedText": "\"THE BEER-WARE LICENSE\" (Revision 42):\nphk@FreeBSD.ORG wrote this file. As long as you retain this notice you\ncan do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp", + "name": "Beer-Ware License (Version 42)", + "seeAlsos": ["http://people.freebsd.org/~phk/"] + + } + + with pytest.raises(SPDXParsingError) as err: + _ = extracted_licensing_info_parser.parse_extracted_licensing_info(extracted_licensing_infos_dict) + + assert err.type == SPDXParsingError + assert err.value.messages == ["Error while constructing ExtractedLicensingInfo : ['SetterError " + 'ExtractedLicensingInfo: type of argument "comment" must be one of (str, ' + "NoneType); got int instead: 56']"] + + +def test_parse_extracted_licensing_info_name(): + extracted_licensing_info_parser = ExtractedLicensingInfoParser() + extracted_licensing_info_name_str = "NOASSERTION" + + extracted_licensing_info_name = extracted_licensing_info_parser.parse_extracted_licensing_info_name( + extracted_licensing_info_name_str) + + assert type(extracted_licensing_info_name) == SpdxNoAssertion diff --git a/tests/parser/test_file_parser.py b/tests/parser/test_file_parser.py new file mode 100644 index 000000000..f24339e29 --- /dev/null +++ b/tests/parser/test_file_parser.py @@ -0,0 +1,127 @@ +# 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. +import pytest + +from src.model.checksum import Checksum, ChecksumAlgorithm +from src.model.file import FileType +from src.model.license_expression import LicenseExpression +from src.parser.error import SPDXParsingError +from src.parser.json.file_parser import FileParser + + +def test_file_parser(): + file_parser = FileParser() + file_dict = { + "SPDXID": "SPDXRef-File", + "attributionTexts": ["Some attribution text."], + "checksums": [{ + "algorithm": "SHA1", + "checksumValue": "d6a770ba38583ed4bb4525bd96e50461655d2758" + }, { + "algorithm": "MD5", + "checksumValue": "624c1abb3664f4b35547e7c73864ad24" + }], + "comment": "The concluded license was taken from the package level that the file was included in.\nThis information was found in the COPYING.txt file in the xyz directory.", + "copyrightText": "Copyright 2008-2010 John Smith", + "fileContributors": ["The Regents of the University of California", + "Modified by Paul Mundt lethal@linux-sh.org", "IBM Corporation"], + "fileName": "./package/foo.c", + "fileTypes": ["SOURCE"], + "licenseComments": "The concluded license was taken from the package level that the file was included in.", + "licenseConcluded": "(LGPL-2.0-only OR LicenseRef-2)", + "licenseInfoInFiles": ["GPL-2.0-only", "LicenseRef-2"], + "noticeText": "Copyright (c) 2001 Aaron Lehmann aaroni@vitelus.com\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: \nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + } + + file = file_parser.parse_file(file_dict) + + assert file.name == "./package/foo.c" + assert file.spdx_id == "SPDXRef-File" + assert file.checksums == [Checksum(ChecksumAlgorithm.SHA1, "d6a770ba38583ed4bb4525bd96e50461655d2758"), + Checksum(ChecksumAlgorithm.MD5, "624c1abb3664f4b35547e7c73864ad24")] + assert file.comment == "The concluded license was taken from the package level that the file was included in.\nThis information was found in the COPYING.txt file in the xyz directory." + assert file.copyright_text == "Copyright 2008-2010 John Smith" + assert file.file_type == [FileType.SOURCE] + assert file.contributors == ["The Regents of the University of California", + "Modified by Paul Mundt lethal@linux-sh.org", "IBM Corporation"] + assert file.concluded_license == LicenseExpression("(LGPL-2.0-only OR LicenseRef-2)") + assert file.license_info_in_file == [LicenseExpression("GPL-2.0-only"), LicenseExpression("LicenseRef-2")] + assert file.license_comment == "The concluded license was taken from the package level that the file was included in." + assert file.attribution_texts == ["Some attribution text."] + + +def test_parse_incomplete_file(): + file_parser = FileParser() + file_dict = { + "SPDXID": "SPDXRef-File", + "fileName": "Incomplete File" + } + + with pytest.raises(SPDXParsingError) as err: + _ = file_parser.parse_file(file_dict) + + assert err.type == SPDXParsingError + assert err.value.messages == ["Error while parsing file Incomplete File: ['No checksums provided, checksums are " + "mandatory for files.']"] + + +def test_parse_falsy_files(): + file_parser = FileParser() + files = [{"SPDXID": "SPDXRef-File", + "fileName": "Incomplete File"}, + {"SPDXID": "SPDXRef-File", + "attributionTexts": ["Some attribution text."], + "checksums": [{ + "algorithm": "SHA1", + "checksumValue": "d6a770ba38583ed4bb4525bd96e50461655d2758" + }, { + "algorithm": "MD5", + "checksumValue": "624c1abb3664f4b35547e7c73864ad24" + }]}, + {"SPDXID": "SPDXRef-File", + "attributionTexts": ["Some attribution text."], + "checksums": [{ + "algorithm": "SHA1", + "checksumValue": "d6a770ba38583ed4bb4525bd96e50461655d2758" + }, { + "algorithm": "MD", + "checksumValue": "624c1abb3664f4b35547e7c73864ad24" + }]}, + ] + + with pytest.raises(SPDXParsingError) as err: + _ = file_parser.parse_files(files) + + assert err.type == SPDXParsingError + assert err.value.messages == ["Error while parsing file Incomplete File: ['No checksums provided, checksums " + "are mandatory for files.']", + "Error while constructing file None: ['SetterError File: type of argument " + '"name" must be str; got NoneType instead: None\']', + 'Error while parsing file None: ["Error while parsing checksum: [\'Algorithm ' + 'MD not valid for checksum.\']"]'] + +def test_parse_file_types(): + file_parser = FileParser() + file_types_list = ["OTHER", "APPLICATION"] + + file_types = file_parser.parse_file_types(file_types_list) + + assert file_types == [FileType.OTHER, FileType.APPLICATION] + +def test_parse_invalid_file_types(): + file_parser = FileParser() + file_types_list = ["OTHER", "APPLICAON"] + + with pytest.raises(SPDXParsingError) as err: + _ = file_parser.parse_file_types(file_types_list) + + assert err.type == SPDXParsingError + assert err.value.messages == ["Error while parsing file_types: ['FileType APPLICAON is not valid.']"] diff --git a/tests/parser/test_json_parser.py b/tests/parser/test_json_parser.py new file mode 100644 index 000000000..6704d36e3 --- /dev/null +++ b/tests/parser/test_json_parser.py @@ -0,0 +1,56 @@ +# 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. + +import os +import pytest + +from src.model.document import Document +from src.parser.error import SPDXParsingError +from src.parser.json.json_parser import JsonParser + +def test_json_parser_file_not_found(): + with pytest.raises(SPDXParsingError) as err: + wrong_file_path = os.path.join(os.path.dirname(__file__), 'test.json') + _ = JsonParser().parse(wrong_file_path) + + assert err.typename == "SPDXParsingError" + assert err.value.messages[0] == f"File {wrong_file_path} not found." + + +def test_json_parser_with_2_3_example(): + doc = JsonParser().parse(os.path.join(os.path.dirname(__file__),"../data/formats/SPDXJSONExample-v2.3.spdx.json")) + assert type(doc) == Document + assert len(doc.annotations) == 5 + assert len(doc.files) == 5 + assert len(doc.packages) == 4 + assert len(doc.snippets) == 1 + assert len(doc.relationships) == 23 + assert len(doc.extracted_licensing_info) == 5 + +def test_json_parser_with_2_2_example(): + doc = JsonParser().parse(os.path.join(os.path.dirname(__file__),"../data/formats/SPDXJSONExample-v2.2.spdx.json")) + assert type(doc) == Document + assert len(doc.annotations) == 5 + assert len(doc.files) == 4 + assert len(doc.packages) == 4 + assert len(doc.snippets) == 1 + assert len(doc.relationships) == 11 + assert len(doc.extracted_licensing_info) == 5 + +def test_json_parser_with(): + doc = JsonParser().parse(os.path.join(os.path.dirname(__file__),"../data/formats/SPDXJsonExample.json")) + assert type(doc) == Document + assert len(doc.annotations) == 1 + assert len(doc.files) == 2 + assert len(doc.packages) == 1 + assert len(doc.snippets) == 1 + assert len(doc.relationships) == 3 + assert len(doc.extracted_licensing_info) == 4 diff --git a/tests/parser/test_license_expression_parser.py b/tests/parser/test_license_expression_parser.py new file mode 100644 index 000000000..0025e6e7d --- /dev/null +++ b/tests/parser/test_license_expression_parser.py @@ -0,0 +1,41 @@ +# 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.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone +from src.parser.json.license_expression_parser import LicenseExpressionParser + + +def test_license_expression_parser(): + license_expression_parser = LicenseExpressionParser() + license_expression_str= "License-Ref1" + + license_expression = license_expression_parser.parse_license_expression(license_expression=license_expression_str) + + assert license_expression.expression_string == "License-Ref1" + +def test_license_expression_no_assert(): + license_expression_parser = LicenseExpressionParser() + license_expression_str= "NOASSERTION" + + spdx_no_assertion = license_expression_parser.parse_license_expression(license_expression=license_expression_str) + + assert type(spdx_no_assertion) == SpdxNoAssertion + +def test_license_expression_none(): + license_expression_parser = LicenseExpressionParser() + license_expression_str = "NONE" + + spdx_none = license_expression_parser.parse_license_expression( + license_expression=license_expression_str) + + assert type(spdx_none) == SpdxNone + + diff --git a/tests/parser/test_package_parser.py b/tests/parser/test_package_parser.py new file mode 100644 index 000000000..2c460c4f6 --- /dev/null +++ b/tests/parser/test_package_parser.py @@ -0,0 +1,204 @@ +# 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 datetime import datetime + +import pytest + +from src.model.actor import Actor, ActorType +from src.model.checksum import Checksum, ChecksumAlgorithm +from src.model.license_expression import LicenseExpression +from src.model.package import PackageVerificationCode, ExternalPackageRef, ExternalPackageRefCategory, PackagePurpose +from src.parser.error import SPDXParsingError +from src.parser.json.package_parser import PackageParser + + +def test_package_parser(): + package_parser = PackageParser() + + package_dict = { + "SPDXID": "SPDXRef-Package", + "attributionTexts": [ + "The GNU C Library is free software. See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed. License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually."], + "builtDate": "2011-01-29T18:30:22Z", + "checksums": [{ + "algorithm": "MD5", + "checksumValue": "624c1abb3664f4b35547e7c73864ad24" + }, { + "algorithm": "SHA1", + "checksumValue": "85ed0817af83a24ad8da68c2b5094de69833983c" + }, { + "algorithm": "SHA256", + "checksumValue": "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd" + }, { + "algorithm": "BLAKE2b-384", + "checksumValue": "aaabd89c926ab525c242e6621f2f5fa73aa4afe3d9e24aed727faaadd6af38b620bdb623dd2b4788b1c8086984af8706" + }], + "comment": "This is a comment.", + "copyrightText": "Copyright 2008-2010 John Smith", + "description": "The GNU C Library defines functions that are specified by the ISO C standard, as well as additional features specific to POSIX and other derivatives of the Unix operating system, and extensions specific to GNU systems.", + "downloadLocation": "http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz", + "externalRefs": [{ + "referenceCategory": "SECURITY", + "referenceLocator": "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*", + "referenceType": "cpe23Type" + }, { + "comment": "This is the external ref for Acme", + "referenceCategory": "OTHER", + "referenceLocator": "acmecorp/acmenator/4.1.3-alpha", + "referenceType": "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge" + }], + "filesAnalyzed": True, + "homepage": "http://ftp.gnu.org/gnu/glibc", + "licenseComments": "The license for this project changed with the release of version x.y. The version of the project included here post-dates the license change.", + "licenseConcluded": "(LGPL-2.0-only OR LicenseRef-3)", + "licenseDeclared": "(LGPL-2.0-only AND LicenseRef-3)", + "licenseInfoFromFiles": ["GPL-2.0-only", "LicenseRef-2", "LicenseRef-1"], + "name": "glibc", + "originator": "Organization: ExampleCodeInspect (contact@example.com)", + "packageFileName": "glibc-2.11.1.tar.gz", + "packageVerificationCode": { + "packageVerificationCodeExcludedFiles": ["./package.spdx"], + "packageVerificationCodeValue": "d6a770ba38583ed4bb4525bd96e50461655d2758" + }, + "primaryPackagePurpose": "SOURCE", + "releaseDate": "2012-01-29T18:30:22Z", + "sourceInfo": "uses glibc-2_11-branch from git://sourceware.org/git/glibc.git.", + "summary": "GNU C library.", + "supplier": "Person: Jane Doe (jane.doe@example.com)", + "validUntilDate": "2014-01-29T18:30:22Z", + "versionInfo": "2.11.1" + } + + package = package_parser.parse_package(package_dict) + + assert package.spdx_id == "SPDXRef-Package" + assert package.name == "glibc" + assert package.download_location == "http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz" + assert package.version == "2.11.1" + assert package.file_name == "glibc-2.11.1.tar.gz" + assert package.supplier == Actor(ActorType.PERSON, "Jane Doe", "jane.doe@example.com") + assert package.originator == Actor(ActorType.ORGANIZATION, "ExampleCodeInspect", "contact@example.com") + assert package.files_analyzed == True + assert package.verification_code == PackageVerificationCode(value="d6a770ba38583ed4bb4525bd96e50461655d2758", + excluded_files=["./package.spdx"]) + assert len(package.checksums) == 4 + assert package.checksums == [Checksum(ChecksumAlgorithm.MD5, "624c1abb3664f4b35547e7c73864ad24"), + Checksum(ChecksumAlgorithm.SHA1, "85ed0817af83a24ad8da68c2b5094de69833983c"), + Checksum(ChecksumAlgorithm.SHA256, + "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd"), + Checksum(ChecksumAlgorithm.BLAKE2B_384, + "aaabd89c926ab525c242e6621f2f5fa73aa4afe3d9e24aed727faaadd6af38b620bdb623dd2b4788b1c8086984af8706")] + assert package.homepage == "http://ftp.gnu.org/gnu/glibc" + assert package.source_info == "uses glibc-2_11-branch from git://sourceware.org/git/glibc.git." + assert package.license_concluded == LicenseExpression("(LGPL-2.0-only OR LicenseRef-3)") + assert package.license_info_from_files == [LicenseExpression("GPL-2.0-only"), LicenseExpression("LicenseRef-2"), + LicenseExpression("LicenseRef-1")] + assert package.license_declared == LicenseExpression("(LGPL-2.0-only AND LicenseRef-3)") + assert package.license_comment == "The license for this project changed with the release of version x.y. The version of the project included here post-dates the license change." + assert package.copyright_text == "Copyright 2008-2010 John Smith" + assert package.summary == "GNU C library." + assert package.description == "The GNU C Library defines functions that are specified by the ISO C standard, as well as additional features specific to POSIX and other derivatives of the Unix operating system, and extensions specific to GNU systems." + assert package.comment == "This is a comment." + assert len(package.external_references) == 2 + assert package.external_references == [ExternalPackageRef(ExternalPackageRefCategory.SECURITY, "cpe23Type", + "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*"), + ExternalPackageRef(ExternalPackageRefCategory.OTHER, + "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge", + locator="acmecorp/acmenator/4.1.3-alpha", + comment="This is the external ref for Acme")] + assert package.attribution_texts == [ + "The GNU C Library is free software. See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed. License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually."] + assert package.primary_package_purpose == PackagePurpose.SOURCE + assert package.release_date == datetime(2012, 1, 29, 18, 30, 22) + assert package.built_date == datetime(2011, 1, 29, 18, 30, 22) + assert package.valid_until_date == datetime(2014, 1, 29, 18, 30, 22) + + +def test_incomplete_package(): + package_parser = PackageParser() + package_dict = { + "SPDXID": "SPDXRef-Package" + } + + with pytest.raises(SPDXParsingError) as err: + _ = package_parser.parse_package(package_dict) + + assert err.type == SPDXParsingError + assert err.value.get_messages() == ["Error while constructing Package None: ['SetterError Package: type of " + 'argument "name" must be str; got NoneType instead: None\', \'SetterError ' + 'Package: type of argument "download_location" must be one of (str, ' + 'src.model.spdx_no_assertion.SpdxNoAssertion, src.model.spdx_none.SpdxNone); ' + "got NoneType instead: None']"] + + +def test_package_with_setter_error(): + package_parser = PackageParser() + package_dict = { + "SPDXID": "SPDXRef-Package", + "name": 5, + "downloadLocation": "NONE" + } + + with pytest.raises(SPDXParsingError) as err: + _ = package_parser.parse_package(package_dict) + + assert err.type == SPDXParsingError + assert err.value.get_messages() == ["Error while constructing Package 5: ['SetterError Package: type of argument " + '"name" must be str; got int instead: 5\']'] + + +def test_package_with_falsy_values(): + package_parser = PackageParser() + package_dict = { + "SPDXID": "SPDXRef-Package", + "name": "Example Package", + "downloadLocation": "NONE", + "checksums": + [{"algorithm": "SHA", + "value": "1234"}] + } + + with pytest.raises(SPDXParsingError) as err: + _ = package_parser.parse_package(package_dict) + + assert err.type == SPDXParsingError + assert err.value.get_messages() == [ + 'Error while parsing Package Example Package: ["Error while parsing checksum: [\'Algorithm SHA not valid for checksum.\']"]'] + + +def test_parse_packages(): + package_parser = PackageParser() + packages_list = [{ + "SPDXID": "SPDXRef-Package", + "name": "Example Package", + "downloadLocation": "NONE", + "checksums": + [{"algorithm": "SHA", + "value": "1234"}] + }, { + "SPDXID": "SPDXRef-Package", + "name": 5, + "downloadLocation": "NONE" + }, + { + "SPDXID": "SPDXRef-Package", + "name": "Example Package", + "downloadLocation": "NONE"} + ] + + with pytest.raises(SPDXParsingError) as err: + _ = package_parser.parse_packages(packages_list) + + assert err.type == SPDXParsingError + assert err.value.messages == ['Error while parsing Package Example Package: ["Error while parsing checksum: ' + '[\'Algorithm SHA not valid for checksum.\']"]', + "Error while constructing Package 5: ['SetterError Package: type of argument " + '"name" must be str; got int instead: 5\']'] diff --git a/tests/parser/test_relationship_parser.py b/tests/parser/test_relationship_parser.py new file mode 100644 index 000000000..ae277fdc2 --- /dev/null +++ b/tests/parser/test_relationship_parser.py @@ -0,0 +1,137 @@ +# 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. +import pytest + +from src.model.relationship import RelationshipType, Relationship +from src.parser.error import SPDXParsingError +from src.parser.json.relationship_parser import RelationshipParser + + +def test_relationship_parser(): + relationship_parser = RelationshipParser() + + relationship_dict = { + "spdxElementId": "SPDXRef-DOCUMENT", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-Package", + "comment": "Comment." + } + + relationship = relationship_parser.parse_relationship(relationship_dict) + + assert relationship.relationship_type == RelationshipType.CONTAINS + assert relationship.spdx_element_id == "SPDXRef-DOCUMENT" + assert relationship.related_spdx_element_id == "SPDXRef-Package" + assert relationship.comment == "Comment." + + +def test_parse_incomplete_relationship(): + relationship_parser = RelationshipParser() + relationship_dict = { + "spdxElementId": "SPDXRef-DOCUMENT", + "relatedSpdxElement": "SPDXRef-Package", + "comment": "Comment." + } + + with pytest.raises(SPDXParsingError) as err: + _ = relationship_parser.parse_relationship(relationship_dict) + + assert err.type == SPDXParsingError + assert err.value.messages == ["Error while parsing relationship: ['RelationshipType must be str, not " + "NoneType.']"] +def test_parse_relationship_type(): + relationship_parser = RelationshipParser() + relationship_type_str = "DEPENDENCY_OF" + + relationship_type = relationship_parser.parse_relationship_type(relationship_type_str) + assert relationship_type == RelationshipType.DEPENDENCY_OF + +def test_creating_describes_relationship(): + relationship_parser = RelationshipParser() + + document_dict = { + "SPDXID": "SPDXRef-DOCUMENT", + "documentDescribes": ["SPDXRef-Package", "SPDXRef-File", "SPDXRef-Snippet"] + } + + relationships = relationship_parser.parse_document_describes(doc_spdx_id="SPDXRef-DOCUMENT", + described_spdx_ids=document_dict.get( + "documentDescribes"), + created_relationships=[]) + + assert len(relationships) == 3 + assert relationships == [Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-Package"), + Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-File"), + Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-Snippet"), ] + + +def test_creating_single_describes_relationship(): + relationship_parser = RelationshipParser() + document_dict = { + "SPDXID": "SPDXRef-DOCUMENT", + "documentDescribes": ["SPDXRef-Package", "SPDXRef-File"], + "relationships": [{"spdxElementId": "SPDXRef-DOCUMENT", "relatedSpdxElement": "SPDXRef-Package", + "relationshipType": "DESCRIBES", + "comment": "This relationship has a comment."}, + {"spdxElementId": "SPDXRef-File", "relatedSpdxElement": "SPDXRef-DOCUMENT", + "relationshipType": "DESCRIBED_BY", "comment": "This relationship has a comment."} + ]} + + relationships = relationship_parser.parse_all_relationships(document_dict) + + assert len(relationships) == 2 + assert relationships == [ + Relationship(related_spdx_element_id="SPDXRef-Package", relationship_type=RelationshipType.DESCRIBES, + spdx_element_id="SPDXRef-DOCUMENT", comment="This relationship has a comment."), + Relationship(related_spdx_element_id="SPDXRef-DOCUMENT", relationship_type=RelationshipType.DESCRIBED_BY, + spdx_element_id="SPDXRef-File", comment="This relationship has a comment.") + ] + + +def test_contains_relationship(): + relationship_parser = RelationshipParser() + document_dict = { + "packages": + [{ + "SPDXID": "SPDXRef-Package", + "hasFiles": ["SPDXRef-File1", "SPDXRef-File2"] + }] + } + + relationships = relationship_parser.parse_has_files(document_dict.get("packages"), created_relationships=[]) + + assert len(relationships) == 2 + assert relationships == [ + Relationship(spdx_element_id="SPDXRef-Package", relationship_type=RelationshipType.CONTAINS, + related_spdx_element_id="SPDXRef-File1"), + Relationship(spdx_element_id="SPDXRef-Package", relationship_type=RelationshipType.CONTAINS, + related_spdx_element_id="SPDXRef-File2")] + + +def test_single_contains_relationship(): + relationship_parser = RelationshipParser() + document_dict = { + "packages": + [{ + "SPDXID": "SPDXRef-Package", + "hasFiles": ["SPDXRef-File1", "SPDXRef-File2"] + }] + } + created_relationships = [ + Relationship(spdx_element_id="SPDXRef-Package", relationship_type=RelationshipType.CONTAINS, + related_spdx_element_id="SPDXRef-File1", comment="This relationship has a comment."), + Relationship(spdx_element_id="SPDXRef-File2", relationship_type=RelationshipType.CONTAINED_BY, + related_spdx_element_id="SPDXRef-Package")] + + relationships = relationship_parser.parse_has_files(document_dict.get("packages"), + created_relationships=created_relationships) + + assert len(relationships) == 0 diff --git a/tests/parser/test_snippet_parser.py b/tests/parser/test_snippet_parser.py new file mode 100644 index 000000000..0e3ccf299 --- /dev/null +++ b/tests/parser/test_snippet_parser.py @@ -0,0 +1,139 @@ +# 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. +import pytest + +from src.model.license_expression import LicenseExpression +from src.parser.error import SPDXParsingError +from src.parser.json.snippet_parser import SnippetParser + + +def test_snippet_parser(): + snippet_parser = SnippetParser() + + snippet_dict = { + "SPDXID": "SPDXRef-Snippet", + "comment": "This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0.", + "copyrightText": "Copyright 2008-2010 John Smith", + "licenseComments": "The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz.", + "licenseConcluded": "GPL-2.0-only", + "licenseInfoInSnippets": ["GPL-2.0-only"], + "name": "from linux kernel", + "ranges": [{ + "endPointer": { + "offset": 420, + "reference": "SPDXRef-DoapSource" + }, + "startPointer": { + "offset": 310, + "reference": "SPDXRef-DoapSource" + } + }, { + "endPointer": { + "lineNumber": 23, + "reference": "SPDXRef-DoapSource" + }, + "startPointer": { + "lineNumber": 5, + "reference": "SPDXRef-DoapSource" + } + }], + "snippetFromFile": "SPDXRef-DoapSource", + "attributionTexts": ["Some example attibution text."] + } + snippet = snippet_parser.parse_snippet(snippet_dict) + + assert snippet.spdx_id == "SPDXRef-Snippet" + assert snippet.name == "from linux kernel" + assert snippet.comment == "This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0." + assert snippet.copyright_text == "Copyright 2008-2010 John Smith" + assert snippet.license_comment == "The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz." + assert snippet.byte_range == (310, 420) + assert snippet.line_range == (5, 23) + assert snippet.file_spdx_id == "SPDXRef-DoapSource" + assert snippet.license_info_in_snippet == [LicenseExpression("GPL-2.0-only")] + assert snippet.concluded_license == LicenseExpression("GPL-2.0-only") + assert snippet.attribution_texts == ["Some example attibution text."] + + +def test_parse_incomplete_snippet(): + snippet_parser = SnippetParser() + incomplete_snippet_dict = { + "SPDXID": "SPDXRef-Snippet", + "file_spdx_id": "SPDXRef-File" + } + + with pytest.raises(SPDXParsingError) as err: + _ = snippet_parser.parse_snippet(incomplete_snippet_dict) + + assert err.type == SPDXParsingError + assert err.value.messages == ["Error while parsing snippet: ['No ranges dict provided.']"] + + +def test_parse_snippet_with_invalid_snippet_range(): + snippet_parser = SnippetParser() + snippet_with_invalid_ranges_list = { + "SPDXID": "SPDXRef-Snippet", + "file_spdx_id": "SPDXRef-File", + "ranges": [ + { + "endPointer": { + "offset": 23, + "reference": "SPDXRef-DoapSource" + }, + "startPointer": { + "offset": "310", + "reference": "SPDXRef-DoapSource" + } + }] + } + + with pytest.raises(SPDXParsingError) as err: + _ = snippet_parser.parse_snippet(snippet_with_invalid_ranges_list) + + assert err.type == SPDXParsingError + assert err.value.messages == ["Error while parsing snippet: ['SetterError Snippet: type of argument " + '"file_spdx_id" must be str; got NoneType instead: None\', \'SetterError ' + 'Snippet: type of argument "byte_range"[0] must be int; got str instead: ' + "(\\'310\\', 23)']"] + +def test_parse_invalid_snippet_range(): + snippet_parser = SnippetParser() + + ranges = [ + { + "endPointer": { + "lineNumber": 23, + "reference": "SPDXRef-DoapSource" + }, + "startPointer": { + "offset": 310, + "reference": "SPDXRef-DoapSource" + } + }, { + "endPointer": { + "offset": 420, + "reference": "SPDXRef-DoapSource" + }, + "startPointer": { + "lineNumber": 5, + "reference": "SPDXRef-DoapSource" + } + } + + ] + + with pytest.raises(SPDXParsingError) as err: + _ = snippet_parser.parse_ranges(ranges) + + assert err.type == SPDXParsingError + assert err.value.messages == ["Error while parsing ranges_dict: ['Type of startpointer is not the same as " + "type of endpointer.', 'Type of startpointer is not the same as type of " + "endpointer.']"] From 4725e2647f85c199a114d5217ab02066243ccfd5 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 14 Dec 2022 10:46:01 +0100 Subject: [PATCH 037/362] [issue-305, refactor] add method to construct an object and raise SPDXParsingError if construction fails Signed-off-by: Meret Behrens --- src/parser/json/actor_parser.py | 21 ++---- src/parser/json/annotation_parser.py | 25 ++++--- src/parser/json/checksum_parser.py | 8 +-- src/parser/json/creation_info_parser.py | 30 ++++----- src/parser/json/dict_parsing_functions.py | 14 +++- src/parser/json/extracted_licensing_parser.py | 16 ++--- src/parser/json/file_parser.py | 25 ++++--- src/parser/json/json_parser.py | 17 ++--- src/parser/json/license_expression_parser.py | 4 +- src/parser/json/package_parser.py | 65 ++++++++++--------- src/parser/json/relationship_parser.py | 13 ++-- src/parser/json/snippet_parser.py | 22 ++++--- tests/parser/test_creation_info_parser.py | 38 +++++++++++ .../test_extracted_licensing_info_parser.py | 2 +- tests/parser/test_file_parser.py | 6 +- tests/parser/test_package_parser.py | 6 +- tests/parser/test_snippet_parser.py | 3 +- 17 files changed, 181 insertions(+), 134 deletions(-) diff --git a/src/parser/json/actor_parser.py b/src/parser/json/actor_parser.py index 04386c5b4..9c8c657cd 100644 --- a/src/parser/json/actor_parser.py +++ b/src/parser/json/actor_parser.py @@ -13,8 +13,8 @@ from src.model.actor import Actor, ActorType from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.error import SPDXParsingError +from src.parser.json.dict_parsing_functions import try_construction_raise_parsing_error class ActorParser: @@ -35,25 +35,18 @@ def parse_actor(actor: str) -> Actor: if tool_match: name: str = tool_match.group(1).strip() - try: - creator = Actor(ActorType.TOOL, name=name) - except ConstructorTypeErrors as err: - raise SPDXParsingError(err.get_messages()) + creator = try_construction_raise_parsing_error(Actor,dict(actor_type=ActorType.TOOL, name=name)) + elif person_match: name: str = person_match.group(1).strip() email: str = person_match.group(4).strip() if person_match.group(4) else None - try: - creator = Actor(ActorType.PERSON, name=name, email=email) - except ConstructorTypeErrors as err: - raise SPDXParsingError(err.get_messages()) + creator = try_construction_raise_parsing_error(Actor, + dict(actor_type=ActorType.PERSON, name=name, email=email)) elif org_match: name: str = org_match.group(1).strip() email: str = org_match.group(4).strip() if org_match.group(4) else None - try: - creator = Actor(ActorType.ORGANIZATION, name=name, email=email) - except ConstructorTypeErrors as err: - raise SPDXParsingError(err.get_messages()) - + creator = try_construction_raise_parsing_error(Actor, dict(actor_type=ActorType.ORGANIZATION, name=name, + email=email)) else: raise SPDXParsingError([f"Actor {actor} doesn't match any of person, organization or tool."]) diff --git a/src/parser/json/annotation_parser.py b/src/parser/json/annotation_parser.py index 8d548a87b..ca768d7d1 100644 --- a/src/parser/json/annotation_parser.py +++ b/src/parser/json/annotation_parser.py @@ -13,10 +13,9 @@ from src.model.actor import Actor from src.model.annotation import Annotation, AnnotationType -from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.error import SPDXParsingError from src.parser.json.actor_parser import ActorParser -from src.parser.json.dict_parsing_functions import datetime_from_str +from src.parser.json.dict_parsing_functions import datetime_from_str, try_construction_raise_parsing_error from src.parser.logger import Logger @@ -61,7 +60,7 @@ def parse_all_annotations(self, input_doc_dict: Dict) -> List[Annotation]: if files: for file in files: file_spdx_id: str = file.get("SPDXID") - file_annotations:List[Dict] = file.get("annotations") + file_annotations: List[Dict] = file.get("annotations") if file_annotations: try: annotations_list.extend(self.parse_annotations(file_annotations, spdx_id=file_spdx_id)) @@ -118,10 +117,11 @@ def parse_annotation(self, annotation: Dict, spdx_id: Optional[str] = None) -> A annotation_comment: str = annotation.get("comment") if logger.has_messages(): raise SPDXParsingError([f"Error while parsing annotation: {logger.get_messages()}"]) - try: - annotation = Annotation(spdx_id, annotation_type, annotator, annotation_date, annotation_comment) - except ConstructorTypeErrors as err: - raise SPDXParsingError([f"Error while constructing annotation: {err.get_messages()}"]) + annotation = try_construction_raise_parsing_error(Annotation, + dict(spdx_id=spdx_id, annotation_type=annotation_type, + annotator=annotator, annotation_date=annotation_date, + annotation_comment=annotation_comment)) + return annotation @staticmethod @@ -131,7 +131,6 @@ def parse_annotation_type(annotation_type: str) -> AnnotationType: except KeyError: raise SPDXParsingError([f"Invalid annotation type: {annotation_type}"]) - def parse_review(self, review_dict: Dict, spdx_id: str) -> Annotation: logger = Logger() try: @@ -149,8 +148,8 @@ def parse_review(self, review_dict: Dict, spdx_id: str) -> Annotation: if logger.has_messages(): raise SPDXParsingError([f"Error while parsing review: {logger.get_messages()}"]) - try: - return Annotation(spdx_id=spdx_id, annotator=annotator, annotation_date=annotation_date, - annotation_type=annotation_type, annotation_comment=comment) - except ConstructorTypeErrors as err: - raise SPDXParsingError([f"Error while constructing review: {err.get_messages()}"]) + annotation = try_construction_raise_parsing_error(Annotation, + dict(spdx_id=spdx_id, annotation_type=annotation_type, + annotator=annotator, annotation_date=annotation_date, + annotation_comment=comment)) + return annotation diff --git a/src/parser/json/checksum_parser.py b/src/parser/json/checksum_parser.py index 4f449e829..e4a7a0876 100644 --- a/src/parser/json/checksum_parser.py +++ b/src/parser/json/checksum_parser.py @@ -13,7 +13,7 @@ from src.model.checksum import Checksum, ChecksumAlgorithm from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import transform_json_str_to_enum_name +from src.parser.json.dict_parsing_functions import transform_json_str_to_enum_name, try_construction_raise_parsing_error from src.parser.logger import Logger @@ -51,8 +51,6 @@ def parse_checksum(checksum_dict: Dict) -> Checksum: checksum_value = checksum_dict.get("checksumValue") if logger.has_messages(): raise SPDXParsingError([f"Error while parsing checksum: {logger.get_messages()}"]) - try: - checksum = Checksum(algorithm=checksum_algorithm, value=checksum_value) - except ConstructorTypeErrors as err: - raise SPDXParsingError([f"Error while constructing checksum: {err.get_messages()}"]) + checksum = try_construction_raise_parsing_error(Checksum, + dict(algorithm=checksum_algorithm, value=checksum_value)) return checksum diff --git a/src/parser/json/creation_info_parser.py b/src/parser/json/creation_info_parser.py index 934998992..df2e0cbbd 100644 --- a/src/parser/json/creation_info_parser.py +++ b/src/parser/json/creation_info_parser.py @@ -16,12 +16,12 @@ from src.model.document import CreationInfo from src.model.external_document_ref import ExternalDocumentRef from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.model.version import Version from src.parser.error import SPDXParsingError from src.parser.json.actor_parser import ActorParser from src.parser.json.checksum_parser import ChecksumParser -from src.parser.json.dict_parsing_functions import datetime_from_str, parse_optional_field +from src.parser.json.dict_parsing_functions import datetime_from_str, parse_optional_field, \ + try_construction_raise_parsing_error from src.parser.logger import Logger @@ -77,14 +77,16 @@ def parse_creation_info(self, doc_dict: Dict) -> CreationInfo: document_comment: Optional[str] = doc_dict.get("comment") if logger.has_messages(): raise SPDXParsingError([f"Error while parsing doc {name}: {logger.get_messages()}"]) - try: - creation_info = CreationInfo(spdx_version=spdx_version, spdx_id=spdx_id, name=name, - document_namespace=document_namespace, creators=creators, created=created, - license_list_version=license_list_version, document_comment=document_comment, - creator_comment=creator_comment, data_license=data_license, - external_document_refs=external_document_refs) - except ConstructorTypeErrors as err: - raise SPDXParsingError([f"Error while parsing doc {name}: {err.get_messages()}"]) + + creation_info = try_construction_raise_parsing_error(CreationInfo, + dict(spdx_version=spdx_version, spdx_id=spdx_id, name=name, + document_namespace=document_namespace, + creators=creators, created=created, + license_list_version=license_list_version, + document_comment=document_comment, + creator_comment=creator_comment, + data_license=data_license, + external_document_refs=external_document_refs)) return creation_info @@ -132,10 +134,8 @@ def parse_external_doc_ref(self, external_doc_ref_dict: Dict) -> ExternalDocumen spdx_document: str = external_doc_ref_dict.get("spdxDocument") if logger.has_messages(): raise SPDXParsingError([f"Error while parsing external_doc_ref: {logger.get_messages()}"]) - try: - external_doc_ref = ExternalDocumentRef(document_ref_id=external_document_id, document_uri=spdx_document, - checksum=checksum) - except ConstructorTypeErrors as err: - raise SPDXParsingError([f"Error while constructing ExternalDocumentRef: {err.get_messages()}"]) + external_doc_ref = try_construction_raise_parsing_error(ExternalDocumentRef, + dict(document_ref_id=external_document_id, + checksum=checksum, document_uri=spdx_document)) return external_doc_ref diff --git a/src/parser/json/dict_parsing_functions.py b/src/parser/json/dict_parsing_functions.py index c3a548b79..0cb790807 100644 --- a/src/parser/json/dict_parsing_functions.py +++ b/src/parser/json/dict_parsing_functions.py @@ -1,5 +1,9 @@ from datetime import datetime -from typing import Any, Callable +from typing import Any, Callable, Dict + +from src.model.external_document_ref import ExternalDocumentRef +from src.model.typing.constructor_type_errors import ConstructorTypeErrors +from src.parser.error import SPDXParsingError def parse_optional_field(field: Any, method_to_parse:Callable=lambda x: x, default=None): @@ -15,3 +19,11 @@ def datetime_from_str(created: str) -> datetime: def transform_json_str_to_enum_name(json_str: str) -> str: return json_str.replace("-","_").upper() + + +def try_construction_raise_parsing_error(object_to_construct: Any, args_for_construction: Dict) -> Any: + try: + constructed_object = object_to_construct(**args_for_construction) + except ConstructorTypeErrors as err: + raise SPDXParsingError([f"Error while constructing {object_to_construct.__name__}: {err.get_messages()}"]) + return constructed_object diff --git a/src/parser/json/extracted_licensing_parser.py b/src/parser/json/extracted_licensing_parser.py index 1f6171b60..3c75983d3 100644 --- a/src/parser/json/extracted_licensing_parser.py +++ b/src/parser/json/extracted_licensing_parser.py @@ -12,9 +12,8 @@ from src.model.extracted_licensing_info import ExtractedLicensingInfo from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import parse_optional_field +from src.parser.json.dict_parsing_functions import parse_optional_field, try_construction_raise_parsing_error from src.parser.logger import Logger @@ -43,13 +42,12 @@ def parse_extracted_licensing_info(self, extracted_licensing_info_dict: Dict) -> extracted_licensing_info_dict.get("name"), self.parse_extracted_licensing_info_name) cross_references: List[str] = extracted_licensing_info_dict.get("seeAlsos") comment: str = extracted_licensing_info_dict.get("comment") - - try: - extracted_licensing_info_dict = ExtractedLicensingInfo(license_id=license_id, extracted_text=extracted_text, - comment=comment, license_name=license_name, - cross_references=cross_references) - except ConstructorTypeErrors as err: - raise SPDXParsingError([f"Error while constructing ExtractedLicensingInfo : {err.get_messages()}"]) + extracted_licensing_info_dict = try_construction_raise_parsing_error(ExtractedLicensingInfo, + dict(license_id=license_id, + extracted_text=extracted_text, + comment=comment, + license_name=license_name, + cross_references=cross_references)) return extracted_licensing_info_dict @staticmethod diff --git a/src/parser/json/file_parser.py b/src/parser/json/file_parser.py index 0be022734..b4007263b 100644 --- a/src/parser/json/file_parser.py +++ b/src/parser/json/file_parser.py @@ -15,10 +15,9 @@ from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone -from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.error import SPDXParsingError from src.parser.json.checksum_parser import ChecksumParser -from src.parser.json.dict_parsing_functions import parse_optional_field +from src.parser.json.dict_parsing_functions import parse_optional_field, try_construction_raise_parsing_error from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -71,7 +70,7 @@ def parse_file(self, file_dict: Dict) -> Optional[File]: license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_optional_field( file_dict.get("licenseConcluded"), self.license_expression_parser.parse_license_expression) - except ConstructorTypeErrors as err: + except SPDXParsingError as err: logger.append_all(err.get_messages()) license_concluded = None try: @@ -79,7 +78,7 @@ def parse_file(self, file_dict: Dict) -> Optional[File]: Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_optional_field( file_dict.get("licenseInfoInFiles"), self.license_expression_parser.parse_license_expression) - except ConstructorTypeErrors as err: + except SPDXParsingError as err: logger.append_all(err.get_messages()) license_info_in_files = None notice_text: Optional[str] = file_dict.get("noticeText") @@ -87,15 +86,15 @@ def parse_file(self, file_dict: Dict) -> Optional[File]: if logger.has_messages(): raise SPDXParsingError([f"Error while parsing file {name}: {logger.get_messages()}"]) - try: - file = File(name=name, spdx_id=spdx_id, checksums=checksums, attribution_texts=attribution_texts, - comment=comment, copyright_text=copyright_text, - file_type=file_types, contributors=file_contributors, license_comment=license_comments, - concluded_license=license_concluded, license_info_in_file=license_info_in_files, - notice=notice_text) - except ConstructorTypeErrors as error: - raise SPDXParsingError([f"Error while constructing file {name}: {error.get_messages()}"]) - + file = try_construction_raise_parsing_error(File, dict(name=name, spdx_id=spdx_id, checksums=checksums, + attribution_texts=attribution_texts, + comment=comment, copyright_text=copyright_text, + file_type=file_types, contributors=file_contributors, + license_comment=license_comments, + concluded_license=license_concluded, + license_info_in_file=license_info_in_files, + notice=notice_text) + ) return file @staticmethod diff --git a/src/parser/json/json_parser.py b/src/parser/json/json_parser.py index 2105de5e3..31fca9d91 100644 --- a/src/parser/json/json_parser.py +++ b/src/parser/json/json_parser.py @@ -12,11 +12,10 @@ from json import JSONDecodeError from src.model.document import Document, CreationInfo -from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.json.annotation_parser import AnnotationParser from src.parser.json.creation_info_parser import CreationInfoParser from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import parse_optional_field +from src.parser.json.dict_parsing_functions import parse_optional_field, try_construction_raise_parsing_error from src.parser.json.extracted_licensing_parser import ExtractedLicensingInfoParser from src.parser.json.file_parser import FileParser from src.parser.logger import Logger @@ -59,7 +58,8 @@ def parse(self, filename: str) -> Document: creation_info: CreationInfo = self.creation_info_parser.parse_creation_info(input_doc_as_dict) try: - packages = parse_optional_field(input_doc_as_dict.get("packages"), self.package_parser.parse_packages, default=[]) + packages = parse_optional_field(input_doc_as_dict.get("packages"), self.package_parser.parse_packages, + default=[]) except SPDXParsingError as err: self.logger.append_all(err.get_messages()) packages = None @@ -90,15 +90,16 @@ def parse(self, filename: str) -> Document: try: extracted_licensing_info = parse_optional_field(input_doc_as_dict.get("hasExtractedLicensingInfos"), self.extracted_licenses_parser.parse_extracted_licensing_infos) - except ConstructorTypeErrors as err: + except SPDXParsingError as err: self.logger.append_all(err.get_messages()) extracted_licensing_info = None if self.logger.has_messages(): raise SPDXParsingError(self.logger.get_messages()) - document: Document = Document(creation_info=creation_info, packages=packages, files=files, - annotations=annotations, - snippets=snippets, relationships=relationships, - extracted_licensing_info=extracted_licensing_info) + document = try_construction_raise_parsing_error(Document, dict(creation_info=creation_info, packages=packages, + files=files, + annotations=annotations, + snippets=snippets, relationships=relationships, + extracted_licensing_info=extracted_licensing_info)) return document diff --git a/src/parser/json/license_expression_parser.py b/src/parser/json/license_expression_parser.py index eb08ead24..fdd296508 100644 --- a/src/parser/json/license_expression_parser.py +++ b/src/parser/json/license_expression_parser.py @@ -13,6 +13,7 @@ from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone +from src.parser.json.dict_parsing_functions import try_construction_raise_parsing_error class LicenseExpressionParser: @@ -22,6 +23,7 @@ def parse_license_expression(self, license_expression: Union[str, List[str]]) -> if license_expression == SpdxNoAssertion().__str__(): return SpdxNoAssertion() elif isinstance(license_expression, str): - return LicenseExpression(license_expression) + license_expression = try_construction_raise_parsing_error(LicenseExpression,dict(expression_string=license_expression)) + return license_expression elif isinstance(license_expression, list): return list(map(self.parse_license_expression, license_expression)) diff --git a/src/parser/json/package_parser.py b/src/parser/json/package_parser.py index 12845c8eb..8e9fd1e2c 100644 --- a/src/parser/json/package_parser.py +++ b/src/parser/json/package_parser.py @@ -18,12 +18,11 @@ ExternalPackageRefCategory from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone -from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.error import SPDXParsingError from src.parser.json.actor_parser import ActorParser from src.parser.json.checksum_parser import ChecksumParser from src.parser.json.dict_parsing_functions import datetime_from_str, parse_optional_field, \ - transform_json_str_to_enum_name + transform_json_str_to_enum_name, try_construction_raise_parsing_error from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -88,14 +87,14 @@ def parse_package(self, package_dict: Dict) -> Package: license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_optional_field( package_dict.get("licenseConcluded"), self.license_expression_parser.parse_license_expression) - except ConstructorTypeErrors as err: + except SPDXParsingError as err: logger.append_all(err.get_messages()) license_concluded = None try: license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_optional_field( package_dict.get("licenseDeclared"), self.license_expression_parser.parse_license_expression) - except ConstructorTypeErrors as err: + except SPDXParsingError as err: logger.append_all(err.get_messages()) license_declared = None try: @@ -103,7 +102,7 @@ def parse_package(self, package_dict: Dict) -> Package: Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_optional_field( package_dict.get("licenseInfoFromFiles"), self.license_expression_parser.parse_license_expression) - except ConstructorTypeErrors as err: + except SPDXParsingError as err: logger.append_all(err.get_messages()) license_info_from_file = None try: @@ -151,22 +150,26 @@ def parse_package(self, package_dict: Dict) -> Package: if logger.has_messages(): raise SPDXParsingError([f"Error while parsing Package {name}: {logger.get_messages()}"]) - try: - package = Package(spdx_id=spdx_id, name=name, download_location=download_location, version=version_info, - file_name=package_file_name, supplier=supplier, originator=originator, - files_analyzed=files_analyzed, - verification_code=package_verification_code, checksums=checksums, homepage=homepage, - source_info=source_info, - license_concluded=license_concluded, license_info_from_files=license_info_from_file, - license_declared=license_declared, - license_comment=license_comments, copyright_text=copyright_text, summary=summary, - description=description, - comment=comment, external_references=external_refs, attribution_texts=attribution_texts, - primary_package_purpose=primary_package_purpose, - release_date=release_date, built_date=built_date, valid_until_date=valid_until_date) - - except ConstructorTypeErrors as err: - raise SPDXParsingError([f"Error while constructing Package {name}: {err.get_messages()}"]) + package = try_construction_raise_parsing_error(Package, dict(spdx_id=spdx_id, name=name, + download_location=download_location, + version=version_info, + file_name=package_file_name, supplier=supplier, + originator=originator, + files_analyzed=files_analyzed, + verification_code=package_verification_code, + checksums=checksums, homepage=homepage, + source_info=source_info, + license_concluded=license_concluded, + license_info_from_files=license_info_from_file, + license_declared=license_declared, + license_comment=license_comments, + copyright_text=copyright_text, summary=summary, + description=description, + comment=comment, external_references=external_refs, + attribution_texts=attribution_texts, + primary_package_purpose=primary_package_purpose, + release_date=release_date, built_date=built_date, + valid_until_date=valid_until_date)) return package @@ -192,11 +195,10 @@ def parse_external_ref(self, external_ref_dict: Dict) -> ExternalPackageRef: comment = external_ref_dict.get("comment") if logger.has_messages(): raise SPDXParsingError([f"Error while parsing external ref: {logger.get_messages()}"]) - try: - external_ref = ExternalPackageRef(category=ref_category, reference_type=ref_type, locator=ref_locator, - comment=comment) - except ConstructorTypeErrors as err: - raise SPDXParsingError([f"Error while constructing external ref: {err.get_messages()}"]) + external_ref = try_construction_raise_parsing_error(ExternalPackageRef, + dict(category=ref_category, reference_type=ref_type, + locator=ref_locator, + comment=comment)) return external_ref @@ -214,14 +216,14 @@ def parse_external_ref_category(external_ref_category_str: str) -> ExternalPacka def parse_package_verification_code(verification_code_dict: Dict) -> PackageVerificationCode: excluded_files: List[str] = verification_code_dict.get("packageVerificationCodeExcludedFiles") verification_code_value: str = verification_code_dict.get("packageVerificationCodeValue") - try: - package_verification_code = PackageVerificationCode(value=verification_code_value, - excluded_files=excluded_files) - except ConstructorTypeErrors as err: - raise SPDXParsingError([f"Error while parsing package verification code: {err.get_messages()}"]) + + package_verification_code = try_construction_raise_parsing_error(PackageVerificationCode, + dict(value=verification_code_value, + excluded_files=excluded_files)) return package_verification_code + @staticmethod def parse_primary_package_purpose(primary_package_purpose: str) -> PackagePurpose: try: @@ -229,6 +231,7 @@ def parse_primary_package_purpose(primary_package_purpose: str) -> PackagePurpos except KeyError: raise SPDXParsingError([f"Invalid primaryPackagePurpose: {primary_package_purpose}"]) + @staticmethod def parse_download_location(download_location: str) -> Union[str, SpdxNoAssertion, SpdxNone]: if download_location == SpdxNone().__str__(): diff --git a/src/parser/json/relationship_parser.py b/src/parser/json/relationship_parser.py index 2c9ff6dd0..48fe0cae2 100644 --- a/src/parser/json/relationship_parser.py +++ b/src/parser/json/relationship_parser.py @@ -13,7 +13,7 @@ from src.model.relationship import Relationship, RelationshipType from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import transform_json_str_to_enum_name +from src.parser.json.dict_parsing_functions import transform_json_str_to_enum_name, try_construction_raise_parsing_error from src.parser.logger import Logger @@ -93,12 +93,11 @@ def parse_relationship(self, relationship_dict: Dict) -> Relationship: relationship_comment: str = relationship_dict.get("comment") if logger.has_messages(): raise SPDXParsingError([f"Error while parsing relationship: {logger.get_messages()}"]) - try: - relationship = Relationship(spdx_element_id=spdx_element_id, - relationship_type=relationship_type, - related_spdx_element_id=related_spdx_element, comment=relationship_comment) - except ConstructorTypeErrors as err: - raise SPDXParsingError([f"Error while constructing relationship: {err.get_messages()}"]) + + relationship = try_construction_raise_parsing_error(Relationship, dict(spdx_element_id=spdx_element_id, + relationship_type=relationship_type, + related_spdx_element_id=related_spdx_element, + comment=relationship_comment)) return relationship @staticmethod diff --git a/src/parser/json/snippet_parser.py b/src/parser/json/snippet_parser.py index 5655d0543..fdd765597 100644 --- a/src/parser/json/snippet_parser.py +++ b/src/parser/json/snippet_parser.py @@ -15,9 +15,8 @@ from src.model.snippet import Snippet from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone -from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import parse_optional_field +from src.parser.json.dict_parsing_functions import parse_optional_field, try_construction_raise_parsing_error from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -81,14 +80,17 @@ def parse_snippet(self, snippet_dict: Dict) -> Snippet: license_info = None if logger.has_messages(): raise SPDXParsingError([f"Error while parsing snippet: {logger.get_messages()}"]) - try: - snippet = Snippet(spdx_id=spdx_id, name=name, byte_range=byte_range, file_spdx_id=file_spdx_id, - line_range=line_range, attribution_texts=attribution_texts, comment=comment, - copyright_text=copyright_text, license_comment=license_comment, - concluded_license=concluded_license, - license_info_in_snippet=license_info) - except ConstructorTypeErrors as err: - raise SPDXParsingError([f"Error while parsing snippet: {err.get_messages()}"]) + + snippet = try_construction_raise_parsing_error(Snippet, + dict(spdx_id=spdx_id, name=name, byte_range=byte_range, + file_spdx_id=file_spdx_id, + line_range=line_range, + attribution_texts=attribution_texts, comment=comment, + copyright_text=copyright_text, + license_comment=license_comment, + concluded_license=concluded_license, + license_info_in_snippet=license_info)) + return snippet def parse_ranges(self, ranges_from_snippet: List[Dict]) -> Dict: diff --git a/tests/parser/test_creation_info_parser.py b/tests/parser/test_creation_info_parser.py index 877c13607..5378405ca 100644 --- a/tests/parser/test_creation_info_parser.py +++ b/tests/parser/test_creation_info_parser.py @@ -13,6 +13,8 @@ import pytest from src.model.actor import Actor, ActorType +from src.model.checksum import Checksum, ChecksumAlgorithm +from src.model.external_document_ref import ExternalDocumentRef from src.model.version import Version from src.parser.error import SPDXParsingError from src.parser.json.creation_info_parser import CreationInfoParser @@ -26,6 +28,14 @@ def test_creation_info_parser(): "name": "Example Document", "dataLicense": "CC0-1.0", "documentNamespace": "namespace", + "externalDocumentRefs": [{ + "externalDocumentId": "DocumentRef-spdx-tool-1.2", + "checksum": { + "algorithm": "SHA1", + "checksumValue": "d6a770ba38583ed4bb4525bd96e50461655d2759" + }, + "spdxDocument": "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301" + }], "creationInfo": { "created": "2010-01-29T18:30:22Z", "creators": ["Tool: LicenseFind-1.0", "Organization: ExampleCodeInspect ()", "Person: Jane Doe ()"], @@ -44,6 +54,11 @@ def test_creation_info_parser(): Actor(ActorType.ORGANIZATION, "ExampleCodeInspect"), Actor(ActorType.PERSON, "Jane Doe")] assert creation_info.license_list_version == Version(3, 7) + assert creation_info.external_document_refs == [ExternalDocumentRef(document_ref_id="DocumentRef-spdx-tool-1.2", + checksum=Checksum( + algorithm=ChecksumAlgorithm.SHA1, + value="d6a770ba38583ed4bb4525bd96e50461655d2759"), + document_uri="http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301")] def test_parse_incomplete_creation_info(): @@ -59,3 +74,26 @@ def test_parse_incomplete_creation_info(): assert err.type == SPDXParsingError assert err.value.messages == ["Error while parsing doc Example Document: ['CreationInfo is not valid.']"] + + +def test_parse_invalid_creation_info(): + creation_info_parser = CreationInfoParser() + doc_dict = { + "spdxVersion": "2.3", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "Example Document", + "creationInfo": { + "created": "2010-01-29T18:30:22Z", + "creators": ["Tool: LicenseFind-1.0", "Organization: ExampleCodeInspect ()", "Person: Jane Doe ()"], + }, + "dataLicense": None + } + + with pytest.raises(SPDXParsingError) as err: + _ = creation_info_parser.parse_creation_info(doc_dict) + + assert err.type == SPDXParsingError + assert err.value.messages == ["Error while constructing CreationInfo: ['SetterError CreationInfo: type of " + 'argument "document_namespace" must be str; got NoneType instead: None\', ' + '\'SetterError CreationInfo: type of argument "data_license" must be str; got ' + "NoneType instead: None']"] diff --git a/tests/parser/test_extracted_licensing_info_parser.py b/tests/parser/test_extracted_licensing_info_parser.py index 1a6d532f1..e34cc06e0 100644 --- a/tests/parser/test_extracted_licensing_info_parser.py +++ b/tests/parser/test_extracted_licensing_info_parser.py @@ -54,7 +54,7 @@ def test_parse_invalid_extracted_licensing_info(): _ = extracted_licensing_info_parser.parse_extracted_licensing_info(extracted_licensing_infos_dict) assert err.type == SPDXParsingError - assert err.value.messages == ["Error while constructing ExtractedLicensingInfo : ['SetterError " + assert err.value.messages == ["Error while constructing ExtractedLicensingInfo: ['SetterError " 'ExtractedLicensingInfo: type of argument "comment" must be one of (str, ' "NoneType); got int instead: 56']"] diff --git a/tests/parser/test_file_parser.py b/tests/parser/test_file_parser.py index f24339e29..d3435e892 100644 --- a/tests/parser/test_file_parser.py +++ b/tests/parser/test_file_parser.py @@ -103,11 +103,12 @@ def test_parse_falsy_files(): assert err.type == SPDXParsingError assert err.value.messages == ["Error while parsing file Incomplete File: ['No checksums provided, checksums " "are mandatory for files.']", - "Error while constructing file None: ['SetterError File: type of argument " - '"name" must be str; got NoneType instead: None\']', + 'Error while constructing File: [\'SetterError File: type of argument "name" ' + "must be str; got NoneType instead: None']", 'Error while parsing file None: ["Error while parsing checksum: [\'Algorithm ' 'MD not valid for checksum.\']"]'] + def test_parse_file_types(): file_parser = FileParser() file_types_list = ["OTHER", "APPLICATION"] @@ -116,6 +117,7 @@ def test_parse_file_types(): assert file_types == [FileType.OTHER, FileType.APPLICATION] + def test_parse_invalid_file_types(): file_parser = FileParser() file_types_list = ["OTHER", "APPLICAON"] diff --git a/tests/parser/test_package_parser.py b/tests/parser/test_package_parser.py index 2c460c4f6..cf52b8259 100644 --- a/tests/parser/test_package_parser.py +++ b/tests/parser/test_package_parser.py @@ -132,7 +132,7 @@ def test_incomplete_package(): _ = package_parser.parse_package(package_dict) assert err.type == SPDXParsingError - assert err.value.get_messages() == ["Error while constructing Package None: ['SetterError Package: type of " + assert err.value.get_messages() == ["Error while constructing Package: ['SetterError Package: type of " 'argument "name" must be str; got NoneType instead: None\', \'SetterError ' 'Package: type of argument "download_location" must be one of (str, ' 'src.model.spdx_no_assertion.SpdxNoAssertion, src.model.spdx_none.SpdxNone); ' @@ -151,7 +151,7 @@ def test_package_with_setter_error(): _ = package_parser.parse_package(package_dict) assert err.type == SPDXParsingError - assert err.value.get_messages() == ["Error while constructing Package 5: ['SetterError Package: type of argument " + assert err.value.get_messages() == ["Error while constructing Package: ['SetterError Package: type of argument " '"name" must be str; got int instead: 5\']'] @@ -200,5 +200,5 @@ def test_parse_packages(): assert err.type == SPDXParsingError assert err.value.messages == ['Error while parsing Package Example Package: ["Error while parsing checksum: ' '[\'Algorithm SHA not valid for checksum.\']"]', - "Error while constructing Package 5: ['SetterError Package: type of argument " + "Error while constructing Package: ['SetterError Package: type of argument " '"name" must be str; got int instead: 5\']'] diff --git a/tests/parser/test_snippet_parser.py b/tests/parser/test_snippet_parser.py index 0e3ccf299..f15d2a30f 100644 --- a/tests/parser/test_snippet_parser.py +++ b/tests/parser/test_snippet_parser.py @@ -99,11 +99,12 @@ def test_parse_snippet_with_invalid_snippet_range(): _ = snippet_parser.parse_snippet(snippet_with_invalid_ranges_list) assert err.type == SPDXParsingError - assert err.value.messages == ["Error while parsing snippet: ['SetterError Snippet: type of argument " + assert err.value.messages == ["Error while constructing Snippet: ['SetterError Snippet: type of argument " '"file_spdx_id" must be str; got NoneType instead: None\', \'SetterError ' 'Snippet: type of argument "byte_range"[0] must be int; got str instead: ' "(\\'310\\', 23)']"] + def test_parse_invalid_snippet_range(): snippet_parser = SnippetParser() From e43c71809d04a4788a6bab23cd17d12b50e45d24 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 14 Dec 2022 10:53:53 +0100 Subject: [PATCH 038/362] [issue-305, refactor] annotation_parser: extract methods to improve readability Signed-off-by: Meret Behrens --- src/parser/json/annotation_parser.py | 53 +++++++++------------------- 1 file changed, 16 insertions(+), 37 deletions(-) diff --git a/src/parser/json/annotation_parser.py b/src/parser/json/annotation_parser.py index ca768d7d1..2efbeff46 100644 --- a/src/parser/json/annotation_parser.py +++ b/src/parser/json/annotation_parser.py @@ -29,59 +29,38 @@ def __init__(self): def parse_all_annotations(self, input_doc_dict: Dict) -> List[Annotation]: annotations_list = [] - doc_spdx_id: str = input_doc_dict.get("SPDXID") - document_annotations: List[Dict] = input_doc_dict.get("annotations") - if document_annotations: - try: - annotations_list.extend(self.parse_annotations(document_annotations, spdx_id=doc_spdx_id)) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) - + self.parse_annotations_from_object(annotations_list, [input_doc_dict]) reviews: List[Dict] = input_doc_dict.get("revieweds") if reviews: for review in reviews: try: - review_annotation: Annotation = self.parse_review(review, spdx_id=doc_spdx_id) + review_annotation: Annotation = self.parse_review(review, spdx_id=input_doc_dict.get("SPDXID")) if review_annotation: annotations_list.append(review_annotation) except SPDXParsingError as err: self.logger.append_all(err.get_messages()) packages: List[Dict] = input_doc_dict.get("packages") - if packages: - for package in packages: - package_spdx_id: str = package.get("SPDXID") - package_annotations: List[Dict] = package.get("annotations") - if package_annotations: - try: - annotations_list.extend(self.parse_annotations(package_annotations, spdx_id=package_spdx_id)) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) + self.parse_annotations_from_object(annotations_list, packages) files: List[Dict] = input_doc_dict.get("files") - if files: - for file in files: - file_spdx_id: str = file.get("SPDXID") - file_annotations: List[Dict] = file.get("annotations") - if file_annotations: - try: - annotations_list.extend(self.parse_annotations(file_annotations, spdx_id=file_spdx_id)) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) - + self.parse_annotations_from_object(annotations_list, files) snippets: List[Dict] = input_doc_dict.get("snippets") - if snippets: - for snippet in snippets: - snippet_spdx_id: str = snippet.get("SPDXID") - snippet_annotations: List[Dict] = snippet.get("annotations") - if snippet_annotations: - try: - annotations_list.extend(self.parse_annotations(snippet_annotations, spdx_id=snippet_spdx_id)) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) + self.parse_annotations_from_object(annotations_list, snippets) if self.logger.has_messages(): raise SPDXParsingError(self.logger.get_messages()) return annotations_list + def parse_annotations_from_object(self, annotations_list, element_list: List[Dict]): + if element_list: + for element in element_list: + element_spdx_id: str = element.get("SPDXID") + element_annotations: List[Dict] = element.get("annotations") + if element_annotations: + try: + annotations_list.extend(self.parse_annotations(element_annotations, spdx_id=element_spdx_id)) + except SPDXParsingError as err: + self.logger.append_all(err.get_messages()) + def parse_annotations(self, annotations_dict_list: List[Dict], spdx_id: Optional[str] = None) -> List[Annotation]: logger = Logger() annotations_list = [] From f7bdd66759b858809f329adb01dc58d5a102dfe8 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 14 Dec 2022 12:17:42 +0100 Subject: [PATCH 039/362] [issue-305, refactor] add methods to parse required/ optional fields with exception handling Signed-off-by: Meret Behrens --- src/parser/json/annotation_parser.py | 54 ++++----- src/parser/json/checksum_parser.py | 1 - src/parser/json/creation_info_parser.py | 69 ++++++----- src/parser/json/dict_parsing_functions.py | 30 ++++- src/parser/json/file_parser.py | 50 ++++---- src/parser/json/json_parser.py | 63 +++++----- src/parser/json/package_parser.py | 136 +++++++++------------- src/parser/json/relationship_parser.py | 20 ++-- src/parser/json/snippet_parser.py | 39 +++---- tests/parser/test_annotation_parser.py | 4 +- 10 files changed, 229 insertions(+), 237 deletions(-) diff --git a/src/parser/json/annotation_parser.py b/src/parser/json/annotation_parser.py index 2efbeff46..41619ac68 100644 --- a/src/parser/json/annotation_parser.py +++ b/src/parser/json/annotation_parser.py @@ -15,7 +15,8 @@ from src.model.annotation import Annotation, AnnotationType from src.parser.error import SPDXParsingError from src.parser.json.actor_parser import ActorParser -from src.parser.json.dict_parsing_functions import datetime_from_str, try_construction_raise_parsing_error +from src.parser.json.dict_parsing_functions import datetime_from_str, try_construction_raise_parsing_error, \ + try_parse_optional_field_append_logger_when_failing, try_parse_required_field_append_logger_when_failing from src.parser.logger import Logger @@ -78,21 +79,21 @@ def parse_annotations(self, annotations_dict_list: List[Dict], spdx_id: Optional def parse_annotation(self, annotation: Dict, spdx_id: Optional[str] = None) -> Annotation: logger = Logger() spdx_id: str = annotation.get("SPDXID") or spdx_id - try: - annotation_type: Optional[AnnotationType] = self.parse_annotation_type(annotation.get("annotationType")) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - annotation_type = None - try: - annotator: Optional[Actor] = self.actor_parser.parse_actor(annotation.get("annotator")) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - annotator = None - try: - annotation_date: Optional[datetime] = datetime_from_str(annotation.get("annotationDate")) - except TypeError: - logger.append("ValueError while parsing annotationDate.") - annotation_date = None + + annotation_type: Optional[AnnotationType] = try_parse_required_field_append_logger_when_failing( + logger=logger, field=annotation.get("annotationType"), + method_to_parse=self.parse_annotation_type) + + annotator: Optional[Actor] = try_parse_required_field_append_logger_when_failing(logger=logger, + field=annotation.get( + "annotator"), + method_to_parse=self.actor_parser.parse_actor) + + annotation_date: Optional[datetime] = try_parse_required_field_append_logger_when_failing(logger=logger, + field=annotation.get( + "annotationDate"), + method_to_parse=datetime_from_str) + annotation_comment: str = annotation.get("comment") if logger.has_messages(): raise SPDXParsingError([f"Error while parsing annotation: {logger.get_messages()}"]) @@ -112,16 +113,17 @@ def parse_annotation_type(annotation_type: str) -> AnnotationType: def parse_review(self, review_dict: Dict, spdx_id: str) -> Annotation: logger = Logger() - try: - annotator: Optional[Actor] = self.actor_parser.parse_actor(review_dict.get("reviewer")) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - annotator = None - try: - annotation_date: Optional[datetime] = datetime_from_str(review_dict.get("reviewDate")) - except TypeError: - logger.append("ValueError while parsing reviewDate.") - annotation_date = None + + annotator: Optional[Actor] = try_parse_optional_field_append_logger_when_failing(logger=logger, + field=review_dict.get( + "reviewer"), + method_to_parse=self.actor_parser.parse_actor) + + annotation_date: Optional[datetime] = try_parse_required_field_append_logger_when_failing(logger=logger, + field=review_dict.get( + "reviewDate"), + method_to_parse=datetime_from_str) + annotation_type = AnnotationType.REVIEW comment: str = review_dict.get("comment") if logger.has_messages(): diff --git a/src/parser/json/checksum_parser.py b/src/parser/json/checksum_parser.py index e4a7a0876..e4f7ed86f 100644 --- a/src/parser/json/checksum_parser.py +++ b/src/parser/json/checksum_parser.py @@ -11,7 +11,6 @@ from typing import Dict, List from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.error import SPDXParsingError from src.parser.json.dict_parsing_functions import transform_json_str_to_enum_name, try_construction_raise_parsing_error from src.parser.logger import Logger diff --git a/src/parser/json/creation_info_parser.py b/src/parser/json/creation_info_parser.py index df2e0cbbd..efe907a36 100644 --- a/src/parser/json/creation_info_parser.py +++ b/src/parser/json/creation_info_parser.py @@ -20,8 +20,9 @@ from src.parser.error import SPDXParsingError from src.parser.json.actor_parser import ActorParser from src.parser.json.checksum_parser import ChecksumParser -from src.parser.json.dict_parsing_functions import datetime_from_str, parse_optional_field, \ - try_construction_raise_parsing_error +from src.parser.json.dict_parsing_functions import datetime_from_str, try_construction_raise_parsing_error, \ + try_parse_optional_field_append_logger_when_failing, \ + try_parse_required_field_append_logger_when_failing from src.parser.logger import Logger @@ -47,33 +48,28 @@ def parse_creation_info(self, doc_dict: Dict) -> CreationInfo: if creation_info_dict is None: logger.append("CreationInfo is not valid.") raise SPDXParsingError([f"Error while parsing doc {name}: {logger.get_messages()}"]) - try: - list_of_creators: List[str] = creation_info_dict.get("creators") - creators: List[Actor] = self.parse_creators(list_of_creators) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - creators = [] - try: - created: Optional[datetime] = datetime_from_str(creation_info_dict.get("created")) - except ValueError: - logger.append("Error while parsing created") - created = None + + list_of_creators: List[str] = creation_info_dict.get("creators") + creators: List[Actor] = try_parse_required_field_append_logger_when_failing(logger=logger, + field=list_of_creators, + method_to_parse=self.parse_creators, + default=[]) + + created: Optional[datetime] = try_parse_required_field_append_logger_when_failing(logger=logger, + field=creation_info_dict.get( + "created"), + method_to_parse=datetime_from_str) creator_comment: Optional[str] = creation_info_dict.get("comment") data_license: str = doc_dict.get("dataLicense") - try: - external_document_refs: List[ExternalDocumentRef] = parse_optional_field( - doc_dict.get("externalDocumentRefs"), - self.parse_external_document_refs) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - external_document_refs = [] - try: - license_list_version: Optional[Version] = parse_optional_field(creation_info_dict.get("licenseListVersion"), - self.parse_version) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - license_list_version = None + + external_document_refs: List[ExternalDocumentRef] = try_parse_optional_field_append_logger_when_failing( + logger=logger, field=doc_dict.get("externalDocumentRefs"), + method_to_parse=self.parse_external_document_refs) + license_list_version: Optional[Version] = try_parse_optional_field_append_logger_when_failing(logger=logger, + field=creation_info_dict.get( + "licenseListVersion"), + method_to_parse=self.parse_version) document_comment: Optional[str] = doc_dict.get("comment") if logger.has_messages(): raise SPDXParsingError([f"Error while parsing doc {name}: {logger.get_messages()}"]) @@ -114,22 +110,23 @@ def parse_external_document_refs(self, external_document_refs_dict: List[Dict]) logger = Logger() external_document_refs = [] for external_ref_dict in external_document_refs_dict: - try: - external_doc_ref: ExternalDocumentRef = self.parse_external_doc_ref(external_ref_dict) - external_document_refs.append(external_doc_ref) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) + external_doc_ref: ExternalDocumentRef = try_parse_optional_field_append_logger_when_failing(logger=logger, + field=external_ref_dict, + method_to_parse=self.parse_external_doc_ref) + + external_document_refs.append(external_doc_ref) + if logger.has_messages(): raise SPDXParsingError(logger.get_messages()) return external_document_refs def parse_external_doc_ref(self, external_doc_ref_dict: Dict) -> ExternalDocumentRef: logger = Logger() - try: - checksum: Optional[Checksum] = self.checksum_parser.parse_checksum(external_doc_ref_dict.get("checksum")) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - checksum = None + checksum: Optional[Checksum] = try_parse_required_field_append_logger_when_failing(logger=logger, + field=external_doc_ref_dict.get( + "checksum"), + method_to_parse=self.checksum_parser.parse_checksum) + external_document_id: str = external_doc_ref_dict.get("externalDocumentId") spdx_document: str = external_doc_ref_dict.get("spdxDocument") if logger.has_messages(): diff --git a/src/parser/json/dict_parsing_functions.py b/src/parser/json/dict_parsing_functions.py index 0cb790807..9c14a822d 100644 --- a/src/parser/json/dict_parsing_functions.py +++ b/src/parser/json/dict_parsing_functions.py @@ -1,24 +1,26 @@ from datetime import datetime from typing import Any, Callable, Dict -from src.model.external_document_ref import ExternalDocumentRef from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.error import SPDXParsingError +from src.parser.logger import Logger -def parse_optional_field(field: Any, method_to_parse:Callable=lambda x: x, default=None): +def parse_optional_field(field: Any, method_to_parse: Callable = lambda x: x, default=None): if not field: return default return method_to_parse(field) -def datetime_from_str(created: str) -> datetime: - date = datetime.strptime(created, "%Y-%m-%dT%H:%M:%SZ") +def datetime_from_str(date_str: str) -> datetime: + if not isinstance(date_str, str): + raise SPDXParsingError([f"Could not convert str to datetime, invalid type: {type(date_str).__name__}"]) + date = datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%SZ") return date def transform_json_str_to_enum_name(json_str: str) -> str: - return json_str.replace("-","_").upper() + return json_str.replace("-", "_").upper() def try_construction_raise_parsing_error(object_to_construct: Any, args_for_construction: Dict) -> Any: @@ -27,3 +29,21 @@ def try_construction_raise_parsing_error(object_to_construct: Any, args_for_cons except ConstructorTypeErrors as err: raise SPDXParsingError([f"Error while constructing {object_to_construct.__name__}: {err.get_messages()}"]) return constructed_object + + +def try_parse_optional_field_append_logger_when_failing(logger: Logger, field: Any, method_to_parse: Callable, default=None): + try: + parsed_element = parse_optional_field(field=field, method_to_parse=method_to_parse, default=default) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + parsed_element = default + return parsed_element + +def try_parse_required_field_append_logger_when_failing(logger: Logger, field: Any, method_to_parse: Callable, default=None): + try: + parsed_element = method_to_parse(field) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + parsed_element = default + return parsed_element + diff --git a/src/parser/json/file_parser.py b/src/parser/json/file_parser.py index b4007263b..7df3fcdaa 100644 --- a/src/parser/json/file_parser.py +++ b/src/parser/json/file_parser.py @@ -17,7 +17,8 @@ from src.model.spdx_none import SpdxNone from src.parser.error import SPDXParsingError from src.parser.json.checksum_parser import ChecksumParser -from src.parser.json.dict_parsing_functions import parse_optional_field, try_construction_raise_parsing_error +from src.parser.json.dict_parsing_functions import try_construction_raise_parsing_error, \ + try_parse_optional_field_append_logger_when_failing, try_parse_required_field_append_logger_when_failing from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -50,37 +51,34 @@ def parse_file(self, file_dict: Dict) -> Optional[File]: name: str = file_dict.get("fileName") spdx_id: str = file_dict.get("SPDXID") checksums_list: List[Dict] = file_dict.get("checksums") - try: - checksums: List[Checksum] = self.checksum_parser.parse_checksums(checksums_list) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - checksums = [] + + checksums: List[Checksum] = try_parse_required_field_append_logger_when_failing(logger=logger, + field=checksums_list, + method_to_parse=self.checksum_parser.parse_checksums) attribution_texts: Optional[str] = file_dict.get("attributionTexts") comment: Optional[str] = file_dict.get("comment") copyright_text: Optional[str] = file_dict.get("copyrightText") file_contributors: List[str] = file_dict.get("fileContributors") - try: - file_types: List[FileType] = parse_optional_field(file_dict.get("fileTypes"), self.parse_file_types) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - file_types = [] + file_types: List[FileType] = try_parse_optional_field_append_logger_when_failing(logger=logger, + field=file_dict.get( + "fileTypes"), + method_to_parse=self.parse_file_types) + license_comments: Optional[str] = file_dict.get("licenseComments") - try: - license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_optional_field( - file_dict.get("licenseConcluded"), - self.license_expression_parser.parse_license_expression) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - license_concluded = None - try: - license_info_in_files: Optional[ - Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_optional_field( - file_dict.get("licenseInfoInFiles"), - self.license_expression_parser.parse_license_expression) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - license_info_in_files = None + + license_concluded: Optional[Union[ + LicenseExpression, SpdxNoAssertion, SpdxNone]] = try_parse_optional_field_append_logger_when_failing( + logger=logger, field=file_dict.get("licenseConcluded"), + method_to_parse=self.license_expression_parser.parse_license_expression) + + license_info_in_files: Optional[ + Union[List[ + LicenseExpression], SpdxNoAssertion, SpdxNone]] = try_parse_optional_field_append_logger_when_failing( + logger=logger, + field=file_dict.get("licenseInfoInFiles"), + method_to_parse=self.license_expression_parser.parse_license_expression) + notice_text: Optional[str] = file_dict.get("noticeText") if logger.has_messages(): diff --git a/src/parser/json/json_parser.py b/src/parser/json/json_parser.py index 31fca9d91..8a469ece0 100644 --- a/src/parser/json/json_parser.py +++ b/src/parser/json/json_parser.py @@ -10,12 +10,15 @@ # limitations under the License. import json from json import JSONDecodeError +from typing import List from src.model.document import Document, CreationInfo +from src.model.package import Package from src.parser.json.annotation_parser import AnnotationParser from src.parser.json.creation_info_parser import CreationInfoParser from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import parse_optional_field, try_construction_raise_parsing_error +from src.parser.json.dict_parsing_functions import try_construction_raise_parsing_error, \ + try_parse_optional_field_append_logger_when_failing, try_parse_required_field_append_logger_when_failing from src.parser.json.extracted_licensing_parser import ExtractedLicensingInfoParser from src.parser.json.file_parser import FileParser from src.parser.logger import Logger @@ -55,44 +58,34 @@ def parse(self, filename: str) -> Document: self.logger.append(f"File {filename} is not a valid JSON file.") raise SPDXParsingError(self.logger.get_messages()) - creation_info: CreationInfo = self.creation_info_parser.parse_creation_info(input_doc_as_dict) + creation_info: CreationInfo = try_parse_required_field_append_logger_when_failing(logger=self.logger, + field=input_doc_as_dict, + method_to_parse=self.creation_info_parser.parse_creation_info) - try: - packages = parse_optional_field(input_doc_as_dict.get("packages"), self.package_parser.parse_packages, - default=[]) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) - packages = None - try: - files = parse_optional_field(input_doc_as_dict.get("files"), self.file_parser.parse_files) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) - files = None + packages: List[Package] = try_parse_optional_field_append_logger_when_failing(logger=self.logger, + field=input_doc_as_dict.get( + "packages"), + method_to_parse=self.package_parser.parse_packages, + default=None) - try: - annotations = self.annotation_parser.parse_all_annotations(input_doc_as_dict) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) - annotations = None + files = try_parse_optional_field_append_logger_when_failing(logger=self.logger, + field=input_doc_as_dict.get("files"), + method_to_parse=self.file_parser.parse_files) - try: - snippets = self.snippet_parser.parse_snippets(input_doc_as_dict.get("snippets")) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) - snippets = None - try: - relationships = self.relationship_parser.parse_all_relationships(input_doc_as_dict) - # documentDescribes(Document), hasFiles(Package), relationships, fileDependencies (File), artifactOf(File) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) - relationships = None + annotations = try_parse_optional_field_append_logger_when_failing(logger=self.logger, + field=input_doc_as_dict, + method_to_parse=self.annotation_parser.parse_all_annotations) + snippets = try_parse_optional_field_append_logger_when_failing(logger=self.logger, + field=input_doc_as_dict.get("snippets"), + method_to_parse=self.snippet_parser.parse_snippets) + relationships = try_parse_optional_field_append_logger_when_failing(logger=self.logger, + field=input_doc_as_dict, + method_to_parse=self.relationship_parser.parse_all_relationships) + extracted_licensing_info = try_parse_optional_field_append_logger_when_failing(logger=self.logger, + field=input_doc_as_dict.get( + "hasExtractedLicensingInfos"), + method_to_parse=self.extracted_licenses_parser.parse_extracted_licensing_infos) - try: - extracted_licensing_info = parse_optional_field(input_doc_as_dict.get("hasExtractedLicensingInfos"), - self.extracted_licenses_parser.parse_extracted_licensing_infos) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) - extracted_licensing_info = None if self.logger.has_messages(): raise SPDXParsingError(self.logger.get_messages()) diff --git a/src/parser/json/package_parser.py b/src/parser/json/package_parser.py index 8e9fd1e2c..5b02017fb 100644 --- a/src/parser/json/package_parser.py +++ b/src/parser/json/package_parser.py @@ -12,7 +12,6 @@ from typing import Dict, List, Optional, Union from src.model.actor import Actor -from src.model.checksum import Checksum from src.model.license_expression import LicenseExpression from src.model.package import Package, ExternalPackageRef, PackageVerificationCode, PackagePurpose, \ ExternalPackageRefCategory @@ -22,7 +21,8 @@ from src.parser.json.actor_parser import ActorParser from src.parser.json.checksum_parser import ChecksumParser from src.parser.json.dict_parsing_functions import datetime_from_str, parse_optional_field, \ - transform_json_str_to_enum_name, try_construction_raise_parsing_error + transform_json_str_to_enum_name, try_construction_raise_parsing_error, \ + try_parse_optional_field_append_logger_when_failing from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -58,93 +58,75 @@ def parse_package(self, package_dict: Dict) -> Package: name: str = package_dict.get("name") spdx_id: str = package_dict.get("SPDXID") attribution_texts: List[str] = package_dict.get("attributionTexts") - try: - built_date: Optional[datetime] = parse_optional_field(package_dict.get("builtDate"), datetime_from_str) - except ValueError: - logger.append("ValueError while parsing builtDate.") - built_date = None - try: - checksums: List[Checksum] = parse_optional_field(package_dict.get("checksums"), - self.checksum_parser.parse_checksums, default=[]) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - checksums = [] + + built_date: Optional[datetime] = try_parse_optional_field_append_logger_when_failing(logger=logger, + field=package_dict.get( + "builtDate"), + method_to_parse=datetime_from_str) + + checksums = try_parse_optional_field_append_logger_when_failing(logger=logger, + field=package_dict.get("checksums"), + method_to_parse=self.checksum_parser.parse_checksums) comment: Optional[str] = package_dict.get("comment") copyright_text: Optional[str] = package_dict.get("copyrightText") description: Optional[str] = package_dict.get("description") download_location: Union[str, SpdxNoAssertion, SpdxNone] = self.parse_download_location( package_dict.get("downloadLocation")) - try: - external_refs: List[ExternalPackageRef] = parse_optional_field(package_dict.get("externalRefs"), - self.parse_external_refs) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - external_refs = [] + + external_refs: List[ExternalPackageRef] = try_parse_optional_field_append_logger_when_failing(logger=logger, + field=package_dict.get( + "externalRefs"), + method_to_parse=self.parse_external_refs) + files_analyzed: Optional[bool] = parse_optional_field(package_dict.get("filesAnalyzed"), default=True) homepage: Optional[str] = package_dict.get("homepage") license_comments: Optional[str] = package_dict.get("licenseComments") - try: - license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_optional_field( - package_dict.get("licenseConcluded"), - self.license_expression_parser.parse_license_expression) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - license_concluded = None - try: - license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_optional_field( - package_dict.get("licenseDeclared"), - self.license_expression_parser.parse_license_expression) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - license_declared = None - try: - license_info_from_file: Optional[ - Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_optional_field( - package_dict.get("licenseInfoFromFiles"), - self.license_expression_parser.parse_license_expression) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - license_info_from_file = None - try: - originator: Optional[Union[Actor, SpdxNoAssertion]] = parse_optional_field(package_dict.get("originator"), - self.actor_parser.parse_actor_or_no_assert) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - originator = None + license_concluded = try_parse_optional_field_append_logger_when_failing(logger, field=package_dict.get( + "licenseConcluded"), + method_to_parse=self.license_expression_parser.parse_license_expression, + default=None) + + license_declared: Optional[ + Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = try_parse_optional_field_append_logger_when_failing( + logger=logger, field=package_dict.get("licenseDeclared"), + method_to_parse=self.license_expression_parser.parse_license_expression) + + license_info_from_file: Optional[ + Union[List[ + LicenseExpression], SpdxNoAssertion, SpdxNone]] = try_parse_optional_field_append_logger_when_failing( + logger=logger, field=package_dict.get("licenseInfoFromFiles"), + method_to_parse=self.license_expression_parser.parse_license_expression) + + originator: Optional[Union[Actor, SpdxNoAssertion]] = try_parse_optional_field_append_logger_when_failing( + logger=logger, field=package_dict.get("originator"), + method_to_parse=self.actor_parser.parse_actor_or_no_assert) + package_file_name: Optional[str] = package_dict.get("packageFileName") - try: - package_verification_code: Optional[PackageVerificationCode] = parse_optional_field( - package_dict.get("packageVerificationCode"), self.parse_package_verification_code) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - package_verification_code = None - try: - primary_package_purpose: Optional[PackagePurpose] = parse_optional_field( - package_dict.get("primaryPackagePurpose"), self.parse_primary_package_purpose) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - primary_package_purpose = None + package_verification_code: Optional[ + PackageVerificationCode] = try_parse_optional_field_append_logger_when_failing(logger=logger, + field=package_dict.get( + "packageVerificationCode"), + method_to_parse=self.parse_package_verification_code) + primary_package_purpose: Optional[PackagePurpose] = try_parse_optional_field_append_logger_when_failing( + logger=logger, field=package_dict.get("primaryPackagePurpose"), + method_to_parse=self.parse_primary_package_purpose) + + release_date: Optional[datetime] = try_parse_optional_field_append_logger_when_failing(logger=logger, + field=package_dict.get( + "releaseDate"), + method_to_parse=datetime_from_str) - try: - release_date: Optional[datetime] = parse_optional_field(package_dict.get("releaseDate"), datetime_from_str) - except ValueError: - logger.append("ValueError while parsing releaseDate.") - release_date = None source_info: Optional[str] = package_dict.get("sourceInfo") summary: Optional[str] = package_dict.get("summary") - try: - supplier: Optional[Union[Actor, SpdxNoAssertion]] = parse_optional_field(package_dict.get("supplier"), - self.actor_parser.parse_actor_or_no_assert) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - supplier = None - try: - valid_until_date: Optional[datetime] = parse_optional_field(package_dict.get("validUntilDate"), - datetime_from_str) - except ValueError: - logger.append("ValueError while parsing validUntilDate.") - valid_until_date = None + supplier: Optional[Union[Actor, SpdxNoAssertion]] = try_parse_optional_field_append_logger_when_failing( + logger=logger, field=package_dict.get("supplier"), + method_to_parse=self.actor_parser.parse_actor_or_no_assert) + + valid_until_date: Optional[datetime] = try_parse_optional_field_append_logger_when_failing(logger=logger, + field=package_dict.get( + "validUntilDate"), + method_to_parse=datetime_from_str) version_info: Optional[str] = package_dict.get("versionInfo") if logger.has_messages(): @@ -223,7 +205,6 @@ def parse_package_verification_code(verification_code_dict: Dict) -> PackageVeri return package_verification_code - @staticmethod def parse_primary_package_purpose(primary_package_purpose: str) -> PackagePurpose: try: @@ -231,7 +212,6 @@ def parse_primary_package_purpose(primary_package_purpose: str) -> PackagePurpos except KeyError: raise SPDXParsingError([f"Invalid primaryPackagePurpose: {primary_package_purpose}"]) - @staticmethod def parse_download_location(download_location: str) -> Union[str, SpdxNoAssertion, SpdxNone]: if download_location == SpdxNone().__str__(): diff --git a/src/parser/json/relationship_parser.py b/src/parser/json/relationship_parser.py index 48fe0cae2..78cf52c67 100644 --- a/src/parser/json/relationship_parser.py +++ b/src/parser/json/relationship_parser.py @@ -13,7 +13,8 @@ from src.model.relationship import Relationship, RelationshipType from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import transform_json_str_to_enum_name, try_construction_raise_parsing_error +from src.parser.json.dict_parsing_functions import transform_json_str_to_enum_name, \ + try_construction_raise_parsing_error, try_parse_required_field_append_logger_when_failing from src.parser.logger import Logger @@ -84,12 +85,9 @@ def parse_relationship(self, relationship_dict: Dict) -> Relationship: logger = Logger() spdx_element_id: str = relationship_dict.get("spdxElementId") related_spdx_element: str = relationship_dict.get("relatedSpdxElement") - try: - relationship_type: Optional[RelationshipType] = self.parse_relationship_type( - relationship_dict.get("relationshipType")) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - relationship_type = None + relationship_type: Optional[RelationshipType] = try_parse_required_field_append_logger_when_failing( + logger=logger, field=relationship_dict.get("relationshipType"), + method_to_parse=self.parse_relationship_type) relationship_comment: str = relationship_dict.get("comment") if logger.has_messages(): raise SPDXParsingError([f"Error while parsing relationship: {logger.get_messages()}"]) @@ -100,6 +98,7 @@ def parse_relationship(self, relationship_dict: Dict) -> Relationship: comment=relationship_comment)) return relationship + @staticmethod def parse_relationship_type(relationship_type_str: str) -> RelationshipType: try: @@ -110,6 +109,7 @@ def parse_relationship_type(relationship_type_str: str) -> RelationshipType: raise SPDXParsingError([f"RelationshipType must be str, not {type(relationship_type_str).__name__}."]) return relationship_type + def parse_document_describes(self, doc_spdx_id: str, described_spdx_ids: List[str], created_relationships: List[Relationship]) -> List[Relationship]: logger = Logger() @@ -129,6 +129,7 @@ def parse_document_describes(self, doc_spdx_id: str, described_spdx_ids: List[st return describes_relationships + def parse_has_files(self, package_dicts: List[Dict], created_relationships: List[Relationship]) -> List[ Relationship]: logger = Logger() @@ -154,6 +155,7 @@ def parse_has_files(self, package_dicts: List[Dict], created_relationships: List return contains_relationships + def check_if_relationship_exists(self, relationship: Relationship, created_relationships: List[Relationship]) -> bool: created_relationships_without_comment: List[Relationship] = self.ignore_any_comments_in_relationship_list( @@ -166,6 +168,7 @@ def check_if_relationship_exists(self, relationship: Relationship, return False + @staticmethod def ignore_any_comments_in_relationship_list(created_relationships: List[Relationship]) -> List[Relationship]: relationships_without_comment = [Relationship(relationship_type=relationship.relationship_type, @@ -174,6 +177,7 @@ def ignore_any_comments_in_relationship_list(created_relationships: List[Relatio created_relationships] return relationships_without_comment + @staticmethod def convert_relationship(relationship: Relationship) -> Relationship: if relationship.relationship_type == RelationshipType.DESCRIBES: @@ -193,6 +197,7 @@ def convert_relationship(relationship: Relationship) -> Relationship: spdx_element_id=relationship.related_spdx_element_id, relationship_type=RelationshipType.CONTAINS, comment=relationship.comment) + @staticmethod def parse_file_dependencies(file_dicts: List[Dict]) -> List[Relationship]: logger = Logger() @@ -215,6 +220,7 @@ def parse_file_dependencies(file_dicts: List[Dict]) -> List[Relationship]: raise SPDXParsingError([f"Error while creating dependency relationships: {logger.get_messages()}"]) return dependency_relationships + @staticmethod def parse_artifact_of(file_dicts: List[Dict]) -> List[Relationship]: generated_relationships = [] diff --git a/src/parser/json/snippet_parser.py b/src/parser/json/snippet_parser.py index fdd765597..b0c361c25 100644 --- a/src/parser/json/snippet_parser.py +++ b/src/parser/json/snippet_parser.py @@ -16,7 +16,8 @@ from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import parse_optional_field, try_construction_raise_parsing_error +from src.parser.json.dict_parsing_functions import try_construction_raise_parsing_error, \ + try_parse_optional_field_append_logger_when_failing, try_parse_required_field_append_logger_when_failing from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -52,32 +53,28 @@ def parse_snippet(self, snippet_dict: Dict) -> Snippet: spdx_id: str = snippet_dict.get("SPDXID") file_spdx_id: str = snippet_dict.get("snippetFromFile") name: Optional[str] = snippet_dict.get("name") - try: - ranges: Dict = self.parse_ranges(snippet_dict.get("ranges")) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - ranges = {} + ranges: Dict = try_parse_required_field_append_logger_when_failing(logger=logger, + field=(snippet_dict.get("ranges")), + method_to_parse=self.parse_ranges, + default={}) byte_range: Tuple[int, int] = ranges.get(RangeType.BYTE) - line_range: Optional[Tuple[int, int]] = ranges.get(RangeType.LINE) attribution_texts: List[str] = snippet_dict.get("attributionTexts") comment: Optional[str] = snippet_dict.get("comment") copyright_text: Optional[str] = snippet_dict.get("copyrightText") license_comment: Optional[str] = snippet_dict.get("licenseComments") - try: - concluded_license: Optional[Union[ - LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_optional_field( - snippet_dict.get("licenseConcluded"), self.license_expression_parser.parse_license_expression) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - concluded_license = None - try: - license_info: Optional[Union[List[ - LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_optional_field( - snippet_dict.get("licenseInfoInSnippets"), self.license_expression_parser.parse_license_expression) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - license_info = None + concluded_license: Optional[Union[ + LicenseExpression, SpdxNoAssertion, SpdxNone]] = try_parse_optional_field_append_logger_when_failing( + logger=logger, + field=snippet_dict.get("licenseConcluded"), + method_to_parse=self.license_expression_parser.parse_license_expression) + + license_info: Optional[Union[List[ + LicenseExpression], SpdxNoAssertion, SpdxNone]] = try_parse_optional_field_append_logger_when_failing( + logger=logger, + field=snippet_dict.get("licenseInfoInSnippets"), + method_to_parse=self.license_expression_parser.parse_license_expression) + if logger.has_messages(): raise SPDXParsingError([f"Error while parsing snippet: {logger.get_messages()}"]) diff --git a/tests/parser/test_annotation_parser.py b/tests/parser/test_annotation_parser.py index 2fc12f17c..323947b13 100644 --- a/tests/parser/test_annotation_parser.py +++ b/tests/parser/test_annotation_parser.py @@ -115,5 +115,5 @@ def test_parse_incomplete_annotation(): _ = annotation_parser.parse_annotation(annotation_dict) assert err.type == SPDXParsingError - assert err.value.messages == ["Error while parsing annotation: ['Invalid annotation type: None', " - "'ValueError while parsing annotationDate.']"] + assert err.value.messages == ["Error while parsing annotation: ['Invalid annotation type: None', 'Could not " + "convert str to datetime, invalid type: NoneType']"] From 4b4ec253e767aa65cd1f1cb00bc21ddbcbce4f10 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 14 Dec 2022 12:31:34 +0100 Subject: [PATCH 040/362] [issue-305, refactor] relationship_parser: extract dict to invert relationships Signed-off-by: Meret Behrens --- src/parser/json/relationship_parser.py | 34 +++++++------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/src/parser/json/relationship_parser.py b/src/parser/json/relationship_parser.py index 78cf52c67..b143ff968 100644 --- a/src/parser/json/relationship_parser.py +++ b/src/parser/json/relationship_parser.py @@ -98,7 +98,6 @@ def parse_relationship(self, relationship_dict: Dict) -> Relationship: comment=relationship_comment)) return relationship - @staticmethod def parse_relationship_type(relationship_type_str: str) -> RelationshipType: try: @@ -109,7 +108,6 @@ def parse_relationship_type(relationship_type_str: str) -> RelationshipType: raise SPDXParsingError([f"RelationshipType must be str, not {type(relationship_type_str).__name__}."]) return relationship_type - def parse_document_describes(self, doc_spdx_id: str, described_spdx_ids: List[str], created_relationships: List[Relationship]) -> List[Relationship]: logger = Logger() @@ -129,7 +127,6 @@ def parse_document_describes(self, doc_spdx_id: str, described_spdx_ids: List[st return describes_relationships - def parse_has_files(self, package_dicts: List[Dict], created_relationships: List[Relationship]) -> List[ Relationship]: logger = Logger() @@ -155,7 +152,6 @@ def parse_has_files(self, package_dicts: List[Dict], created_relationships: List return contains_relationships - def check_if_relationship_exists(self, relationship: Relationship, created_relationships: List[Relationship]) -> bool: created_relationships_without_comment: List[Relationship] = self.ignore_any_comments_in_relationship_list( @@ -168,7 +164,6 @@ def check_if_relationship_exists(self, relationship: Relationship, return False - @staticmethod def ignore_any_comments_in_relationship_list(created_relationships: List[Relationship]) -> List[Relationship]: relationships_without_comment = [Relationship(relationship_type=relationship.relationship_type, @@ -177,26 +172,16 @@ def ignore_any_comments_in_relationship_list(created_relationships: List[Relatio created_relationships] return relationships_without_comment + def convert_relationship(self, relationship: Relationship) -> Relationship: + return Relationship(related_spdx_element_id=relationship.spdx_element_id, + spdx_element_id=relationship.related_spdx_element_id, + relationship_type=self.convert_relationship_types[relationship.relationship_type], + comment=relationship.comment) - @staticmethod - def convert_relationship(relationship: Relationship) -> Relationship: - if relationship.relationship_type == RelationshipType.DESCRIBES: - return Relationship(related_spdx_element_id=relationship.spdx_element_id, - spdx_element_id=relationship.related_spdx_element_id, - relationship_type=RelationshipType.DESCRIBED_BY, comment=relationship.comment) - if relationship.relationship_type == RelationshipType.DESCRIBED_BY: - return Relationship(related_spdx_element_id=relationship.spdx_element_id, - spdx_element_id=relationship.related_spdx_element_id, - relationship_type=RelationshipType.DESCRIBES, comment=relationship.comment) - if relationship.relationship_type == RelationshipType.CONTAINS: - return Relationship(related_spdx_element_id=relationship.spdx_element_id, - spdx_element_id=relationship.related_spdx_element_id, - relationship_type=RelationshipType.CONTAINED_BY, comment=relationship.comment) - if relationship.relationship_type == RelationshipType.CONTAINED_BY: - return Relationship(related_spdx_element_id=relationship.spdx_element_id, - spdx_element_id=relationship.related_spdx_element_id, - relationship_type=RelationshipType.CONTAINS, comment=relationship.comment) - + convert_relationship_types = {RelationshipType.DESCRIBES: RelationshipType.DESCRIBED_BY, + RelationshipType.DESCRIBED_BY: RelationshipType.DESCRIBES, + RelationshipType.CONTAINS: RelationshipType.CONTAINED_BY, + RelationshipType.CONTAINED_BY: RelationshipType.CONTAINS} @staticmethod def parse_file_dependencies(file_dicts: List[Dict]) -> List[Relationship]: @@ -220,7 +205,6 @@ def parse_file_dependencies(file_dicts: List[Dict]) -> List[Relationship]: raise SPDXParsingError([f"Error while creating dependency relationships: {logger.get_messages()}"]) return dependency_relationships - @staticmethod def parse_artifact_of(file_dicts: List[Dict]) -> List[Relationship]: generated_relationships = [] From f2b6b54d7d028fcb4df986807e1e90e998af0442 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 14 Dec 2022 14:40:01 +0100 Subject: [PATCH 041/362] [issue-305, refactor] add method to raise error if logger has messages and method to handle exceptions when appending a list Signed-off-by: Meret Behrens --- src/parser/json/annotation_parser.py | 50 +++++------ src/parser/json/checksum_parser.py | 20 ++--- src/parser/json/creation_info_parser.py | 28 +++--- src/parser/json/dict_parsing_functions.py | 37 +++++++- src/parser/json/extracted_licensing_parser.py | 17 ++-- src/parser/json/file_parser.py | 25 ++---- src/parser/json/json_parser.py | 6 +- src/parser/json/package_parser.py | 43 ++++----- src/parser/json/relationship_parser.py | 89 +++++++++---------- tests/parser/test_annotation_parser.py | 2 +- tests/parser/test_checksum_parser.py | 2 +- tests/parser/test_file_parser.py | 2 +- tests/parser/test_package_parser.py | 4 +- 13 files changed, 169 insertions(+), 156 deletions(-) diff --git a/src/parser/json/annotation_parser.py b/src/parser/json/annotation_parser.py index 41619ac68..83703d538 100644 --- a/src/parser/json/annotation_parser.py +++ b/src/parser/json/annotation_parser.py @@ -16,7 +16,8 @@ from src.parser.error import SPDXParsingError from src.parser.json.actor_parser import ActorParser from src.parser.json.dict_parsing_functions import datetime_from_str, try_construction_raise_parsing_error, \ - try_parse_optional_field_append_logger_when_failing, try_parse_required_field_append_logger_when_failing + try_parse_optional_field_append_logger_when_failing, try_parse_required_field_append_logger_when_failing, \ + append_list_if_object_could_be_parsed_append_logger_if_not, raise_parsing_error_if_logger_has_messages from src.parser.logger import Logger @@ -34,12 +35,10 @@ def parse_all_annotations(self, input_doc_dict: Dict) -> List[Annotation]: reviews: List[Dict] = input_doc_dict.get("revieweds") if reviews: for review in reviews: - try: - review_annotation: Annotation = self.parse_review(review, spdx_id=input_doc_dict.get("SPDXID")) - if review_annotation: - annotations_list.append(review_annotation) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) + annotations_list = append_list_if_object_could_be_parsed_append_logger_if_not( + list_to_append=annotations_list, logger=self.logger, field=review, + method_to_parse=lambda x: self.parse_review(x, spdx_id=input_doc_dict.get("SPDXID"))) + packages: List[Dict] = input_doc_dict.get("packages") self.parse_annotations_from_object(annotations_list, packages) files: List[Dict] = input_doc_dict.get("files") @@ -47,8 +46,7 @@ def parse_all_annotations(self, input_doc_dict: Dict) -> List[Annotation]: snippets: List[Dict] = input_doc_dict.get("snippets") self.parse_annotations_from_object(annotations_list, snippets) - if self.logger.has_messages(): - raise SPDXParsingError(self.logger.get_messages()) + raise_parsing_error_if_logger_has_messages(self.logger, "Annotations") return annotations_list def parse_annotations_from_object(self, annotations_list, element_list: List[Dict]): @@ -57,22 +55,27 @@ def parse_annotations_from_object(self, annotations_list, element_list: List[Dic element_spdx_id: str = element.get("SPDXID") element_annotations: List[Dict] = element.get("annotations") if element_annotations: - try: - annotations_list.extend(self.parse_annotations(element_annotations, spdx_id=element_spdx_id)) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) + annotations_list.extend(try_parse_required_field_append_logger_when_failing(logger=self.logger, + field=element_annotations, + method_to_parse=lambda + x: self.parse_annotations( + x, + spdx_id=element_spdx_id), + default=[])) def parse_annotations(self, annotations_dict_list: List[Dict], spdx_id: Optional[str] = None) -> List[Annotation]: logger = Logger() annotations_list = [] for annotation_dict in annotations_dict_list: - try: - annotation: Annotation = self.parse_annotation(annotation_dict, spdx_id=spdx_id) - annotations_list.append(annotation) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - if logger.has_messages(): - raise SPDXParsingError(logger.get_messages()) + annotations_list = append_list_if_object_could_be_parsed_append_logger_if_not( + list_to_append=annotations_list, + logger=self.logger, + field=annotation_dict, + method_to_parse=lambda + x: self.parse_annotation( + x, + spdx_id=spdx_id)) + raise_parsing_error_if_logger_has_messages(logger, "Annotations") return annotations_list @@ -95,8 +98,7 @@ def parse_annotation(self, annotation: Dict, spdx_id: Optional[str] = None) -> A method_to_parse=datetime_from_str) annotation_comment: str = annotation.get("comment") - if logger.has_messages(): - raise SPDXParsingError([f"Error while parsing annotation: {logger.get_messages()}"]) + raise_parsing_error_if_logger_has_messages(logger, "Annotation") annotation = try_construction_raise_parsing_error(Annotation, dict(spdx_id=spdx_id, annotation_type=annotation_type, annotator=annotator, annotation_date=annotation_date, @@ -113,7 +115,6 @@ def parse_annotation_type(annotation_type: str) -> AnnotationType: def parse_review(self, review_dict: Dict, spdx_id: str) -> Annotation: logger = Logger() - annotator: Optional[Actor] = try_parse_optional_field_append_logger_when_failing(logger=logger, field=review_dict.get( "reviewer"), @@ -126,8 +127,7 @@ def parse_review(self, review_dict: Dict, spdx_id: str) -> Annotation: annotation_type = AnnotationType.REVIEW comment: str = review_dict.get("comment") - if logger.has_messages(): - raise SPDXParsingError([f"Error while parsing review: {logger.get_messages()}"]) + raise_parsing_error_if_logger_has_messages(logger, "Review") annotation = try_construction_raise_parsing_error(Annotation, dict(spdx_id=spdx_id, annotation_type=annotation_type, diff --git a/src/parser/json/checksum_parser.py b/src/parser/json/checksum_parser.py index e4f7ed86f..ca0f1432e 100644 --- a/src/parser/json/checksum_parser.py +++ b/src/parser/json/checksum_parser.py @@ -12,7 +12,10 @@ from src.model.checksum import Checksum, ChecksumAlgorithm from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import transform_json_str_to_enum_name, try_construction_raise_parsing_error +from src.parser.json.dict_parsing_functions import append_list_if_object_could_be_parsed_append_logger_if_not, \ + raise_parsing_error_if_logger_has_messages, \ + raise_parsing_error_without_additional_text_if_logger_has_messages, \ + transform_json_str_to_enum_name, try_construction_raise_parsing_error from src.parser.logger import Logger @@ -28,14 +31,12 @@ def parse_checksums(self, checksum_dicts_list: List[Dict]) -> List[Checksum]: checksum_list = [] for checksum_dict in checksum_dicts_list: - try: - checksum_list.append(self.parse_checksum(checksum_dict)) - except SPDXParsingError as err: - self.auxiliary_logger.append_all(err.get_messages()) - continue - if self.auxiliary_logger.has_messages(): - raise SPDXParsingError(self.auxiliary_logger.get_messages()) + checksum_list = append_list_if_object_could_be_parsed_append_logger_if_not(logger=self.auxiliary_logger, + list_to_append=checksum_list, + field=checksum_dict, + method_to_parse=self.parse_checksum) + raise_parsing_error_without_additional_text_if_logger_has_messages(self.auxiliary_logger) return checksum_list @staticmethod @@ -48,8 +49,7 @@ def parse_checksum(checksum_dict: Dict) -> Checksum: logger.append(f"Algorithm {algorithm} not valid for checksum.") checksum_algorithm = None checksum_value = checksum_dict.get("checksumValue") - if logger.has_messages(): - raise SPDXParsingError([f"Error while parsing checksum: {logger.get_messages()}"]) + raise_parsing_error_if_logger_has_messages(logger, "Checksum") checksum = try_construction_raise_parsing_error(Checksum, dict(algorithm=checksum_algorithm, value=checksum_value)) return checksum diff --git a/src/parser/json/creation_info_parser.py b/src/parser/json/creation_info_parser.py index efe907a36..c202f342e 100644 --- a/src/parser/json/creation_info_parser.py +++ b/src/parser/json/creation_info_parser.py @@ -15,12 +15,14 @@ from src.model.checksum import Checksum from src.model.document import CreationInfo from src.model.external_document_ref import ExternalDocumentRef -from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.version import Version from src.parser.error import SPDXParsingError from src.parser.json.actor_parser import ActorParser from src.parser.json.checksum_parser import ChecksumParser -from src.parser.json.dict_parsing_functions import datetime_from_str, try_construction_raise_parsing_error, \ +from src.parser.json.dict_parsing_functions import append_list_if_object_could_be_parsed_append_logger_if_not, \ + datetime_from_str, raise_parsing_error_if_logger_has_messages, \ + raise_parsing_error_without_additional_text_if_logger_has_messages, \ + try_construction_raise_parsing_error, \ try_parse_optional_field_append_logger_when_failing, \ try_parse_required_field_append_logger_when_failing from src.parser.logger import Logger @@ -71,8 +73,7 @@ def parse_creation_info(self, doc_dict: Dict) -> CreationInfo: "licenseListVersion"), method_to_parse=self.parse_version) document_comment: Optional[str] = doc_dict.get("comment") - if logger.has_messages(): - raise SPDXParsingError([f"Error while parsing doc {name}: {logger.get_messages()}"]) + raise_parsing_error_if_logger_has_messages(logger, f"Document: {name}") creation_info = try_construction_raise_parsing_error(CreationInfo, dict(spdx_version=spdx_version, spdx_id=spdx_id, name=name, @@ -90,13 +91,12 @@ def parse_creators(self, creators_dict_list: List[str]) -> List[Actor]: logger = Logger() creators_list = [] for creator_dict in creators_dict_list: - try: - creator: Union[Actor, SpdxNoAssertion] = self.actor_parser.parse_actor_or_no_assert(creator_dict) - creators_list.append(creator) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - if logger.has_messages(): - raise SPDXParsingError(logger.get_messages()) + creators_list = append_list_if_object_could_be_parsed_append_logger_if_not(list_to_append=creators_list, + logger=logger, + field=creator_dict, + method_to_parse=self.actor_parser.parse_actor_or_no_assert) + + raise_parsing_error_without_additional_text_if_logger_has_messages(logger) return creators_list @staticmethod @@ -116,8 +116,7 @@ def parse_external_document_refs(self, external_document_refs_dict: List[Dict]) external_document_refs.append(external_doc_ref) - if logger.has_messages(): - raise SPDXParsingError(logger.get_messages()) + raise_parsing_error_without_additional_text_if_logger_has_messages(logger) return external_document_refs def parse_external_doc_ref(self, external_doc_ref_dict: Dict) -> ExternalDocumentRef: @@ -129,8 +128,7 @@ def parse_external_doc_ref(self, external_doc_ref_dict: Dict) -> ExternalDocumen external_document_id: str = external_doc_ref_dict.get("externalDocumentId") spdx_document: str = external_doc_ref_dict.get("spdxDocument") - if logger.has_messages(): - raise SPDXParsingError([f"Error while parsing external_doc_ref: {logger.get_messages()}"]) + raise_parsing_error_if_logger_has_messages(logger, "ExternalDocRef") external_doc_ref = try_construction_raise_parsing_error(ExternalDocumentRef, dict(document_ref_id=external_document_id, checksum=checksum, document_uri=spdx_document)) diff --git a/src/parser/json/dict_parsing_functions.py b/src/parser/json/dict_parsing_functions.py index 9c14a822d..45cc1b1bb 100644 --- a/src/parser/json/dict_parsing_functions.py +++ b/src/parser/json/dict_parsing_functions.py @@ -1,5 +1,15 @@ +# 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 datetime import datetime -from typing import Any, Callable, Dict +from typing import Any, Callable, Dict, List from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.error import SPDXParsingError @@ -31,7 +41,8 @@ def try_construction_raise_parsing_error(object_to_construct: Any, args_for_cons return constructed_object -def try_parse_optional_field_append_logger_when_failing(logger: Logger, field: Any, method_to_parse: Callable, default=None): +def try_parse_optional_field_append_logger_when_failing(logger: Logger, field: Any, method_to_parse: Callable, + default=None): try: parsed_element = parse_optional_field(field=field, method_to_parse=method_to_parse, default=default) except SPDXParsingError as err: @@ -39,7 +50,9 @@ def try_parse_optional_field_append_logger_when_failing(logger: Logger, field: A parsed_element = default return parsed_element -def try_parse_required_field_append_logger_when_failing(logger: Logger, field: Any, method_to_parse: Callable, default=None): + +def try_parse_required_field_append_logger_when_failing(logger: Logger, field: Any, method_to_parse: Callable, + default=None): try: parsed_element = method_to_parse(field) except SPDXParsingError as err: @@ -47,3 +60,21 @@ def try_parse_required_field_append_logger_when_failing(logger: Logger, field: A parsed_element = default return parsed_element + +def append_list_if_object_could_be_parsed_append_logger_if_not(logger: Logger, list_to_append: List[Any], field: Any, + method_to_parse: Callable): + try: + parsed_element = method_to_parse(field) + list_to_append.append(parsed_element) + except SPDXParsingError as err: + logger.append_all(err.get_messages()) + return list_to_append + + +def raise_parsing_error_if_logger_has_messages(logger: Logger, parsed_object_name: str): + if logger.has_messages(): + raise SPDXParsingError([f"Error while parsing {parsed_object_name}: {logger.get_messages()}"]) + +def raise_parsing_error_without_additional_text_if_logger_has_messages(logger: Logger): + if logger.has_messages(): + raise SPDXParsingError(logger.get_messages()) diff --git a/src/parser/json/extracted_licensing_parser.py b/src/parser/json/extracted_licensing_parser.py index 3c75983d3..403a83b08 100644 --- a/src/parser/json/extracted_licensing_parser.py +++ b/src/parser/json/extracted_licensing_parser.py @@ -12,8 +12,9 @@ from src.model.extracted_licensing_info import ExtractedLicensingInfo from src.model.spdx_no_assertion import SpdxNoAssertion -from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import parse_optional_field, try_construction_raise_parsing_error +from src.parser.json.dict_parsing_functions import parse_optional_field, \ + raise_parsing_error_without_additional_text_if_logger_has_messages, \ + append_list_if_object_could_be_parsed_append_logger_if_not, try_construction_raise_parsing_error from src.parser.logger import Logger @@ -27,12 +28,12 @@ def parse_extracted_licensing_infos(self, extracted_licensing_info_dicts: List[D ExtractedLicensingInfo]: extracted_licensing_info_list = [] for extracted_licensing_info_dict in extracted_licensing_info_dicts: - try: - extracted_licensing_info_list.append(self.parse_extracted_licensing_info(extracted_licensing_info_dict)) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) - if self.logger.has_messages(): - raise SPDXParsingError(self.logger.get_messages()) + extracted_licensing_info_list = append_list_if_object_could_be_parsed_append_logger_if_not( + list_to_append=extracted_licensing_info_list, + logger=self.logger, field=extracted_licensing_info_dict, + method_to_parse=self.parse_extracted_licensing_info) + + raise_parsing_error_without_additional_text_if_logger_has_messages(self.logger) return extracted_licensing_info_list def parse_extracted_licensing_info(self, extracted_licensing_info_dict: Dict) -> ExtractedLicensingInfo: diff --git a/src/parser/json/file_parser.py b/src/parser/json/file_parser.py index 7df3fcdaa..e02e5788a 100644 --- a/src/parser/json/file_parser.py +++ b/src/parser/json/file_parser.py @@ -15,9 +15,10 @@ from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone -from src.parser.error import SPDXParsingError from src.parser.json.checksum_parser import ChecksumParser -from src.parser.json.dict_parsing_functions import try_construction_raise_parsing_error, \ +from src.parser.json.dict_parsing_functions import append_list_if_object_could_be_parsed_append_logger_if_not, \ + raise_parsing_error_without_additional_text_if_logger_has_messages, \ + raise_parsing_error_if_logger_has_messages, try_construction_raise_parsing_error, \ try_parse_optional_field_append_logger_when_failing, try_parse_required_field_append_logger_when_failing from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -36,14 +37,10 @@ def __init__(self): def parse_files(self, file_dict_list) -> List[File]: file_list = [] for file_dict in file_dict_list: - try: - file: File = self.parse_file(file_dict) - file_list.append(file) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) - continue - if self.logger.has_messages(): - raise SPDXParsingError(self.logger.get_messages()) + file_list = append_list_if_object_could_be_parsed_append_logger_if_not(list_to_append=file_list, + logger=self.logger, field=file_dict, + method_to_parse=self.parse_file) + raise_parsing_error_without_additional_text_if_logger_has_messages(self.logger) return file_list def parse_file(self, file_dict: Dict) -> Optional[File]: @@ -78,11 +75,8 @@ def parse_file(self, file_dict: Dict) -> Optional[File]: logger=logger, field=file_dict.get("licenseInfoInFiles"), method_to_parse=self.license_expression_parser.parse_license_expression) - notice_text: Optional[str] = file_dict.get("noticeText") - - if logger.has_messages(): - raise SPDXParsingError([f"Error while parsing file {name}: {logger.get_messages()}"]) + raise_parsing_error_if_logger_has_messages(logger, f"file {name}") file = try_construction_raise_parsing_error(File, dict(name=name, spdx_id=spdx_id, checksums=checksums, attribution_texts=attribution_texts, @@ -106,6 +100,5 @@ def parse_file_types(file_types_list: List[str]) -> List[FileType]: logger.append(f"FileType {file_type} is not valid.") continue file_types.append(file_type) - if logger.has_messages(): - raise SPDXParsingError([f"Error while parsing file_types: {logger.get_messages()}"]) + raise_parsing_error_if_logger_has_messages(logger, "file_types") return file_types diff --git a/src/parser/json/json_parser.py b/src/parser/json/json_parser.py index 8a469ece0..62984a10b 100644 --- a/src/parser/json/json_parser.py +++ b/src/parser/json/json_parser.py @@ -17,7 +17,8 @@ from src.parser.json.annotation_parser import AnnotationParser from src.parser.json.creation_info_parser import CreationInfoParser from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import try_construction_raise_parsing_error, \ +from src.parser.json.dict_parsing_functions import raise_parsing_error_without_additional_text_if_logger_has_messages, \ + try_construction_raise_parsing_error, \ try_parse_optional_field_append_logger_when_failing, try_parse_required_field_append_logger_when_failing from src.parser.json.extracted_licensing_parser import ExtractedLicensingInfoParser from src.parser.json.file_parser import FileParser @@ -86,8 +87,7 @@ def parse(self, filename: str) -> Document: "hasExtractedLicensingInfos"), method_to_parse=self.extracted_licenses_parser.parse_extracted_licensing_infos) - if self.logger.has_messages(): - raise SPDXParsingError(self.logger.get_messages()) + raise_parsing_error_without_additional_text_if_logger_has_messages(self.logger) document = try_construction_raise_parsing_error(Document, dict(creation_info=creation_info, packages=packages, files=files, diff --git a/src/parser/json/package_parser.py b/src/parser/json/package_parser.py index 5b02017fb..af223f4c8 100644 --- a/src/parser/json/package_parser.py +++ b/src/parser/json/package_parser.py @@ -20,9 +20,11 @@ from src.parser.error import SPDXParsingError from src.parser.json.actor_parser import ActorParser from src.parser.json.checksum_parser import ChecksumParser -from src.parser.json.dict_parsing_functions import datetime_from_str, parse_optional_field, \ +from src.parser.json.dict_parsing_functions import append_list_if_object_could_be_parsed_append_logger_if_not, \ + datetime_from_str, parse_optional_field, raise_parsing_error_without_additional_text_if_logger_has_messages, \ + raise_parsing_error_if_logger_has_messages, \ transform_json_str_to_enum_name, try_construction_raise_parsing_error, \ - try_parse_optional_field_append_logger_when_failing + try_parse_optional_field_append_logger_when_failing, try_parse_required_field_append_logger_when_failing from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -42,14 +44,12 @@ def __init__(self): def parse_packages(self, packages_dict_list: List[Dict]) -> List[Package]: packages_list = [] for package_dict in packages_dict_list: - try: - package: Package = self.parse_package(package_dict) - packages_list.append(package) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) - continue - if self.logger.has_messages(): - raise SPDXParsingError(self.logger.get_messages()) + packages_list = append_list_if_object_could_be_parsed_append_logger_if_not(logger=self.logger, + list_to_append=packages_list, + field=package_dict, + method_to_parse=self.parse_package) + + raise_parsing_error_without_additional_text_if_logger_has_messages(self.logger) return packages_list @@ -129,8 +129,7 @@ def parse_package(self, package_dict: Dict) -> Package: method_to_parse=datetime_from_str) version_info: Optional[str] = package_dict.get("versionInfo") - if logger.has_messages(): - raise SPDXParsingError([f"Error while parsing Package {name}: {logger.get_messages()}"]) + raise_parsing_error_if_logger_has_messages(logger, f"Package {name}") package = try_construction_raise_parsing_error(Package, dict(spdx_id=spdx_id, name=name, download_location=download_location, @@ -158,25 +157,21 @@ def parse_package(self, package_dict: Dict) -> Package: def parse_external_refs(self, external_ref_dicts: List[Dict]) -> List[ExternalPackageRef]: external_refs = [] for external_ref_dict in external_ref_dicts: - try: - external_ref: ExternalPackageRef = self.parse_external_ref(external_ref_dict) - external_refs.append(external_ref) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) + external_refs = append_list_if_object_could_be_parsed_append_logger_if_not(logger=self.logger, + list_to_append=external_refs, + field=external_ref_dict, + method_to_parse=self.parse_external_ref) + return external_refs def parse_external_ref(self, external_ref_dict: Dict) -> ExternalPackageRef: logger = Logger() - try: - ref_category = self.parse_external_ref_category(external_ref_dict.get("referenceCategory")) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - ref_category = None + ref_category = try_parse_required_field_append_logger_when_failing(logger=logger, field=external_ref_dict.get( + "referenceCategory"), method_to_parse=self.parse_external_ref_category) ref_locator = external_ref_dict.get("referenceLocator") ref_type = external_ref_dict.get("referenceType") comment = external_ref_dict.get("comment") - if logger.has_messages(): - raise SPDXParsingError([f"Error while parsing external ref: {logger.get_messages()}"]) + raise_parsing_error_if_logger_has_messages(logger, "external ref") external_ref = try_construction_raise_parsing_error(ExternalPackageRef, dict(category=ref_category, reference_type=ref_type, locator=ref_locator, diff --git a/src/parser/json/relationship_parser.py b/src/parser/json/relationship_parser.py index b143ff968..2f6a30f32 100644 --- a/src/parser/json/relationship_parser.py +++ b/src/parser/json/relationship_parser.py @@ -13,7 +13,9 @@ from src.model.relationship import Relationship, RelationshipType from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import transform_json_str_to_enum_name, \ +from src.parser.json.dict_parsing_functions import append_list_if_object_could_be_parsed_append_logger_if_not, \ + raise_parsing_error_if_logger_has_messages, raise_parsing_error_without_additional_text_if_logger_has_messages, \ + transform_json_str_to_enum_name, \ try_construction_raise_parsing_error, try_parse_required_field_append_logger_when_failing from src.parser.logger import Logger @@ -28,44 +30,42 @@ def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]: relationships_list = [] relationships_dicts: List[Dict] = input_doc_dict.get("relationships") if relationships_dicts: - try: - relationships = self.parse_relationships(relationship_dicts=relationships_dicts) - relationships_list.extend(relationships) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) + relationships_list.extend( + try_parse_required_field_append_logger_when_failing(logger=self.logger, field=relationships_dicts, + method_to_parse=self.parse_relationships, + default=[])) document_describes: List[str] = input_doc_dict.get("documentDescribes") doc_spdx_id: str = input_doc_dict.get("SPDXID") if document_describes: - try: - describes_relationships = self.parse_document_describes(doc_spdx_id=doc_spdx_id, - described_spdx_ids=document_describes, - created_relationships=relationships_list) - relationships_list.extend(describes_relationships) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) - - package_dicts: List[Dict] = input_doc_dict.get("packages") - if package_dicts: - try: - contains_relationships = self.parse_has_files(package_dicts=package_dicts, - created_relationships=relationships_list) - relationships_list.extend(contains_relationships) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) - - file_dicts: List[Dict] = input_doc_dict.get("files") - if file_dicts: - # not implemented yet, deal with deprecated fields in file - try: - dependency_relationships = self.parse_file_dependencies(file_dicts=file_dicts) - relationships_list.extend(dependency_relationships) - except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) + relationships_list.extend( + try_parse_required_field_append_logger_when_failing(logger=self.logger, field=document_describes, + method_to_parse=lambda + x: self.parse_document_describes( + doc_spdx_id=doc_spdx_id, described_spdx_ids=x, + created_relationships=relationships_list), + default=[])) + + package_dicts: List[Dict] = input_doc_dict.get("packages") + if package_dicts: + relationships_list.extend( + try_parse_required_field_append_logger_when_failing(logger=self.logger, field=package_dicts, + method_to_parse=lambda x: self.parse_has_files( + package_dicts=x, + created_relationships=relationships_list), + default=[])) + + file_dicts: List[Dict] = input_doc_dict.get("files") + if file_dicts: + # not implemented yet, deal with deprecated fields in file + relationships_list.extend( + try_parse_required_field_append_logger_when_failing(logger=self.logger, field=file_dicts, + method_to_parse=self.parse_file_dependencies, + default=[])) + generated_relationships = self.parse_artifact_of(file_dicts=file_dicts) - if self.logger.has_messages(): - raise SPDXParsingError(self.logger.get_messages()) + raise_parsing_error_without_additional_text_if_logger_has_messages(self.logger) return relationships_list @@ -73,12 +73,11 @@ def parse_relationships(self, relationship_dicts: List[Dict]) -> List[Relationsh logger = Logger() relationship_list = [] for relationship_dict in relationship_dicts: - try: - relationship_list.append(self.parse_relationship(relationship_dict)) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - if logger.has_messages(): - raise SPDXParsingError(logger.has_messages()) + relationship_list = append_list_if_object_could_be_parsed_append_logger_if_not(logger=logger, + list_to_append=relationship_list, + field=relationship_dict, + method_to_parse=self.parse_relationship) + raise_parsing_error_without_additional_text_if_logger_has_messages(logger) return relationship_list def parse_relationship(self, relationship_dict: Dict) -> Relationship: @@ -89,8 +88,7 @@ def parse_relationship(self, relationship_dict: Dict) -> Relationship: logger=logger, field=relationship_dict.get("relationshipType"), method_to_parse=self.parse_relationship_type) relationship_comment: str = relationship_dict.get("comment") - if logger.has_messages(): - raise SPDXParsingError([f"Error while parsing relationship: {logger.get_messages()}"]) + raise_parsing_error_if_logger_has_messages(logger, "relationship") relationship = try_construction_raise_parsing_error(Relationship, dict(spdx_element_id=spdx_element_id, relationship_type=relationship_type, @@ -122,8 +120,7 @@ def parse_document_describes(self, doc_spdx_id: str, described_spdx_ids: List[st continue if not self.check_if_relationship_exists(describes_relationship, created_relationships): describes_relationships.append(describes_relationship) - if logger.has_messages(): - raise SPDXParsingError([f"Error while creating describes_relationship : {logger.get_messages()}"]) + raise_parsing_error_if_logger_has_messages(logger, "describes_relationship") return describes_relationships @@ -147,8 +144,7 @@ def parse_has_files(self, package_dicts: List[Dict], created_relationships: List if not self.check_if_relationship_exists(relationship=contains_relationship, created_relationships=created_relationships): contains_relationships.append(contains_relationship) - if logger.has_messages(): - raise SPDXParsingError([f"Error while creating describes_relationship : {logger.get_messages()}"]) + raise_parsing_error_if_logger_has_messages(logger, "describes_relationship") return contains_relationships @@ -201,8 +197,7 @@ def parse_file_dependencies(file_dicts: List[Dict]) -> List[Relationship]: logger.append_all(err.get_messages()) continue dependency_relationships.append(dependency_relationship) - if logger.has_messages(): - raise SPDXParsingError([f"Error while creating dependency relationships: {logger.get_messages()}"]) + raise_parsing_error_if_logger_has_messages(logger, "dependency relationship") return dependency_relationships @staticmethod diff --git a/tests/parser/test_annotation_parser.py b/tests/parser/test_annotation_parser.py index 323947b13..061b9c8ec 100644 --- a/tests/parser/test_annotation_parser.py +++ b/tests/parser/test_annotation_parser.py @@ -115,5 +115,5 @@ def test_parse_incomplete_annotation(): _ = annotation_parser.parse_annotation(annotation_dict) assert err.type == SPDXParsingError - assert err.value.messages == ["Error while parsing annotation: ['Invalid annotation type: None', 'Could not " + assert err.value.messages == ["Error while parsing Annotation: ['Invalid annotation type: None', 'Could not " "convert str to datetime, invalid type: NoneType']"] diff --git a/tests/parser/test_checksum_parser.py b/tests/parser/test_checksum_parser.py index 035ff8de8..2e8768706 100644 --- a/tests/parser/test_checksum_parser.py +++ b/tests/parser/test_checksum_parser.py @@ -39,5 +39,5 @@ def test_invalid_checksum(): _ = checksum_parser.parse_checksum(checksum_dict) assert err.typename == 'SPDXParsingError' - assert err.value.messages[0] == "Error while parsing checksum: ['Algorithm SHA not valid for checksum.']" + assert err.value.messages[0] == "Error while parsing Checksum: ['Algorithm SHA not valid for checksum.']" diff --git a/tests/parser/test_file_parser.py b/tests/parser/test_file_parser.py index d3435e892..c9c09a64d 100644 --- a/tests/parser/test_file_parser.py +++ b/tests/parser/test_file_parser.py @@ -105,7 +105,7 @@ def test_parse_falsy_files(): "are mandatory for files.']", 'Error while constructing File: [\'SetterError File: type of argument "name" ' "must be str; got NoneType instead: None']", - 'Error while parsing file None: ["Error while parsing checksum: [\'Algorithm ' + 'Error while parsing file None: ["Error while parsing Checksum: [\'Algorithm ' 'MD not valid for checksum.\']"]'] diff --git a/tests/parser/test_package_parser.py b/tests/parser/test_package_parser.py index cf52b8259..fa4f39a7b 100644 --- a/tests/parser/test_package_parser.py +++ b/tests/parser/test_package_parser.py @@ -171,7 +171,7 @@ def test_package_with_falsy_values(): assert err.type == SPDXParsingError assert err.value.get_messages() == [ - 'Error while parsing Package Example Package: ["Error while parsing checksum: [\'Algorithm SHA not valid for checksum.\']"]'] + 'Error while parsing Package Example Package: ["Error while parsing Checksum: [\'Algorithm SHA not valid for checksum.\']"]'] def test_parse_packages(): @@ -198,7 +198,7 @@ def test_parse_packages(): _ = package_parser.parse_packages(packages_list) assert err.type == SPDXParsingError - assert err.value.messages == ['Error while parsing Package Example Package: ["Error while parsing checksum: ' + assert err.value.messages == ['Error while parsing Package Example Package: ["Error while parsing Checksum: ' '[\'Algorithm SHA not valid for checksum.\']"]', "Error while constructing Package: ['SetterError Package: type of argument " '"name" must be str; got int instead: 5\']'] From 4613789d867e3b826d73e28117d3edc54a312895 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 15 Dec 2022 15:24:06 +0100 Subject: [PATCH 042/362] [issue-305, review] refactor methods in dict_parsing_functions.py, small changes Signed-off-by: Meret Behrens --- src/parser/json/actor_parser.py | 32 ++-- src/parser/json/annotation_parser.py | 78 ++++----- src/parser/json/checksum_parser.py | 22 ++- src/parser/json/creation_info_parser.py | 90 +++++----- src/parser/json/dict_parsing_functions.py | 54 +++--- ....py => extracted_licensing_info_parser.py} | 30 ++-- src/parser/json/file_parser.py | 54 +++--- src/parser/json/json_parser.py | 72 ++++---- src/parser/json/license_expression_parser.py | 4 +- src/parser/json/package_parser.py | 161 +++++++++--------- src/parser/json/relationship_parser.py | 67 ++++---- src/parser/json/snippet_parser.py | 43 +++-- src/parser/logger.py | 2 +- tests/parser/test_creation_info_parser.py | 2 +- .../test_extracted_licensing_info_parser.py | 2 +- 15 files changed, 332 insertions(+), 381 deletions(-) rename src/parser/json/{extracted_licensing_parser.py => extracted_licensing_info_parser.py} (67%) diff --git a/src/parser/json/actor_parser.py b/src/parser/json/actor_parser.py index 9c8c657cd..b5a68aa29 100644 --- a/src/parser/json/actor_parser.py +++ b/src/parser/json/actor_parser.py @@ -9,12 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. import re -from typing import Union, Pattern, Match +from typing import Union, Pattern, Match, Optional from src.model.actor import Actor, ActorType from src.model.spdx_no_assertion import SpdxNoAssertion from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import try_construction_raise_parsing_error +from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error class ActorParser: @@ -26,28 +26,36 @@ def parse_actor_or_no_assertion(self, actor_or_no_assertion: str) -> Union[SpdxN @staticmethod def parse_actor(actor: str) -> Actor: - tool_re: Pattern = re.compile(r"Tool:\s*(.+)", re.UNICODE) - person_re: Pattern = re.compile(r"Person:\s*(([^(])+)(\((.*)\))?", re.UNICODE) - org_re: Pattern = re.compile(r"Organization:\s*(([^(])+)(\((.*)\))?", re.UNICODE) + tool_re: Pattern = re.compile(r"^Tool:\s*(.+)", re.UNICODE) + person_re: Pattern = re.compile(r"^Person:\s*(([^(])+)(\((.*)\))?", re.UNICODE) + org_re: Pattern = re.compile(r"^Organization:\s*(([^(])+)(\((.*)\))?", re.UNICODE) tool_match: Match = tool_re.match(actor) person_match: Match = person_re.match(actor) org_match: Match = org_re.match(actor) if tool_match: name: str = tool_match.group(1).strip() - creator = try_construction_raise_parsing_error(Actor,dict(actor_type=ActorType.TOOL, name=name)) + creator = construct_or_raise_parsing_error(Actor, dict(actor_type=ActorType.TOOL, name=name)) elif person_match: name: str = person_match.group(1).strip() - email: str = person_match.group(4).strip() if person_match.group(4) else None - creator = try_construction_raise_parsing_error(Actor, - dict(actor_type=ActorType.PERSON, name=name, email=email)) + email: Optional[str] = ActorParser.get_email_or_none(person_match) + creator = construct_or_raise_parsing_error(Actor, dict(actor_type=ActorType.PERSON, name=name, email=email)) elif org_match: name: str = org_match.group(1).strip() - email: str = org_match.group(4).strip() if org_match.group(4) else None - creator = try_construction_raise_parsing_error(Actor, dict(actor_type=ActorType.ORGANIZATION, name=name, - email=email)) + email: Optional[str] = ActorParser.get_email_or_none(org_match) + creator = construct_or_raise_parsing_error(Actor, + dict(actor_type=ActorType.ORGANIZATION, name=name, email=email)) else: raise SPDXParsingError([f"Actor {actor} doesn't match any of person, organization or tool."]) return creator + + @staticmethod + def get_email_or_none(match: Match) -> Optional[str]: + email_match = match.group(4) + if email_match and email_match.strip(): + email = email_match.strip() + else: + email = None + return email diff --git a/src/parser/json/annotation_parser.py b/src/parser/json/annotation_parser.py index 83703d538..4094eb203 100644 --- a/src/parser/json/annotation_parser.py +++ b/src/parser/json/annotation_parser.py @@ -15,9 +15,8 @@ from src.model.annotation import Annotation, AnnotationType from src.parser.error import SPDXParsingError from src.parser.json.actor_parser import ActorParser -from src.parser.json.dict_parsing_functions import datetime_from_str, try_construction_raise_parsing_error, \ - try_parse_optional_field_append_logger_when_failing, try_parse_required_field_append_logger_when_failing, \ - append_list_if_object_could_be_parsed_append_logger_if_not, raise_parsing_error_if_logger_has_messages +from src.parser.json.dict_parsing_functions import datetime_from_str, construct_or_raise_parsing_error, \ + parse_field_or_log_error, append_parsed_field_or_log_error, raise_parsing_error_if_logger_has_messages from src.parser.logger import Logger @@ -35,8 +34,8 @@ def parse_all_annotations(self, input_doc_dict: Dict) -> List[Annotation]: reviews: List[Dict] = input_doc_dict.get("revieweds") if reviews: for review in reviews: - annotations_list = append_list_if_object_could_be_parsed_append_logger_if_not( - list_to_append=annotations_list, logger=self.logger, field=review, + annotations_list = append_parsed_field_or_log_error( + list_to_append_to=annotations_list, logger=self.logger, field=review, method_to_parse=lambda x: self.parse_review(x, spdx_id=input_doc_dict.get("SPDXID"))) packages: List[Dict] = input_doc_dict.get("packages") @@ -55,26 +54,20 @@ def parse_annotations_from_object(self, annotations_list, element_list: List[Dic element_spdx_id: str = element.get("SPDXID") element_annotations: List[Dict] = element.get("annotations") if element_annotations: - annotations_list.extend(try_parse_required_field_append_logger_when_failing(logger=self.logger, - field=element_annotations, - method_to_parse=lambda - x: self.parse_annotations( - x, - spdx_id=element_spdx_id), - default=[])) + annotations_list.extend(parse_field_or_log_error( + logger=self.logger, field=element_annotations, + parsing_method=lambda x: self.parse_annotations(x, spdx_id=element_spdx_id), + default=[])) def parse_annotations(self, annotations_dict_list: List[Dict], spdx_id: Optional[str] = None) -> List[Annotation]: logger = Logger() annotations_list = [] for annotation_dict in annotations_dict_list: - annotations_list = append_list_if_object_could_be_parsed_append_logger_if_not( - list_to_append=annotations_list, + annotations_list = append_parsed_field_or_log_error( + list_to_append_to=annotations_list, logger=self.logger, field=annotation_dict, - method_to_parse=lambda - x: self.parse_annotation( - x, - spdx_id=spdx_id)) + method_to_parse=lambda x: self.parse_annotation(x, spdx_id=spdx_id)) raise_parsing_error_if_logger_has_messages(logger, "Annotations") return annotations_list @@ -83,26 +76,23 @@ def parse_annotation(self, annotation: Dict, spdx_id: Optional[str] = None) -> A logger = Logger() spdx_id: str = annotation.get("SPDXID") or spdx_id - annotation_type: Optional[AnnotationType] = try_parse_required_field_append_logger_when_failing( - logger=logger, field=annotation.get("annotationType"), - method_to_parse=self.parse_annotation_type) + annotation_type: Optional[AnnotationType] = parse_field_or_log_error(logger=logger, + field=annotation.get("annotationType"), + parsing_method=self.parse_annotation_type) - annotator: Optional[Actor] = try_parse_required_field_append_logger_when_failing(logger=logger, - field=annotation.get( - "annotator"), - method_to_parse=self.actor_parser.parse_actor) + annotator: Optional[Actor] = parse_field_or_log_error(logger=logger, field=annotation.get("annotator"), + parsing_method=self.actor_parser.parse_actor) - annotation_date: Optional[datetime] = try_parse_required_field_append_logger_when_failing(logger=logger, - field=annotation.get( - "annotationDate"), - method_to_parse=datetime_from_str) + annotation_date: Optional[datetime] = parse_field_or_log_error(logger=logger, + field=annotation.get("annotationDate"), + parsing_method=datetime_from_str) annotation_comment: str = annotation.get("comment") raise_parsing_error_if_logger_has_messages(logger, "Annotation") - annotation = try_construction_raise_parsing_error(Annotation, - dict(spdx_id=spdx_id, annotation_type=annotation_type, - annotator=annotator, annotation_date=annotation_date, - annotation_comment=annotation_comment)) + annotation = construct_or_raise_parsing_error(Annotation, + dict(spdx_id=spdx_id, annotation_type=annotation_type, + annotator=annotator, annotation_date=annotation_date, + annotation_comment=annotation_comment)) return annotation @@ -115,22 +105,20 @@ def parse_annotation_type(annotation_type: str) -> AnnotationType: def parse_review(self, review_dict: Dict, spdx_id: str) -> Annotation: logger = Logger() - annotator: Optional[Actor] = try_parse_optional_field_append_logger_when_failing(logger=logger, - field=review_dict.get( - "reviewer"), - method_to_parse=self.actor_parser.parse_actor) + annotator: Optional[Actor] = parse_field_or_log_error(logger=logger, + field=review_dict.get("reviewer"), + parsing_method=self.actor_parser.parse_actor, + optional=True) - annotation_date: Optional[datetime] = try_parse_required_field_append_logger_when_failing(logger=logger, - field=review_dict.get( - "reviewDate"), - method_to_parse=datetime_from_str) + annotation_date: Optional[datetime] = parse_field_or_log_error(logger=logger, field=review_dict.get("reviewDate"), + parsing_method=datetime_from_str) annotation_type = AnnotationType.REVIEW comment: str = review_dict.get("comment") raise_parsing_error_if_logger_has_messages(logger, "Review") - annotation = try_construction_raise_parsing_error(Annotation, - dict(spdx_id=spdx_id, annotation_type=annotation_type, - annotator=annotator, annotation_date=annotation_date, - annotation_comment=comment)) + annotation = construct_or_raise_parsing_error(Annotation, + dict(spdx_id=spdx_id, annotation_type=annotation_type, + annotator=annotator, annotation_date=annotation_date, + annotation_comment=comment)) return annotation diff --git a/src/parser/json/checksum_parser.py b/src/parser/json/checksum_parser.py index ca0f1432e..c870809de 100644 --- a/src/parser/json/checksum_parser.py +++ b/src/parser/json/checksum_parser.py @@ -12,10 +12,8 @@ from src.model.checksum import Checksum, ChecksumAlgorithm from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import append_list_if_object_could_be_parsed_append_logger_if_not, \ - raise_parsing_error_if_logger_has_messages, \ - raise_parsing_error_without_additional_text_if_logger_has_messages, \ - transform_json_str_to_enum_name, try_construction_raise_parsing_error +from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, \ + raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, construct_or_raise_parsing_error from src.parser.logger import Logger @@ -31,18 +29,18 @@ def parse_checksums(self, checksum_dicts_list: List[Dict]) -> List[Checksum]: checksum_list = [] for checksum_dict in checksum_dicts_list: - checksum_list = append_list_if_object_could_be_parsed_append_logger_if_not(logger=self.auxiliary_logger, - list_to_append=checksum_list, - field=checksum_dict, - method_to_parse=self.parse_checksum) + checksum_list = append_parsed_field_or_log_error(logger=self.auxiliary_logger, + list_to_append_to=checksum_list, + field=checksum_dict, + method_to_parse=self.parse_checksum) - raise_parsing_error_without_additional_text_if_logger_has_messages(self.auxiliary_logger) + raise_parsing_error_if_logger_has_messages(self.auxiliary_logger) return checksum_list @staticmethod def parse_checksum(checksum_dict: Dict) -> Checksum: logger = Logger() - algorithm = transform_json_str_to_enum_name(checksum_dict.get("algorithm")) + algorithm = json_str_to_enum_name(checksum_dict.get("algorithm")) try: checksum_algorithm = ChecksumAlgorithm[algorithm] except KeyError: @@ -50,6 +48,6 @@ def parse_checksum(checksum_dict: Dict) -> Checksum: checksum_algorithm = None checksum_value = checksum_dict.get("checksumValue") raise_parsing_error_if_logger_has_messages(logger, "Checksum") - checksum = try_construction_raise_parsing_error(Checksum, - dict(algorithm=checksum_algorithm, value=checksum_value)) + checksum = construct_or_raise_parsing_error(Checksum, + dict(algorithm=checksum_algorithm, value=checksum_value)) return checksum diff --git a/src/parser/json/creation_info_parser.py b/src/parser/json/creation_info_parser.py index c202f342e..8efdf0a11 100644 --- a/src/parser/json/creation_info_parser.py +++ b/src/parser/json/creation_info_parser.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from datetime import datetime -from typing import Dict, Optional, List, Union +from typing import Dict, Optional, List from src.model.actor import Actor from src.model.checksum import Checksum @@ -19,12 +19,8 @@ from src.parser.error import SPDXParsingError from src.parser.json.actor_parser import ActorParser from src.parser.json.checksum_parser import ChecksumParser -from src.parser.json.dict_parsing_functions import append_list_if_object_could_be_parsed_append_logger_if_not, \ - datetime_from_str, raise_parsing_error_if_logger_has_messages, \ - raise_parsing_error_without_additional_text_if_logger_has_messages, \ - try_construction_raise_parsing_error, \ - try_parse_optional_field_append_logger_when_failing, \ - try_parse_required_field_append_logger_when_failing +from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, datetime_from_str, \ + raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error, parse_field_or_log_error from src.parser.logger import Logger @@ -48,55 +44,52 @@ def parse_creation_info(self, doc_dict: Dict) -> CreationInfo: # There are nested required properties. If creationInfo is not set, we cannot continue parsing. if creation_info_dict is None: - logger.append("CreationInfo is not valid.") + logger.append("CreationInfo does not exist.") raise SPDXParsingError([f"Error while parsing doc {name}: {logger.get_messages()}"]) list_of_creators: List[str] = creation_info_dict.get("creators") - creators: List[Actor] = try_parse_required_field_append_logger_when_failing(logger=logger, - field=list_of_creators, - method_to_parse=self.parse_creators, - default=[]) + creators: List[Actor] = parse_field_or_log_error(logger=logger, field=list_of_creators, + parsing_method=self.parse_creators, default=[]) - created: Optional[datetime] = try_parse_required_field_append_logger_when_failing(logger=logger, - field=creation_info_dict.get( - "created"), - method_to_parse=datetime_from_str) + created: Optional[datetime] = parse_field_or_log_error(logger=logger, field=creation_info_dict.get( + "created"), parsing_method=datetime_from_str) creator_comment: Optional[str] = creation_info_dict.get("comment") data_license: str = doc_dict.get("dataLicense") - external_document_refs: List[ExternalDocumentRef] = try_parse_optional_field_append_logger_when_failing( + external_document_refs: List[ExternalDocumentRef] = parse_field_or_log_error( logger=logger, field=doc_dict.get("externalDocumentRefs"), - method_to_parse=self.parse_external_document_refs) - license_list_version: Optional[Version] = try_parse_optional_field_append_logger_when_failing(logger=logger, - field=creation_info_dict.get( - "licenseListVersion"), - method_to_parse=self.parse_version) + parsing_method=self.parse_external_document_refs, optional=True) + license_list_version: Optional[Version] = parse_field_or_log_error(logger=logger, + field=creation_info_dict.get( + "licenseListVersion"), + parsing_method=self.parse_version, + optional=True) document_comment: Optional[str] = doc_dict.get("comment") raise_parsing_error_if_logger_has_messages(logger, f"Document: {name}") - creation_info = try_construction_raise_parsing_error(CreationInfo, - dict(spdx_version=spdx_version, spdx_id=spdx_id, name=name, - document_namespace=document_namespace, - creators=creators, created=created, - license_list_version=license_list_version, - document_comment=document_comment, - creator_comment=creator_comment, - data_license=data_license, - external_document_refs=external_document_refs)) + creation_info = construct_or_raise_parsing_error(CreationInfo, + dict(spdx_version=spdx_version, spdx_id=spdx_id, name=name, + document_namespace=document_namespace, + creators=creators, created=created, + license_list_version=license_list_version, + document_comment=document_comment, + creator_comment=creator_comment, + data_license=data_license, + external_document_refs=external_document_refs)) return creation_info - def parse_creators(self, creators_dict_list: List[str]) -> List[Actor]: + def parse_creators(self, creators_list_from_dict: List[str]) -> List[Actor]: logger = Logger() creators_list = [] - for creator_dict in creators_dict_list: - creators_list = append_list_if_object_could_be_parsed_append_logger_if_not(list_to_append=creators_list, - logger=logger, - field=creator_dict, - method_to_parse=self.actor_parser.parse_actor_or_no_assert) + for creator_dict in creators_list_from_dict: + creators_list = append_parsed_field_or_log_error(list_to_append_to=creators_list, + logger=logger, + field=creator_dict, + method_to_parse=self.actor_parser.parse_actor_or_no_assertion) - raise_parsing_error_without_additional_text_if_logger_has_messages(logger) + raise_parsing_error_if_logger_has_messages(logger) return creators_list @staticmethod @@ -110,27 +103,26 @@ def parse_external_document_refs(self, external_document_refs_dict: List[Dict]) logger = Logger() external_document_refs = [] for external_ref_dict in external_document_refs_dict: - external_doc_ref: ExternalDocumentRef = try_parse_optional_field_append_logger_when_failing(logger=logger, - field=external_ref_dict, - method_to_parse=self.parse_external_doc_ref) + external_doc_ref: ExternalDocumentRef = parse_field_or_log_error(logger=logger, + field=external_ref_dict, + parsing_method=self.parse_external_doc_ref, + optional=True) external_document_refs.append(external_doc_ref) - raise_parsing_error_without_additional_text_if_logger_has_messages(logger) + raise_parsing_error_if_logger_has_messages(logger) return external_document_refs def parse_external_doc_ref(self, external_doc_ref_dict: Dict) -> ExternalDocumentRef: logger = Logger() - checksum: Optional[Checksum] = try_parse_required_field_append_logger_when_failing(logger=logger, - field=external_doc_ref_dict.get( - "checksum"), - method_to_parse=self.checksum_parser.parse_checksum) + checksum: Optional[Checksum] = parse_field_or_log_error(logger=logger, field=external_doc_ref_dict.get( + "checksum"), parsing_method=self.checksum_parser.parse_checksum) external_document_id: str = external_doc_ref_dict.get("externalDocumentId") spdx_document: str = external_doc_ref_dict.get("spdxDocument") raise_parsing_error_if_logger_has_messages(logger, "ExternalDocRef") - external_doc_ref = try_construction_raise_parsing_error(ExternalDocumentRef, - dict(document_ref_id=external_document_id, - checksum=checksum, document_uri=spdx_document)) + external_doc_ref = construct_or_raise_parsing_error(ExternalDocumentRef, + dict(document_ref_id=external_document_id, + checksum=checksum, document_uri=spdx_document)) return external_doc_ref diff --git a/src/parser/json/dict_parsing_functions.py b/src/parser/json/dict_parsing_functions.py index 45cc1b1bb..431f56c9a 100644 --- a/src/parser/json/dict_parsing_functions.py +++ b/src/parser/json/dict_parsing_functions.py @@ -16,12 +16,6 @@ from src.parser.logger import Logger -def parse_optional_field(field: Any, method_to_parse: Callable = lambda x: x, default=None): - if not field: - return default - return method_to_parse(field) - - def datetime_from_str(date_str: str) -> datetime: if not isinstance(date_str, str): raise SPDXParsingError([f"Could not convert str to datetime, invalid type: {type(date_str).__name__}"]) @@ -29,11 +23,11 @@ def datetime_from_str(date_str: str) -> datetime: return date -def transform_json_str_to_enum_name(json_str: str) -> str: +def json_str_to_enum_name(json_str: str) -> str: return json_str.replace("-", "_").upper() -def try_construction_raise_parsing_error(object_to_construct: Any, args_for_construction: Dict) -> Any: +def construct_or_raise_parsing_error(object_to_construct: Any, args_for_construction: Dict) -> Any: try: constructed_object = object_to_construct(**args_for_construction) except ConstructorTypeErrors as err: @@ -41,40 +35,34 @@ def try_construction_raise_parsing_error(object_to_construct: Any, args_for_cons return constructed_object -def try_parse_optional_field_append_logger_when_failing(logger: Logger, field: Any, method_to_parse: Callable, - default=None): +def parse_field_or_log_error(logger: Logger, field: Any, parsing_method: Callable = lambda x: x, default=None, + optional=False) -> Any: try: - parsed_element = parse_optional_field(field=field, method_to_parse=method_to_parse, default=default) + if optional: + if not field: + return default + parsed_element = parsing_method(field) + else: + parsed_element = parsing_method(field) except SPDXParsingError as err: - logger.append_all(err.get_messages()) + logger.extend(err.get_messages()) parsed_element = default return parsed_element -def try_parse_required_field_append_logger_when_failing(logger: Logger, field: Any, method_to_parse: Callable, - default=None): +def append_parsed_field_or_log_error(logger: Logger, list_to_append_to: List[Any], field: Any, + method_to_parse: Callable) -> List[Any]: try: parsed_element = method_to_parse(field) + list_to_append_to.append(parsed_element) except SPDXParsingError as err: - logger.append_all(err.get_messages()) - parsed_element = default - return parsed_element - + logger.extend(err.get_messages()) + return list_to_append_to -def append_list_if_object_could_be_parsed_append_logger_if_not(logger: Logger, list_to_append: List[Any], field: Any, - method_to_parse: Callable): - try: - parsed_element = method_to_parse(field) - list_to_append.append(parsed_element) - except SPDXParsingError as err: - logger.append_all(err.get_messages()) - return list_to_append - - -def raise_parsing_error_if_logger_has_messages(logger: Logger, parsed_object_name: str): - if logger.has_messages(): - raise SPDXParsingError([f"Error while parsing {parsed_object_name}: {logger.get_messages()}"]) -def raise_parsing_error_without_additional_text_if_logger_has_messages(logger: Logger): +def raise_parsing_error_if_logger_has_messages(logger: Logger, parsed_object_name: str = None): if logger.has_messages(): - raise SPDXParsingError(logger.get_messages()) + if parsed_object_name: + raise SPDXParsingError([f"Error while parsing {parsed_object_name}: {logger.get_messages()}"]) + else: + raise SPDXParsingError(logger.get_messages()) diff --git a/src/parser/json/extracted_licensing_parser.py b/src/parser/json/extracted_licensing_info_parser.py similarity index 67% rename from src/parser/json/extracted_licensing_parser.py rename to src/parser/json/extracted_licensing_info_parser.py index 403a83b08..1248b26c0 100644 --- a/src/parser/json/extracted_licensing_parser.py +++ b/src/parser/json/extracted_licensing_info_parser.py @@ -12,9 +12,8 @@ from src.model.extracted_licensing_info import ExtractedLicensingInfo from src.model.spdx_no_assertion import SpdxNoAssertion -from src.parser.json.dict_parsing_functions import parse_optional_field, \ - raise_parsing_error_without_additional_text_if_logger_has_messages, \ - append_list_if_object_could_be_parsed_append_logger_if_not, try_construction_raise_parsing_error +from src.parser.json.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ + append_parsed_field_or_log_error, construct_or_raise_parsing_error, parse_field_or_log_error from src.parser.logger import Logger @@ -28,27 +27,30 @@ def parse_extracted_licensing_infos(self, extracted_licensing_info_dicts: List[D ExtractedLicensingInfo]: extracted_licensing_info_list = [] for extracted_licensing_info_dict in extracted_licensing_info_dicts: - extracted_licensing_info_list = append_list_if_object_could_be_parsed_append_logger_if_not( - list_to_append=extracted_licensing_info_list, + extracted_licensing_info_list = append_parsed_field_or_log_error( + list_to_append_to=extracted_licensing_info_list, logger=self.logger, field=extracted_licensing_info_dict, method_to_parse=self.parse_extracted_licensing_info) - raise_parsing_error_without_additional_text_if_logger_has_messages(self.logger) + raise_parsing_error_if_logger_has_messages(self.logger) return extracted_licensing_info_list def parse_extracted_licensing_info(self, extracted_licensing_info_dict: Dict) -> ExtractedLicensingInfo: license_id: Optional[str] = extracted_licensing_info_dict.get("licenseId") extracted_text: Optional[str] = extracted_licensing_info_dict.get("extractedText") - license_name: Optional[Union[str, SpdxNoAssertion]] = parse_optional_field( - extracted_licensing_info_dict.get("name"), self.parse_extracted_licensing_info_name) + license_name: Optional[Union[str, SpdxNoAssertion]] = parse_field_or_log_error(logger=self.logger, + field=extracted_licensing_info_dict.get( + "name"), + parsing_method=self.parse_extracted_licensing_info_name, + optional=True) cross_references: List[str] = extracted_licensing_info_dict.get("seeAlsos") comment: str = extracted_licensing_info_dict.get("comment") - extracted_licensing_info_dict = try_construction_raise_parsing_error(ExtractedLicensingInfo, - dict(license_id=license_id, - extracted_text=extracted_text, - comment=comment, - license_name=license_name, - cross_references=cross_references)) + extracted_licensing_info_dict = construct_or_raise_parsing_error(ExtractedLicensingInfo, + dict(license_id=license_id, + extracted_text=extracted_text, + comment=comment, + license_name=license_name, + cross_references=cross_references)) return extracted_licensing_info_dict @staticmethod diff --git a/src/parser/json/file_parser.py b/src/parser/json/file_parser.py index e02e5788a..1012fb175 100644 --- a/src/parser/json/file_parser.py +++ b/src/parser/json/file_parser.py @@ -16,10 +16,8 @@ from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone from src.parser.json.checksum_parser import ChecksumParser -from src.parser.json.dict_parsing_functions import append_list_if_object_could_be_parsed_append_logger_if_not, \ - raise_parsing_error_without_additional_text_if_logger_has_messages, \ - raise_parsing_error_if_logger_has_messages, try_construction_raise_parsing_error, \ - try_parse_optional_field_append_logger_when_failing, try_parse_required_field_append_logger_when_failing +from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, \ + raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error, parse_field_or_log_error from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -37,10 +35,10 @@ def __init__(self): def parse_files(self, file_dict_list) -> List[File]: file_list = [] for file_dict in file_dict_list: - file_list = append_list_if_object_could_be_parsed_append_logger_if_not(list_to_append=file_list, - logger=self.logger, field=file_dict, - method_to_parse=self.parse_file) - raise_parsing_error_without_additional_text_if_logger_has_messages(self.logger) + file_list = append_parsed_field_or_log_error(list_to_append_to=file_list, + logger=self.logger, field=file_dict, + method_to_parse=self.parse_file) + raise_parsing_error_if_logger_has_messages(self.logger) return file_list def parse_file(self, file_dict: Dict) -> Optional[File]: @@ -49,44 +47,40 @@ def parse_file(self, file_dict: Dict) -> Optional[File]: spdx_id: str = file_dict.get("SPDXID") checksums_list: List[Dict] = file_dict.get("checksums") - checksums: List[Checksum] = try_parse_required_field_append_logger_when_failing(logger=logger, - field=checksums_list, - method_to_parse=self.checksum_parser.parse_checksums) + checksums: List[Checksum] = parse_field_or_log_error(logger=logger, field=checksums_list, + parsing_method=self.checksum_parser.parse_checksums) attribution_texts: Optional[str] = file_dict.get("attributionTexts") comment: Optional[str] = file_dict.get("comment") copyright_text: Optional[str] = file_dict.get("copyrightText") file_contributors: List[str] = file_dict.get("fileContributors") - file_types: List[FileType] = try_parse_optional_field_append_logger_when_failing(logger=logger, - field=file_dict.get( - "fileTypes"), - method_to_parse=self.parse_file_types) + file_types: List[FileType] = parse_field_or_log_error(logger=logger, field=file_dict.get("fileTypes"), + parsing_method=self.parse_file_types, optional=True) license_comments: Optional[str] = file_dict.get("licenseComments") license_concluded: Optional[Union[ - LicenseExpression, SpdxNoAssertion, SpdxNone]] = try_parse_optional_field_append_logger_when_failing( + LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( logger=logger, field=file_dict.get("licenseConcluded"), - method_to_parse=self.license_expression_parser.parse_license_expression) + parsing_method=self.license_expression_parser.parse_license_expression, optional=True) license_info_in_files: Optional[ Union[List[ - LicenseExpression], SpdxNoAssertion, SpdxNone]] = try_parse_optional_field_append_logger_when_failing( - logger=logger, - field=file_dict.get("licenseInfoInFiles"), - method_to_parse=self.license_expression_parser.parse_license_expression) + LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( + logger=logger, field=file_dict.get("licenseInfoInFiles"), + parsing_method=self.license_expression_parser.parse_license_expression, optional=True) notice_text: Optional[str] = file_dict.get("noticeText") raise_parsing_error_if_logger_has_messages(logger, f"file {name}") - file = try_construction_raise_parsing_error(File, dict(name=name, spdx_id=spdx_id, checksums=checksums, - attribution_texts=attribution_texts, - comment=comment, copyright_text=copyright_text, - file_type=file_types, contributors=file_contributors, - license_comment=license_comments, - concluded_license=license_concluded, - license_info_in_file=license_info_in_files, - notice=notice_text) - ) + file = construct_or_raise_parsing_error(File, dict(name=name, spdx_id=spdx_id, checksums=checksums, + attribution_texts=attribution_texts, + comment=comment, copyright_text=copyright_text, + file_type=file_types, contributors=file_contributors, + license_comment=license_comments, + concluded_license=license_concluded, + license_info_in_file=license_info_in_files, + notice=notice_text) + ) return file @staticmethod diff --git a/src/parser/json/json_parser.py b/src/parser/json/json_parser.py index 62984a10b..bf9160f3f 100644 --- a/src/parser/json/json_parser.py +++ b/src/parser/json/json_parser.py @@ -12,15 +12,19 @@ from json import JSONDecodeError from typing import List +from src.model.annotation import Annotation from src.model.document import Document, CreationInfo +from src.model.extracted_licensing_info import ExtractedLicensingInfo +from src.model.file import File from src.model.package import Package +from src.model.relationship import Relationship +from src.model.snippet import Snippet from src.parser.json.annotation_parser import AnnotationParser from src.parser.json.creation_info_parser import CreationInfoParser from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import raise_parsing_error_without_additional_text_if_logger_has_messages, \ - try_construction_raise_parsing_error, \ - try_parse_optional_field_append_logger_when_failing, try_parse_required_field_append_logger_when_failing -from src.parser.json.extracted_licensing_parser import ExtractedLicensingInfoParser +from src.parser.json.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ + construct_or_raise_parsing_error, parse_field_or_log_error +from src.parser.json.extracted_licensing_info_parser import ExtractedLicensingInfoParser from src.parser.json.file_parser import FileParser from src.parser.logger import Logger from src.parser.json.package_parser import PackageParser @@ -34,7 +38,7 @@ class JsonParser: package_parser: PackageParser file_parser: FileParser snippet_parser: SnippetParser - extracted_licenses_parser: ExtractedLicensingInfoParser + extracted_licensing_info_parser: ExtractedLicensingInfoParser relationship_parser: RelationshipParser annotation_parser: AnnotationParser @@ -44,7 +48,7 @@ def __init__(self): self.package_parser = PackageParser() self.file_parser = FileParser() self.snippet_parser = SnippetParser() - self.extracted_licenses_parser = ExtractedLicensingInfoParser() + self.extracted_licensing_info_parser = ExtractedLicensingInfoParser() self.relationship_parser = RelationshipParser() self.annotation_parser = AnnotationParser() @@ -59,40 +63,36 @@ def parse(self, filename: str) -> Document: self.logger.append(f"File {filename} is not a valid JSON file.") raise SPDXParsingError(self.logger.get_messages()) - creation_info: CreationInfo = try_parse_required_field_append_logger_when_failing(logger=self.logger, - field=input_doc_as_dict, - method_to_parse=self.creation_info_parser.parse_creation_info) + creation_info: CreationInfo = parse_field_or_log_error(logger=self.logger, field=input_doc_as_dict, + parsing_method=self.creation_info_parser.parse_creation_info) - packages: List[Package] = try_parse_optional_field_append_logger_when_failing(logger=self.logger, - field=input_doc_as_dict.get( - "packages"), - method_to_parse=self.package_parser.parse_packages, - default=None) + packages: List[Package] = parse_field_or_log_error(logger=self.logger, field=input_doc_as_dict.get("packages"), + parsing_method=self.package_parser.parse_packages, + optional=True) - files = try_parse_optional_field_append_logger_when_failing(logger=self.logger, - field=input_doc_as_dict.get("files"), - method_to_parse=self.file_parser.parse_files) + files: List[File] = parse_field_or_log_error(logger=self.logger, field=input_doc_as_dict.get("files"), + parsing_method=self.file_parser.parse_files, optional=True) - annotations = try_parse_optional_field_append_logger_when_failing(logger=self.logger, - field=input_doc_as_dict, - method_to_parse=self.annotation_parser.parse_all_annotations) - snippets = try_parse_optional_field_append_logger_when_failing(logger=self.logger, - field=input_doc_as_dict.get("snippets"), - method_to_parse=self.snippet_parser.parse_snippets) - relationships = try_parse_optional_field_append_logger_when_failing(logger=self.logger, - field=input_doc_as_dict, - method_to_parse=self.relationship_parser.parse_all_relationships) - extracted_licensing_info = try_parse_optional_field_append_logger_when_failing(logger=self.logger, - field=input_doc_as_dict.get( - "hasExtractedLicensingInfos"), - method_to_parse=self.extracted_licenses_parser.parse_extracted_licensing_infos) + annotations: List[Annotation] = parse_field_or_log_error(logger=self.logger, field=input_doc_as_dict, + parsing_method=self.annotation_parser.parse_all_annotations, + optional=True) + snippets: List[Snippet] = parse_field_or_log_error(logger=self.logger, field=input_doc_as_dict.get("snippets"), + parsing_method=self.snippet_parser.parse_snippets, + optional=True) + relationships: List[Relationship] = parse_field_or_log_error(logger=self.logger, field=input_doc_as_dict, + parsing_method=self.relationship_parser.parse_all_relationships, + optional=True) + extracted_licensing_info: List[ExtractedLicensingInfo] = parse_field_or_log_error(logger=self.logger, + field=input_doc_as_dict.get("hasExtractedLicensingInfos"), + parsing_method=self.extracted_licensing_info_parser.parse_extracted_licensing_infos, + optional=True) - raise_parsing_error_without_additional_text_if_logger_has_messages(self.logger) + raise_parsing_error_if_logger_has_messages(self.logger) - document = try_construction_raise_parsing_error(Document, dict(creation_info=creation_info, packages=packages, - files=files, - annotations=annotations, - snippets=snippets, relationships=relationships, - extracted_licensing_info=extracted_licensing_info)) + document = construct_or_raise_parsing_error(Document, dict(creation_info=creation_info, packages=packages, + files=files, + annotations=annotations, + snippets=snippets, relationships=relationships, + extracted_licensing_info=extracted_licensing_info)) return document diff --git a/src/parser/json/license_expression_parser.py b/src/parser/json/license_expression_parser.py index fdd296508..e7fe9059d 100644 --- a/src/parser/json/license_expression_parser.py +++ b/src/parser/json/license_expression_parser.py @@ -13,7 +13,7 @@ from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone -from src.parser.json.dict_parsing_functions import try_construction_raise_parsing_error +from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error class LicenseExpressionParser: @@ -23,7 +23,7 @@ def parse_license_expression(self, license_expression: Union[str, List[str]]) -> if license_expression == SpdxNoAssertion().__str__(): return SpdxNoAssertion() elif isinstance(license_expression, str): - license_expression = try_construction_raise_parsing_error(LicenseExpression,dict(expression_string=license_expression)) + license_expression = construct_or_raise_parsing_error(LicenseExpression, dict(expression_string=license_expression)) return license_expression elif isinstance(license_expression, list): return list(map(self.parse_license_expression, license_expression)) diff --git a/src/parser/json/package_parser.py b/src/parser/json/package_parser.py index af223f4c8..a81ff0396 100644 --- a/src/parser/json/package_parser.py +++ b/src/parser/json/package_parser.py @@ -20,11 +20,9 @@ from src.parser.error import SPDXParsingError from src.parser.json.actor_parser import ActorParser from src.parser.json.checksum_parser import ChecksumParser -from src.parser.json.dict_parsing_functions import append_list_if_object_could_be_parsed_append_logger_if_not, \ - datetime_from_str, parse_optional_field, raise_parsing_error_without_additional_text_if_logger_has_messages, \ - raise_parsing_error_if_logger_has_messages, \ - transform_json_str_to_enum_name, try_construction_raise_parsing_error, \ - try_parse_optional_field_append_logger_when_failing, try_parse_required_field_append_logger_when_failing +from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, datetime_from_str, \ + raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, construct_or_raise_parsing_error, \ + parse_field_or_log_error from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -44,12 +42,10 @@ def __init__(self): def parse_packages(self, packages_dict_list: List[Dict]) -> List[Package]: packages_list = [] for package_dict in packages_dict_list: - packages_list = append_list_if_object_could_be_parsed_append_logger_if_not(logger=self.logger, - list_to_append=packages_list, - field=package_dict, - method_to_parse=self.parse_package) + packages_list = append_parsed_field_or_log_error(logger=self.logger, list_to_append_to=packages_list, + field=package_dict, method_to_parse=self.parse_package) - raise_parsing_error_without_additional_text_if_logger_has_messages(self.logger) + raise_parsing_error_if_logger_has_messages(self.logger) return packages_list @@ -59,123 +55,118 @@ def parse_package(self, package_dict: Dict) -> Package: spdx_id: str = package_dict.get("SPDXID") attribution_texts: List[str] = package_dict.get("attributionTexts") - built_date: Optional[datetime] = try_parse_optional_field_append_logger_when_failing(logger=logger, - field=package_dict.get( - "builtDate"), - method_to_parse=datetime_from_str) + built_date: Optional[datetime] = parse_field_or_log_error(logger=logger, field=package_dict.get("builtDate"), + parsing_method=datetime_from_str, optional=True) - checksums = try_parse_optional_field_append_logger_when_failing(logger=logger, - field=package_dict.get("checksums"), - method_to_parse=self.checksum_parser.parse_checksums) + checksums = parse_field_or_log_error(logger=logger, field=package_dict.get("checksums"), + parsing_method=self.checksum_parser.parse_checksums, optional=True) comment: Optional[str] = package_dict.get("comment") copyright_text: Optional[str] = package_dict.get("copyrightText") description: Optional[str] = package_dict.get("description") download_location: Union[str, SpdxNoAssertion, SpdxNone] = self.parse_download_location( package_dict.get("downloadLocation")) - external_refs: List[ExternalPackageRef] = try_parse_optional_field_append_logger_when_failing(logger=logger, - field=package_dict.get( - "externalRefs"), - method_to_parse=self.parse_external_refs) + external_refs: List[ExternalPackageRef] = parse_field_or_log_error(logger=logger, + field=package_dict.get("externalRefs"), + parsing_method=self.parse_external_refs, + optional=True) - files_analyzed: Optional[bool] = parse_optional_field(package_dict.get("filesAnalyzed"), default=True) + files_analyzed: Optional[bool] = parse_field_or_log_error(logger=logger, + field=package_dict.get("filesAnalyzed"), + parsing_method=lambda x: x, + optional=True, default=True) homepage: Optional[str] = package_dict.get("homepage") license_comments: Optional[str] = package_dict.get("licenseComments") - license_concluded = try_parse_optional_field_append_logger_when_failing(logger, field=package_dict.get( - "licenseConcluded"), - method_to_parse=self.license_expression_parser.parse_license_expression, - default=None) + license_concluded = parse_field_or_log_error(logger=logger, field=package_dict.get("licenseConcluded"), + parsing_method=self.license_expression_parser.parse_license_expression, + default=None, optional=True) - license_declared: Optional[ - Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = try_parse_optional_field_append_logger_when_failing( + license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( logger=logger, field=package_dict.get("licenseDeclared"), - method_to_parse=self.license_expression_parser.parse_license_expression) + parsing_method=self.license_expression_parser.parse_license_expression, optional=True) license_info_from_file: Optional[ - Union[List[ - LicenseExpression], SpdxNoAssertion, SpdxNone]] = try_parse_optional_field_append_logger_when_failing( + Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( logger=logger, field=package_dict.get("licenseInfoFromFiles"), - method_to_parse=self.license_expression_parser.parse_license_expression) + parsing_method=self.license_expression_parser.parse_license_expression, optional=True) - originator: Optional[Union[Actor, SpdxNoAssertion]] = try_parse_optional_field_append_logger_when_failing( + originator: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error( logger=logger, field=package_dict.get("originator"), - method_to_parse=self.actor_parser.parse_actor_or_no_assert) + parsing_method=self.actor_parser.parse_actor_or_no_assertion, optional=True) package_file_name: Optional[str] = package_dict.get("packageFileName") package_verification_code: Optional[ - PackageVerificationCode] = try_parse_optional_field_append_logger_when_failing(logger=logger, - field=package_dict.get( - "packageVerificationCode"), - method_to_parse=self.parse_package_verification_code) - primary_package_purpose: Optional[PackagePurpose] = try_parse_optional_field_append_logger_when_failing( + PackageVerificationCode] = parse_field_or_log_error(logger=logger, + field=package_dict.get("packageVerificationCode"), + parsing_method=self.parse_package_verification_code, + optional=True) + primary_package_purpose: Optional[PackagePurpose] = parse_field_or_log_error( logger=logger, field=package_dict.get("primaryPackagePurpose"), - method_to_parse=self.parse_primary_package_purpose) + parsing_method=self.parse_primary_package_purpose, optional=True) - release_date: Optional[datetime] = try_parse_optional_field_append_logger_when_failing(logger=logger, - field=package_dict.get( - "releaseDate"), - method_to_parse=datetime_from_str) + release_date: Optional[datetime] = parse_field_or_log_error(logger=logger, + field=package_dict.get("releaseDate"), + parsing_method=datetime_from_str, optional=True) source_info: Optional[str] = package_dict.get("sourceInfo") summary: Optional[str] = package_dict.get("summary") - supplier: Optional[Union[Actor, SpdxNoAssertion]] = try_parse_optional_field_append_logger_when_failing( + supplier: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error( logger=logger, field=package_dict.get("supplier"), - method_to_parse=self.actor_parser.parse_actor_or_no_assert) + parsing_method=self.actor_parser.parse_actor_or_no_assertion, optional=True) - valid_until_date: Optional[datetime] = try_parse_optional_field_append_logger_when_failing(logger=logger, - field=package_dict.get( - "validUntilDate"), - method_to_parse=datetime_from_str) + valid_until_date: Optional[datetime] = parse_field_or_log_error(logger=logger, + field=package_dict.get("validUntilDate"), + parsing_method=datetime_from_str, + optional=True) version_info: Optional[str] = package_dict.get("versionInfo") raise_parsing_error_if_logger_has_messages(logger, f"Package {name}") - package = try_construction_raise_parsing_error(Package, dict(spdx_id=spdx_id, name=name, - download_location=download_location, - version=version_info, - file_name=package_file_name, supplier=supplier, - originator=originator, - files_analyzed=files_analyzed, - verification_code=package_verification_code, - checksums=checksums, homepage=homepage, - source_info=source_info, - license_concluded=license_concluded, - license_info_from_files=license_info_from_file, - license_declared=license_declared, - license_comment=license_comments, - copyright_text=copyright_text, summary=summary, - description=description, - comment=comment, external_references=external_refs, - attribution_texts=attribution_texts, - primary_package_purpose=primary_package_purpose, - release_date=release_date, built_date=built_date, - valid_until_date=valid_until_date)) + package = construct_or_raise_parsing_error(Package, dict(spdx_id=spdx_id, name=name, + download_location=download_location, + version=version_info, + file_name=package_file_name, supplier=supplier, + originator=originator, + files_analyzed=files_analyzed, + verification_code=package_verification_code, + checksums=checksums, homepage=homepage, + source_info=source_info, + license_concluded=license_concluded, + license_info_from_files=license_info_from_file, + license_declared=license_declared, + license_comment=license_comments, + copyright_text=copyright_text, summary=summary, + description=description, + comment=comment, external_references=external_refs, + attribution_texts=attribution_texts, + primary_package_purpose=primary_package_purpose, + release_date=release_date, built_date=built_date, + valid_until_date=valid_until_date)) return package def parse_external_refs(self, external_ref_dicts: List[Dict]) -> List[ExternalPackageRef]: external_refs = [] for external_ref_dict in external_ref_dicts: - external_refs = append_list_if_object_could_be_parsed_append_logger_if_not(logger=self.logger, - list_to_append=external_refs, - field=external_ref_dict, - method_to_parse=self.parse_external_ref) + external_refs = append_parsed_field_or_log_error(logger=self.logger, list_to_append_to=external_refs, + field=external_ref_dict, + method_to_parse=self.parse_external_ref) return external_refs def parse_external_ref(self, external_ref_dict: Dict) -> ExternalPackageRef: logger = Logger() - ref_category = try_parse_required_field_append_logger_when_failing(logger=logger, field=external_ref_dict.get( - "referenceCategory"), method_to_parse=self.parse_external_ref_category) + ref_category = parse_field_or_log_error(logger=logger, field=external_ref_dict.get("referenceCategory"), + parsing_method=self.parse_external_ref_category) ref_locator = external_ref_dict.get("referenceLocator") ref_type = external_ref_dict.get("referenceType") comment = external_ref_dict.get("comment") raise_parsing_error_if_logger_has_messages(logger, "external ref") - external_ref = try_construction_raise_parsing_error(ExternalPackageRef, - dict(category=ref_category, reference_type=ref_type, - locator=ref_locator, - comment=comment)) + external_ref = construct_or_raise_parsing_error(ExternalPackageRef, + dict(category=ref_category, reference_type=ref_type, + locator=ref_locator, + comment=comment)) return external_ref @@ -183,7 +174,7 @@ def parse_external_ref(self, external_ref_dict: Dict) -> ExternalPackageRef: def parse_external_ref_category(external_ref_category_str: str) -> ExternalPackageRefCategory: try: external_ref_category = ExternalPackageRefCategory[ - transform_json_str_to_enum_name(external_ref_category_str)] + json_str_to_enum_name(external_ref_category_str)] except KeyError: raise SPDXParsingError([f"Category {external_ref_category_str} not valid for externalPackageRef."]) @@ -194,16 +185,16 @@ def parse_package_verification_code(verification_code_dict: Dict) -> PackageVeri excluded_files: List[str] = verification_code_dict.get("packageVerificationCodeExcludedFiles") verification_code_value: str = verification_code_dict.get("packageVerificationCodeValue") - package_verification_code = try_construction_raise_parsing_error(PackageVerificationCode, - dict(value=verification_code_value, - excluded_files=excluded_files)) + package_verification_code = construct_or_raise_parsing_error(PackageVerificationCode, + dict(value=verification_code_value, + excluded_files=excluded_files)) return package_verification_code @staticmethod def parse_primary_package_purpose(primary_package_purpose: str) -> PackagePurpose: try: - return PackagePurpose[transform_json_str_to_enum_name(primary_package_purpose)] + return PackagePurpose[json_str_to_enum_name(primary_package_purpose)] except KeyError: raise SPDXParsingError([f"Invalid primaryPackagePurpose: {primary_package_purpose}"]) diff --git a/src/parser/json/relationship_parser.py b/src/parser/json/relationship_parser.py index 2f6a30f32..7d0d886de 100644 --- a/src/parser/json/relationship_parser.py +++ b/src/parser/json/relationship_parser.py @@ -13,10 +13,9 @@ from src.model.relationship import Relationship, RelationshipType from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import append_list_if_object_could_be_parsed_append_logger_if_not, \ - raise_parsing_error_if_logger_has_messages, raise_parsing_error_without_additional_text_if_logger_has_messages, \ - transform_json_str_to_enum_name, \ - try_construction_raise_parsing_error, try_parse_required_field_append_logger_when_failing +from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, \ + raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, construct_or_raise_parsing_error, \ + parse_field_or_log_error from src.parser.logger import Logger @@ -31,41 +30,36 @@ def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]: relationships_dicts: List[Dict] = input_doc_dict.get("relationships") if relationships_dicts: relationships_list.extend( - try_parse_required_field_append_logger_when_failing(logger=self.logger, field=relationships_dicts, - method_to_parse=self.parse_relationships, - default=[])) + parse_field_or_log_error(logger=self.logger, field=relationships_dicts, + parsing_method=self.parse_relationships, default=[])) document_describes: List[str] = input_doc_dict.get("documentDescribes") doc_spdx_id: str = input_doc_dict.get("SPDXID") if document_describes: relationships_list.extend( - try_parse_required_field_append_logger_when_failing(logger=self.logger, field=document_describes, - method_to_parse=lambda - x: self.parse_document_describes( - doc_spdx_id=doc_spdx_id, described_spdx_ids=x, - created_relationships=relationships_list), - default=[])) + parse_field_or_log_error(logger=self.logger, field=document_describes, parsing_method=lambda + x: self.parse_document_describes( + doc_spdx_id=doc_spdx_id, described_spdx_ids=x, + created_relationships=relationships_list), default=[])) package_dicts: List[Dict] = input_doc_dict.get("packages") if package_dicts: relationships_list.extend( - try_parse_required_field_append_logger_when_failing(logger=self.logger, field=package_dicts, - method_to_parse=lambda x: self.parse_has_files( - package_dicts=x, - created_relationships=relationships_list), - default=[])) + parse_field_or_log_error(logger=self.logger, field=package_dicts, + parsing_method=lambda x: self.parse_has_files( + package_dicts=x, + created_relationships=relationships_list), default=[])) file_dicts: List[Dict] = input_doc_dict.get("files") if file_dicts: # not implemented yet, deal with deprecated fields in file relationships_list.extend( - try_parse_required_field_append_logger_when_failing(logger=self.logger, field=file_dicts, - method_to_parse=self.parse_file_dependencies, - default=[])) + parse_field_or_log_error(logger=self.logger, field=file_dicts, + parsing_method=self.parse_file_dependencies, default=[])) generated_relationships = self.parse_artifact_of(file_dicts=file_dicts) - raise_parsing_error_without_additional_text_if_logger_has_messages(self.logger) + raise_parsing_error_if_logger_has_messages(self.logger) return relationships_list @@ -73,33 +67,34 @@ def parse_relationships(self, relationship_dicts: List[Dict]) -> List[Relationsh logger = Logger() relationship_list = [] for relationship_dict in relationship_dicts: - relationship_list = append_list_if_object_could_be_parsed_append_logger_if_not(logger=logger, - list_to_append=relationship_list, - field=relationship_dict, - method_to_parse=self.parse_relationship) - raise_parsing_error_without_additional_text_if_logger_has_messages(logger) + relationship_list = append_parsed_field_or_log_error(logger=logger, + list_to_append_to=relationship_list, + field=relationship_dict, + method_to_parse=self.parse_relationship) + raise_parsing_error_if_logger_has_messages(logger) return relationship_list def parse_relationship(self, relationship_dict: Dict) -> Relationship: logger = Logger() spdx_element_id: str = relationship_dict.get("spdxElementId") related_spdx_element: str = relationship_dict.get("relatedSpdxElement") - relationship_type: Optional[RelationshipType] = try_parse_required_field_append_logger_when_failing( - logger=logger, field=relationship_dict.get("relationshipType"), - method_to_parse=self.parse_relationship_type) + relationship_type: Optional[RelationshipType] = parse_field_or_log_error(logger=logger, + field=relationship_dict.get( + "relationshipType"), + parsing_method=self.parse_relationship_type) relationship_comment: str = relationship_dict.get("comment") raise_parsing_error_if_logger_has_messages(logger, "relationship") - relationship = try_construction_raise_parsing_error(Relationship, dict(spdx_element_id=spdx_element_id, - relationship_type=relationship_type, - related_spdx_element_id=related_spdx_element, - comment=relationship_comment)) + relationship = construct_or_raise_parsing_error(Relationship, dict(spdx_element_id=spdx_element_id, + relationship_type=relationship_type, + related_spdx_element_id=related_spdx_element, + comment=relationship_comment)) return relationship @staticmethod def parse_relationship_type(relationship_type_str: str) -> RelationshipType: try: - relationship_type = RelationshipType[transform_json_str_to_enum_name(relationship_type_str)] + relationship_type = RelationshipType[json_str_to_enum_name(relationship_type_str)] except KeyError: raise SPDXParsingError([f"RelationshipType {relationship_type_str} is not valid."]) except AttributeError: @@ -194,7 +189,7 @@ def parse_file_dependencies(file_dicts: List[Dict]) -> List[Relationship]: relationship_type=RelationshipType.DEPENDENCY_OF, related_spdx_element_id=file_spdx_id) except ConstructorTypeErrors as err: - logger.append_all(err.get_messages()) + logger.extend(err.get_messages()) continue dependency_relationships.append(dependency_relationship) raise_parsing_error_if_logger_has_messages(logger, "dependency relationship") diff --git a/src/parser/json/snippet_parser.py b/src/parser/json/snippet_parser.py index b0c361c25..c52e2cf81 100644 --- a/src/parser/json/snippet_parser.py +++ b/src/parser/json/snippet_parser.py @@ -16,8 +16,7 @@ from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import try_construction_raise_parsing_error, \ - try_parse_optional_field_append_logger_when_failing, try_parse_required_field_append_logger_when_failing +from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error, parse_field_or_log_error from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -42,7 +41,7 @@ def parse_snippets(self, snippet_dicts_list: List[Dict]) -> List[Snippet]: try: snippets_list.append(self.parse_snippet(snippet_dict)) except SPDXParsingError as err: - self.logger.append_all(err.get_messages()) + self.logger.extend(err.get_messages()) if self.logger.has_messages(): raise SPDXParsingError(self.logger.get_messages()) @@ -53,10 +52,8 @@ def parse_snippet(self, snippet_dict: Dict) -> Snippet: spdx_id: str = snippet_dict.get("SPDXID") file_spdx_id: str = snippet_dict.get("snippetFromFile") name: Optional[str] = snippet_dict.get("name") - ranges: Dict = try_parse_required_field_append_logger_when_failing(logger=logger, - field=(snippet_dict.get("ranges")), - method_to_parse=self.parse_ranges, - default={}) + ranges: Dict = parse_field_or_log_error(logger=logger, field=(snippet_dict.get("ranges")), + parsing_method=self.parse_ranges, default={}) byte_range: Tuple[int, int] = ranges.get(RangeType.BYTE) line_range: Optional[Tuple[int, int]] = ranges.get(RangeType.LINE) attribution_texts: List[str] = snippet_dict.get("attributionTexts") @@ -64,29 +61,27 @@ def parse_snippet(self, snippet_dict: Dict) -> Snippet: copyright_text: Optional[str] = snippet_dict.get("copyrightText") license_comment: Optional[str] = snippet_dict.get("licenseComments") concluded_license: Optional[Union[ - LicenseExpression, SpdxNoAssertion, SpdxNone]] = try_parse_optional_field_append_logger_when_failing( - logger=logger, - field=snippet_dict.get("licenseConcluded"), - method_to_parse=self.license_expression_parser.parse_license_expression) + LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( + logger=logger, field=snippet_dict.get("licenseConcluded"), + parsing_method=self.license_expression_parser.parse_license_expression, optional=True) license_info: Optional[Union[List[ - LicenseExpression], SpdxNoAssertion, SpdxNone]] = try_parse_optional_field_append_logger_when_failing( - logger=logger, - field=snippet_dict.get("licenseInfoInSnippets"), - method_to_parse=self.license_expression_parser.parse_license_expression) + LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( + logger=logger, field=snippet_dict.get("licenseInfoInSnippets"), + parsing_method=self.license_expression_parser.parse_license_expression, optional=True) if logger.has_messages(): raise SPDXParsingError([f"Error while parsing snippet: {logger.get_messages()}"]) - snippet = try_construction_raise_parsing_error(Snippet, - dict(spdx_id=spdx_id, name=name, byte_range=byte_range, - file_spdx_id=file_spdx_id, - line_range=line_range, - attribution_texts=attribution_texts, comment=comment, - copyright_text=copyright_text, - license_comment=license_comment, - concluded_license=concluded_license, - license_info_in_snippet=license_info)) + snippet = construct_or_raise_parsing_error(Snippet, + dict(spdx_id=spdx_id, name=name, byte_range=byte_range, + file_spdx_id=file_spdx_id, + line_range=line_range, + attribution_texts=attribution_texts, comment=comment, + copyright_text=copyright_text, + license_comment=license_comment, + concluded_license=concluded_license, + license_info_in_snippet=license_info)) return snippet diff --git a/src/parser/logger.py b/src/parser/logger.py index cbf4b5d90..497d39dbe 100644 --- a/src/parser/logger.py +++ b/src/parser/logger.py @@ -20,7 +20,7 @@ def __init__(self): def append(self, message: str): self.messages.append(message) - def append_all(self, messages_to_append: List[str]): + def extend(self, messages_to_append: List[str]): for message in messages_to_append: self.messages.append(message) diff --git a/tests/parser/test_creation_info_parser.py b/tests/parser/test_creation_info_parser.py index 5378405ca..820da1443 100644 --- a/tests/parser/test_creation_info_parser.py +++ b/tests/parser/test_creation_info_parser.py @@ -73,7 +73,7 @@ def test_parse_incomplete_creation_info(): _ = creation_info_parser.parse_creation_info(doc_dict) assert err.type == SPDXParsingError - assert err.value.messages == ["Error while parsing doc Example Document: ['CreationInfo is not valid.']"] + assert err.value.messages == ["Error while parsing doc Example Document: ['CreationInfo does not exist.']"] def test_parse_invalid_creation_info(): diff --git a/tests/parser/test_extracted_licensing_info_parser.py b/tests/parser/test_extracted_licensing_info_parser.py index e34cc06e0..7f480be3d 100644 --- a/tests/parser/test_extracted_licensing_info_parser.py +++ b/tests/parser/test_extracted_licensing_info_parser.py @@ -12,7 +12,7 @@ from src.model.spdx_no_assertion import SpdxNoAssertion from src.parser.error import SPDXParsingError -from src.parser.json.extracted_licensing_parser import ExtractedLicensingInfoParser +from src.parser.json.extracted_licensing_info_parser import ExtractedLicensingInfoParser def test_extracted_licensing_info_parser(): From c844dbec8dc305d70c1e014304d1dacc43afcb24 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 15 Dec 2022 15:46:04 +0100 Subject: [PATCH 043/362] [issue-305, refactor] json_parser Signed-off-by: Meret Behrens --- src/parser/json/json_parser.py | 67 +++++++++++--------------------- tests/parser/test_json_parser.py | 6 +-- 2 files changed, 25 insertions(+), 48 deletions(-) diff --git a/src/parser/json/json_parser.py b/src/parser/json/json_parser.py index bf9160f3f..1a8b72656 100644 --- a/src/parser/json/json_parser.py +++ b/src/parser/json/json_parser.py @@ -9,21 +9,13 @@ # See the License for the specific language governing permissions and # limitations under the License. import json -from json import JSONDecodeError -from typing import List -from src.model.annotation import Annotation -from src.model.document import Document, CreationInfo -from src.model.extracted_licensing_info import ExtractedLicensingInfo -from src.model.file import File -from src.model.package import Package -from src.model.relationship import Relationship -from src.model.snippet import Snippet +from src.model.document import Document +from src.parser.error import SPDXParsingError from src.parser.json.annotation_parser import AnnotationParser from src.parser.json.creation_info_parser import CreationInfoParser -from src.parser.error import SPDXParsingError from src.parser.json.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ - construct_or_raise_parsing_error, parse_field_or_log_error + construct_or_raise_parsing_error from src.parser.json.extracted_licensing_info_parser import ExtractedLicensingInfoParser from src.parser.json.file_parser import FileParser from src.parser.logger import Logger @@ -53,46 +45,31 @@ def __init__(self): self.annotation_parser = AnnotationParser() def parse(self, filename: str) -> Document: - try: - with open(filename) as file: - input_doc_as_dict = json.load(file) - except FileNotFoundError: - self.logger.append(f"File {filename} not found.") - raise SPDXParsingError(self.logger.get_messages()) - except JSONDecodeError: - self.logger.append(f"File {filename} is not a valid JSON file.") - raise SPDXParsingError(self.logger.get_messages()) - creation_info: CreationInfo = parse_field_or_log_error(logger=self.logger, field=input_doc_as_dict, - parsing_method=self.creation_info_parser.parse_creation_info) + with open(filename) as file: + input_doc_as_dict = json.load(file) - packages: List[Package] = parse_field_or_log_error(logger=self.logger, field=input_doc_as_dict.get("packages"), - parsing_method=self.package_parser.parse_packages, - optional=True) + fields_to_parse = [("creation_info", input_doc_as_dict, self.creation_info_parser.parse_creation_info, False), + ("packages", input_doc_as_dict.get("packages"), self.package_parser.parse_packages, True), + ("files", input_doc_as_dict.get("files"), self.file_parser.parse_files, True), + ("annotations", input_doc_as_dict, self.annotation_parser.parse_all_annotations, True), + ("snippets", input_doc_as_dict.get("snippets"), self.snippet_parser.parse_snippets, True), + ("relationships", input_doc_as_dict, self.relationship_parser.parse_all_relationships, True), + ("extracted_licensing_info", input_doc_as_dict.get("hasExtractedLicensingInfos"), + self.extracted_licensing_info_parser.parse_extracted_licensing_infos, True)] - files: List[File] = parse_field_or_log_error(logger=self.logger, field=input_doc_as_dict.get("files"), - parsing_method=self.file_parser.parse_files, optional=True) + parsed_fields = {} - annotations: List[Annotation] = parse_field_or_log_error(logger=self.logger, field=input_doc_as_dict, - parsing_method=self.annotation_parser.parse_all_annotations, - optional=True) - snippets: List[Snippet] = parse_field_or_log_error(logger=self.logger, field=input_doc_as_dict.get("snippets"), - parsing_method=self.snippet_parser.parse_snippets, - optional=True) - relationships: List[Relationship] = parse_field_or_log_error(logger=self.logger, field=input_doc_as_dict, - parsing_method=self.relationship_parser.parse_all_relationships, - optional=True) - extracted_licensing_info: List[ExtractedLicensingInfo] = parse_field_or_log_error(logger=self.logger, - field=input_doc_as_dict.get("hasExtractedLicensingInfos"), - parsing_method=self.extracted_licensing_info_parser.parse_extracted_licensing_infos, - optional=True) + for argument_name, field, parsing_method, optional in fields_to_parse: + if optional and not field: + continue + try: + parsed_fields[argument_name] = parsing_method(field) + except SPDXParsingError as err: + self.logger.extend(err.get_messages()) raise_parsing_error_if_logger_has_messages(self.logger) - document = construct_or_raise_parsing_error(Document, dict(creation_info=creation_info, packages=packages, - files=files, - annotations=annotations, - snippets=snippets, relationships=relationships, - extracted_licensing_info=extracted_licensing_info)) + document = construct_or_raise_parsing_error(Document, parsed_fields) return document diff --git a/tests/parser/test_json_parser.py b/tests/parser/test_json_parser.py index 6704d36e3..3bf27fbd3 100644 --- a/tests/parser/test_json_parser.py +++ b/tests/parser/test_json_parser.py @@ -17,12 +17,12 @@ from src.parser.json.json_parser import JsonParser def test_json_parser_file_not_found(): - with pytest.raises(SPDXParsingError) as err: + with pytest.raises(FileNotFoundError) as err: wrong_file_path = os.path.join(os.path.dirname(__file__), 'test.json') _ = JsonParser().parse(wrong_file_path) - assert err.typename == "SPDXParsingError" - assert err.value.messages[0] == f"File {wrong_file_path} not found." + assert err.type == FileNotFoundError + assert err.value.args[1] == "No such file or directory" def test_json_parser_with_2_3_example(): From 1582d977ec42d5537aa6179bdf7aa1cc48043ad1 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 19 Dec 2022 08:18:22 +0100 Subject: [PATCH 044/362] [issue-305, reformat] Signed-off-by: Meret Behrens --- src/parser/json/annotation_parser.py | 19 +++----- src/parser/json/checksum_parser.py | 9 ++-- src/parser/json/creation_info_parser.py | 10 ++-- .../json/extracted_licensing_info_parser.py | 9 ++-- src/parser/json/file_parser.py | 18 +++---- src/parser/json/package_parser.py | 48 +++++++++---------- src/parser/json/relationship_parser.py | 21 ++++---- src/parser/json/snippet_parser.py | 6 +-- 8 files changed, 57 insertions(+), 83 deletions(-) diff --git a/src/parser/json/annotation_parser.py b/src/parser/json/annotation_parser.py index 4094eb203..35a008199 100644 --- a/src/parser/json/annotation_parser.py +++ b/src/parser/json/annotation_parser.py @@ -56,17 +56,14 @@ def parse_annotations_from_object(self, annotations_list, element_list: List[Dic if element_annotations: annotations_list.extend(parse_field_or_log_error( logger=self.logger, field=element_annotations, - parsing_method=lambda x: self.parse_annotations(x, spdx_id=element_spdx_id), - default=[])) + parsing_method=lambda x: self.parse_annotations(x, spdx_id=element_spdx_id), default=[])) def parse_annotations(self, annotations_dict_list: List[Dict], spdx_id: Optional[str] = None) -> List[Annotation]: logger = Logger() annotations_list = [] for annotation_dict in annotations_dict_list: annotations_list = append_parsed_field_or_log_error( - list_to_append_to=annotations_list, - logger=self.logger, - field=annotation_dict, + list_to_append_to=annotations_list, logger=self.logger, field=annotation_dict, method_to_parse=lambda x: self.parse_annotation(x, spdx_id=spdx_id)) raise_parsing_error_if_logger_has_messages(logger, "Annotations") @@ -76,15 +73,13 @@ def parse_annotation(self, annotation: Dict, spdx_id: Optional[str] = None) -> A logger = Logger() spdx_id: str = annotation.get("SPDXID") or spdx_id - annotation_type: Optional[AnnotationType] = parse_field_or_log_error(logger=logger, - field=annotation.get("annotationType"), + annotation_type: Optional[AnnotationType] = parse_field_or_log_error(logger=logger, field=annotation.get("annotationType"), parsing_method=self.parse_annotation_type) annotator: Optional[Actor] = parse_field_or_log_error(logger=logger, field=annotation.get("annotator"), parsing_method=self.actor_parser.parse_actor) - annotation_date: Optional[datetime] = parse_field_or_log_error(logger=logger, - field=annotation.get("annotationDate"), + annotation_date: Optional[datetime] = parse_field_or_log_error(logger=logger, field=annotation.get("annotationDate"), parsing_method=datetime_from_str) annotation_comment: str = annotation.get("comment") @@ -105,10 +100,8 @@ def parse_annotation_type(annotation_type: str) -> AnnotationType: def parse_review(self, review_dict: Dict, spdx_id: str) -> Annotation: logger = Logger() - annotator: Optional[Actor] = parse_field_or_log_error(logger=logger, - field=review_dict.get("reviewer"), - parsing_method=self.actor_parser.parse_actor, - optional=True) + annotator: Optional[Actor] = parse_field_or_log_error(logger=logger, field=review_dict.get("reviewer"), + parsing_method=self.actor_parser.parse_actor, optional=True) annotation_date: Optional[datetime] = parse_field_or_log_error(logger=logger, field=review_dict.get("reviewDate"), parsing_method=datetime_from_str) diff --git a/src/parser/json/checksum_parser.py b/src/parser/json/checksum_parser.py index c870809de..03bec45fb 100644 --- a/src/parser/json/checksum_parser.py +++ b/src/parser/json/checksum_parser.py @@ -29,10 +29,8 @@ def parse_checksums(self, checksum_dicts_list: List[Dict]) -> List[Checksum]: checksum_list = [] for checksum_dict in checksum_dicts_list: - checksum_list = append_parsed_field_or_log_error(logger=self.auxiliary_logger, - list_to_append_to=checksum_list, - field=checksum_dict, - method_to_parse=self.parse_checksum) + checksum_list = append_parsed_field_or_log_error(logger=self.auxiliary_logger, list_to_append_to=checksum_list, + field=checksum_dict, method_to_parse=self.parse_checksum) raise_parsing_error_if_logger_has_messages(self.auxiliary_logger) return checksum_list @@ -48,6 +46,5 @@ def parse_checksum(checksum_dict: Dict) -> Checksum: checksum_algorithm = None checksum_value = checksum_dict.get("checksumValue") raise_parsing_error_if_logger_has_messages(logger, "Checksum") - checksum = construct_or_raise_parsing_error(Checksum, - dict(algorithm=checksum_algorithm, value=checksum_value)) + checksum = construct_or_raise_parsing_error(Checksum, dict(algorithm=checksum_algorithm, value=checksum_value)) return checksum diff --git a/src/parser/json/creation_info_parser.py b/src/parser/json/creation_info_parser.py index 8efdf0a11..8c3ca3e8c 100644 --- a/src/parser/json/creation_info_parser.py +++ b/src/parser/json/creation_info_parser.py @@ -73,8 +73,7 @@ def parse_creation_info(self, doc_dict: Dict) -> CreationInfo: document_namespace=document_namespace, creators=creators, created=created, license_list_version=license_list_version, - document_comment=document_comment, - creator_comment=creator_comment, + document_comment=document_comment, creator_comment=creator_comment, data_license=data_license, external_document_refs=external_document_refs)) @@ -84,9 +83,7 @@ def parse_creators(self, creators_list_from_dict: List[str]) -> List[Actor]: logger = Logger() creators_list = [] for creator_dict in creators_list_from_dict: - creators_list = append_parsed_field_or_log_error(list_to_append_to=creators_list, - logger=logger, - field=creator_dict, + creators_list = append_parsed_field_or_log_error(list_to_append_to=creators_list, logger=logger, field=creator_dict, method_to_parse=self.actor_parser.parse_actor_or_no_assertion) raise_parsing_error_if_logger_has_messages(logger) @@ -103,8 +100,7 @@ def parse_external_document_refs(self, external_document_refs_dict: List[Dict]) logger = Logger() external_document_refs = [] for external_ref_dict in external_document_refs_dict: - external_doc_ref: ExternalDocumentRef = parse_field_or_log_error(logger=logger, - field=external_ref_dict, + external_doc_ref: ExternalDocumentRef = parse_field_or_log_error(logger=logger, field=external_ref_dict, parsing_method=self.parse_external_doc_ref, optional=True) diff --git a/src/parser/json/extracted_licensing_info_parser.py b/src/parser/json/extracted_licensing_info_parser.py index 1248b26c0..e4d9a213f 100644 --- a/src/parser/json/extracted_licensing_info_parser.py +++ b/src/parser/json/extracted_licensing_info_parser.py @@ -39,17 +39,14 @@ def parse_extracted_licensing_info(self, extracted_licensing_info_dict: Dict) -> license_id: Optional[str] = extracted_licensing_info_dict.get("licenseId") extracted_text: Optional[str] = extracted_licensing_info_dict.get("extractedText") license_name: Optional[Union[str, SpdxNoAssertion]] = parse_field_or_log_error(logger=self.logger, - field=extracted_licensing_info_dict.get( - "name"), + field=extracted_licensing_info_dict.get("name"), parsing_method=self.parse_extracted_licensing_info_name, optional=True) cross_references: List[str] = extracted_licensing_info_dict.get("seeAlsos") comment: str = extracted_licensing_info_dict.get("comment") extracted_licensing_info_dict = construct_or_raise_parsing_error(ExtractedLicensingInfo, - dict(license_id=license_id, - extracted_text=extracted_text, - comment=comment, - license_name=license_name, + dict(license_id=license_id, extracted_text=extracted_text, + comment=comment, license_name=license_name, cross_references=cross_references)) return extracted_licensing_info_dict diff --git a/src/parser/json/file_parser.py b/src/parser/json/file_parser.py index 1012fb175..912c64a01 100644 --- a/src/parser/json/file_parser.py +++ b/src/parser/json/file_parser.py @@ -35,8 +35,8 @@ def __init__(self): def parse_files(self, file_dict_list) -> List[File]: file_list = [] for file_dict in file_dict_list: - file_list = append_parsed_field_or_log_error(list_to_append_to=file_list, - logger=self.logger, field=file_dict, + file_list = append_parsed_field_or_log_error(list_to_append_to=file_list, logger=self.logger, + field=file_dict, method_to_parse=self.parse_file) raise_parsing_error_if_logger_has_messages(self.logger) return file_list @@ -60,9 +60,11 @@ def parse_file(self, file_dict: Dict) -> Optional[File]: license_comments: Optional[str] = file_dict.get("licenseComments") license_concluded: Optional[Union[ - LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger=logger, field=file_dict.get("licenseConcluded"), - parsing_method=self.license_expression_parser.parse_license_expression, optional=True) + LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger=logger, + field=file_dict.get( + "licenseConcluded"), + parsing_method=self.license_expression_parser.parse_license_expression, + optional=True) license_info_in_files: Optional[ Union[List[ @@ -73,9 +75,9 @@ def parse_file(self, file_dict: Dict) -> Optional[File]: raise_parsing_error_if_logger_has_messages(logger, f"file {name}") file = construct_or_raise_parsing_error(File, dict(name=name, spdx_id=spdx_id, checksums=checksums, - attribution_texts=attribution_texts, - comment=comment, copyright_text=copyright_text, - file_type=file_types, contributors=file_contributors, + attribution_texts=attribution_texts, comment=comment, + copyright_text=copyright_text, file_type=file_types, + contributors=file_contributors, license_comment=license_comments, concluded_license=license_concluded, license_info_in_file=license_info_in_files, diff --git a/src/parser/json/package_parser.py b/src/parser/json/package_parser.py index a81ff0396..b3c7ed410 100644 --- a/src/parser/json/package_parser.py +++ b/src/parser/json/package_parser.py @@ -73,8 +73,8 @@ def parse_package(self, package_dict: Dict) -> Package: files_analyzed: Optional[bool] = parse_field_or_log_error(logger=logger, field=package_dict.get("filesAnalyzed"), - parsing_method=lambda x: x, - optional=True, default=True) + parsing_method=lambda x: x, optional=True, + default=True) homepage: Optional[str] = package_dict.get("homepage") license_comments: Optional[str] = package_dict.get("licenseComments") license_concluded = parse_field_or_log_error(logger=logger, field=package_dict.get("licenseConcluded"), @@ -123,26 +123,24 @@ def parse_package(self, package_dict: Dict) -> Package: version_info: Optional[str] = package_dict.get("versionInfo") raise_parsing_error_if_logger_has_messages(logger, f"Package {name}") - package = construct_or_raise_parsing_error(Package, dict(spdx_id=spdx_id, name=name, - download_location=download_location, - version=version_info, - file_name=package_file_name, supplier=supplier, - originator=originator, - files_analyzed=files_analyzed, - verification_code=package_verification_code, - checksums=checksums, homepage=homepage, - source_info=source_info, - license_concluded=license_concluded, - license_info_from_files=license_info_from_file, - license_declared=license_declared, - license_comment=license_comments, - copyright_text=copyright_text, summary=summary, - description=description, - comment=comment, external_references=external_refs, - attribution_texts=attribution_texts, - primary_package_purpose=primary_package_purpose, - release_date=release_date, built_date=built_date, - valid_until_date=valid_until_date)) + package = construct_or_raise_parsing_error(Package, + dict(spdx_id=spdx_id, name=name, download_location=download_location, + version=version_info, file_name=package_file_name, + supplier=supplier, originator=originator, + files_analyzed=files_analyzed, + verification_code=package_verification_code, + checksums=checksums, homepage=homepage, source_info=source_info, + license_concluded=license_concluded, + license_info_from_files=license_info_from_file, + license_declared=license_declared, + license_comment=license_comments, + copyright_text=copyright_text, summary=summary, + description=description, comment=comment, + external_references=external_refs, + attribution_texts=attribution_texts, + primary_package_purpose=primary_package_purpose, + release_date=release_date, built_date=built_date, + valid_until_date=valid_until_date)) return package @@ -150,8 +148,7 @@ def parse_external_refs(self, external_ref_dicts: List[Dict]) -> List[ExternalPa external_refs = [] for external_ref_dict in external_ref_dicts: external_refs = append_parsed_field_or_log_error(logger=self.logger, list_to_append_to=external_refs, - field=external_ref_dict, - method_to_parse=self.parse_external_ref) + field=external_ref_dict, method_to_parse=self.parse_external_ref) return external_refs @@ -165,8 +162,7 @@ def parse_external_ref(self, external_ref_dict: Dict) -> ExternalPackageRef: raise_parsing_error_if_logger_has_messages(logger, "external ref") external_ref = construct_or_raise_parsing_error(ExternalPackageRef, dict(category=ref_category, reference_type=ref_type, - locator=ref_locator, - comment=comment)) + locator=ref_locator, comment=comment)) return external_ref diff --git a/src/parser/json/relationship_parser.py b/src/parser/json/relationship_parser.py index 7d0d886de..8a4b9c58c 100644 --- a/src/parser/json/relationship_parser.py +++ b/src/parser/json/relationship_parser.py @@ -37,18 +37,16 @@ def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]: doc_spdx_id: str = input_doc_dict.get("SPDXID") if document_describes: relationships_list.extend( - parse_field_or_log_error(logger=self.logger, field=document_describes, parsing_method=lambda - x: self.parse_document_describes( - doc_spdx_id=doc_spdx_id, described_spdx_ids=x, - created_relationships=relationships_list), default=[])) + parse_field_or_log_error(logger=self.logger, field=document_describes, + parsing_method=lambda x: self.parse_document_describes(doc_spdx_id=doc_spdx_id, described_spdx_ids=x, created_relationships=relationships_list), + default=[])) package_dicts: List[Dict] = input_doc_dict.get("packages") if package_dicts: relationships_list.extend( parse_field_or_log_error(logger=self.logger, field=package_dicts, - parsing_method=lambda x: self.parse_has_files( - package_dicts=x, - created_relationships=relationships_list), default=[])) + parsing_method=lambda x: self.parse_has_files(package_dicts=x, created_relationships=relationships_list), + default=[])) file_dicts: List[Dict] = input_doc_dict.get("files") if file_dicts: @@ -67,10 +65,8 @@ def parse_relationships(self, relationship_dicts: List[Dict]) -> List[Relationsh logger = Logger() relationship_list = [] for relationship_dict in relationship_dicts: - relationship_list = append_parsed_field_or_log_error(logger=logger, - list_to_append_to=relationship_list, - field=relationship_dict, - method_to_parse=self.parse_relationship) + relationship_list = append_parsed_field_or_log_error(logger=logger, list_to_append_to=relationship_list, + field=relationship_dict, method_to_parse=self.parse_relationship) raise_parsing_error_if_logger_has_messages(logger) return relationship_list @@ -79,8 +75,7 @@ def parse_relationship(self, relationship_dict: Dict) -> Relationship: spdx_element_id: str = relationship_dict.get("spdxElementId") related_spdx_element: str = relationship_dict.get("relatedSpdxElement") relationship_type: Optional[RelationshipType] = parse_field_or_log_error(logger=logger, - field=relationship_dict.get( - "relationshipType"), + field=relationship_dict.get("relationshipType"), parsing_method=self.parse_relationship_type) relationship_comment: str = relationship_dict.get("comment") raise_parsing_error_if_logger_has_messages(logger, "relationship") diff --git a/src/parser/json/snippet_parser.py b/src/parser/json/snippet_parser.py index c52e2cf81..e420d611b 100644 --- a/src/parser/json/snippet_parser.py +++ b/src/parser/json/snippet_parser.py @@ -75,11 +75,9 @@ def parse_snippet(self, snippet_dict: Dict) -> Snippet: snippet = construct_or_raise_parsing_error(Snippet, dict(spdx_id=spdx_id, name=name, byte_range=byte_range, - file_spdx_id=file_spdx_id, - line_range=line_range, + file_spdx_id=file_spdx_id, line_range=line_range, attribution_texts=attribution_texts, comment=comment, - copyright_text=copyright_text, - license_comment=license_comment, + copyright_text=copyright_text, license_comment=license_comment, concluded_license=concluded_license, license_info_in_snippet=license_info)) From 86a441c88303dc930a7b1461c487843093004208 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 19 Dec 2022 16:13:11 +0100 Subject: [PATCH 045/362] [issue-305] add testcases and update license_expression parser Signed-off-by: Meret Behrens --- src/parser/json/license_expression_parser.py | 36 +++++++++++---- tests/parser/test_actor_parser.py | 29 ++++++++---- tests/parser/test_checksum_parser.py | 11 +++++ .../parser/test_license_expression_parser.py | 46 +++++++++++++++++-- 4 files changed, 101 insertions(+), 21 deletions(-) diff --git a/src/parser/json/license_expression_parser.py b/src/parser/json/license_expression_parser.py index e7fe9059d..5c576da43 100644 --- a/src/parser/json/license_expression_parser.py +++ b/src/parser/json/license_expression_parser.py @@ -13,17 +13,37 @@ from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone -from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error +from src.parser.error import SPDXParsingError +from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error, append_parsed_field_or_log_error, \ + raise_parsing_error_if_logger_has_messages +from src.parser.logger import Logger class LicenseExpressionParser: - def parse_license_expression(self, license_expression: Union[str, List[str]]) -> Union[LicenseExpression, SpdxNoAssertion, SpdxNone, List[LicenseExpression]]: - if license_expression == SpdxNone().__str__(): + def parse_license_expression(self, license_expression_str_or_list: Union[str, List[str]]) -> Union[ + LicenseExpression, SpdxNoAssertion, SpdxNone, List[LicenseExpression]]: + if license_expression_str_or_list == SpdxNone().__str__(): return SpdxNone() - if license_expression == SpdxNoAssertion().__str__(): + if license_expression_str_or_list == SpdxNoAssertion().__str__(): return SpdxNoAssertion() - elif isinstance(license_expression, str): - license_expression = construct_or_raise_parsing_error(LicenseExpression, dict(expression_string=license_expression)) + elif isinstance(license_expression_str_or_list, list): + return self.parse_license_expressions(license_expression_str_or_list) + + else: + license_expression = construct_or_raise_parsing_error(LicenseExpression, + dict( + expression_string=license_expression_str_or_list)) return license_expression - elif isinstance(license_expression, list): - return list(map(self.parse_license_expression, license_expression)) + + def parse_license_expressions(self, license_expression_str_or_list: List[str]) -> List[LicenseExpression]: + license_expressions = [] + logger = Logger() + for license_expression_str in license_expression_str_or_list: + try: + license_expressions = append_parsed_field_or_log_error(logger, license_expressions, + license_expression_str, + self.parse_license_expression) + except SPDXParsingError as err: + logger.append(err.get_messages()) + raise_parsing_error_if_logger_has_messages(logger) + return license_expressions diff --git a/tests/parser/test_actor_parser.py b/tests/parser/test_actor_parser.py index 7dcc6b908..5015d627c 100644 --- a/tests/parser/test_actor_parser.py +++ b/tests/parser/test_actor_parser.py @@ -15,21 +15,34 @@ from src.parser.json.actor_parser import ActorParser -def test_actor_parser(): +@pytest.mark.parametrize("actor_string,expected_type,expected_name,expected_mail", [ + ("Person: Jane Doe (jane.doe@example.com)", ActorType.PERSON, "Jane Doe", "jane.doe@example.com"), + ("Organization: Example organization (organization@exaple.com)", ActorType.ORGANIZATION, "Example organization", + "organization@exaple.com"), + ("Organization: Example organization ( )", ActorType.ORGANIZATION, "Example organization", None), + ("Tool: Example tool ", ActorType.TOOL, "Example tool", None)]) +def test_actor_parser(actor_string, expected_type, expected_name, expected_mail): actor_parser = ActorParser() - actor_string = "Person: Jane Doe (jane.doe@example.com)" actor = actor_parser.parse_actor(actor_string) - assert actor.actor_type == ActorType.PERSON - assert actor.name == "Jane Doe" - assert actor.email == "jane.doe@example.com" + assert actor.actor_type == expected_type + assert actor.name == expected_name + assert actor.email == expected_mail -def test_invalid_actor(): + +@pytest.mark.parametrize("actor_string,expected_message", [ + ("Perso: Jane Doe (jane.doe@example.com)", + "Actor Perso: Jane Doe (jane.doe@example.com) doesn't match any of person, organization or tool."), + ("Toole Example Tool ()", + "Actor Toole Example Tool () doesn't match any of person, organization or tool.") +]) +def test_invalid_actor(actor_string, expected_message): actor_parser = ActorParser() - actor_string = "Perso: Jane Doe (jane.doe@example.com)" + actor_string = actor_string with pytest.raises(SPDXParsingError) as err: _ = actor_parser.parse_actor(actor_string) assert err.typename == 'SPDXParsingError' - assert err.value.messages[0] == "Actor Perso: Jane Doe (jane.doe@example.com) doesn't match any of person, organization or tool." + assert err.value.messages[ + 0] == expected_message diff --git a/tests/parser/test_checksum_parser.py b/tests/parser/test_checksum_parser.py index 2e8768706..bf57f1a0f 100644 --- a/tests/parser/test_checksum_parser.py +++ b/tests/parser/test_checksum_parser.py @@ -41,3 +41,14 @@ def test_invalid_checksum(): assert err.typename == 'SPDXParsingError' assert err.value.messages[0] == "Error while parsing Checksum: ['Algorithm SHA not valid for checksum.']" +def test_incomplete_checksum(): + checksum_parser = ChecksumParser() + checksum_dict= { + "algorithm": "SHA1" + } + + with pytest.raises(SPDXParsingError) as err: + _ = checksum_parser.parse_checksum(checksum_dict) + + assert err.type == SPDXParsingError + assert err.value.messages == ["Error while constructing Checksum: ['SetterError Checksum: type of argument \"value\" must be str; got NoneType instead: None']"] diff --git a/tests/parser/test_license_expression_parser.py b/tests/parser/test_license_expression_parser.py index 0025e6e7d..54c0eb546 100644 --- a/tests/parser/test_license_expression_parser.py +++ b/tests/parser/test_license_expression_parser.py @@ -8,34 +8,70 @@ # 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 pytest + +from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone +from src.parser.error import SPDXParsingError from src.parser.json.license_expression_parser import LicenseExpressionParser def test_license_expression_parser(): license_expression_parser = LicenseExpressionParser() - license_expression_str= "License-Ref1" + license_expression_str = "License-Ref1" - license_expression = license_expression_parser.parse_license_expression(license_expression=license_expression_str) + license_expression = license_expression_parser.parse_license_expression( + license_expression_str_or_list=license_expression_str) assert license_expression.expression_string == "License-Ref1" + def test_license_expression_no_assert(): license_expression_parser = LicenseExpressionParser() - license_expression_str= "NOASSERTION" + license_expression_str = "NOASSERTION" - spdx_no_assertion = license_expression_parser.parse_license_expression(license_expression=license_expression_str) + spdx_no_assertion = license_expression_parser.parse_license_expression( + license_expression_str_or_list=license_expression_str) assert type(spdx_no_assertion) == SpdxNoAssertion + def test_license_expression_none(): license_expression_parser = LicenseExpressionParser() license_expression_str = "NONE" spdx_none = license_expression_parser.parse_license_expression( - license_expression=license_expression_str) + license_expression_str_or_list=license_expression_str) assert type(spdx_none) == SpdxNone +@pytest.mark.parametrize("invalid_license_expression,expected_message", + [(56, ["Error while constructing LicenseExpression: ['SetterError LicenseExpression: " + 'type of argument "expression_string" must be str; got int instead: 56\']'] + ), + (["First Expression", 4, 6], + ["Error while constructing LicenseExpression: ['SetterError LicenseExpression: " + 'type of argument "expression_string" must be str; got int instead: 4\']', + "Error while constructing LicenseExpression: ['SetterError LicenseExpression: " + 'type of argument "expression_string" must be str; got int instead: 6\']'])]) +def test_invalid_license_expression(invalid_license_expression, expected_message): + license_expression_parser = LicenseExpressionParser() + + with pytest.raises(SPDXParsingError) as err: + _ = license_expression_parser.parse_license_expression(invalid_license_expression) + + assert err.type == SPDXParsingError + assert err.value.messages == expected_message + + +def test_license_expressions(): + license_expression_parser = LicenseExpressionParser() + license_expressions_list = ["First License", "Second License", "Third License"] + + license_expressions = license_expression_parser.parse_license_expression(license_expressions_list) + + assert len(license_expressions) == 3 + assert license_expressions == [LicenseExpression("First License"), LicenseExpression("Second License"), + LicenseExpression("Third License")] From 74676f344f81c612adf6786ce5d5c3c23ffa8b4b Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 20 Dec 2022 10:22:27 +0100 Subject: [PATCH 046/362] [issue-305, refactor] delete duplicated check for error type Signed-off-by: Meret Behrens --- tests/parser/test_actor_parser.py | 5 ++--- tests/parser/test_annotation_parser.py | 1 - tests/parser/test_checksum_parser.py | 2 -- tests/parser/test_creation_info_parser.py | 2 -- tests/parser/test_extracted_licensing_info_parser.py | 1 - tests/parser/test_file_parser.py | 3 --- tests/parser/test_json_parser.py | 1 - tests/parser/test_license_expression_parser.py | 1 - tests/parser/test_package_parser.py | 4 ---- tests/parser/test_relationship_parser.py | 1 - tests/parser/test_snippet_parser.py | 3 --- 11 files changed, 2 insertions(+), 22 deletions(-) diff --git a/tests/parser/test_actor_parser.py b/tests/parser/test_actor_parser.py index 5015d627c..0758ddc85 100644 --- a/tests/parser/test_actor_parser.py +++ b/tests/parser/test_actor_parser.py @@ -43,6 +43,5 @@ def test_invalid_actor(actor_string, expected_message): with pytest.raises(SPDXParsingError) as err: _ = actor_parser.parse_actor(actor_string) - assert err.typename == 'SPDXParsingError' - assert err.value.messages[ - 0] == expected_message + + assert err.value.messages[0] == expected_message diff --git a/tests/parser/test_annotation_parser.py b/tests/parser/test_annotation_parser.py index 061b9c8ec..ee927b397 100644 --- a/tests/parser/test_annotation_parser.py +++ b/tests/parser/test_annotation_parser.py @@ -114,6 +114,5 @@ def test_parse_incomplete_annotation(): with pytest.raises(SPDXParsingError) as err: _ = annotation_parser.parse_annotation(annotation_dict) - assert err.type == SPDXParsingError assert err.value.messages == ["Error while parsing Annotation: ['Invalid annotation type: None', 'Could not " "convert str to datetime, invalid type: NoneType']"] diff --git a/tests/parser/test_checksum_parser.py b/tests/parser/test_checksum_parser.py index bf57f1a0f..8fe11215c 100644 --- a/tests/parser/test_checksum_parser.py +++ b/tests/parser/test_checksum_parser.py @@ -38,7 +38,6 @@ def test_invalid_checksum(): with pytest.raises(SPDXParsingError) as err: _ = checksum_parser.parse_checksum(checksum_dict) - assert err.typename == 'SPDXParsingError' assert err.value.messages[0] == "Error while parsing Checksum: ['Algorithm SHA not valid for checksum.']" def test_incomplete_checksum(): @@ -50,5 +49,4 @@ def test_incomplete_checksum(): with pytest.raises(SPDXParsingError) as err: _ = checksum_parser.parse_checksum(checksum_dict) - assert err.type == SPDXParsingError assert err.value.messages == ["Error while constructing Checksum: ['SetterError Checksum: type of argument \"value\" must be str; got NoneType instead: None']"] diff --git a/tests/parser/test_creation_info_parser.py b/tests/parser/test_creation_info_parser.py index 820da1443..d57202b30 100644 --- a/tests/parser/test_creation_info_parser.py +++ b/tests/parser/test_creation_info_parser.py @@ -72,7 +72,6 @@ def test_parse_incomplete_creation_info(): with pytest.raises(SPDXParsingError) as err: _ = creation_info_parser.parse_creation_info(doc_dict) - assert err.type == SPDXParsingError assert err.value.messages == ["Error while parsing doc Example Document: ['CreationInfo does not exist.']"] @@ -92,7 +91,6 @@ def test_parse_invalid_creation_info(): with pytest.raises(SPDXParsingError) as err: _ = creation_info_parser.parse_creation_info(doc_dict) - assert err.type == SPDXParsingError assert err.value.messages == ["Error while constructing CreationInfo: ['SetterError CreationInfo: type of " 'argument "document_namespace" must be str; got NoneType instead: None\', ' '\'SetterError CreationInfo: type of argument "data_license" must be str; got ' diff --git a/tests/parser/test_extracted_licensing_info_parser.py b/tests/parser/test_extracted_licensing_info_parser.py index 7f480be3d..b5bcb8b42 100644 --- a/tests/parser/test_extracted_licensing_info_parser.py +++ b/tests/parser/test_extracted_licensing_info_parser.py @@ -53,7 +53,6 @@ def test_parse_invalid_extracted_licensing_info(): with pytest.raises(SPDXParsingError) as err: _ = extracted_licensing_info_parser.parse_extracted_licensing_info(extracted_licensing_infos_dict) - assert err.type == SPDXParsingError assert err.value.messages == ["Error while constructing ExtractedLicensingInfo: ['SetterError " 'ExtractedLicensingInfo: type of argument "comment" must be one of (str, ' "NoneType); got int instead: 56']"] diff --git a/tests/parser/test_file_parser.py b/tests/parser/test_file_parser.py index c9c09a64d..dea0a014d 100644 --- a/tests/parser/test_file_parser.py +++ b/tests/parser/test_file_parser.py @@ -68,7 +68,6 @@ def test_parse_incomplete_file(): with pytest.raises(SPDXParsingError) as err: _ = file_parser.parse_file(file_dict) - assert err.type == SPDXParsingError assert err.value.messages == ["Error while parsing file Incomplete File: ['No checksums provided, checksums are " "mandatory for files.']"] @@ -100,7 +99,6 @@ def test_parse_falsy_files(): with pytest.raises(SPDXParsingError) as err: _ = file_parser.parse_files(files) - assert err.type == SPDXParsingError assert err.value.messages == ["Error while parsing file Incomplete File: ['No checksums provided, checksums " "are mandatory for files.']", 'Error while constructing File: [\'SetterError File: type of argument "name" ' @@ -125,5 +123,4 @@ def test_parse_invalid_file_types(): with pytest.raises(SPDXParsingError) as err: _ = file_parser.parse_file_types(file_types_list) - assert err.type == SPDXParsingError assert err.value.messages == ["Error while parsing file_types: ['FileType APPLICAON is not valid.']"] diff --git a/tests/parser/test_json_parser.py b/tests/parser/test_json_parser.py index 3bf27fbd3..e4048fa14 100644 --- a/tests/parser/test_json_parser.py +++ b/tests/parser/test_json_parser.py @@ -21,7 +21,6 @@ def test_json_parser_file_not_found(): wrong_file_path = os.path.join(os.path.dirname(__file__), 'test.json') _ = JsonParser().parse(wrong_file_path) - assert err.type == FileNotFoundError assert err.value.args[1] == "No such file or directory" diff --git a/tests/parser/test_license_expression_parser.py b/tests/parser/test_license_expression_parser.py index 54c0eb546..883fa56cf 100644 --- a/tests/parser/test_license_expression_parser.py +++ b/tests/parser/test_license_expression_parser.py @@ -62,7 +62,6 @@ def test_invalid_license_expression(invalid_license_expression, expected_message with pytest.raises(SPDXParsingError) as err: _ = license_expression_parser.parse_license_expression(invalid_license_expression) - assert err.type == SPDXParsingError assert err.value.messages == expected_message diff --git a/tests/parser/test_package_parser.py b/tests/parser/test_package_parser.py index fa4f39a7b..707f19c72 100644 --- a/tests/parser/test_package_parser.py +++ b/tests/parser/test_package_parser.py @@ -131,7 +131,6 @@ def test_incomplete_package(): with pytest.raises(SPDXParsingError) as err: _ = package_parser.parse_package(package_dict) - assert err.type == SPDXParsingError assert err.value.get_messages() == ["Error while constructing Package: ['SetterError Package: type of " 'argument "name" must be str; got NoneType instead: None\', \'SetterError ' 'Package: type of argument "download_location" must be one of (str, ' @@ -150,7 +149,6 @@ def test_package_with_setter_error(): with pytest.raises(SPDXParsingError) as err: _ = package_parser.parse_package(package_dict) - assert err.type == SPDXParsingError assert err.value.get_messages() == ["Error while constructing Package: ['SetterError Package: type of argument " '"name" must be str; got int instead: 5\']'] @@ -169,7 +167,6 @@ def test_package_with_falsy_values(): with pytest.raises(SPDXParsingError) as err: _ = package_parser.parse_package(package_dict) - assert err.type == SPDXParsingError assert err.value.get_messages() == [ 'Error while parsing Package Example Package: ["Error while parsing Checksum: [\'Algorithm SHA not valid for checksum.\']"]'] @@ -197,7 +194,6 @@ def test_parse_packages(): with pytest.raises(SPDXParsingError) as err: _ = package_parser.parse_packages(packages_list) - assert err.type == SPDXParsingError assert err.value.messages == ['Error while parsing Package Example Package: ["Error while parsing Checksum: ' '[\'Algorithm SHA not valid for checksum.\']"]', "Error while constructing Package: ['SetterError Package: type of argument " diff --git a/tests/parser/test_relationship_parser.py b/tests/parser/test_relationship_parser.py index ae277fdc2..839f2d0a4 100644 --- a/tests/parser/test_relationship_parser.py +++ b/tests/parser/test_relationship_parser.py @@ -44,7 +44,6 @@ def test_parse_incomplete_relationship(): with pytest.raises(SPDXParsingError) as err: _ = relationship_parser.parse_relationship(relationship_dict) - assert err.type == SPDXParsingError assert err.value.messages == ["Error while parsing relationship: ['RelationshipType must be str, not " "NoneType.']"] def test_parse_relationship_type(): diff --git a/tests/parser/test_snippet_parser.py b/tests/parser/test_snippet_parser.py index f15d2a30f..4d5cf3b81 100644 --- a/tests/parser/test_snippet_parser.py +++ b/tests/parser/test_snippet_parser.py @@ -73,7 +73,6 @@ def test_parse_incomplete_snippet(): with pytest.raises(SPDXParsingError) as err: _ = snippet_parser.parse_snippet(incomplete_snippet_dict) - assert err.type == SPDXParsingError assert err.value.messages == ["Error while parsing snippet: ['No ranges dict provided.']"] @@ -98,7 +97,6 @@ def test_parse_snippet_with_invalid_snippet_range(): with pytest.raises(SPDXParsingError) as err: _ = snippet_parser.parse_snippet(snippet_with_invalid_ranges_list) - assert err.type == SPDXParsingError assert err.value.messages == ["Error while constructing Snippet: ['SetterError Snippet: type of argument " '"file_spdx_id" must be str; got NoneType instead: None\', \'SetterError ' 'Snippet: type of argument "byte_range"[0] must be int; got str instead: ' @@ -134,7 +132,6 @@ def test_parse_invalid_snippet_range(): with pytest.raises(SPDXParsingError) as err: _ = snippet_parser.parse_ranges(ranges) - assert err.type == SPDXParsingError assert err.value.messages == ["Error while parsing ranges_dict: ['Type of startpointer is not the same as " "type of endpointer.', 'Type of startpointer is not the same as type of " "endpointer.']"] From bedeef6ab24ed40e72e6740359172e4cbbf0c10a Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 21 Dec 2022 08:48:28 +0100 Subject: [PATCH 047/362] [issue-305, review] fix messages, naming, type hints Signed-off-by: Meret Behrens --- src/model/typing/constructor_type_errors.py | 2 +- src/parser/error.py | 2 +- src/parser/json/annotation_parser.py | 66 ++++++----- src/parser/json/checksum_parser.py | 19 ++- src/parser/json/creation_info_parser.py | 73 ++++++------ src/parser/json/dict_parsing_functions.py | 3 +- .../json/extracted_licensing_info_parser.py | 25 ++-- src/parser/json/file_parser.py | 38 +++--- src/parser/json/package_parser.py | 108 ++++++++---------- src/parser/json/relationship_parser.py | 61 +++++----- src/parser/json/snippet_parser.py | 24 ++-- src/parser/logger.py | 3 +- tests/parser/test_creation_info_parser.py | 25 ++-- tests/parser/test_file_parser.py | 8 +- 14 files changed, 217 insertions(+), 240 deletions(-) diff --git a/src/model/typing/constructor_type_errors.py b/src/model/typing/constructor_type_errors.py index 2f9143a9e..52b3a4db8 100644 --- a/src/model/typing/constructor_type_errors.py +++ b/src/model/typing/constructor_type_errors.py @@ -12,4 +12,4 @@ def __init__(self, messages: List[str]): self.messages = messages def get_messages(self): - return self.messages + return list(self.messages) diff --git a/src/parser/error.py b/src/parser/error.py index c48b716a9..580c4cfad 100644 --- a/src/parser/error.py +++ b/src/parser/error.py @@ -18,4 +18,4 @@ def __init__(self, messages: List[str]): self.messages = messages def get_messages(self): - return self.messages + return list(self.messages) diff --git a/src/parser/json/annotation_parser.py b/src/parser/json/annotation_parser.py index 35a008199..1788a81f8 100644 --- a/src/parser/json/annotation_parser.py +++ b/src/parser/json/annotation_parser.py @@ -31,58 +31,56 @@ def __init__(self): def parse_all_annotations(self, input_doc_dict: Dict) -> List[Annotation]: annotations_list = [] self.parse_annotations_from_object(annotations_list, [input_doc_dict]) - reviews: List[Dict] = input_doc_dict.get("revieweds") - if reviews: - for review in reviews: - annotations_list = append_parsed_field_or_log_error( - list_to_append_to=annotations_list, logger=self.logger, field=review, - method_to_parse=lambda x: self.parse_review(x, spdx_id=input_doc_dict.get("SPDXID"))) - - packages: List[Dict] = input_doc_dict.get("packages") + reviews: List[Dict] = input_doc_dict.get("revieweds", []) + for review in reviews: + annotations_list = append_parsed_field_or_log_error(self.logger, annotations_list, review, + lambda x: self.parse_review(x, + spdx_id=input_doc_dict.get( + "SPDXID"))) + + packages: List[Dict] = input_doc_dict.get("packages", []) self.parse_annotations_from_object(annotations_list, packages) - files: List[Dict] = input_doc_dict.get("files") + files: List[Dict] = input_doc_dict.get("files", []) self.parse_annotations_from_object(annotations_list, files) - snippets: List[Dict] = input_doc_dict.get("snippets") + snippets: List[Dict] = input_doc_dict.get("snippets", []) self.parse_annotations_from_object(annotations_list, snippets) raise_parsing_error_if_logger_has_messages(self.logger, "Annotations") return annotations_list def parse_annotations_from_object(self, annotations_list, element_list: List[Dict]): - if element_list: - for element in element_list: - element_spdx_id: str = element.get("SPDXID") - element_annotations: List[Dict] = element.get("annotations") - if element_annotations: - annotations_list.extend(parse_field_or_log_error( - logger=self.logger, field=element_annotations, - parsing_method=lambda x: self.parse_annotations(x, spdx_id=element_spdx_id), default=[])) + for element in element_list: + element_spdx_id: Optional[str] = element.get("SPDXID") + element_annotations: List[Dict] = element.get("annotations", []) + annotations_list.extend(parse_field_or_log_error(self.logger, element_annotations, + lambda x: self.parse_annotations(x, + spdx_id=element_spdx_id), + [])) def parse_annotations(self, annotations_dict_list: List[Dict], spdx_id: Optional[str] = None) -> List[Annotation]: logger = Logger() annotations_list = [] for annotation_dict in annotations_dict_list: - annotations_list = append_parsed_field_or_log_error( - list_to_append_to=annotations_list, logger=self.logger, field=annotation_dict, - method_to_parse=lambda x: self.parse_annotation(x, spdx_id=spdx_id)) + annotations_list = append_parsed_field_or_log_error(self.logger, annotations_list, annotation_dict, + lambda x: self.parse_annotation(x, spdx_id=spdx_id)) raise_parsing_error_if_logger_has_messages(logger, "Annotations") return annotations_list def parse_annotation(self, annotation: Dict, spdx_id: Optional[str] = None) -> Annotation: logger = Logger() - spdx_id: str = annotation.get("SPDXID") or spdx_id + spdx_id: Optional[str] = annotation.get("SPDXID") or spdx_id - annotation_type: Optional[AnnotationType] = parse_field_or_log_error(logger=logger, field=annotation.get("annotationType"), - parsing_method=self.parse_annotation_type) + annotation_type: Optional[AnnotationType] = parse_field_or_log_error(logger, annotation.get("annotationType"), + self.parse_annotation_type) - annotator: Optional[Actor] = parse_field_or_log_error(logger=logger, field=annotation.get("annotator"), - parsing_method=self.actor_parser.parse_actor) + annotator: Optional[Actor] = parse_field_or_log_error(logger, annotation.get("annotator"), + self.actor_parser.parse_actor) - annotation_date: Optional[datetime] = parse_field_or_log_error(logger=logger, field=annotation.get("annotationDate"), - parsing_method=datetime_from_str) + annotation_date: Optional[datetime] = parse_field_or_log_error(logger, annotation.get("annotationDate"), + datetime_from_str) - annotation_comment: str = annotation.get("comment") + annotation_comment: Optional[str] = annotation.get("comment") raise_parsing_error_if_logger_has_messages(logger, "Annotation") annotation = construct_or_raise_parsing_error(Annotation, dict(spdx_id=spdx_id, annotation_type=annotation_type, @@ -100,14 +98,14 @@ def parse_annotation_type(annotation_type: str) -> AnnotationType: def parse_review(self, review_dict: Dict, spdx_id: str) -> Annotation: logger = Logger() - annotator: Optional[Actor] = parse_field_or_log_error(logger=logger, field=review_dict.get("reviewer"), - parsing_method=self.actor_parser.parse_actor, optional=True) + annotator: Optional[Actor] = parse_field_or_log_error(logger, review_dict.get("reviewer"), + self.actor_parser.parse_actor, True) - annotation_date: Optional[datetime] = parse_field_or_log_error(logger=logger, field=review_dict.get("reviewDate"), - parsing_method=datetime_from_str) + annotation_date: Optional[datetime] = parse_field_or_log_error(logger, review_dict.get("reviewDate"), + datetime_from_str) annotation_type = AnnotationType.REVIEW - comment: str = review_dict.get("comment") + comment: Optional[str] = review_dict.get("comment") raise_parsing_error_if_logger_has_messages(logger, "Review") annotation = construct_or_raise_parsing_error(Annotation, diff --git a/src/parser/json/checksum_parser.py b/src/parser/json/checksum_parser.py index 03bec45fb..4dd459256 100644 --- a/src/parser/json/checksum_parser.py +++ b/src/parser/json/checksum_parser.py @@ -8,7 +8,7 @@ # 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 typing import Dict, List +from typing import Dict, List, Optional from src.model.checksum import Checksum, ChecksumAlgorithm from src.parser.error import SPDXParsingError @@ -18,33 +18,30 @@ class ChecksumParser: - auxiliary_logger: Logger + logger: Logger def __init__(self): - self.auxiliary_logger = Logger() + self.logger = Logger() def parse_checksums(self, checksum_dicts_list: List[Dict]) -> List[Checksum]: - if not checksum_dicts_list: - raise SPDXParsingError([f"No checksums provided, checksums are mandatory for files."]) - checksum_list = [] for checksum_dict in checksum_dicts_list: - checksum_list = append_parsed_field_or_log_error(logger=self.auxiliary_logger, list_to_append_to=checksum_list, - field=checksum_dict, method_to_parse=self.parse_checksum) + checksum_list = append_parsed_field_or_log_error(self.logger, checksum_list, checksum_dict, + self.parse_checksum) - raise_parsing_error_if_logger_has_messages(self.auxiliary_logger) + raise_parsing_error_if_logger_has_messages(self.logger) return checksum_list @staticmethod def parse_checksum(checksum_dict: Dict) -> Checksum: logger = Logger() - algorithm = json_str_to_enum_name(checksum_dict.get("algorithm")) + algorithm: str = json_str_to_enum_name(checksum_dict.get("algorithm", "")) try: checksum_algorithm = ChecksumAlgorithm[algorithm] except KeyError: logger.append(f"Algorithm {algorithm} not valid for checksum.") checksum_algorithm = None - checksum_value = checksum_dict.get("checksumValue") + checksum_value: Optional[str] = checksum_dict.get("checksumValue") raise_parsing_error_if_logger_has_messages(logger, "Checksum") checksum = construct_or_raise_parsing_error(Checksum, dict(algorithm=checksum_algorithm, value=checksum_value)) return checksum diff --git a/src/parser/json/creation_info_parser.py b/src/parser/json/creation_info_parser.py index 8c3ca3e8c..477278e07 100644 --- a/src/parser/json/creation_info_parser.py +++ b/src/parser/json/creation_info_parser.py @@ -36,44 +36,44 @@ def __init__(self): def parse_creation_info(self, doc_dict: Dict) -> CreationInfo: logger = Logger() - spdx_version: str = doc_dict.get("spdxVersion") - spdx_id: str = doc_dict.get("SPDXID") - name: str = doc_dict.get("name") - document_namespace: str = doc_dict.get("documentNamespace") - creation_info_dict: Dict = doc_dict.get("creationInfo") + spdx_version: Optional[str] = doc_dict.get("spdxVersion") + spdx_id: Optional[str] = doc_dict.get("SPDXID") + name: Optional[str] = doc_dict.get("name") + document_namespace: Optional[str] = doc_dict.get("documentNamespace") + creation_info_dict: Optional[Dict] = doc_dict.get("creationInfo") # There are nested required properties. If creationInfo is not set, we cannot continue parsing. if creation_info_dict is None: logger.append("CreationInfo does not exist.") - raise SPDXParsingError([f"Error while parsing doc {name}: {logger.get_messages()}"]) + raise SPDXParsingError([f"Error while parsing document {name}: {logger.get_messages()}"]) - list_of_creators: List[str] = creation_info_dict.get("creators") - creators: List[Actor] = parse_field_or_log_error(logger=logger, field=list_of_creators, - parsing_method=self.parse_creators, default=[]) + creators: List[Actor] = parse_field_or_log_error(logger, creation_info_dict.get("creators"), + self.parse_creators, True) - created: Optional[datetime] = parse_field_or_log_error(logger=logger, field=creation_info_dict.get( - "created"), parsing_method=datetime_from_str) + created: Optional[datetime] = parse_field_or_log_error(logger, creation_info_dict.get("created"), + datetime_from_str) creator_comment: Optional[str] = creation_info_dict.get("comment") - data_license: str = doc_dict.get("dataLicense") - - external_document_refs: List[ExternalDocumentRef] = parse_field_or_log_error( - logger=logger, field=doc_dict.get("externalDocumentRefs"), - parsing_method=self.parse_external_document_refs, optional=True) - license_list_version: Optional[Version] = parse_field_or_log_error(logger=logger, - field=creation_info_dict.get( - "licenseListVersion"), - parsing_method=self.parse_version, - optional=True) + data_license: Optional[str] = doc_dict.get("dataLicense") + + external_document_refs: List[ExternalDocumentRef] = parse_field_or_log_error(logger, + doc_dict.get( + "externalDocumentRefs"), + self.parse_external_document_refs, + True) + license_list_version: Optional[Version] = parse_field_or_log_error(logger, + creation_info_dict.get("licenseListVersion"), + self.parse_version, True) document_comment: Optional[str] = doc_dict.get("comment") - raise_parsing_error_if_logger_has_messages(logger, f"Document: {name}") + raise_parsing_error_if_logger_has_messages(logger, f"Document {name}") creation_info = construct_or_raise_parsing_error(CreationInfo, dict(spdx_version=spdx_version, spdx_id=spdx_id, name=name, document_namespace=document_namespace, creators=creators, created=created, license_list_version=license_list_version, - document_comment=document_comment, creator_comment=creator_comment, + document_comment=document_comment, + creator_comment=creator_comment, data_license=data_license, external_document_refs=external_document_refs)) @@ -83,8 +83,8 @@ def parse_creators(self, creators_list_from_dict: List[str]) -> List[Actor]: logger = Logger() creators_list = [] for creator_dict in creators_list_from_dict: - creators_list = append_parsed_field_or_log_error(list_to_append_to=creators_list, logger=logger, field=creator_dict, - method_to_parse=self.actor_parser.parse_actor_or_no_assertion) + creators_list = append_parsed_field_or_log_error(logger, creators_list, creator_dict, + self.actor_parser.parse_actor_or_no_assertion) raise_parsing_error_if_logger_has_messages(logger) return creators_list @@ -100,25 +100,26 @@ def parse_external_document_refs(self, external_document_refs_dict: List[Dict]) logger = Logger() external_document_refs = [] for external_ref_dict in external_document_refs_dict: - external_doc_ref: ExternalDocumentRef = parse_field_or_log_error(logger=logger, field=external_ref_dict, - parsing_method=self.parse_external_doc_ref, - optional=True) + external_doc_ref: ExternalDocumentRef = parse_field_or_log_error(logger, external_ref_dict, + self.parse_external_document_ref, True) external_document_refs.append(external_doc_ref) raise_parsing_error_if_logger_has_messages(logger) return external_document_refs - def parse_external_doc_ref(self, external_doc_ref_dict: Dict) -> ExternalDocumentRef: + def parse_external_document_ref(self, external_document_ref_dict: Dict) -> ExternalDocumentRef: logger = Logger() - checksum: Optional[Checksum] = parse_field_or_log_error(logger=logger, field=external_doc_ref_dict.get( - "checksum"), parsing_method=self.checksum_parser.parse_checksum) + checksum: Optional[Checksum] = parse_field_or_log_error(logger, external_document_ref_dict.get("checksum"), + self.checksum_parser.parse_checksum) - external_document_id: str = external_doc_ref_dict.get("externalDocumentId") - spdx_document: str = external_doc_ref_dict.get("spdxDocument") + external_document_id: Optional[str] = external_document_ref_dict.get("externalDocumentId") + document_uri: Optional[str] = external_document_ref_dict.get("spdxDocument") raise_parsing_error_if_logger_has_messages(logger, "ExternalDocRef") - external_doc_ref = construct_or_raise_parsing_error(ExternalDocumentRef, - dict(document_ref_id=external_document_id, - checksum=checksum, document_uri=spdx_document)) + external_doc_ref: ExternalDocumentRef = construct_or_raise_parsing_error(ExternalDocumentRef, + dict( + document_ref_id=external_document_id, + checksum=checksum, + document_uri=document_uri)) return external_doc_ref diff --git a/src/parser/json/dict_parsing_functions.py b/src/parser/json/dict_parsing_functions.py index 431f56c9a..9dfd56b01 100644 --- a/src/parser/json/dict_parsing_functions.py +++ b/src/parser/json/dict_parsing_functions.py @@ -35,8 +35,7 @@ def construct_or_raise_parsing_error(object_to_construct: Any, args_for_construc return constructed_object -def parse_field_or_log_error(logger: Logger, field: Any, parsing_method: Callable = lambda x: x, default=None, - optional=False) -> Any: +def parse_field_or_log_error(logger: Logger, field: Any, parsing_method: Callable = lambda x: x, optional=False, default=None,) -> Any: try: if optional: if not field: diff --git a/src/parser/json/extracted_licensing_info_parser.py b/src/parser/json/extracted_licensing_info_parser.py index e4d9a213f..7e9b2cdf6 100644 --- a/src/parser/json/extracted_licensing_info_parser.py +++ b/src/parser/json/extracted_licensing_info_parser.py @@ -27,10 +27,9 @@ def parse_extracted_licensing_infos(self, extracted_licensing_info_dicts: List[D ExtractedLicensingInfo]: extracted_licensing_info_list = [] for extracted_licensing_info_dict in extracted_licensing_info_dicts: - extracted_licensing_info_list = append_parsed_field_or_log_error( - list_to_append_to=extracted_licensing_info_list, - logger=self.logger, field=extracted_licensing_info_dict, - method_to_parse=self.parse_extracted_licensing_info) + extracted_licensing_info_list = append_parsed_field_or_log_error(self.logger, extracted_licensing_info_list, + extracted_licensing_info_dict, + self.parse_extracted_licensing_info) raise_parsing_error_if_logger_has_messages(self.logger) return extracted_licensing_info_list @@ -38,15 +37,17 @@ def parse_extracted_licensing_infos(self, extracted_licensing_info_dicts: List[D def parse_extracted_licensing_info(self, extracted_licensing_info_dict: Dict) -> ExtractedLicensingInfo: license_id: Optional[str] = extracted_licensing_info_dict.get("licenseId") extracted_text: Optional[str] = extracted_licensing_info_dict.get("extractedText") - license_name: Optional[Union[str, SpdxNoAssertion]] = parse_field_or_log_error(logger=self.logger, - field=extracted_licensing_info_dict.get("name"), - parsing_method=self.parse_extracted_licensing_info_name, - optional=True) - cross_references: List[str] = extracted_licensing_info_dict.get("seeAlsos") - comment: str = extracted_licensing_info_dict.get("comment") + license_name: Optional[Union[str, SpdxNoAssertion]] = parse_field_or_log_error(self.logger, + extracted_licensing_info_dict.get("name"), + self.parse_extracted_licensing_info_name, + True) + cross_references: List[str] = extracted_licensing_info_dict.get("seeAlsos", []) + comment: Optional[str] = extracted_licensing_info_dict.get("comment") extracted_licensing_info_dict = construct_or_raise_parsing_error(ExtractedLicensingInfo, - dict(license_id=license_id, extracted_text=extracted_text, - comment=comment, license_name=license_name, + dict(license_id=license_id, + extracted_text=extracted_text, + comment=comment, + license_name=license_name, cross_references=cross_references)) return extracted_licensing_info_dict diff --git a/src/parser/json/file_parser.py b/src/parser/json/file_parser.py index 912c64a01..0e349d1be 100644 --- a/src/parser/json/file_parser.py +++ b/src/parser/json/file_parser.py @@ -35,42 +35,32 @@ def __init__(self): def parse_files(self, file_dict_list) -> List[File]: file_list = [] for file_dict in file_dict_list: - file_list = append_parsed_field_or_log_error(list_to_append_to=file_list, logger=self.logger, - field=file_dict, - method_to_parse=self.parse_file) + file_list = append_parsed_field_or_log_error(self.logger, file_list, file_dict, self.parse_file) raise_parsing_error_if_logger_has_messages(self.logger) return file_list def parse_file(self, file_dict: Dict) -> Optional[File]: logger = Logger() - name: str = file_dict.get("fileName") - spdx_id: str = file_dict.get("SPDXID") - checksums_list: List[Dict] = file_dict.get("checksums") + name: Optional[str] = file_dict.get("fileName") + spdx_id: Optional[str] = file_dict.get("SPDXID") + checksums_list: List[Dict] = file_dict.get("checksums", []) + checksums: List[Checksum] = parse_field_or_log_error(logger, checksums_list, + self.checksum_parser.parse_checksums, True) - checksums: List[Checksum] = parse_field_or_log_error(logger=logger, field=checksums_list, - parsing_method=self.checksum_parser.parse_checksums) - - attribution_texts: Optional[str] = file_dict.get("attributionTexts") + attribution_texts: List[str] = file_dict.get("attributionTexts", []) comment: Optional[str] = file_dict.get("comment") copyright_text: Optional[str] = file_dict.get("copyrightText") - file_contributors: List[str] = file_dict.get("fileContributors") - file_types: List[FileType] = parse_field_or_log_error(logger=logger, field=file_dict.get("fileTypes"), - parsing_method=self.parse_file_types, optional=True) + file_contributors: List[str] = file_dict.get("fileContributors", []) + file_types: List[FileType] = parse_field_or_log_error(logger, file_dict.get("fileTypes"), self.parse_file_types, + True) license_comments: Optional[str] = file_dict.get("licenseComments") - license_concluded: Optional[Union[ - LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger=logger, - field=file_dict.get( - "licenseConcluded"), - parsing_method=self.license_expression_parser.parse_license_expression, - optional=True) + license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( + logger, file_dict.get("licenseConcluded"), self.license_expression_parser.parse_license_expression, True) - license_info_in_files: Optional[ - Union[List[ - LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger=logger, field=file_dict.get("licenseInfoInFiles"), - parsing_method=self.license_expression_parser.parse_license_expression, optional=True) + license_info_in_files: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( + logger, file_dict.get("licenseInfoInFiles"), self.license_expression_parser.parse_license_expression, True) notice_text: Optional[str] = file_dict.get("noticeText") raise_parsing_error_if_logger_has_messages(logger, f"file {name}") diff --git a/src/parser/json/package_parser.py b/src/parser/json/package_parser.py index b3c7ed410..a0dd2b8c9 100644 --- a/src/parser/json/package_parser.py +++ b/src/parser/json/package_parser.py @@ -42,8 +42,8 @@ def __init__(self): def parse_packages(self, packages_dict_list: List[Dict]) -> List[Package]: packages_list = [] for package_dict in packages_dict_list: - packages_list = append_parsed_field_or_log_error(logger=self.logger, list_to_append_to=packages_list, - field=package_dict, method_to_parse=self.parse_package) + packages_list = append_parsed_field_or_log_error(self.logger, packages_list, package_dict, + self.parse_package) raise_parsing_error_if_logger_has_messages(self.logger) @@ -51,74 +51,63 @@ def parse_packages(self, packages_dict_list: List[Dict]) -> List[Package]: def parse_package(self, package_dict: Dict) -> Package: logger = Logger() - name: str = package_dict.get("name") - spdx_id: str = package_dict.get("SPDXID") - attribution_texts: List[str] = package_dict.get("attributionTexts") + name: Optional[str] = package_dict.get("name") + spdx_id: Optional[str] = package_dict.get("SPDXID") + attribution_texts: List[str] = package_dict.get("attributionTexts", []) - built_date: Optional[datetime] = parse_field_or_log_error(logger=logger, field=package_dict.get("builtDate"), - parsing_method=datetime_from_str, optional=True) + built_date: Optional[datetime] = parse_field_or_log_error(logger, package_dict.get("builtDate"), + datetime_from_str, True) - checksums = parse_field_or_log_error(logger=logger, field=package_dict.get("checksums"), - parsing_method=self.checksum_parser.parse_checksums, optional=True) + checksums = parse_field_or_log_error(logger, package_dict.get("checksums"), + self.checksum_parser.parse_checksums, True) comment: Optional[str] = package_dict.get("comment") copyright_text: Optional[str] = package_dict.get("copyrightText") description: Optional[str] = package_dict.get("description") - download_location: Union[str, SpdxNoAssertion, SpdxNone] = self.parse_download_location( + download_location: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = self.parse_download_location( package_dict.get("downloadLocation")) - external_refs: List[ExternalPackageRef] = parse_field_or_log_error(logger=logger, - field=package_dict.get("externalRefs"), - parsing_method=self.parse_external_refs, - optional=True) + external_refs: List[ExternalPackageRef] = parse_field_or_log_error(logger, package_dict.get("externalRefs"), + self.parse_external_refs, True) - files_analyzed: Optional[bool] = parse_field_or_log_error(logger=logger, - field=package_dict.get("filesAnalyzed"), - parsing_method=lambda x: x, optional=True, - default=True) + files_analyzed: Optional[bool] = parse_field_or_log_error(logger, package_dict.get("filesAnalyzed"), + lambda x: x, True, True) homepage: Optional[str] = package_dict.get("homepage") license_comments: Optional[str] = package_dict.get("licenseComments") - license_concluded = parse_field_or_log_error(logger=logger, field=package_dict.get("licenseConcluded"), - parsing_method=self.license_expression_parser.parse_license_expression, - default=None, optional=True) + license_concluded = parse_field_or_log_error(logger, package_dict.get("licenseConcluded"), + self.license_expression_parser.parse_license_expression, True, + None) license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger=logger, field=package_dict.get("licenseDeclared"), - parsing_method=self.license_expression_parser.parse_license_expression, optional=True) + logger, package_dict.get("licenseDeclared"), self.license_expression_parser.parse_license_expression, True) license_info_from_file: Optional[ - Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger=logger, field=package_dict.get("licenseInfoFromFiles"), - parsing_method=self.license_expression_parser.parse_license_expression, optional=True) - - originator: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error( - logger=logger, field=package_dict.get("originator"), - parsing_method=self.actor_parser.parse_actor_or_no_assertion, optional=True) - + Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger, + package_dict.get( + "licenseInfoFromFiles"), + self.license_expression_parser.parse_license_expression, + True) + originator: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error(logger, + package_dict.get("originator"), + self.actor_parser.parse_actor_or_no_assertion, + True) package_file_name: Optional[str] = package_dict.get("packageFileName") package_verification_code: Optional[ - PackageVerificationCode] = parse_field_or_log_error(logger=logger, - field=package_dict.get("packageVerificationCode"), - parsing_method=self.parse_package_verification_code, - optional=True) - primary_package_purpose: Optional[PackagePurpose] = parse_field_or_log_error( - logger=logger, field=package_dict.get("primaryPackagePurpose"), - parsing_method=self.parse_primary_package_purpose, optional=True) - - release_date: Optional[datetime] = parse_field_or_log_error(logger=logger, - field=package_dict.get("releaseDate"), - parsing_method=datetime_from_str, optional=True) + PackageVerificationCode] = parse_field_or_log_error(logger, package_dict.get("packageVerificationCode"), + self.parse_package_verification_code, True) + primary_package_purpose: Optional[PackagePurpose] = parse_field_or_log_error(logger, package_dict.get( + "primaryPackagePurpose"), self.parse_primary_package_purpose, True) + release_date: Optional[datetime] = parse_field_or_log_error(logger, package_dict.get("releaseDate"), + datetime_from_str, True) source_info: Optional[str] = package_dict.get("sourceInfo") summary: Optional[str] = package_dict.get("summary") - supplier: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error( - logger=logger, field=package_dict.get("supplier"), - parsing_method=self.actor_parser.parse_actor_or_no_assertion, optional=True) - - valid_until_date: Optional[datetime] = parse_field_or_log_error(logger=logger, - field=package_dict.get("validUntilDate"), - parsing_method=datetime_from_str, - optional=True) + supplier: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error(logger, + package_dict.get("supplier"), + self.actor_parser.parse_actor_or_no_assertion, + True) + valid_until_date: Optional[datetime] = parse_field_or_log_error(logger, package_dict.get("validUntilDate"), + datetime_from_str, True) version_info: Optional[str] = package_dict.get("versionInfo") raise_parsing_error_if_logger_has_messages(logger, f"Package {name}") @@ -147,18 +136,17 @@ def parse_package(self, package_dict: Dict) -> Package: def parse_external_refs(self, external_ref_dicts: List[Dict]) -> List[ExternalPackageRef]: external_refs = [] for external_ref_dict in external_ref_dicts: - external_refs = append_parsed_field_or_log_error(logger=self.logger, list_to_append_to=external_refs, - field=external_ref_dict, method_to_parse=self.parse_external_ref) - + external_refs = append_parsed_field_or_log_error(self.logger, external_refs, external_ref_dict, + self.parse_external_ref) return external_refs def parse_external_ref(self, external_ref_dict: Dict) -> ExternalPackageRef: logger = Logger() - ref_category = parse_field_or_log_error(logger=logger, field=external_ref_dict.get("referenceCategory"), - parsing_method=self.parse_external_ref_category) - ref_locator = external_ref_dict.get("referenceLocator") - ref_type = external_ref_dict.get("referenceType") - comment = external_ref_dict.get("comment") + ref_category = parse_field_or_log_error(logger, external_ref_dict.get("referenceCategory"), + self.parse_external_ref_category) + ref_locator: Optional[str] = external_ref_dict.get("referenceLocator") + ref_type: Optional[str] = external_ref_dict.get("referenceType") + comment: Optional[str] = external_ref_dict.get("comment") raise_parsing_error_if_logger_has_messages(logger, "external ref") external_ref = construct_or_raise_parsing_error(ExternalPackageRef, dict(category=ref_category, reference_type=ref_type, @@ -178,8 +166,8 @@ def parse_external_ref_category(external_ref_category_str: str) -> ExternalPacka @staticmethod def parse_package_verification_code(verification_code_dict: Dict) -> PackageVerificationCode: - excluded_files: List[str] = verification_code_dict.get("packageVerificationCodeExcludedFiles") - verification_code_value: str = verification_code_dict.get("packageVerificationCodeValue") + excluded_files: List[str] = verification_code_dict.get("packageVerificationCodeExcludedFiles", []) + verification_code_value: Optional[str] = verification_code_dict.get("packageVerificationCodeValue") package_verification_code = construct_or_raise_parsing_error(PackageVerificationCode, dict(value=verification_code_value, diff --git a/src/parser/json/relationship_parser.py b/src/parser/json/relationship_parser.py index 8a4b9c58c..457183bf9 100644 --- a/src/parser/json/relationship_parser.py +++ b/src/parser/json/relationship_parser.py @@ -27,37 +27,37 @@ def __init__(self): def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]: relationships_list = [] - relationships_dicts: List[Dict] = input_doc_dict.get("relationships") + relationships_dicts: List[Dict] = input_doc_dict.get("relationships", []) if relationships_dicts: relationships_list.extend( - parse_field_or_log_error(logger=self.logger, field=relationships_dicts, - parsing_method=self.parse_relationships, default=[])) + parse_field_or_log_error(self.logger, relationships_dicts, self.parse_relationships, default=[])) - document_describes: List[str] = input_doc_dict.get("documentDescribes") - doc_spdx_id: str = input_doc_dict.get("SPDXID") + document_describes: List[str] = input_doc_dict.get("documentDescribes", []) + doc_spdx_id: Optional[str] = input_doc_dict.get("SPDXID") if document_describes: relationships_list.extend( - parse_field_or_log_error(logger=self.logger, field=document_describes, - parsing_method=lambda x: self.parse_document_describes(doc_spdx_id=doc_spdx_id, described_spdx_ids=x, created_relationships=relationships_list), + parse_field_or_log_error(self.logger, document_describes, + lambda x: self.parse_document_describes(doc_spdx_id=doc_spdx_id, + described_spdx_ids=x, + created_relationships=relationships_list), default=[])) - package_dicts: List[Dict] = input_doc_dict.get("packages") - if package_dicts: - relationships_list.extend( - parse_field_or_log_error(logger=self.logger, field=package_dicts, - parsing_method=lambda x: self.parse_has_files(package_dicts=x, created_relationships=relationships_list), - default=[])) + package_dicts: List[Dict] = input_doc_dict.get("packages", []) + if package_dicts: + relationships_list.extend(parse_field_or_log_error(self.logger, package_dicts, + lambda x: self.parse_has_files(package_dicts=x, + created_relationships=relationships_list), + default=[])) - file_dicts: List[Dict] = input_doc_dict.get("files") - if file_dicts: - # not implemented yet, deal with deprecated fields in file - relationships_list.extend( - parse_field_or_log_error(logger=self.logger, field=file_dicts, - parsing_method=self.parse_file_dependencies, default=[])) + file_dicts: List[Dict] = input_doc_dict.get("files", []) + if file_dicts: + # not implemented yet: deal with deprecated fields in file + relationships_list.extend( + parse_field_or_log_error(self.logger, file_dicts, self.parse_file_dependencies, default=[])) - generated_relationships = self.parse_artifact_of(file_dicts=file_dicts) + generated_relationships = self.parse_artifact_of(file_dicts=file_dicts) - raise_parsing_error_if_logger_has_messages(self.logger) + raise_parsing_error_if_logger_has_messages(self.logger) return relationships_list @@ -65,19 +65,18 @@ def parse_relationships(self, relationship_dicts: List[Dict]) -> List[Relationsh logger = Logger() relationship_list = [] for relationship_dict in relationship_dicts: - relationship_list = append_parsed_field_or_log_error(logger=logger, list_to_append_to=relationship_list, - field=relationship_dict, method_to_parse=self.parse_relationship) + relationship_list = append_parsed_field_or_log_error(logger, relationship_list, relationship_dict, + self.parse_relationship) raise_parsing_error_if_logger_has_messages(logger) return relationship_list def parse_relationship(self, relationship_dict: Dict) -> Relationship: logger = Logger() - spdx_element_id: str = relationship_dict.get("spdxElementId") - related_spdx_element: str = relationship_dict.get("relatedSpdxElement") - relationship_type: Optional[RelationshipType] = parse_field_or_log_error(logger=logger, - field=relationship_dict.get("relationshipType"), - parsing_method=self.parse_relationship_type) - relationship_comment: str = relationship_dict.get("comment") + spdx_element_id: Optional[str] = relationship_dict.get("spdxElementId") + related_spdx_element: Optional[str] = relationship_dict.get("relatedSpdxElement") + relationship_type: Optional[RelationshipType] = parse_field_or_log_error(logger, relationship_dict.get( + "relationshipType"), self.parse_relationship_type) + relationship_comment: Optional[str] = relationship_dict.get("comment") raise_parsing_error_if_logger_has_messages(logger, "relationship") relationship = construct_or_raise_parsing_error(Relationship, dict(spdx_element_id=spdx_element_id, @@ -119,8 +118,8 @@ def parse_has_files(self, package_dicts: List[Dict], created_relationships: List logger = Logger() contains_relationships = [] for package in package_dicts: - package_spdx_id = package.get("SPDXID") - contained_files = package.get("hasFiles") + package_spdx_id: Optional[str] = package.get("SPDXID") + contained_files: Optional[str] = package.get("hasFiles") if not contained_files: continue for file_spdx_id in contained_files: diff --git a/src/parser/json/snippet_parser.py b/src/parser/json/snippet_parser.py index e420d611b..b8281b947 100644 --- a/src/parser/json/snippet_parser.py +++ b/src/parser/json/snippet_parser.py @@ -49,27 +49,23 @@ def parse_snippets(self, snippet_dicts_list: List[Dict]) -> List[Snippet]: def parse_snippet(self, snippet_dict: Dict) -> Snippet: logger = Logger() - spdx_id: str = snippet_dict.get("SPDXID") - file_spdx_id: str = snippet_dict.get("snippetFromFile") + spdx_id: Optional[str] = snippet_dict.get("SPDXID") + file_spdx_id: Optional[str] = snippet_dict.get("snippetFromFile") name: Optional[str] = snippet_dict.get("name") - ranges: Dict = parse_field_or_log_error(logger=logger, field=(snippet_dict.get("ranges")), - parsing_method=self.parse_ranges, default={}) + ranges: Dict = parse_field_or_log_error(logger, snippet_dict.get("ranges"), self.parse_ranges, default={}) byte_range: Tuple[int, int] = ranges.get(RangeType.BYTE) line_range: Optional[Tuple[int, int]] = ranges.get(RangeType.LINE) - attribution_texts: List[str] = snippet_dict.get("attributionTexts") + attribution_texts: List[str] = snippet_dict.get("attributionTexts", []) comment: Optional[str] = snippet_dict.get("comment") copyright_text: Optional[str] = snippet_dict.get("copyrightText") license_comment: Optional[str] = snippet_dict.get("licenseComments") concluded_license: Optional[Union[ - LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger=logger, field=snippet_dict.get("licenseConcluded"), - parsing_method=self.license_expression_parser.parse_license_expression, optional=True) + LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger, snippet_dict.get( + "licenseConcluded"), self.license_expression_parser.parse_license_expression, True) license_info: Optional[Union[List[ - LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger=logger, field=snippet_dict.get("licenseInfoInSnippets"), - parsing_method=self.license_expression_parser.parse_license_expression, optional=True) - + LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger, snippet_dict.get( + "licenseInfoInSnippets"), self.license_expression_parser.parse_license_expression, True) if logger.has_messages(): raise SPDXParsingError([f"Error while parsing snippet: {logger.get_messages()}"]) @@ -114,8 +110,8 @@ def get_start_end_tuple(range_dict: Dict, range_type: RangeType) -> Tuple[int, i def validate_range_and_get_type(self, range_dict: Dict) -> RangeType: if ("startPointer" not in range_dict) or ("endPointer" not in range_dict): raise ValueError("Start-/ Endpointer missing in ranges_dict.") - start_pointer_type = self.validate_pointer_and_get_type(range_dict["startPointer"]) - end_pointer_type = self.validate_pointer_and_get_type(range_dict["endPointer"]) + start_pointer_type: RangeType = self.validate_pointer_and_get_type(range_dict["startPointer"]) + end_pointer_type: RangeType = self.validate_pointer_and_get_type(range_dict["endPointer"]) if start_pointer_type != end_pointer_type: raise ValueError("Type of startpointer is not the same as type of endpointer.") return start_pointer_type diff --git a/src/parser/logger.py b/src/parser/logger.py index 497d39dbe..312a959d0 100644 --- a/src/parser/logger.py +++ b/src/parser/logger.py @@ -21,8 +21,7 @@ def append(self, message: str): self.messages.append(message) def extend(self, messages_to_append: List[str]): - for message in messages_to_append: - self.messages.append(message) + self.messages.extend(messages_to_append) def has_messages(self): return bool(self.messages) diff --git a/tests/parser/test_creation_info_parser.py b/tests/parser/test_creation_info_parser.py index d57202b30..d33579379 100644 --- a/tests/parser/test_creation_info_parser.py +++ b/tests/parser/test_creation_info_parser.py @@ -61,18 +61,27 @@ def test_creation_info_parser(): document_uri="http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301")] -def test_parse_incomplete_creation_info(): +@pytest.mark.parametrize("incomplete_dict,expected_message", + [({"spdxVersion": "2.3", "SPDXID": "SPDXRef-DOCUMENT", "name": "Example Document"}, + ["Error while parsing document Example Document: ['CreationInfo does not exist.']"]), + ({"creationInfo": {"created": "2019-02-01T11:30:40Z"}}, + ["Error while constructing CreationInfo: ['SetterError CreationInfo: type of " + 'argument "spdx_version" must be str; got NoneType instead: None\', ' + '\'SetterError CreationInfo: type of argument "spdx_id" must be str; got ' + "NoneType instead: None', 'SetterError CreationInfo: type of argument " + '"name" must be str; got NoneType instead: None\', \'SetterError ' + 'CreationInfo: type of argument "document_namespace" must be str; got ' + "NoneType instead: None', 'SetterError CreationInfo: type of argument " + '"creators" must be a list; got NoneType instead: None\', \'SetterError ' + 'CreationInfo: type of argument "data_license" must be str; got NoneType ' + "instead: None']"])]) +def test_parse_incomplete_document_info(incomplete_dict, expected_message): creation_info_parser = CreationInfoParser() - doc_dict = { - "spdxVersion": "2.3", - "SPDXID": "SPDXRef-DOCUMENT", - "name": "Example Document" - } with pytest.raises(SPDXParsingError) as err: - _ = creation_info_parser.parse_creation_info(doc_dict) + _ = creation_info_parser.parse_creation_info(incomplete_dict) - assert err.value.messages == ["Error while parsing doc Example Document: ['CreationInfo does not exist.']"] + assert err.value.messages == expected_message def test_parse_invalid_creation_info(): diff --git a/tests/parser/test_file_parser.py b/tests/parser/test_file_parser.py index dea0a014d..450a204f7 100644 --- a/tests/parser/test_file_parser.py +++ b/tests/parser/test_file_parser.py @@ -68,8 +68,8 @@ def test_parse_incomplete_file(): with pytest.raises(SPDXParsingError) as err: _ = file_parser.parse_file(file_dict) - assert err.value.messages == ["Error while parsing file Incomplete File: ['No checksums provided, checksums are " - "mandatory for files.']"] + assert err.value.messages == ["Error while constructing File: ['SetterError File: type of argument " + '"checksums" must be a list; got NoneType instead: None\']'] def test_parse_falsy_files(): @@ -99,8 +99,8 @@ def test_parse_falsy_files(): with pytest.raises(SPDXParsingError) as err: _ = file_parser.parse_files(files) - assert err.value.messages == ["Error while parsing file Incomplete File: ['No checksums provided, checksums " - "are mandatory for files.']", + assert err.value.messages == ["Error while constructing File: ['SetterError File: type of argument " + '"checksums" must be a list; got NoneType instead: None\']', 'Error while constructing File: [\'SetterError File: type of argument "name" ' "must be str; got NoneType instead: None']", 'Error while parsing file None: ["Error while parsing Checksum: [\'Algorithm ' From 39558288675a29b6fbec0c8e6a611577b08ebc31 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 21 Dec 2022 13:56:24 +0100 Subject: [PATCH 048/362] [issue-305, review] refactor relationship_parser Signed-off-by: Meret Behrens --- src/parser/json/relationship_parser.py | 84 ++++++++++-------------- tests/parser/test_relationship_parser.py | 6 +- 2 files changed, 36 insertions(+), 54 deletions(-) diff --git a/src/parser/json/relationship_parser.py b/src/parser/json/relationship_parser.py index 457183bf9..640cee60f 100644 --- a/src/parser/json/relationship_parser.py +++ b/src/parser/json/relationship_parser.py @@ -34,28 +34,24 @@ def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]: document_describes: List[str] = input_doc_dict.get("documentDescribes", []) doc_spdx_id: Optional[str] = input_doc_dict.get("SPDXID") - if document_describes: - relationships_list.extend( - parse_field_or_log_error(self.logger, document_describes, - lambda x: self.parse_document_describes(doc_spdx_id=doc_spdx_id, - described_spdx_ids=x, - created_relationships=relationships_list), - default=[])) + + relationships_list.extend(parse_field_or_log_error(self.logger, document_describes, + lambda x: self.parse_document_describes( + doc_spdx_id=doc_spdx_id, described_spdx_ids=x, + existing_relationships=relationships_list), default=[])) package_dicts: List[Dict] = input_doc_dict.get("packages", []) - if package_dicts: - relationships_list.extend(parse_field_or_log_error(self.logger, package_dicts, - lambda x: self.parse_has_files(package_dicts=x, - created_relationships=relationships_list), - default=[])) + + relationships_list.extend(parse_field_or_log_error(self.logger, package_dicts, + lambda x: self.parse_has_files(package_dicts=x, + existing_relationships=relationships_list), + default=[])) file_dicts: List[Dict] = input_doc_dict.get("files", []) - if file_dicts: - # not implemented yet: deal with deprecated fields in file - relationships_list.extend( - parse_field_or_log_error(self.logger, file_dicts, self.parse_file_dependencies, default=[])) + # not implemented yet: deal with deprecated fields in file: https://github.com/spdx/tools-python/issues/294 & https://github.com/spdx/tools-python/issues/387 generated_relationships = self.parse_artifact_of(file_dicts=file_dicts) + dependency_relationships = self.parse_file_dependencies(file_dicts=file_dicts) raise_parsing_error_if_logger_has_messages(self.logger) @@ -96,7 +92,7 @@ def parse_relationship_type(relationship_type_str: str) -> RelationshipType: return relationship_type def parse_document_describes(self, doc_spdx_id: str, described_spdx_ids: List[str], - created_relationships: List[Relationship]) -> List[Relationship]: + existing_relationships: List[Relationship]) -> List[Relationship]: logger = Logger() describes_relationships = [] for spdx_id in described_spdx_ids: @@ -107,13 +103,13 @@ def parse_document_describes(self, doc_spdx_id: str, described_spdx_ids: List[st except ConstructorTypeErrors as err: logger.append(err.get_messages()) continue - if not self.check_if_relationship_exists(describes_relationship, created_relationships): + if not self.check_if_relationship_exists(describes_relationship, existing_relationships): describes_relationships.append(describes_relationship) raise_parsing_error_if_logger_has_messages(logger, "describes_relationship") return describes_relationships - def parse_has_files(self, package_dicts: List[Dict], created_relationships: List[Relationship]) -> List[ + def parse_has_files(self, package_dicts: List[Dict], existing_relationships: List[Relationship]) -> List[ Relationship]: logger = Logger() contains_relationships = [] @@ -131,66 +127,52 @@ def parse_has_files(self, package_dicts: List[Dict], created_relationships: List logger.append(err.get_messages()) continue if not self.check_if_relationship_exists(relationship=contains_relationship, - created_relationships=created_relationships): + existing_relationships=existing_relationships): contains_relationships.append(contains_relationship) raise_parsing_error_if_logger_has_messages(logger, "describes_relationship") return contains_relationships def check_if_relationship_exists(self, relationship: Relationship, - created_relationships: List[Relationship]) -> bool: - created_relationships_without_comment: List[Relationship] = self.ignore_any_comments_in_relationship_list( - created_relationships) - if relationship in created_relationships_without_comment: + existing_relationships: List[Relationship]) -> bool: + existing_relationships_without_comments: List[Relationship] = self.get_all_relationships_without_comments( + existing_relationships) + if relationship in existing_relationships_without_comments: return True - relationship_converted: Relationship = self.convert_relationship(relationship) - if relationship_converted in created_relationships_without_comment: + relationship_inverted: Relationship = self.invert_relationship(relationship) + if relationship_inverted in existing_relationships_without_comments: return True return False @staticmethod - def ignore_any_comments_in_relationship_list(created_relationships: List[Relationship]) -> List[Relationship]: - relationships_without_comment = [Relationship(relationship_type=relationship.relationship_type, + def get_all_relationships_without_comments(existing_relationships: List[Relationship]) -> List[Relationship]: + relationships_without_comments = [Relationship(relationship_type=relationship.relationship_type, related_spdx_element_id=relationship.related_spdx_element_id, spdx_element_id=relationship.spdx_element_id) for relationship in - created_relationships] - return relationships_without_comment + existing_relationships] + return relationships_without_comments - def convert_relationship(self, relationship: Relationship) -> Relationship: + def invert_relationship(self, relationship: Relationship) -> Relationship: return Relationship(related_spdx_element_id=relationship.spdx_element_id, spdx_element_id=relationship.related_spdx_element_id, - relationship_type=self.convert_relationship_types[relationship.relationship_type], + relationship_type=self.invvert_relationship_types[relationship.relationship_type], comment=relationship.comment) - convert_relationship_types = {RelationshipType.DESCRIBES: RelationshipType.DESCRIBED_BY, + invvert_relationship_types = {RelationshipType.DESCRIBES: RelationshipType.DESCRIBED_BY, RelationshipType.DESCRIBED_BY: RelationshipType.DESCRIBES, RelationshipType.CONTAINS: RelationshipType.CONTAINED_BY, RelationshipType.CONTAINED_BY: RelationshipType.CONTAINS} @staticmethod - def parse_file_dependencies(file_dicts: List[Dict]) -> List[Relationship]: - logger = Logger() + def parse_file_dependencies(file_dicts: List[Dict]) -> List[ + Relationship]: dependency_relationships = [] - for file in file_dicts: - file_spdx_id: str = file.get("SPDXID") - dependency_of: List[str] = file.get("fileDependencies") - if not dependency_of: - continue - for dependency in dependency_of: - try: - dependency_relationship = Relationship(spdx_element_id=dependency, - relationship_type=RelationshipType.DEPENDENCY_OF, - related_spdx_element_id=file_spdx_id) - except ConstructorTypeErrors as err: - logger.extend(err.get_messages()) - continue - dependency_relationships.append(dependency_relationship) - raise_parsing_error_if_logger_has_messages(logger, "dependency relationship") + # the field fileDependencies is deprecated and should be converted to a relationship (https://github.com/spdx/tools-python/issues/387) return dependency_relationships @staticmethod def parse_artifact_of(file_dicts: List[Dict]) -> List[Relationship]: generated_relationships = [] - # TODO: artifactOfs is deprecated and should be converted to an external package and a generated from relationship + # artifactOfs is deprecated and should be converted to an external package and a generated from relationship (https://github.com/spdx/tools-python/issues/294) return generated_relationships diff --git a/tests/parser/test_relationship_parser.py b/tests/parser/test_relationship_parser.py index 839f2d0a4..c8cfa5509 100644 --- a/tests/parser/test_relationship_parser.py +++ b/tests/parser/test_relationship_parser.py @@ -64,7 +64,7 @@ def test_creating_describes_relationship(): relationships = relationship_parser.parse_document_describes(doc_spdx_id="SPDXRef-DOCUMENT", described_spdx_ids=document_dict.get( "documentDescribes"), - created_relationships=[]) + existing_relationships=[]) assert len(relationships) == 3 assert relationships == [Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-Package"), @@ -105,7 +105,7 @@ def test_contains_relationship(): }] } - relationships = relationship_parser.parse_has_files(document_dict.get("packages"), created_relationships=[]) + relationships = relationship_parser.parse_has_files(document_dict.get("packages"), existing_relationships=[]) assert len(relationships) == 2 assert relationships == [ @@ -131,6 +131,6 @@ def test_single_contains_relationship(): related_spdx_element_id="SPDXRef-Package")] relationships = relationship_parser.parse_has_files(document_dict.get("packages"), - created_relationships=created_relationships) + existing_relationships=created_relationships) assert len(relationships) == 0 From dd3bf13fa243b4edb475cd82d5e78fb04701593d Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 21 Dec 2022 14:10:49 +0100 Subject: [PATCH 049/362] [issue-305, review] refactor snippet_parser Signed-off-by: Meret Behrens --- src/parser/json/snippet_parser.py | 39 ++++++++++++++--------------- tests/parser/test_snippet_parser.py | 7 ++++-- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/parser/json/snippet_parser.py b/src/parser/json/snippet_parser.py index b8281b947..06b7ad2c4 100644 --- a/src/parser/json/snippet_parser.py +++ b/src/parser/json/snippet_parser.py @@ -16,7 +16,8 @@ from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error, parse_field_or_log_error +from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error, parse_field_or_log_error, \ + raise_parsing_error_if_logger_has_messages, append_parsed_field_or_log_error from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -35,24 +36,21 @@ def __init__(self): self.logger = Logger() self.license_expression_parser = LicenseExpressionParser() - def parse_snippets(self, snippet_dicts_list: List[Dict]) -> List[Snippet]: - snippets_list = [] - for snippet_dict in snippet_dicts_list: - try: - snippets_list.append(self.parse_snippet(snippet_dict)) - except SPDXParsingError as err: - self.logger.extend(err.get_messages()) - if self.logger.has_messages(): - raise SPDXParsingError(self.logger.get_messages()) + def parse_snippets(self, snippet_dicts: List[Dict]) -> List[Snippet]: + snippets = [] + for snippet_dict in snippet_dicts: + snippets = append_parsed_field_or_log_error(self.logger, snippets, snippet_dict, self.parse_snippet) + + raise_parsing_error_if_logger_has_messages(self.logger) - return snippets_list + return snippets def parse_snippet(self, snippet_dict: Dict) -> Snippet: logger = Logger() spdx_id: Optional[str] = snippet_dict.get("SPDXID") file_spdx_id: Optional[str] = snippet_dict.get("snippetFromFile") name: Optional[str] = snippet_dict.get("name") - ranges: Dict = parse_field_or_log_error(logger, snippet_dict.get("ranges"), self.parse_ranges, default={}) + ranges: Dict = parse_field_or_log_error(logger, snippet_dict.get("ranges", []), self.parse_ranges, default={}) byte_range: Tuple[int, int] = ranges.get(RangeType.BYTE) line_range: Optional[Tuple[int, int]] = ranges.get(RangeType.LINE) attribution_texts: List[str] = snippet_dict.get("attributionTexts", []) @@ -80,8 +78,6 @@ def parse_snippet(self, snippet_dict: Dict) -> Snippet: return snippet def parse_ranges(self, ranges_from_snippet: List[Dict]) -> Dict: - if not ranges_from_snippet: - raise SPDXParsingError([f"No ranges dict provided."]) logger = Logger() ranges = {} for range_dict in ranges_from_snippet: @@ -92,7 +88,7 @@ def parse_ranges(self, ranges_from_snippet: List[Dict]) -> Dict: except ValueError as error: logger.append(error.args[0]) if logger.has_messages(): - raise SPDXParsingError([f"Error while parsing ranges_dict: {logger.get_messages()}"]) + raise SPDXParsingError([f"Error while parsing snippet ranges: {logger.get_messages()}"]) return ranges @staticmethod @@ -108,8 +104,10 @@ def get_start_end_tuple(range_dict: Dict, range_type: RangeType) -> Tuple[int, i return start, end def validate_range_and_get_type(self, range_dict: Dict) -> RangeType: - if ("startPointer" not in range_dict) or ("endPointer" not in range_dict): - raise ValueError("Start-/ Endpointer missing in ranges_dict.") + if "startPointer" not in range_dict: + raise ValueError("Startpointer missing in snippet ranges.") + if "endPointer" not in range_dict: + raise ValueError("Endpointer missing in snippet ranges.") start_pointer_type: RangeType = self.validate_pointer_and_get_type(range_dict["startPointer"]) end_pointer_type: RangeType = self.validate_pointer_and_get_type(range_dict["endPointer"]) if start_pointer_type != end_pointer_type: @@ -118,7 +116,8 @@ def validate_range_and_get_type(self, range_dict: Dict) -> RangeType: @staticmethod def validate_pointer_and_get_type(pointer: Dict) -> RangeType: - if ("offset" in pointer and "lineNumber" in pointer) or ( - "offset" not in pointer and "lineNumber" not in pointer): - raise ValueError("Couldn't determine type of pointer.") + if "offset" in pointer and "lineNumber" in pointer: + raise ValueError ('Couldn\'t determine type of pointer: "offset" and "lineNumber" provided as key.') + if "offset" not in pointer and "lineNumber" not in pointer: + raise ValueError('Couldn\'t determine type of pointer: neither "offset" nor "lineNumber" provided as key.') return RangeType.BYTE if "offset" in pointer else RangeType.LINE diff --git a/tests/parser/test_snippet_parser.py b/tests/parser/test_snippet_parser.py index 4d5cf3b81..9ead2d6d9 100644 --- a/tests/parser/test_snippet_parser.py +++ b/tests/parser/test_snippet_parser.py @@ -73,7 +73,10 @@ def test_parse_incomplete_snippet(): with pytest.raises(SPDXParsingError) as err: _ = snippet_parser.parse_snippet(incomplete_snippet_dict) - assert err.value.messages == ["Error while parsing snippet: ['No ranges dict provided.']"] + assert err.value.messages == ["Error while constructing Snippet: ['SetterError Snippet: type of argument " + '"file_spdx_id" must be str; got NoneType instead: None\', \'SetterError ' + 'Snippet: type of argument "byte_range" must be a tuple; got NoneType ' + "instead: None']"] def test_parse_snippet_with_invalid_snippet_range(): @@ -132,6 +135,6 @@ def test_parse_invalid_snippet_range(): with pytest.raises(SPDXParsingError) as err: _ = snippet_parser.parse_ranges(ranges) - assert err.value.messages == ["Error while parsing ranges_dict: ['Type of startpointer is not the same as " + assert err.value.messages == ["Error while parsing snippet ranges: ['Type of startpointer is not the same as " "type of endpointer.', 'Type of startpointer is not the same as type of " "endpointer.']"] From 69935edafd7c8f1b2e5750d1914de9c144a42609 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 21 Dec 2022 14:37:13 +0100 Subject: [PATCH 050/362] [issue-305, review] make naming consistent Signed-off-by: Meret Behrens --- src/parser/json/annotation_parser.py | 51 +++++++++---------- src/parser/json/checksum_parser.py | 10 ++-- src/parser/json/creation_info_parser.py | 22 ++++---- .../json/extracted_licensing_info_parser.py | 10 ++-- src/parser/json/file_parser.py | 10 ++-- src/parser/json/package_parser.py | 11 ++-- src/parser/json/relationship_parser.py | 42 +++++++-------- 7 files changed, 77 insertions(+), 79 deletions(-) diff --git a/src/parser/json/annotation_parser.py b/src/parser/json/annotation_parser.py index 1788a81f8..e5fca901a 100644 --- a/src/parser/json/annotation_parser.py +++ b/src/parser/json/annotation_parser.py @@ -29,65 +29,64 @@ def __init__(self): self.actor_parser = ActorParser() def parse_all_annotations(self, input_doc_dict: Dict) -> List[Annotation]: - annotations_list = [] - self.parse_annotations_from_object(annotations_list, [input_doc_dict]) + annotations = [] + self.parse_annotations_from_object(annotations, [input_doc_dict]) reviews: List[Dict] = input_doc_dict.get("revieweds", []) for review in reviews: - annotations_list = append_parsed_field_or_log_error(self.logger, annotations_list, review, + annotations = append_parsed_field_or_log_error(self.logger, annotations, review, lambda x: self.parse_review(x, spdx_id=input_doc_dict.get( "SPDXID"))) packages: List[Dict] = input_doc_dict.get("packages", []) - self.parse_annotations_from_object(annotations_list, packages) + self.parse_annotations_from_object(annotations, packages) files: List[Dict] = input_doc_dict.get("files", []) - self.parse_annotations_from_object(annotations_list, files) + self.parse_annotations_from_object(annotations, files) snippets: List[Dict] = input_doc_dict.get("snippets", []) - self.parse_annotations_from_object(annotations_list, snippets) + self.parse_annotations_from_object(annotations, snippets) raise_parsing_error_if_logger_has_messages(self.logger, "Annotations") - return annotations_list + return annotations - def parse_annotations_from_object(self, annotations_list, element_list: List[Dict]): + def parse_annotations_from_object(self, annotations: List[Annotation], element_list: List[Dict]): for element in element_list: element_spdx_id: Optional[str] = element.get("SPDXID") element_annotations: List[Dict] = element.get("annotations", []) - annotations_list.extend(parse_field_or_log_error(self.logger, element_annotations, - lambda x: self.parse_annotations(x, - spdx_id=element_spdx_id), - [])) + annotations.extend(parse_field_or_log_error(self.logger, element_annotations, + lambda x: self.parse_annotations(x, spdx_id=element_spdx_id), + [])) - def parse_annotations(self, annotations_dict_list: List[Dict], spdx_id: Optional[str] = None) -> List[Annotation]: + def parse_annotations(self, annotation_dicts: List[Dict], spdx_id: Optional[str] = None) -> List[Annotation]: logger = Logger() - annotations_list = [] - for annotation_dict in annotations_dict_list: - annotations_list = append_parsed_field_or_log_error(self.logger, annotations_list, annotation_dict, + annotations = [] + for annotation_dict in annotation_dicts: + annotations = append_parsed_field_or_log_error(self.logger, annotations, annotation_dict, lambda x: self.parse_annotation(x, spdx_id=spdx_id)) raise_parsing_error_if_logger_has_messages(logger, "Annotations") - return annotations_list + return annotations - def parse_annotation(self, annotation: Dict, spdx_id: Optional[str] = None) -> Annotation: + def parse_annotation(self, annotation_dict: Dict, spdx_id: Optional[str] = None) -> Annotation: logger = Logger() - spdx_id: Optional[str] = annotation.get("SPDXID") or spdx_id + spdx_id: Optional[str] = annotation_dict.get("SPDXID") or spdx_id - annotation_type: Optional[AnnotationType] = parse_field_or_log_error(logger, annotation.get("annotationType"), + annotation_type: Optional[AnnotationType] = parse_field_or_log_error(logger, annotation_dict.get("annotationType"), self.parse_annotation_type) - annotator: Optional[Actor] = parse_field_or_log_error(logger, annotation.get("annotator"), + annotator: Optional[Actor] = parse_field_or_log_error(logger, annotation_dict.get("annotator"), self.actor_parser.parse_actor) - annotation_date: Optional[datetime] = parse_field_or_log_error(logger, annotation.get("annotationDate"), + annotation_date: Optional[datetime] = parse_field_or_log_error(logger, annotation_dict.get("annotationDate"), datetime_from_str) - annotation_comment: Optional[str] = annotation.get("comment") + annotation_comment: Optional[str] = annotation_dict.get("comment") raise_parsing_error_if_logger_has_messages(logger, "Annotation") - annotation = construct_or_raise_parsing_error(Annotation, - dict(spdx_id=spdx_id, annotation_type=annotation_type, + annotation_dict = construct_or_raise_parsing_error(Annotation, + dict(spdx_id=spdx_id, annotation_type=annotation_type, annotator=annotator, annotation_date=annotation_date, annotation_comment=annotation_comment)) - return annotation + return annotation_dict @staticmethod def parse_annotation_type(annotation_type: str) -> AnnotationType: diff --git a/src/parser/json/checksum_parser.py b/src/parser/json/checksum_parser.py index 4dd459256..7c7bc2dbd 100644 --- a/src/parser/json/checksum_parser.py +++ b/src/parser/json/checksum_parser.py @@ -23,14 +23,14 @@ class ChecksumParser: def __init__(self): self.logger = Logger() - def parse_checksums(self, checksum_dicts_list: List[Dict]) -> List[Checksum]: - checksum_list = [] - for checksum_dict in checksum_dicts_list: - checksum_list = append_parsed_field_or_log_error(self.logger, checksum_list, checksum_dict, + def parse_checksums(self, checksum_dicts: List[Dict]) -> List[Checksum]: + checksums = [] + for checksum_dict in checksum_dicts: + checksums = append_parsed_field_or_log_error(self.logger, checksums, checksum_dict, self.parse_checksum) raise_parsing_error_if_logger_has_messages(self.logger) - return checksum_list + return checksums @staticmethod def parse_checksum(checksum_dict: Dict) -> Checksum: diff --git a/src/parser/json/creation_info_parser.py b/src/parser/json/creation_info_parser.py index 477278e07..d9bf6b5c6 100644 --- a/src/parser/json/creation_info_parser.py +++ b/src/parser/json/creation_info_parser.py @@ -81,13 +81,13 @@ def parse_creation_info(self, doc_dict: Dict) -> CreationInfo: def parse_creators(self, creators_list_from_dict: List[str]) -> List[Actor]: logger = Logger() - creators_list = [] - for creator_dict in creators_list_from_dict: - creators_list = append_parsed_field_or_log_error(logger, creators_list, creator_dict, + creators = [] + for creator_str in creators_list_from_dict: + creators = append_parsed_field_or_log_error(logger, creators, creator_str, self.actor_parser.parse_actor_or_no_assertion) raise_parsing_error_if_logger_has_messages(logger) - return creators_list + return creators @staticmethod def parse_version(version_str: str) -> Version: @@ -96,14 +96,14 @@ def parse_version(version_str: str) -> Version: except ValueError as err: raise SPDXParsingError([f"Error while parsing version {version_str}: {err.args[0]}"]) - def parse_external_document_refs(self, external_document_refs_dict: List[Dict]) -> List[ExternalDocumentRef]: + def parse_external_document_refs(self, external_document_ref_dicts: List[Dict]) -> List[ExternalDocumentRef]: logger = Logger() external_document_refs = [] - for external_ref_dict in external_document_refs_dict: - external_doc_ref: ExternalDocumentRef = parse_field_or_log_error(logger, external_ref_dict, + for external_document_ref_dict in external_document_ref_dicts: + external_document_ref: ExternalDocumentRef = parse_field_or_log_error(logger, external_document_ref_dict, self.parse_external_document_ref, True) - external_document_refs.append(external_doc_ref) + external_document_refs.append(external_document_ref) raise_parsing_error_if_logger_has_messages(logger) return external_document_refs @@ -115,11 +115,11 @@ def parse_external_document_ref(self, external_document_ref_dict: Dict) -> Exter external_document_id: Optional[str] = external_document_ref_dict.get("externalDocumentId") document_uri: Optional[str] = external_document_ref_dict.get("spdxDocument") - raise_parsing_error_if_logger_has_messages(logger, "ExternalDocRef") - external_doc_ref: ExternalDocumentRef = construct_or_raise_parsing_error(ExternalDocumentRef, + raise_parsing_error_if_logger_has_messages(logger, "ExternalDocumentRef") + external_document_ref: ExternalDocumentRef = construct_or_raise_parsing_error(ExternalDocumentRef, dict( document_ref_id=external_document_id, checksum=checksum, document_uri=document_uri)) - return external_doc_ref + return external_document_ref diff --git a/src/parser/json/extracted_licensing_info_parser.py b/src/parser/json/extracted_licensing_info_parser.py index 7e9b2cdf6..5076a1f2a 100644 --- a/src/parser/json/extracted_licensing_info_parser.py +++ b/src/parser/json/extracted_licensing_info_parser.py @@ -25,14 +25,14 @@ def __init__(self): def parse_extracted_licensing_infos(self, extracted_licensing_info_dicts: List[Dict]) -> List[ ExtractedLicensingInfo]: - extracted_licensing_info_list = [] + extracted_licensing_infos = [] for extracted_licensing_info_dict in extracted_licensing_info_dicts: - extracted_licensing_info_list = append_parsed_field_or_log_error(self.logger, extracted_licensing_info_list, + extracted_licensing_infos = append_parsed_field_or_log_error(self.logger, extracted_licensing_infos, extracted_licensing_info_dict, self.parse_extracted_licensing_info) raise_parsing_error_if_logger_has_messages(self.logger) - return extracted_licensing_info_list + return extracted_licensing_infos def parse_extracted_licensing_info(self, extracted_licensing_info_dict: Dict) -> ExtractedLicensingInfo: license_id: Optional[str] = extracted_licensing_info_dict.get("licenseId") @@ -43,13 +43,13 @@ def parse_extracted_licensing_info(self, extracted_licensing_info_dict: Dict) -> True) cross_references: List[str] = extracted_licensing_info_dict.get("seeAlsos", []) comment: Optional[str] = extracted_licensing_info_dict.get("comment") - extracted_licensing_info_dict = construct_or_raise_parsing_error(ExtractedLicensingInfo, + extracted_licensing_info = construct_or_raise_parsing_error(ExtractedLicensingInfo, dict(license_id=license_id, extracted_text=extracted_text, comment=comment, license_name=license_name, cross_references=cross_references)) - return extracted_licensing_info_dict + return extracted_licensing_info @staticmethod def parse_extracted_licensing_info_name(extracted_licensing_info_name_or_no_assertion) -> Union[str, SpdxNoAssertion]: diff --git a/src/parser/json/file_parser.py b/src/parser/json/file_parser.py index 0e349d1be..2686a9954 100644 --- a/src/parser/json/file_parser.py +++ b/src/parser/json/file_parser.py @@ -32,12 +32,12 @@ def __init__(self): self.checksum_parser = ChecksumParser() self.license_expression_parser = LicenseExpressionParser() - def parse_files(self, file_dict_list) -> List[File]: - file_list = [] - for file_dict in file_dict_list: - file_list = append_parsed_field_or_log_error(self.logger, file_list, file_dict, self.parse_file) + def parse_files(self, file_dicts: List[Dict]) -> List[File]: + files = [] + for file_dict in file_dicts: + files = append_parsed_field_or_log_error(self.logger, files, file_dict, self.parse_file) raise_parsing_error_if_logger_has_messages(self.logger) - return file_list + return files def parse_file(self, file_dict: Dict) -> Optional[File]: logger = Logger() diff --git a/src/parser/json/package_parser.py b/src/parser/json/package_parser.py index a0dd2b8c9..687dbf4c7 100644 --- a/src/parser/json/package_parser.py +++ b/src/parser/json/package_parser.py @@ -39,15 +39,14 @@ def __init__(self): self.license_expression_parser = LicenseExpressionParser() self.logger = Logger() - def parse_packages(self, packages_dict_list: List[Dict]) -> List[Package]: - packages_list = [] - for package_dict in packages_dict_list: - packages_list = append_parsed_field_or_log_error(self.logger, packages_list, package_dict, - self.parse_package) + def parse_packages(self, package_dicts: List[Dict]) -> List[Package]: + packages = [] + for package_dict in package_dicts: + packages = append_parsed_field_or_log_error(self.logger, packages, package_dict, self.parse_package) raise_parsing_error_if_logger_has_messages(self.logger) - return packages_list + return packages def parse_package(self, package_dict: Dict) -> Package: logger = Logger() diff --git a/src/parser/json/relationship_parser.py b/src/parser/json/relationship_parser.py index 640cee60f..d8ee9e69c 100644 --- a/src/parser/json/relationship_parser.py +++ b/src/parser/json/relationship_parser.py @@ -26,26 +26,26 @@ def __init__(self): self.logger = Logger() def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]: - relationships_list = [] - relationships_dicts: List[Dict] = input_doc_dict.get("relationships", []) - if relationships_dicts: - relationships_list.extend( - parse_field_or_log_error(self.logger, relationships_dicts, self.parse_relationships, default=[])) + relationships = [] + relationship_dicts: List[Dict] = input_doc_dict.get("relationships", []) + if relationship_dicts: + relationships.extend( + parse_field_or_log_error(self.logger, relationship_dicts, self.parse_relationships, default=[])) document_describes: List[str] = input_doc_dict.get("documentDescribes", []) doc_spdx_id: Optional[str] = input_doc_dict.get("SPDXID") - relationships_list.extend(parse_field_or_log_error(self.logger, document_describes, - lambda x: self.parse_document_describes( - doc_spdx_id=doc_spdx_id, described_spdx_ids=x, - existing_relationships=relationships_list), default=[])) + relationships.extend(parse_field_or_log_error(self.logger, document_describes, + lambda x: self.parse_document_describes( + doc_spdx_id=doc_spdx_id, described_spdx_ids=x, + existing_relationships=relationships), default=[])) package_dicts: List[Dict] = input_doc_dict.get("packages", []) - relationships_list.extend(parse_field_or_log_error(self.logger, package_dicts, - lambda x: self.parse_has_files(package_dicts=x, - existing_relationships=relationships_list), - default=[])) + relationships.extend(parse_field_or_log_error(self.logger, package_dicts, + lambda x: self.parse_has_files(package_dicts=x, + existing_relationships=relationships), + default=[])) file_dicts: List[Dict] = input_doc_dict.get("files", []) @@ -55,16 +55,16 @@ def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]: raise_parsing_error_if_logger_has_messages(self.logger) - return relationships_list + return relationships def parse_relationships(self, relationship_dicts: List[Dict]) -> List[Relationship]: logger = Logger() - relationship_list = [] + relationships = [] for relationship_dict in relationship_dicts: - relationship_list = append_parsed_field_or_log_error(logger, relationship_list, relationship_dict, - self.parse_relationship) + relationships = append_parsed_field_or_log_error(logger, relationships, relationship_dict, + self.parse_relationship) raise_parsing_error_if_logger_has_messages(logger) - return relationship_list + return relationships def parse_relationship(self, relationship_dict: Dict) -> Relationship: logger = Logger() @@ -148,9 +148,9 @@ def check_if_relationship_exists(self, relationship: Relationship, @staticmethod def get_all_relationships_without_comments(existing_relationships: List[Relationship]) -> List[Relationship]: relationships_without_comments = [Relationship(relationship_type=relationship.relationship_type, - related_spdx_element_id=relationship.related_spdx_element_id, - spdx_element_id=relationship.spdx_element_id) for relationship in - existing_relationships] + related_spdx_element_id=relationship.related_spdx_element_id, + spdx_element_id=relationship.spdx_element_id) for relationship in + existing_relationships] return relationships_without_comments def invert_relationship(self, relationship: Relationship) -> Relationship: From d085abfb6fcb3213e96a5e1896b95679fd38f082 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 21 Dec 2022 14:46:08 +0100 Subject: [PATCH 051/362] [issue-305, review] add test for dict parsing functions and catch ValueError due to wrong format Signed-off-by: Meret Behrens --- src/parser/json/dict_parsing_functions.py | 9 ++++-- tests/parser/test_dict_parsing_functions.py | 35 +++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 tests/parser/test_dict_parsing_functions.py diff --git a/src/parser/json/dict_parsing_functions.py b/src/parser/json/dict_parsing_functions.py index 9dfd56b01..cff3e8a2f 100644 --- a/src/parser/json/dict_parsing_functions.py +++ b/src/parser/json/dict_parsing_functions.py @@ -19,7 +19,11 @@ def datetime_from_str(date_str: str) -> datetime: if not isinstance(date_str, str): raise SPDXParsingError([f"Could not convert str to datetime, invalid type: {type(date_str).__name__}"]) - date = datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%SZ") + try: + date = datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%SZ") + except ValueError: + raise SPDXParsingError( + [f'Could not convert str to datetime, format of {date_str} does not match "%Y-%m-%dT%H:%M:%SZ"']) return date @@ -35,7 +39,8 @@ def construct_or_raise_parsing_error(object_to_construct: Any, args_for_construc return constructed_object -def parse_field_or_log_error(logger: Logger, field: Any, parsing_method: Callable = lambda x: x, optional=False, default=None,) -> Any: +def parse_field_or_log_error(logger: Logger, field: Any, parsing_method: Callable = lambda x: x, optional=False, + default=None, ) -> Any: try: if optional: if not field: diff --git a/tests/parser/test_dict_parsing_functions.py b/tests/parser/test_dict_parsing_functions.py new file mode 100644 index 000000000..bcbd606bf --- /dev/null +++ b/tests/parser/test_dict_parsing_functions.py @@ -0,0 +1,35 @@ +# 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 datetime import datetime + +import pytest + +from src.parser.error import SPDXParsingError +from src.parser.json.dict_parsing_functions import datetime_from_str + + +def test_datetime_from_str(): + date_str = "2010-03-04T05:45:11Z" + + date = datetime_from_str(date_str) + + assert date == datetime(2010, 3, 4, 5, 45, 11) + + +@pytest.mark.parametrize("invalid_date_str,expected_message", + [(5, ['Could not convert str to datetime, invalid type: int']), + ("2010-02-03", ['Could not convert str to datetime, format of 2010-02-03 does not match ' + '"%Y-%m-%dT%H:%M:%SZ"'])]) +def test_datetime_from_str_error(invalid_date_str, expected_message): + with pytest.raises(SPDXParsingError) as err: + _ = datetime_from_str(invalid_date_str) + + assert err.value.messages == expected_message From af9ff6bab1c2ff875ced87e43cd10709a69af627 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 21 Dec 2022 15:52:04 +0100 Subject: [PATCH 052/362] [issue-305, review] add None handling for required fields Signed-off-by: Meret Behrens --- src/parser/json/annotation_parser.py | 7 +++-- src/parser/json/creation_info_parser.py | 13 ++++---- src/parser/json/dict_parsing_functions.py | 15 ++++------ .../json/extracted_licensing_info_parser.py | 6 ++-- src/parser/json/file_parser.py | 11 ++++--- src/parser/json/package_parser.py | 30 ++++++++----------- src/parser/json/relationship_parser.py | 21 +++++++------ src/parser/json/snippet_parser.py | 4 +-- tests/parser/test_annotation_parser.py | 10 +++++-- tests/parser/test_relationship_parser.py | 8 +++-- 10 files changed, 61 insertions(+), 64 deletions(-) diff --git a/src/parser/json/annotation_parser.py b/src/parser/json/annotation_parser.py index e5fca901a..9e5a0e36b 100644 --- a/src/parser/json/annotation_parser.py +++ b/src/parser/json/annotation_parser.py @@ -54,7 +54,7 @@ def parse_annotations_from_object(self, annotations: List[Annotation], element_l element_annotations: List[Dict] = element.get("annotations", []) annotations.extend(parse_field_or_log_error(self.logger, element_annotations, lambda x: self.parse_annotations(x, spdx_id=element_spdx_id), - [])) + default=[])) def parse_annotations(self, annotation_dicts: List[Dict], spdx_id: Optional[str] = None) -> List[Annotation]: logger = Logger() @@ -70,7 +70,8 @@ def parse_annotation(self, annotation_dict: Dict, spdx_id: Optional[str] = None) logger = Logger() spdx_id: Optional[str] = annotation_dict.get("SPDXID") or spdx_id - annotation_type: Optional[AnnotationType] = parse_field_or_log_error(logger, annotation_dict.get("annotationType"), + annotation_type: Optional[AnnotationType] = parse_field_or_log_error(logger, + annotation_dict.get("annotationType"), self.parse_annotation_type) annotator: Optional[Actor] = parse_field_or_log_error(logger, annotation_dict.get("annotator"), @@ -98,7 +99,7 @@ def parse_annotation_type(annotation_type: str) -> AnnotationType: def parse_review(self, review_dict: Dict, spdx_id: str) -> Annotation: logger = Logger() annotator: Optional[Actor] = parse_field_or_log_error(logger, review_dict.get("reviewer"), - self.actor_parser.parse_actor, True) + self.actor_parser.parse_actor) annotation_date: Optional[datetime] = parse_field_or_log_error(logger, review_dict.get("reviewDate"), datetime_from_str) diff --git a/src/parser/json/creation_info_parser.py b/src/parser/json/creation_info_parser.py index d9bf6b5c6..9d0675b91 100644 --- a/src/parser/json/creation_info_parser.py +++ b/src/parser/json/creation_info_parser.py @@ -48,7 +48,7 @@ def parse_creation_info(self, doc_dict: Dict) -> CreationInfo: raise SPDXParsingError([f"Error while parsing document {name}: {logger.get_messages()}"]) creators: List[Actor] = parse_field_or_log_error(logger, creation_info_dict.get("creators"), - self.parse_creators, True) + self.parse_creators) created: Optional[datetime] = parse_field_or_log_error(logger, creation_info_dict.get("created"), datetime_from_str) @@ -56,14 +56,11 @@ def parse_creation_info(self, doc_dict: Dict) -> CreationInfo: creator_comment: Optional[str] = creation_info_dict.get("comment") data_license: Optional[str] = doc_dict.get("dataLicense") - external_document_refs: List[ExternalDocumentRef] = parse_field_or_log_error(logger, - doc_dict.get( - "externalDocumentRefs"), - self.parse_external_document_refs, - True) + external_document_refs: List[ExternalDocumentRef] = parse_field_or_log_error(logger, doc_dict.get( + "externalDocumentRefs"), self.parse_external_document_refs) license_list_version: Optional[Version] = parse_field_or_log_error(logger, creation_info_dict.get("licenseListVersion"), - self.parse_version, True) + self.parse_version) document_comment: Optional[str] = doc_dict.get("comment") raise_parsing_error_if_logger_has_messages(logger, f"Document {name}") @@ -101,7 +98,7 @@ def parse_external_document_refs(self, external_document_ref_dicts: List[Dict]) external_document_refs = [] for external_document_ref_dict in external_document_ref_dicts: external_document_ref: ExternalDocumentRef = parse_field_or_log_error(logger, external_document_ref_dict, - self.parse_external_document_ref, True) + self.parse_external_document_ref) external_document_refs.append(external_document_ref) diff --git a/src/parser/json/dict_parsing_functions.py b/src/parser/json/dict_parsing_functions.py index cff3e8a2f..191a4b517 100644 --- a/src/parser/json/dict_parsing_functions.py +++ b/src/parser/json/dict_parsing_functions.py @@ -39,19 +39,14 @@ def construct_or_raise_parsing_error(object_to_construct: Any, args_for_construc return constructed_object -def parse_field_or_log_error(logger: Logger, field: Any, parsing_method: Callable = lambda x: x, optional=False, - default=None, ) -> Any: +def parse_field_or_log_error(logger: Logger, field: Any, parsing_method: Callable = lambda x: x, default=None) -> Any: + if not field: + return default try: - if optional: - if not field: - return default - parsed_element = parsing_method(field) - else: - parsed_element = parsing_method(field) + return parsing_method(field) except SPDXParsingError as err: logger.extend(err.get_messages()) - parsed_element = default - return parsed_element + return default def append_parsed_field_or_log_error(logger: Logger, list_to_append_to: List[Any], field: Any, diff --git a/src/parser/json/extracted_licensing_info_parser.py b/src/parser/json/extracted_licensing_info_parser.py index 5076a1f2a..4fc3a67c3 100644 --- a/src/parser/json/extracted_licensing_info_parser.py +++ b/src/parser/json/extracted_licensing_info_parser.py @@ -38,9 +38,9 @@ def parse_extracted_licensing_info(self, extracted_licensing_info_dict: Dict) -> license_id: Optional[str] = extracted_licensing_info_dict.get("licenseId") extracted_text: Optional[str] = extracted_licensing_info_dict.get("extractedText") license_name: Optional[Union[str, SpdxNoAssertion]] = parse_field_or_log_error(self.logger, - extracted_licensing_info_dict.get("name"), - self.parse_extracted_licensing_info_name, - True) + extracted_licensing_info_dict.get( + "name"), + self.parse_extracted_licensing_info_name) cross_references: List[str] = extracted_licensing_info_dict.get("seeAlsos", []) comment: Optional[str] = extracted_licensing_info_dict.get("comment") extracted_licensing_info = construct_or_raise_parsing_error(ExtractedLicensingInfo, diff --git a/src/parser/json/file_parser.py b/src/parser/json/file_parser.py index 2686a9954..85cc856eb 100644 --- a/src/parser/json/file_parser.py +++ b/src/parser/json/file_parser.py @@ -43,24 +43,23 @@ def parse_file(self, file_dict: Dict) -> Optional[File]: logger = Logger() name: Optional[str] = file_dict.get("fileName") spdx_id: Optional[str] = file_dict.get("SPDXID") - checksums_list: List[Dict] = file_dict.get("checksums", []) + checksums_list: List[Dict] = file_dict.get("checksums") checksums: List[Checksum] = parse_field_or_log_error(logger, checksums_list, - self.checksum_parser.parse_checksums, True) + self.checksum_parser.parse_checksums) attribution_texts: List[str] = file_dict.get("attributionTexts", []) comment: Optional[str] = file_dict.get("comment") copyright_text: Optional[str] = file_dict.get("copyrightText") file_contributors: List[str] = file_dict.get("fileContributors", []) - file_types: List[FileType] = parse_field_or_log_error(logger, file_dict.get("fileTypes"), self.parse_file_types, - True) + file_types: List[FileType] = parse_field_or_log_error(logger, file_dict.get("fileTypes"), self.parse_file_types) license_comments: Optional[str] = file_dict.get("licenseComments") license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger, file_dict.get("licenseConcluded"), self.license_expression_parser.parse_license_expression, True) + logger, file_dict.get("licenseConcluded"), self.license_expression_parser.parse_license_expression) license_info_in_files: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger, file_dict.get("licenseInfoInFiles"), self.license_expression_parser.parse_license_expression, True) + logger, file_dict.get("licenseInfoInFiles"), self.license_expression_parser.parse_license_expression) notice_text: Optional[str] = file_dict.get("noticeText") raise_parsing_error_if_logger_has_messages(logger, f"file {name}") diff --git a/src/parser/json/package_parser.py b/src/parser/json/package_parser.py index 687dbf4c7..10aea5adb 100644 --- a/src/parser/json/package_parser.py +++ b/src/parser/json/package_parser.py @@ -55,10 +55,10 @@ def parse_package(self, package_dict: Dict) -> Package: attribution_texts: List[str] = package_dict.get("attributionTexts", []) built_date: Optional[datetime] = parse_field_or_log_error(logger, package_dict.get("builtDate"), - datetime_from_str, True) + datetime_from_str) checksums = parse_field_or_log_error(logger, package_dict.get("checksums"), - self.checksum_parser.parse_checksums, True) + self.checksum_parser.parse_checksums) comment: Optional[str] = package_dict.get("comment") copyright_text: Optional[str] = package_dict.get("copyrightText") description: Optional[str] = package_dict.get("description") @@ -66,47 +66,43 @@ def parse_package(self, package_dict: Dict) -> Package: package_dict.get("downloadLocation")) external_refs: List[ExternalPackageRef] = parse_field_or_log_error(logger, package_dict.get("externalRefs"), - self.parse_external_refs, True) + self.parse_external_refs) files_analyzed: Optional[bool] = parse_field_or_log_error(logger, package_dict.get("filesAnalyzed"), - lambda x: x, True, True) + lambda x: x, True) homepage: Optional[str] = package_dict.get("homepage") license_comments: Optional[str] = package_dict.get("licenseComments") license_concluded = parse_field_or_log_error(logger, package_dict.get("licenseConcluded"), - self.license_expression_parser.parse_license_expression, True, - None) + self.license_expression_parser.parse_license_expression, None) license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger, package_dict.get("licenseDeclared"), self.license_expression_parser.parse_license_expression, True) + logger, package_dict.get("licenseDeclared"), self.license_expression_parser.parse_license_expression) license_info_from_file: Optional[ Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger, package_dict.get( "licenseInfoFromFiles"), - self.license_expression_parser.parse_license_expression, - True) + self.license_expression_parser.parse_license_expression) originator: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error(logger, package_dict.get("originator"), - self.actor_parser.parse_actor_or_no_assertion, - True) + self.actor_parser.parse_actor_or_no_assertion) package_file_name: Optional[str] = package_dict.get("packageFileName") package_verification_code: Optional[ PackageVerificationCode] = parse_field_or_log_error(logger, package_dict.get("packageVerificationCode"), - self.parse_package_verification_code, True) + self.parse_package_verification_code) primary_package_purpose: Optional[PackagePurpose] = parse_field_or_log_error(logger, package_dict.get( - "primaryPackagePurpose"), self.parse_primary_package_purpose, True) + "primaryPackagePurpose"), self.parse_primary_package_purpose) release_date: Optional[datetime] = parse_field_or_log_error(logger, package_dict.get("releaseDate"), - datetime_from_str, True) + datetime_from_str) source_info: Optional[str] = package_dict.get("sourceInfo") summary: Optional[str] = package_dict.get("summary") supplier: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error(logger, package_dict.get("supplier"), - self.actor_parser.parse_actor_or_no_assertion, - True) + self.actor_parser.parse_actor_or_no_assertion) valid_until_date: Optional[datetime] = parse_field_or_log_error(logger, package_dict.get("validUntilDate"), - datetime_from_str, True) + datetime_from_str) version_info: Optional[str] = package_dict.get("versionInfo") raise_parsing_error_if_logger_has_messages(logger, f"Package {name}") diff --git a/src/parser/json/relationship_parser.py b/src/parser/json/relationship_parser.py index d8ee9e69c..e33a6bd5b 100644 --- a/src/parser/json/relationship_parser.py +++ b/src/parser/json/relationship_parser.py @@ -28,24 +28,23 @@ def __init__(self): def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]: relationships = [] relationship_dicts: List[Dict] = input_doc_dict.get("relationships", []) - if relationship_dicts: - relationships.extend( - parse_field_or_log_error(self.logger, relationship_dicts, self.parse_relationships, default=[])) + relationships.extend( + parse_field_or_log_error(self.logger, relationship_dicts, self.parse_relationships, [])) document_describes: List[str] = input_doc_dict.get("documentDescribes", []) doc_spdx_id: Optional[str] = input_doc_dict.get("SPDXID") - relationships.extend(parse_field_or_log_error(self.logger, document_describes, - lambda x: self.parse_document_describes( - doc_spdx_id=doc_spdx_id, described_spdx_ids=x, - existing_relationships=relationships), default=[])) + relationships.extend( + parse_field_or_log_error(self.logger, document_describes, lambda x: self.parse_document_describes( + doc_spdx_id=doc_spdx_id, described_spdx_ids=x, + existing_relationships=relationships), [])) package_dicts: List[Dict] = input_doc_dict.get("packages", []) - relationships.extend(parse_field_or_log_error(self.logger, package_dicts, - lambda x: self.parse_has_files(package_dicts=x, - existing_relationships=relationships), - default=[])) + relationships.extend( + parse_field_or_log_error(self.logger, package_dicts, lambda x: self.parse_has_files(package_dicts=x, + existing_relationships=relationships), + [])) file_dicts: List[Dict] = input_doc_dict.get("files", []) diff --git a/src/parser/json/snippet_parser.py b/src/parser/json/snippet_parser.py index 06b7ad2c4..4123451ee 100644 --- a/src/parser/json/snippet_parser.py +++ b/src/parser/json/snippet_parser.py @@ -59,11 +59,11 @@ def parse_snippet(self, snippet_dict: Dict) -> Snippet: license_comment: Optional[str] = snippet_dict.get("licenseComments") concluded_license: Optional[Union[ LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger, snippet_dict.get( - "licenseConcluded"), self.license_expression_parser.parse_license_expression, True) + "licenseConcluded"), self.license_expression_parser.parse_license_expression) license_info: Optional[Union[List[ LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger, snippet_dict.get( - "licenseInfoInSnippets"), self.license_expression_parser.parse_license_expression, True) + "licenseInfoInSnippets"), self.license_expression_parser.parse_license_expression) if logger.has_messages(): raise SPDXParsingError([f"Error while parsing snippet: {logger.get_messages()}"]) diff --git a/tests/parser/test_annotation_parser.py b/tests/parser/test_annotation_parser.py index ee927b397..1165121c2 100644 --- a/tests/parser/test_annotation_parser.py +++ b/tests/parser/test_annotation_parser.py @@ -114,5 +114,11 @@ def test_parse_incomplete_annotation(): with pytest.raises(SPDXParsingError) as err: _ = annotation_parser.parse_annotation(annotation_dict) - assert err.value.messages == ["Error while parsing Annotation: ['Invalid annotation type: None', 'Could not " - "convert str to datetime, invalid type: NoneType']"] + assert err.value.messages == ["Error while constructing Annotation: ['SetterError Annotation: type of " + 'argument "spdx_id" must be str; got NoneType instead: None\', \'SetterError ' + 'Annotation: type of argument "annotation_type" must be ' + "src.model.annotation.AnnotationType; got NoneType instead: None', " + '\'SetterError Annotation: type of argument "annotation_date" must be ' + "datetime.datetime; got NoneType instead: None', 'SetterError Annotation: " + 'type of argument "annotation_comment" must be str; got NoneType instead: ' + "None']"] diff --git a/tests/parser/test_relationship_parser.py b/tests/parser/test_relationship_parser.py index c8cfa5509..ca721af71 100644 --- a/tests/parser/test_relationship_parser.py +++ b/tests/parser/test_relationship_parser.py @@ -44,8 +44,11 @@ def test_parse_incomplete_relationship(): with pytest.raises(SPDXParsingError) as err: _ = relationship_parser.parse_relationship(relationship_dict) - assert err.value.messages == ["Error while parsing relationship: ['RelationshipType must be str, not " - "NoneType.']"] + assert err.value.messages == ["Error while constructing Relationship: ['SetterError Relationship: type of " + 'argument "relationship_type" must be ' + "src.model.relationship.RelationshipType; got NoneType instead: None']"] + + def test_parse_relationship_type(): relationship_parser = RelationshipParser() relationship_type_str = "DEPENDENCY_OF" @@ -53,6 +56,7 @@ def test_parse_relationship_type(): relationship_type = relationship_parser.parse_relationship_type(relationship_type_str) assert relationship_type == RelationshipType.DEPENDENCY_OF + def test_creating_describes_relationship(): relationship_parser = RelationshipParser() From 9c21537745b9696f92356df9ecf93ee386025dda Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 28 Dec 2022 15:07:01 +0100 Subject: [PATCH 053/362] [issue-305, review] make error messages consistent, add test for json_str_to_enum_name Signed-off-by: Meret Behrens --- src/parser/json/annotation_parser.py | 8 ++++---- src/parser/json/checksum_parser.py | 2 +- src/parser/json/creation_info_parser.py | 2 +- src/parser/json/dict_parsing_functions.py | 2 ++ src/parser/json/file_parser.py | 6 +++--- src/parser/json/package_parser.py | 8 ++++---- src/parser/json/relationship_parser.py | 10 ++++------ tests/parser/test_checksum_parser.py | 8 +++++--- tests/parser/test_dict_parsing_functions.py | 19 +++++++++++++++++-- tests/parser/test_file_parser.py | 6 +++--- tests/parser/test_package_parser.py | 6 +++--- 11 files changed, 47 insertions(+), 30 deletions(-) diff --git a/src/parser/json/annotation_parser.py b/src/parser/json/annotation_parser.py index 9e5a0e36b..5e09bab4a 100644 --- a/src/parser/json/annotation_parser.py +++ b/src/parser/json/annotation_parser.py @@ -45,7 +45,7 @@ def parse_all_annotations(self, input_doc_dict: Dict) -> List[Annotation]: snippets: List[Dict] = input_doc_dict.get("snippets", []) self.parse_annotations_from_object(annotations, snippets) - raise_parsing_error_if_logger_has_messages(self.logger, "Annotations") + raise_parsing_error_if_logger_has_messages(self.logger, "annotations") return annotations def parse_annotations_from_object(self, annotations: List[Annotation], element_list: List[Dict]): @@ -62,7 +62,7 @@ def parse_annotations(self, annotation_dicts: List[Dict], spdx_id: Optional[str] for annotation_dict in annotation_dicts: annotations = append_parsed_field_or_log_error(self.logger, annotations, annotation_dict, lambda x: self.parse_annotation(x, spdx_id=spdx_id)) - raise_parsing_error_if_logger_has_messages(logger, "Annotations") + raise_parsing_error_if_logger_has_messages(logger, "annotations") return annotations @@ -94,7 +94,7 @@ def parse_annotation_type(annotation_type: str) -> AnnotationType: try: return AnnotationType[annotation_type] except KeyError: - raise SPDXParsingError([f"Invalid annotation type: {annotation_type}"]) + raise SPDXParsingError([f"Invalid AnnotationType: {annotation_type}"]) def parse_review(self, review_dict: Dict, spdx_id: str) -> Annotation: logger = Logger() @@ -106,7 +106,7 @@ def parse_review(self, review_dict: Dict, spdx_id: str) -> Annotation: annotation_type = AnnotationType.REVIEW comment: Optional[str] = review_dict.get("comment") - raise_parsing_error_if_logger_has_messages(logger, "Review") + raise_parsing_error_if_logger_has_messages(logger, "Annotation from revieweds") annotation = construct_or_raise_parsing_error(Annotation, dict(spdx_id=spdx_id, annotation_type=annotation_type, diff --git a/src/parser/json/checksum_parser.py b/src/parser/json/checksum_parser.py index 7c7bc2dbd..ec5e2408c 100644 --- a/src/parser/json/checksum_parser.py +++ b/src/parser/json/checksum_parser.py @@ -39,7 +39,7 @@ def parse_checksum(checksum_dict: Dict) -> Checksum: try: checksum_algorithm = ChecksumAlgorithm[algorithm] except KeyError: - logger.append(f"Algorithm {algorithm} not valid for checksum.") + logger.append(f"Invalid Algorithm for checksum: {algorithm}") checksum_algorithm = None checksum_value: Optional[str] = checksum_dict.get("checksumValue") raise_parsing_error_if_logger_has_messages(logger, "Checksum") diff --git a/src/parser/json/creation_info_parser.py b/src/parser/json/creation_info_parser.py index 9d0675b91..ef9304144 100644 --- a/src/parser/json/creation_info_parser.py +++ b/src/parser/json/creation_info_parser.py @@ -62,7 +62,7 @@ def parse_creation_info(self, doc_dict: Dict) -> CreationInfo: creation_info_dict.get("licenseListVersion"), self.parse_version) document_comment: Optional[str] = doc_dict.get("comment") - raise_parsing_error_if_logger_has_messages(logger, f"Document {name}") + raise_parsing_error_if_logger_has_messages(logger, "Document") creation_info = construct_or_raise_parsing_error(CreationInfo, dict(spdx_version=spdx_version, spdx_id=spdx_id, name=name, diff --git a/src/parser/json/dict_parsing_functions.py b/src/parser/json/dict_parsing_functions.py index 191a4b517..93c34c907 100644 --- a/src/parser/json/dict_parsing_functions.py +++ b/src/parser/json/dict_parsing_functions.py @@ -28,6 +28,8 @@ def datetime_from_str(date_str: str) -> datetime: def json_str_to_enum_name(json_str: str) -> str: + if not isinstance(json_str, str): + raise SPDXParsingError([f"Type for enum must be str not {type(json_str).__name__}"]) return json_str.replace("-", "_").upper() diff --git a/src/parser/json/file_parser.py b/src/parser/json/file_parser.py index 85cc856eb..3982ddc5d 100644 --- a/src/parser/json/file_parser.py +++ b/src/parser/json/file_parser.py @@ -61,7 +61,7 @@ def parse_file(self, file_dict: Dict) -> Optional[File]: license_info_in_files: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( logger, file_dict.get("licenseInfoInFiles"), self.license_expression_parser.parse_license_expression) notice_text: Optional[str] = file_dict.get("noticeText") - raise_parsing_error_if_logger_has_messages(logger, f"file {name}") + raise_parsing_error_if_logger_has_messages(logger, "File") file = construct_or_raise_parsing_error(File, dict(name=name, spdx_id=spdx_id, checksums=checksums, attribution_texts=attribution_texts, comment=comment, @@ -82,8 +82,8 @@ def parse_file_types(file_types_list: List[str]) -> List[FileType]: try: file_type = FileType[file_type] except KeyError: - logger.append(f"FileType {file_type} is not valid.") + logger.append(f"Invalid FileType: {file_type}") continue file_types.append(file_type) - raise_parsing_error_if_logger_has_messages(logger, "file_types") + raise_parsing_error_if_logger_has_messages(logger, "FileType") return file_types diff --git a/src/parser/json/package_parser.py b/src/parser/json/package_parser.py index 10aea5adb..b528f9b32 100644 --- a/src/parser/json/package_parser.py +++ b/src/parser/json/package_parser.py @@ -105,7 +105,7 @@ def parse_package(self, package_dict: Dict) -> Package: datetime_from_str) version_info: Optional[str] = package_dict.get("versionInfo") - raise_parsing_error_if_logger_has_messages(logger, f"Package {name}") + raise_parsing_error_if_logger_has_messages(logger, "Package") package = construct_or_raise_parsing_error(Package, dict(spdx_id=spdx_id, name=name, download_location=download_location, @@ -142,7 +142,7 @@ def parse_external_ref(self, external_ref_dict: Dict) -> ExternalPackageRef: ref_locator: Optional[str] = external_ref_dict.get("referenceLocator") ref_type: Optional[str] = external_ref_dict.get("referenceType") comment: Optional[str] = external_ref_dict.get("comment") - raise_parsing_error_if_logger_has_messages(logger, "external ref") + raise_parsing_error_if_logger_has_messages(logger, "ExternalPackageRef") external_ref = construct_or_raise_parsing_error(ExternalPackageRef, dict(category=ref_category, reference_type=ref_type, locator=ref_locator, comment=comment)) @@ -155,7 +155,7 @@ def parse_external_ref_category(external_ref_category_str: str) -> ExternalPacka external_ref_category = ExternalPackageRefCategory[ json_str_to_enum_name(external_ref_category_str)] except KeyError: - raise SPDXParsingError([f"Category {external_ref_category_str} not valid for externalPackageRef."]) + raise SPDXParsingError([f"Invalid Category for ExternalPackageRef {external_ref_category_str}"]) return external_ref_category @@ -175,7 +175,7 @@ def parse_primary_package_purpose(primary_package_purpose: str) -> PackagePurpos try: return PackagePurpose[json_str_to_enum_name(primary_package_purpose)] except KeyError: - raise SPDXParsingError([f"Invalid primaryPackagePurpose: {primary_package_purpose}"]) + raise SPDXParsingError([f"Invalid PrimaryPackagePurpose: {primary_package_purpose}"]) @staticmethod def parse_download_location(download_location: str) -> Union[str, SpdxNoAssertion, SpdxNone]: diff --git a/src/parser/json/relationship_parser.py b/src/parser/json/relationship_parser.py index e33a6bd5b..c19c41114 100644 --- a/src/parser/json/relationship_parser.py +++ b/src/parser/json/relationship_parser.py @@ -72,7 +72,7 @@ def parse_relationship(self, relationship_dict: Dict) -> Relationship: relationship_type: Optional[RelationshipType] = parse_field_or_log_error(logger, relationship_dict.get( "relationshipType"), self.parse_relationship_type) relationship_comment: Optional[str] = relationship_dict.get("comment") - raise_parsing_error_if_logger_has_messages(logger, "relationship") + raise_parsing_error_if_logger_has_messages(logger, "Relationship") relationship = construct_or_raise_parsing_error(Relationship, dict(spdx_element_id=spdx_element_id, relationship_type=relationship_type, @@ -85,9 +85,7 @@ def parse_relationship_type(relationship_type_str: str) -> RelationshipType: try: relationship_type = RelationshipType[json_str_to_enum_name(relationship_type_str)] except KeyError: - raise SPDXParsingError([f"RelationshipType {relationship_type_str} is not valid."]) - except AttributeError: - raise SPDXParsingError([f"RelationshipType must be str, not {type(relationship_type_str).__name__}."]) + raise SPDXParsingError([f"Invalid RelationshipType: {relationship_type_str}"]) return relationship_type def parse_document_describes(self, doc_spdx_id: str, described_spdx_ids: List[str], @@ -104,7 +102,7 @@ def parse_document_describes(self, doc_spdx_id: str, described_spdx_ids: List[st continue if not self.check_if_relationship_exists(describes_relationship, existing_relationships): describes_relationships.append(describes_relationship) - raise_parsing_error_if_logger_has_messages(logger, "describes_relationship") + raise_parsing_error_if_logger_has_messages(logger, "document describes relationships") return describes_relationships @@ -128,7 +126,7 @@ def parse_has_files(self, package_dicts: List[Dict], existing_relationships: Lis if not self.check_if_relationship_exists(relationship=contains_relationship, existing_relationships=existing_relationships): contains_relationships.append(contains_relationship) - raise_parsing_error_if_logger_has_messages(logger, "describes_relationship") + raise_parsing_error_if_logger_has_messages(logger, "package contains relationships") return contains_relationships diff --git a/tests/parser/test_checksum_parser.py b/tests/parser/test_checksum_parser.py index 8fe11215c..12044f0b5 100644 --- a/tests/parser/test_checksum_parser.py +++ b/tests/parser/test_checksum_parser.py @@ -38,15 +38,17 @@ def test_invalid_checksum(): with pytest.raises(SPDXParsingError) as err: _ = checksum_parser.parse_checksum(checksum_dict) - assert err.value.messages[0] == "Error while parsing Checksum: ['Algorithm SHA not valid for checksum.']" + assert err.value.messages[0] == "Error while parsing Checksum: ['Invalid Algorithm for checksum: SHA']" + def test_incomplete_checksum(): checksum_parser = ChecksumParser() - checksum_dict= { + checksum_dict = { "algorithm": "SHA1" } with pytest.raises(SPDXParsingError) as err: _ = checksum_parser.parse_checksum(checksum_dict) - assert err.value.messages == ["Error while constructing Checksum: ['SetterError Checksum: type of argument \"value\" must be str; got NoneType instead: None']"] + assert err.value.messages == [ + "Error while constructing Checksum: ['SetterError Checksum: type of argument \"value\" must be str; got NoneType instead: None']"] diff --git a/tests/parser/test_dict_parsing_functions.py b/tests/parser/test_dict_parsing_functions.py index bcbd606bf..f81c6942f 100644 --- a/tests/parser/test_dict_parsing_functions.py +++ b/tests/parser/test_dict_parsing_functions.py @@ -13,7 +13,7 @@ import pytest from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import datetime_from_str +from src.parser.json.dict_parsing_functions import datetime_from_str, json_str_to_enum_name def test_datetime_from_str(): @@ -25,7 +25,7 @@ def test_datetime_from_str(): @pytest.mark.parametrize("invalid_date_str,expected_message", - [(5, ['Could not convert str to datetime, invalid type: int']), + [(5, ["Could not convert str to datetime, invalid type: int"]), ("2010-02-03", ['Could not convert str to datetime, format of 2010-02-03 does not match ' '"%Y-%m-%dT%H:%M:%SZ"'])]) def test_datetime_from_str_error(invalid_date_str, expected_message): @@ -33,3 +33,18 @@ def test_datetime_from_str_error(invalid_date_str, expected_message): _ = datetime_from_str(invalid_date_str) assert err.value.messages == expected_message + +def test_json_str_to_enum(): + json_str = "BLAKE2b-256" + + enum_name = json_str_to_enum_name(json_str) + + assert enum_name == "BLAKE2B_256" + +@pytest.mark.parametrize("invalid_json_str,expected_message", + [(5, ["Type for enum must be str not int"])]) +def test_invalid_json_str_to_enum(invalid_json_str,expected_message): + with pytest.raises(SPDXParsingError) as err: + _ = json_str_to_enum_name(invalid_json_str) + + assert err.value.messages == expected_message diff --git a/tests/parser/test_file_parser.py b/tests/parser/test_file_parser.py index 450a204f7..3a6327213 100644 --- a/tests/parser/test_file_parser.py +++ b/tests/parser/test_file_parser.py @@ -103,8 +103,8 @@ def test_parse_falsy_files(): '"checksums" must be a list; got NoneType instead: None\']', 'Error while constructing File: [\'SetterError File: type of argument "name" ' "must be str; got NoneType instead: None']", - 'Error while parsing file None: ["Error while parsing Checksum: [\'Algorithm ' - 'MD not valid for checksum.\']"]'] + 'Error while parsing File: ["Error while parsing Checksum: [\'Invalid Algorithm ' + 'for checksum: MD\']"]'] def test_parse_file_types(): @@ -123,4 +123,4 @@ def test_parse_invalid_file_types(): with pytest.raises(SPDXParsingError) as err: _ = file_parser.parse_file_types(file_types_list) - assert err.value.messages == ["Error while parsing file_types: ['FileType APPLICAON is not valid.']"] + assert err.value.messages == ["Error while parsing FileType: ['Invalid FileType: APPLICAON']"] diff --git a/tests/parser/test_package_parser.py b/tests/parser/test_package_parser.py index 707f19c72..7feca0e03 100644 --- a/tests/parser/test_package_parser.py +++ b/tests/parser/test_package_parser.py @@ -168,7 +168,7 @@ def test_package_with_falsy_values(): _ = package_parser.parse_package(package_dict) assert err.value.get_messages() == [ - 'Error while parsing Package Example Package: ["Error while parsing Checksum: [\'Algorithm SHA not valid for checksum.\']"]'] + 'Error while parsing Package: ["Error while parsing Checksum: [\'Invalid Algorithm for checksum: SHA\']"]'] def test_parse_packages(): @@ -194,7 +194,7 @@ def test_parse_packages(): with pytest.raises(SPDXParsingError) as err: _ = package_parser.parse_packages(packages_list) - assert err.value.messages == ['Error while parsing Package Example Package: ["Error while parsing Checksum: ' - '[\'Algorithm SHA not valid for checksum.\']"]', + assert err.value.messages == ['Error while parsing Package: ["Error while parsing Checksum: ' + '[\'Invalid Algorithm for checksum: SHA\']"]', "Error while constructing Package: ['SetterError Package: type of argument " '"name" must be str; got int instead: 5\']'] From 9bc0464b651cb153978666f34fafff0b7c597390 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 22 Dec 2022 09:13:26 +0100 Subject: [PATCH 054/362] [issue-305, review] add tests, change test data, naming of tests and use assertCountEqual to compare lists Signed-off-by: Meret Behrens --- tests/parser/test_actor_parser.py | 17 ++-- tests/parser/test_annotation_parser.py | 88 ++++++++--------- tests/parser/test_checksum_parser.py | 19 ++-- tests/parser/test_creation_info_parser.py | 21 ++-- tests/parser/test_dict_parsing_functions.py | 9 +- .../test_extracted_licensing_info_parser.py | 11 ++- tests/parser/test_file_parser.py | 44 +++++---- tests/parser/test_json_parser.py | 12 +-- .../parser/test_license_expression_parser.py | 21 ++-- tests/parser/test_package_parser.py | 99 ++++++++++--------- tests/parser/test_relationship_parser.py | 35 +++---- tests/parser/test_snippet_parser.py | 31 +++--- 12 files changed, 206 insertions(+), 201 deletions(-) diff --git a/tests/parser/test_actor_parser.py b/tests/parser/test_actor_parser.py index 0758ddc85..d73376c9d 100644 --- a/tests/parser/test_actor_parser.py +++ b/tests/parser/test_actor_parser.py @@ -9,6 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import pytest +from unittest import TestCase from src.model.actor import ActorType from src.parser.error import SPDXParsingError @@ -17,11 +18,11 @@ @pytest.mark.parametrize("actor_string,expected_type,expected_name,expected_mail", [ ("Person: Jane Doe (jane.doe@example.com)", ActorType.PERSON, "Jane Doe", "jane.doe@example.com"), - ("Organization: Example organization (organization@exaple.com)", ActorType.ORGANIZATION, "Example organization", - "organization@exaple.com"), + ("Organization: Example organization (organization@example.com)", ActorType.ORGANIZATION, "Example organization", + "organization@example.com"), ("Organization: Example organization ( )", ActorType.ORGANIZATION, "Example organization", None), ("Tool: Example tool ", ActorType.TOOL, "Example tool", None)]) -def test_actor_parser(actor_string, expected_type, expected_name, expected_mail): +def test_parse_actor(actor_string, expected_type, expected_name, expected_mail): actor_parser = ActorParser() actor = actor_parser.parse_actor(actor_string) @@ -33,15 +34,15 @@ def test_actor_parser(actor_string, expected_type, expected_name, expected_mail) @pytest.mark.parametrize("actor_string,expected_message", [ ("Perso: Jane Doe (jane.doe@example.com)", - "Actor Perso: Jane Doe (jane.doe@example.com) doesn't match any of person, organization or tool."), + ["Actor Perso: Jane Doe (jane.doe@example.com) doesn't match any of person, organization or tool."]), ("Toole Example Tool ()", - "Actor Toole Example Tool () doesn't match any of person, organization or tool.") + ["Actor Toole Example Tool () doesn't match any of person, organization or tool."]) ]) -def test_invalid_actor(actor_string, expected_message): +def test_parse_invalid_actor(actor_string, expected_message): actor_parser = ActorParser() actor_string = actor_string with pytest.raises(SPDXParsingError) as err: - _ = actor_parser.parse_actor(actor_string) + actor_parser.parse_actor(actor_string) - assert err.value.messages[0] == expected_message + TestCase().assertCountEqual(err.value.get_messages(), expected_message) diff --git a/tests/parser/test_annotation_parser.py b/tests/parser/test_annotation_parser.py index 1165121c2..0330b5b0b 100644 --- a/tests/parser/test_annotation_parser.py +++ b/tests/parser/test_annotation_parser.py @@ -9,6 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import datetime +from unittest import TestCase import pytest @@ -18,7 +19,7 @@ from src.parser.json.annotation_parser import AnnotationParser -def test_annotation_parser(): +def test_parse_annotation(): annotation_parser = AnnotationParser() annotation_dict = { "annotationDate": "2010-01-29T18:30:22Z", @@ -33,6 +34,7 @@ def test_annotation_parser(): assert annotation.annotation_type == AnnotationType.OTHER assert annotation.annotation_date == datetime.datetime(2010, 1, 29, 18, 30, 22) assert annotation.annotation_comment == "Document level annotation" + assert annotation.spdx_id == "SPDXRef-DOCUMENT" def test_parse_all_annotations(): @@ -42,9 +44,9 @@ def test_parse_all_annotations(): "packages": [ {"SPDXID": "SPDXRef-Package", "annotations": [ - {"annotationDate": "2010-01-29T18:30:22Z", - "annotationType": "OTHER", - "annotator": "Person: Jane Doe ()", + {"annotationDate": "2010-01-29T17:30:22Z", + "annotationType": "REVIEW", + "annotator": "Person: Mick Doe ()", "comment": "Package level annotation"} ]}], "files": [ @@ -59,9 +61,9 @@ def test_parse_all_annotations(): "snippets": [ {"SPDXID": "SPDXRef-Snippet", "annotations": [ - {"annotationDate": "2010-01-29T18:30:22Z", - "annotationType": "OTHER", - "annotator": "Person: Jane Doe ()", + {"annotationDate": "2022-01-29T18:30:32Z", + "annotationType": "REVIEW", + "annotator": "Person: Jonas Rie (jonas@example.com)", "comment": "Snippet level annotation"} ]}], "revieweds": @@ -75,50 +77,40 @@ def test_parse_all_annotations(): annotations = annotation_parser.parse_all_annotations(input_doc_dict=doc_dict) assert len(annotations) == 4 - assert annotations == [Annotation(spdx_id='SPDXRef-DOCUMENT', - annotation_type=AnnotationType.REVIEW, - annotator=Actor(actor_type=ActorType.PERSON, - name='Jane Doe', - email=None), - annotation_date=datetime.datetime(2010, 1, 29, 18, 30, 22), - annotation_comment='Review annotation'), - Annotation(spdx_id='SPDXRef-Package', - annotation_type=AnnotationType.OTHER, - annotator=Actor(actor_type=ActorType.PERSON, - name='Jane Doe', - email=None), - annotation_date=datetime.datetime(2010, 1, 29, 18, 30, 22), - annotation_comment='Package level annotation'), - Annotation(spdx_id='SPDXRef-File', - annotation_type=AnnotationType.OTHER, - annotator=Actor(actor_type=ActorType.PERSON, - name='Jane Doe', - email=None), - annotation_date=datetime.datetime(2010, 1, 29, 18, 30, 22), - annotation_comment='File level annotation'), - Annotation(spdx_id='SPDXRef-Snippet', - annotation_type=AnnotationType.OTHER, - annotator=Actor(actor_type=ActorType.PERSON, - name='Jane Doe', - email=None), - annotation_date=datetime.datetime(2010, 1, 29, 18, 30, 22), - annotation_comment='Snippet level annotation')] + test_case = TestCase() + test_case.maxDiff = None + test_case.assertCountEqual(annotations, [Annotation(spdx_id="SPDXRef-DOCUMENT", + annotation_type=AnnotationType.REVIEW, + annotator=Actor(actor_type=ActorType.PERSON, name="Jane Doe", + email=None), + annotation_date=datetime.datetime(2010, 1, 29, 18, 30, 22), + annotation_comment="Review annotation"), + Annotation(spdx_id="SPDXRef-Package", + annotation_type=AnnotationType.REVIEW, + annotator=Actor(actor_type=ActorType.PERSON, name="Mick Doe", + email=None), + annotation_date=datetime.datetime(2010, 1, 29, 17, 30, 22), + annotation_comment="Package level annotation"), + Annotation(spdx_id="SPDXRef-File", annotation_type=AnnotationType.OTHER, + annotator=Actor(actor_type=ActorType.PERSON, name="Jane Doe", + email=None), + annotation_date=datetime.datetime(2010, 1, 29, 18, 30, 22), + annotation_comment="File level annotation"), + Annotation(spdx_id="SPDXRef-Snippet", + annotation_type=AnnotationType.REVIEW, + annotator=Actor(actor_type=ActorType.PERSON, name="Jonas Rie", + email="jonas@example.com"), + annotation_date=datetime.datetime(2022, 1, 29, 18, 30, 32), + annotation_comment="Snippet level annotation")]) -def test_parse_incomplete_annotation(): +@pytest.mark.parametrize("incomplete_annotation_dict,expected_message", [({"annotator": "Person: Jane Doe ()"}, [ + "Error while constructing Annotation: ['SetterError Annotation: type of " 'argument "spdx_id" must be str; got NoneType instead: None\', \'SetterError Annotation: type of argument "annotation_type" must be ' "src.model.annotation.AnnotationType; got NoneType instead: None', " '\'SetterError Annotation: type of argument "annotation_date" must be ' "datetime.datetime; got NoneType instead: None', 'SetterError Annotation: " 'type of argument "annotation_comment" must be str; got NoneType instead: ' "None']"]), + ({"annotationDate": "2010-01-29T18:30:22Z"}, ["Error while constructing Annotation: ['SetterError Annotation: type of " 'argument "spdx_id" must be str; got NoneType instead: None\', \'SetterError Annotation: type of argument "annotation_type" must be ' "src.model.annotation.AnnotationType; got NoneType instead: None', " '\'SetterError Annotation: type of argument "annotator" must be ' "src.model.actor.Actor; got NoneType instead: None', 'SetterError Annotation: " 'type of argument "annotation_comment" must be str; got NoneType instead: ' "None']"])]) +def test_parse_incomplete_annotation(incomplete_annotation_dict, expected_message): annotation_parser = AnnotationParser() - annotation_dict = { - "annotator": "Person: Jane Doe ()" - } with pytest.raises(SPDXParsingError) as err: - _ = annotation_parser.parse_annotation(annotation_dict) + annotation_parser.parse_annotation(incomplete_annotation_dict) - assert err.value.messages == ["Error while constructing Annotation: ['SetterError Annotation: type of " - 'argument "spdx_id" must be str; got NoneType instead: None\', \'SetterError ' - 'Annotation: type of argument "annotation_type" must be ' - "src.model.annotation.AnnotationType; got NoneType instead: None', " - '\'SetterError Annotation: type of argument "annotation_date" must be ' - "datetime.datetime; got NoneType instead: None', 'SetterError Annotation: " - 'type of argument "annotation_comment" must be str; got NoneType instead: ' - "None']"] + TestCase().assertCountEqual(err.value.get_messages(), expected_message) diff --git a/tests/parser/test_checksum_parser.py b/tests/parser/test_checksum_parser.py index 12044f0b5..cd27a8bed 100644 --- a/tests/parser/test_checksum_parser.py +++ b/tests/parser/test_checksum_parser.py @@ -8,6 +8,8 @@ # 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.model.checksum import ChecksumAlgorithm @@ -15,7 +17,7 @@ from src.parser.json.checksum_parser import ChecksumParser -def test_checksum_parser(): +def test_parse_checksum(): checksum_parser = ChecksumParser() checksum_dict = { "algorithm": "SHA1", @@ -28,7 +30,7 @@ def test_checksum_parser(): assert checksum.algorithm == ChecksumAlgorithm.SHA1 -def test_invalid_checksum(): +def test_parse_invalid_checksum(): checksum_parser = ChecksumParser() checksum_dict = { "algorithm": "SHA", @@ -36,19 +38,20 @@ def test_invalid_checksum(): } with pytest.raises(SPDXParsingError) as err: - _ = checksum_parser.parse_checksum(checksum_dict) + checksum_parser.parse_checksum(checksum_dict) - assert err.value.messages[0] == "Error while parsing Checksum: ['Invalid Algorithm for checksum: SHA']" + TestCase().assertCountEqual(err.value.get_messages(), + ["Error while parsing Checksum: ['Invalid Algorithm for checksum: SHA']"]) -def test_incomplete_checksum(): +def test_parse_incomplete_checksum(): checksum_parser = ChecksumParser() checksum_dict = { "algorithm": "SHA1" } with pytest.raises(SPDXParsingError) as err: - _ = checksum_parser.parse_checksum(checksum_dict) + checksum_parser.parse_checksum(checksum_dict) - assert err.value.messages == [ - "Error while constructing Checksum: ['SetterError Checksum: type of argument \"value\" must be str; got NoneType instead: None']"] + TestCase().assertCountEqual(err.value.get_messages(), [ + "Error while constructing Checksum: ['SetterError Checksum: type of argument \"value\" must be str; got NoneType instead: None']"]) diff --git a/tests/parser/test_creation_info_parser.py b/tests/parser/test_creation_info_parser.py index d33579379..daa2785bb 100644 --- a/tests/parser/test_creation_info_parser.py +++ b/tests/parser/test_creation_info_parser.py @@ -9,6 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from datetime import datetime +from unittest import TestCase import pytest @@ -20,7 +21,7 @@ from src.parser.json.creation_info_parser import CreationInfoParser -def test_creation_info_parser(): +def test_pares_creation_info(): creation_info_parser = CreationInfoParser() doc_dict = { "spdxVersion": "2.3", @@ -50,9 +51,9 @@ def test_creation_info_parser(): assert creation_info.name == "Example Document" assert creation_info.document_namespace == "namespace" assert creation_info.created == datetime(2010, 1, 29, 18, 30, 22) - assert creation_info.creators == [Actor(ActorType.TOOL, "LicenseFind-1.0"), - Actor(ActorType.ORGANIZATION, "ExampleCodeInspect"), - Actor(ActorType.PERSON, "Jane Doe")] + TestCase().assertCountEqual(creation_info.creators, [Actor(ActorType.TOOL, "LicenseFind-1.0"), + Actor(ActorType.ORGANIZATION, "ExampleCodeInspect"), + Actor(ActorType.PERSON, "Jane Doe")]) assert creation_info.license_list_version == Version(3, 7) assert creation_info.external_document_refs == [ExternalDocumentRef(document_ref_id="DocumentRef-spdx-tool-1.2", checksum=Checksum( @@ -79,9 +80,9 @@ def test_parse_incomplete_document_info(incomplete_dict, expected_message): creation_info_parser = CreationInfoParser() with pytest.raises(SPDXParsingError) as err: - _ = creation_info_parser.parse_creation_info(incomplete_dict) + creation_info_parser.parse_creation_info(incomplete_dict) - assert err.value.messages == expected_message + TestCase().assertCountEqual(err.value.get_messages(), expected_message) def test_parse_invalid_creation_info(): @@ -98,9 +99,7 @@ def test_parse_invalid_creation_info(): } with pytest.raises(SPDXParsingError) as err: - _ = creation_info_parser.parse_creation_info(doc_dict) + creation_info_parser.parse_creation_info(doc_dict) - assert err.value.messages == ["Error while constructing CreationInfo: ['SetterError CreationInfo: type of " - 'argument "document_namespace" must be str; got NoneType instead: None\', ' - '\'SetterError CreationInfo: type of argument "data_license" must be str; got ' - "NoneType instead: None']"] + TestCase().assertCountEqual(err.value.get_messages(), [ + "Error while constructing CreationInfo: ['SetterError CreationInfo: type of " 'argument "document_namespace" must be str; got NoneType instead: None\', \'SetterError CreationInfo: type of argument "data_license" must be str; got ' "NoneType instead: None']"]) diff --git a/tests/parser/test_dict_parsing_functions.py b/tests/parser/test_dict_parsing_functions.py index f81c6942f..4f1c91162 100644 --- a/tests/parser/test_dict_parsing_functions.py +++ b/tests/parser/test_dict_parsing_functions.py @@ -9,6 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from datetime import datetime +from unittest import TestCase import pytest @@ -30,9 +31,9 @@ def test_datetime_from_str(): '"%Y-%m-%dT%H:%M:%SZ"'])]) def test_datetime_from_str_error(invalid_date_str, expected_message): with pytest.raises(SPDXParsingError) as err: - _ = datetime_from_str(invalid_date_str) + datetime_from_str(invalid_date_str) - assert err.value.messages == expected_message + TestCase().assertCountEqual(err.value.get_messages(), expected_message) def test_json_str_to_enum(): json_str = "BLAKE2b-256" @@ -45,6 +46,6 @@ def test_json_str_to_enum(): [(5, ["Type for enum must be str not int"])]) def test_invalid_json_str_to_enum(invalid_json_str,expected_message): with pytest.raises(SPDXParsingError) as err: - _ = json_str_to_enum_name(invalid_json_str) + json_str_to_enum_name(invalid_json_str) - assert err.value.messages == expected_message + TestCase().assertCountEqual(err.value.get_messages(), expected_message) diff --git a/tests/parser/test_extracted_licensing_info_parser.py b/tests/parser/test_extracted_licensing_info_parser.py index b5bcb8b42..2d5b4825f 100644 --- a/tests/parser/test_extracted_licensing_info_parser.py +++ b/tests/parser/test_extracted_licensing_info_parser.py @@ -8,6 +8,8 @@ # 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.model.spdx_no_assertion import SpdxNoAssertion @@ -15,7 +17,7 @@ from src.parser.json.extracted_licensing_info_parser import ExtractedLicensingInfoParser -def test_extracted_licensing_info_parser(): +def test_parse_extracted_licensing_info(): extracted_licensing_info_parser = ExtractedLicensingInfoParser() extracted_licensing_infos_dict = { @@ -51,11 +53,10 @@ def test_parse_invalid_extracted_licensing_info(): } with pytest.raises(SPDXParsingError) as err: - _ = extracted_licensing_info_parser.parse_extracted_licensing_info(extracted_licensing_infos_dict) + extracted_licensing_info_parser.parse_extracted_licensing_info(extracted_licensing_infos_dict) - assert err.value.messages == ["Error while constructing ExtractedLicensingInfo: ['SetterError " - 'ExtractedLicensingInfo: type of argument "comment" must be one of (str, ' - "NoneType); got int instead: 56']"] + TestCase().assertCountEqual(err.value.get_messages(), [ + "Error while constructing ExtractedLicensingInfo: ['SetterError " 'ExtractedLicensingInfo: type of argument "comment" must be one of (str, ' "NoneType); got int instead: 56']"]) def test_parse_extracted_licensing_info_name(): diff --git a/tests/parser/test_file_parser.py b/tests/parser/test_file_parser.py index 3a6327213..9735fdf15 100644 --- a/tests/parser/test_file_parser.py +++ b/tests/parser/test_file_parser.py @@ -8,6 +8,8 @@ # 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.model.checksum import Checksum, ChecksumAlgorithm @@ -17,7 +19,7 @@ from src.parser.json.file_parser import FileParser -def test_file_parser(): +def test_parse_file(): file_parser = FileParser() file_dict = { "SPDXID": "SPDXRef-File", @@ -45,15 +47,17 @@ def test_file_parser(): assert file.name == "./package/foo.c" assert file.spdx_id == "SPDXRef-File" - assert file.checksums == [Checksum(ChecksumAlgorithm.SHA1, "d6a770ba38583ed4bb4525bd96e50461655d2758"), - Checksum(ChecksumAlgorithm.MD5, "624c1abb3664f4b35547e7c73864ad24")] + TestCase().assertCountEqual(file.checksums, + [Checksum(ChecksumAlgorithm.SHA1, "d6a770ba38583ed4bb4525bd96e50461655d2758"), + Checksum(ChecksumAlgorithm.MD5, "624c1abb3664f4b35547e7c73864ad24")]) assert file.comment == "The concluded license was taken from the package level that the file was included in.\nThis information was found in the COPYING.txt file in the xyz directory." assert file.copyright_text == "Copyright 2008-2010 John Smith" assert file.file_type == [FileType.SOURCE] - assert file.contributors == ["The Regents of the University of California", - "Modified by Paul Mundt lethal@linux-sh.org", "IBM Corporation"] + TestCase().assertCountEqual(file.contributors, ["The Regents of the University of California", + "Modified by Paul Mundt lethal@linux-sh.org", "IBM Corporation"]) assert file.concluded_license == LicenseExpression("(LGPL-2.0-only OR LicenseRef-2)") - assert file.license_info_in_file == [LicenseExpression("GPL-2.0-only"), LicenseExpression("LicenseRef-2")] + TestCase().assertCountEqual(file.license_info_in_file, + [LicenseExpression("GPL-2.0-only"), LicenseExpression("LicenseRef-2")]) assert file.license_comment == "The concluded license was taken from the package level that the file was included in." assert file.attribution_texts == ["Some attribution text."] @@ -66,13 +70,14 @@ def test_parse_incomplete_file(): } with pytest.raises(SPDXParsingError) as err: - _ = file_parser.parse_file(file_dict) + file_parser.parse_file(file_dict) - assert err.value.messages == ["Error while constructing File: ['SetterError File: type of argument " - '"checksums" must be a list; got NoneType instead: None\']'] + TestCase().assertCountEqual(err.value.get_messages(), + ["Error while constructing File: ['SetterError File: type of argument " + '"checksums" must be a list; got NoneType instead: None\']']) -def test_parse_falsy_files(): +def test_parse_invalid_files(): file_parser = FileParser() files = [{"SPDXID": "SPDXRef-File", "fileName": "Incomplete File"}, @@ -97,14 +102,11 @@ def test_parse_falsy_files(): ] with pytest.raises(SPDXParsingError) as err: - _ = file_parser.parse_files(files) - - assert err.value.messages == ["Error while constructing File: ['SetterError File: type of argument " - '"checksums" must be a list; got NoneType instead: None\']', - 'Error while constructing File: [\'SetterError File: type of argument "name" ' - "must be str; got NoneType instead: None']", - 'Error while parsing File: ["Error while parsing Checksum: [\'Invalid Algorithm ' - 'for checksum: MD\']"]'] + file_parser.parse_files(files) + TestCase().assertCountEqual(err.value.get_messages(), [ + "Error while constructing File: ['SetterError File: type of argument " '"checksums" must be a list; got NoneType instead: None\']', + 'Error while constructing File: [\'SetterError File: type of argument "name" ' "must be str; got NoneType instead: None']", + 'Error while parsing File: ["Error while parsing Checksum: [\'Invalid Algorithm for checksum: MD\']"]']) def test_parse_file_types(): @@ -113,7 +115,7 @@ def test_parse_file_types(): file_types = file_parser.parse_file_types(file_types_list) - assert file_types == [FileType.OTHER, FileType.APPLICATION] + TestCase().assertCountEqual(file_types, [FileType.OTHER, FileType.APPLICATION]) def test_parse_invalid_file_types(): @@ -121,6 +123,6 @@ def test_parse_invalid_file_types(): file_types_list = ["OTHER", "APPLICAON"] with pytest.raises(SPDXParsingError) as err: - _ = file_parser.parse_file_types(file_types_list) + file_parser.parse_file_types(file_types_list) - assert err.value.messages == ["Error while parsing FileType: ['Invalid FileType: APPLICAON']"] + TestCase().assertCountEqual(err.value.get_messages(), ["Error while parsing FileType: ['Invalid FileType: APPLICAON']"]) diff --git a/tests/parser/test_json_parser.py b/tests/parser/test_json_parser.py index e4048fa14..3b6ef3133 100644 --- a/tests/parser/test_json_parser.py +++ b/tests/parser/test_json_parser.py @@ -16,15 +16,15 @@ from src.parser.error import SPDXParsingError from src.parser.json.json_parser import JsonParser -def test_json_parser_file_not_found(): +def test_parse_json_file_not_found(): with pytest.raises(FileNotFoundError) as err: - wrong_file_path = os.path.join(os.path.dirname(__file__), 'test.json') - _ = JsonParser().parse(wrong_file_path) + wrong_file_path = os.path.join(os.path.dirname(__file__), 'hnjfkjsedhnflsiafg.json') + JsonParser().parse(wrong_file_path) assert err.value.args[1] == "No such file or directory" -def test_json_parser_with_2_3_example(): +def test_parse_json_with_2_3_example(): doc = JsonParser().parse(os.path.join(os.path.dirname(__file__),"../data/formats/SPDXJSONExample-v2.3.spdx.json")) assert type(doc) == Document assert len(doc.annotations) == 5 @@ -34,7 +34,7 @@ def test_json_parser_with_2_3_example(): assert len(doc.relationships) == 23 assert len(doc.extracted_licensing_info) == 5 -def test_json_parser_with_2_2_example(): +def test_parse_json_with_2_2_example(): doc = JsonParser().parse(os.path.join(os.path.dirname(__file__),"../data/formats/SPDXJSONExample-v2.2.spdx.json")) assert type(doc) == Document assert len(doc.annotations) == 5 @@ -44,7 +44,7 @@ def test_json_parser_with_2_2_example(): assert len(doc.relationships) == 11 assert len(doc.extracted_licensing_info) == 5 -def test_json_parser_with(): +def test_parse_json_with_2_1_example(): doc = JsonParser().parse(os.path.join(os.path.dirname(__file__),"../data/formats/SPDXJsonExample.json")) assert type(doc) == Document assert len(doc.annotations) == 1 diff --git a/tests/parser/test_license_expression_parser.py b/tests/parser/test_license_expression_parser.py index 883fa56cf..6abac093c 100644 --- a/tests/parser/test_license_expression_parser.py +++ b/tests/parser/test_license_expression_parser.py @@ -8,6 +8,8 @@ # 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.model.license_expression import LicenseExpression @@ -17,7 +19,7 @@ from src.parser.json.license_expression_parser import LicenseExpressionParser -def test_license_expression_parser(): +def test_parse_license_expression(): license_expression_parser = LicenseExpressionParser() license_expression_str = "License-Ref1" @@ -27,7 +29,7 @@ def test_license_expression_parser(): assert license_expression.expression_string == "License-Ref1" -def test_license_expression_no_assert(): +def test_parse_license_expression_no_assert(): license_expression_parser = LicenseExpressionParser() license_expression_str = "NOASSERTION" @@ -37,7 +39,7 @@ def test_license_expression_no_assert(): assert type(spdx_no_assertion) == SpdxNoAssertion -def test_license_expression_none(): +def test_parse_license_expression_none(): license_expression_parser = LicenseExpressionParser() license_expression_str = "NONE" @@ -56,21 +58,22 @@ def test_license_expression_none(): 'type of argument "expression_string" must be str; got int instead: 4\']', "Error while constructing LicenseExpression: ['SetterError LicenseExpression: " 'type of argument "expression_string" must be str; got int instead: 6\']'])]) -def test_invalid_license_expression(invalid_license_expression, expected_message): +def test_parse_invalid_license_expression(invalid_license_expression, expected_message): license_expression_parser = LicenseExpressionParser() with pytest.raises(SPDXParsingError) as err: - _ = license_expression_parser.parse_license_expression(invalid_license_expression) + license_expression_parser.parse_license_expression(invalid_license_expression) - assert err.value.messages == expected_message + TestCase().assertCountEqual(err.value.get_messages(), expected_message) -def test_license_expressions(): +def test_parse_license_expressions(): license_expression_parser = LicenseExpressionParser() license_expressions_list = ["First License", "Second License", "Third License"] license_expressions = license_expression_parser.parse_license_expression(license_expressions_list) assert len(license_expressions) == 3 - assert license_expressions == [LicenseExpression("First License"), LicenseExpression("Second License"), - LicenseExpression("Third License")] + TestCase().assertCountEqual(license_expressions, + [LicenseExpression("First License"), LicenseExpression("Second License"), + LicenseExpression("Third License")]) diff --git a/tests/parser/test_package_parser.py b/tests/parser/test_package_parser.py index 7feca0e03..0577b599e 100644 --- a/tests/parser/test_package_parser.py +++ b/tests/parser/test_package_parser.py @@ -9,6 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from datetime import datetime +from unittest import TestCase import pytest @@ -20,7 +21,7 @@ from src.parser.json.package_parser import PackageParser -def test_package_parser(): +def test_parse_package(): package_parser = PackageParser() package_dict = { @@ -90,17 +91,19 @@ def test_package_parser(): assert package.verification_code == PackageVerificationCode(value="d6a770ba38583ed4bb4525bd96e50461655d2758", excluded_files=["./package.spdx"]) assert len(package.checksums) == 4 - assert package.checksums == [Checksum(ChecksumAlgorithm.MD5, "624c1abb3664f4b35547e7c73864ad24"), - Checksum(ChecksumAlgorithm.SHA1, "85ed0817af83a24ad8da68c2b5094de69833983c"), - Checksum(ChecksumAlgorithm.SHA256, - "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd"), - Checksum(ChecksumAlgorithm.BLAKE2B_384, - "aaabd89c926ab525c242e6621f2f5fa73aa4afe3d9e24aed727faaadd6af38b620bdb623dd2b4788b1c8086984af8706")] + TestCase().assertCountEqual(package.checksums, [Checksum(ChecksumAlgorithm.MD5, "624c1abb3664f4b35547e7c73864ad24"), + Checksum(ChecksumAlgorithm.SHA1, + "85ed0817af83a24ad8da68c2b5094de69833983c"), + Checksum(ChecksumAlgorithm.SHA256, + "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd"), + Checksum(ChecksumAlgorithm.BLAKE2B_384, + "aaabd89c926ab525c242e6621f2f5fa73aa4afe3d9e24aed727faaadd6af38b620bdb623dd2b4788b1c8086984af8706")]) assert package.homepage == "http://ftp.gnu.org/gnu/glibc" assert package.source_info == "uses glibc-2_11-branch from git://sourceware.org/git/glibc.git." assert package.license_concluded == LicenseExpression("(LGPL-2.0-only OR LicenseRef-3)") - assert package.license_info_from_files == [LicenseExpression("GPL-2.0-only"), LicenseExpression("LicenseRef-2"), - LicenseExpression("LicenseRef-1")] + TestCase().assertCountEqual(package.license_info_from_files, + [LicenseExpression("GPL-2.0-only"), LicenseExpression("LicenseRef-2"), + LicenseExpression("LicenseRef-1")]) assert package.license_declared == LicenseExpression("(LGPL-2.0-only AND LicenseRef-3)") assert package.license_comment == "The license for this project changed with the release of version x.y. The version of the project included here post-dates the license change." assert package.copyright_text == "Copyright 2008-2010 John Smith" @@ -108,12 +111,13 @@ def test_package_parser(): assert package.description == "The GNU C Library defines functions that are specified by the ISO C standard, as well as additional features specific to POSIX and other derivatives of the Unix operating system, and extensions specific to GNU systems." assert package.comment == "This is a comment." assert len(package.external_references) == 2 - assert package.external_references == [ExternalPackageRef(ExternalPackageRefCategory.SECURITY, "cpe23Type", - "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*"), - ExternalPackageRef(ExternalPackageRefCategory.OTHER, - "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge", - locator="acmecorp/acmenator/4.1.3-alpha", - comment="This is the external ref for Acme")] + TestCase().assertCountEqual(package.external_references, [ExternalPackageRef(ExternalPackageRefCategory.SECURITY, + "cpe23Type", + "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*"), + ExternalPackageRef(ExternalPackageRefCategory.OTHER, + "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge", + locator="acmecorp/acmenator/4.1.3-alpha", + comment="This is the external ref for Acme")]) assert package.attribution_texts == [ "The GNU C Library is free software. See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed. License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually."] assert package.primary_package_purpose == PackagePurpose.SOURCE @@ -122,38 +126,21 @@ def test_package_parser(): assert package.valid_until_date == datetime(2014, 1, 29, 18, 30, 22) -def test_incomplete_package(): +@pytest.mark.parametrize("incomplete_package_dict,expected_message", [({"SPDXID": "SPDXRef-Package"}, [ + "Error while constructing Package: ['SetterError Package: type of " 'argument "name" must be str; got NoneType instead: None\', \'SetterError Package: type of argument "download_location" must be one of (str, src.model.spdx_no_assertion.SpdxNoAssertion, src.model.spdx_none.SpdxNone); ' "got NoneType instead: None']"]), + ({"SPDXID": "SPDXRef-Package", "name": 5, + "downloadLocation": "NONE"}, [ + "Error while constructing Package: ['SetterError Package: type of argument " '"name" must be str; got int instead: 5\']'])]) +def test_parse_incomplete_package(incomplete_package_dict, expected_message): package_parser = PackageParser() - package_dict = { - "SPDXID": "SPDXRef-Package" - } - - with pytest.raises(SPDXParsingError) as err: - _ = package_parser.parse_package(package_dict) - - assert err.value.get_messages() == ["Error while constructing Package: ['SetterError Package: type of " - 'argument "name" must be str; got NoneType instead: None\', \'SetterError ' - 'Package: type of argument "download_location" must be one of (str, ' - 'src.model.spdx_no_assertion.SpdxNoAssertion, src.model.spdx_none.SpdxNone); ' - "got NoneType instead: None']"] - - -def test_package_with_setter_error(): - package_parser = PackageParser() - package_dict = { - "SPDXID": "SPDXRef-Package", - "name": 5, - "downloadLocation": "NONE" - } with pytest.raises(SPDXParsingError) as err: - _ = package_parser.parse_package(package_dict) + package_parser.parse_package(incomplete_package_dict) - assert err.value.get_messages() == ["Error while constructing Package: ['SetterError Package: type of argument " - '"name" must be str; got int instead: 5\']'] + TestCase().assertCountEqual(err.value.get_messages(), expected_message) -def test_package_with_falsy_values(): +def test_parse_invalid_package(): package_parser = PackageParser() package_dict = { "SPDXID": "SPDXRef-Package", @@ -165,10 +152,10 @@ def test_package_with_falsy_values(): } with pytest.raises(SPDXParsingError) as err: - _ = package_parser.parse_package(package_dict) + package_parser.parse_package(package_dict) - assert err.value.get_messages() == [ - 'Error while parsing Package: ["Error while parsing Checksum: [\'Invalid Algorithm for checksum: SHA\']"]'] + TestCase().assertCountEqual(err.value.get_messages(), [ + 'Error while parsing Package: ["Error while parsing Checksum: [\'Invalid Algorithm for checksum: SHA\']"]']) def test_parse_packages(): @@ -192,9 +179,23 @@ def test_parse_packages(): ] with pytest.raises(SPDXParsingError) as err: - _ = package_parser.parse_packages(packages_list) + package_parser.parse_packages(packages_list) + + TestCase().assertCountEqual(err.value.get_messages(), + ['Error while parsing Package: ["Error while parsing Checksum: ' + '[\'Invalid Algorithm for checksum: SHA\']"]', + "Error while constructing Package: ['SetterError Package: type of argument " + '"name" must be str; got int instead: 5\']']) + + +def test_parse_external_ref(): + package_parser = PackageParser() + external_ref = { + "referenceType": "fix" + } + + with pytest.raises(SPDXParsingError) as err: + package_parser.parse_external_ref(external_ref) - assert err.value.messages == ['Error while parsing Package: ["Error while parsing Checksum: ' - '[\'Invalid Algorithm for checksum: SHA\']"]', - "Error while constructing Package: ['SetterError Package: type of argument " - '"name" must be str; got int instead: 5\']'] + TestCase().assertCountEqual(err.value.get_messages(), [ + "Error while constructing ExternalPackageRef: ['SetterError " 'ExternalPackageRef: type of argument "category" must be ' "src.model.package.ExternalPackageRefCategory; got NoneType instead: None', " '\'SetterError ExternalPackageRef: type of argument "locator" must be str; ' "got NoneType instead: None']"]) diff --git a/tests/parser/test_relationship_parser.py b/tests/parser/test_relationship_parser.py index ca721af71..2b814e1d2 100644 --- a/tests/parser/test_relationship_parser.py +++ b/tests/parser/test_relationship_parser.py @@ -8,6 +8,8 @@ # 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.model.relationship import RelationshipType, Relationship @@ -15,7 +17,7 @@ from src.parser.json.relationship_parser import RelationshipParser -def test_relationship_parser(): +def test_parse_relationship(): relationship_parser = RelationshipParser() relationship_dict = { @@ -42,11 +44,10 @@ def test_parse_incomplete_relationship(): } with pytest.raises(SPDXParsingError) as err: - _ = relationship_parser.parse_relationship(relationship_dict) + relationship_parser.parse_relationship(relationship_dict) - assert err.value.messages == ["Error while constructing Relationship: ['SetterError Relationship: type of " - 'argument "relationship_type" must be ' - "src.model.relationship.RelationshipType; got NoneType instead: None']"] + TestCase().assertCountEqual(err.value.get_messages(), [ + "Error while constructing Relationship: ['SetterError Relationship: type of " 'argument "relationship_type" must be ' "src.model.relationship.RelationshipType; got NoneType instead: None']"]) def test_parse_relationship_type(): @@ -57,7 +58,7 @@ def test_parse_relationship_type(): assert relationship_type == RelationshipType.DEPENDENCY_OF -def test_creating_describes_relationship(): +def test_parse_document_describes(): relationship_parser = RelationshipParser() document_dict = { @@ -71,12 +72,13 @@ def test_creating_describes_relationship(): existing_relationships=[]) assert len(relationships) == 3 - assert relationships == [Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-Package"), - Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-File"), - Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-Snippet"), ] + TestCase().assertCountEqual(relationships, + [Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-Package"), + Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-File"), + Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-Snippet")]) -def test_creating_single_describes_relationship(): +def test_parse_document_describes_without_duplicating_relationships(): relationship_parser = RelationshipParser() document_dict = { "SPDXID": "SPDXRef-DOCUMENT", @@ -91,15 +93,14 @@ def test_creating_single_describes_relationship(): relationships = relationship_parser.parse_all_relationships(document_dict) assert len(relationships) == 2 - assert relationships == [ + TestCase().assertCountEqual(relationships, [ Relationship(related_spdx_element_id="SPDXRef-Package", relationship_type=RelationshipType.DESCRIBES, spdx_element_id="SPDXRef-DOCUMENT", comment="This relationship has a comment."), Relationship(related_spdx_element_id="SPDXRef-DOCUMENT", relationship_type=RelationshipType.DESCRIBED_BY, - spdx_element_id="SPDXRef-File", comment="This relationship has a comment.") - ] + spdx_element_id="SPDXRef-File", comment="This relationship has a comment.")]) -def test_contains_relationship(): +def test_parse_has_files(): relationship_parser = RelationshipParser() document_dict = { "packages": @@ -112,14 +113,14 @@ def test_contains_relationship(): relationships = relationship_parser.parse_has_files(document_dict.get("packages"), existing_relationships=[]) assert len(relationships) == 2 - assert relationships == [ + TestCase().assertCountEqual(relationships, [ Relationship(spdx_element_id="SPDXRef-Package", relationship_type=RelationshipType.CONTAINS, related_spdx_element_id="SPDXRef-File1"), Relationship(spdx_element_id="SPDXRef-Package", relationship_type=RelationshipType.CONTAINS, - related_spdx_element_id="SPDXRef-File2")] + related_spdx_element_id="SPDXRef-File2")]) -def test_single_contains_relationship(): +def test_parse_has_files_without_duplicating_relationships(): relationship_parser = RelationshipParser() document_dict = { "packages": diff --git a/tests/parser/test_snippet_parser.py b/tests/parser/test_snippet_parser.py index 9ead2d6d9..96e1baee9 100644 --- a/tests/parser/test_snippet_parser.py +++ b/tests/parser/test_snippet_parser.py @@ -8,6 +8,8 @@ # 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.model.license_expression import LicenseExpression @@ -15,7 +17,7 @@ from src.parser.json.snippet_parser import SnippetParser -def test_snippet_parser(): +def test_parse_snippet(): snippet_parser = SnippetParser() snippet_dict = { @@ -71,12 +73,11 @@ def test_parse_incomplete_snippet(): } with pytest.raises(SPDXParsingError) as err: - _ = snippet_parser.parse_snippet(incomplete_snippet_dict) + snippet_parser.parse_snippet(incomplete_snippet_dict) - assert err.value.messages == ["Error while constructing Snippet: ['SetterError Snippet: type of argument " - '"file_spdx_id" must be str; got NoneType instead: None\', \'SetterError ' - 'Snippet: type of argument "byte_range" must be a tuple; got NoneType ' - "instead: None']"] + TestCase().assertCountEqual(err.value.get_messages(), [ + "Error while constructing Snippet: ['SetterError Snippet: type of argument " '"file_spdx_id" must be str; got NoneType instead: None\', \'SetterError Snippet: type of argument "byte_range" must be a tuple; got NoneType ' + "instead: None']"]) def test_parse_snippet_with_invalid_snippet_range(): @@ -98,12 +99,13 @@ def test_parse_snippet_with_invalid_snippet_range(): } with pytest.raises(SPDXParsingError) as err: - _ = snippet_parser.parse_snippet(snippet_with_invalid_ranges_list) + snippet_parser.parse_snippet(snippet_with_invalid_ranges_list) - assert err.value.messages == ["Error while constructing Snippet: ['SetterError Snippet: type of argument " - '"file_spdx_id" must be str; got NoneType instead: None\', \'SetterError ' - 'Snippet: type of argument "byte_range"[0] must be int; got str instead: ' - "(\\'310\\', 23)']"] + TestCase().assertCountEqual(err.value.get_messages(), + ["Error while constructing Snippet: ['SetterError Snippet: type of argument " + '"file_spdx_id" must be str; got NoneType instead: None\', \'SetterError ' + 'Snippet: type of argument "byte_range"[0] must be int; got str instead: ' + "(\\'310\\', 23)']"]) def test_parse_invalid_snippet_range(): @@ -133,8 +135,7 @@ def test_parse_invalid_snippet_range(): ] with pytest.raises(SPDXParsingError) as err: - _ = snippet_parser.parse_ranges(ranges) + snippet_parser.parse_ranges(ranges) - assert err.value.messages == ["Error while parsing snippet ranges: ['Type of startpointer is not the same as " - "type of endpointer.', 'Type of startpointer is not the same as type of " - "endpointer.']"] + TestCase().assertCountEqual(err.value.get_messages(), [ + "Error while parsing snippet ranges: ['Type of startpointer is not the same as type of endpointer.', 'Type of startpointer is not the same as type of endpointer.']"]) From ee621c8c1a243d8805674ee6cf2ef78390b9e467 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 22 Dec 2022 12:27:11 +0100 Subject: [PATCH 055/362] [issue-305, review] add method to parse fields that can be SpdxNone or SpdxNoAssertion Signed-off-by: Meret Behrens --- src/parser/json/actor_parser.py | 5 -- src/parser/json/creation_info_parser.py | 6 +- src/parser/json/dict_parsing_functions.py | 18 +++++- .../json/extracted_licensing_info_parser.py | 29 ++++------ src/parser/json/file_parser.py | 7 ++- src/parser/json/license_expression_parser.py | 25 +++------ src/parser/json/package_parser.py | 27 +++++---- src/parser/json/snippet_parser.py | 16 ++++-- tests/parser/test_dict_parsing_functions.py | 24 +++++++- .../test_extracted_licensing_info_parser.py | 9 --- .../parser/test_license_expression_parser.py | 56 ++++++------------- 11 files changed, 106 insertions(+), 116 deletions(-) diff --git a/src/parser/json/actor_parser.py b/src/parser/json/actor_parser.py index b5a68aa29..cfd835d5b 100644 --- a/src/parser/json/actor_parser.py +++ b/src/parser/json/actor_parser.py @@ -18,11 +18,6 @@ class ActorParser: - def parse_actor_or_no_assertion(self, actor_or_no_assertion: str) -> Union[SpdxNoAssertion, Actor]: - if actor_or_no_assertion == SpdxNoAssertion.__str__: - return SpdxNoAssertion() - else: - return self.parse_actor(actor_or_no_assertion) @staticmethod def parse_actor(actor: str) -> Actor: diff --git a/src/parser/json/creation_info_parser.py b/src/parser/json/creation_info_parser.py index ef9304144..5f8eaab40 100644 --- a/src/parser/json/creation_info_parser.py +++ b/src/parser/json/creation_info_parser.py @@ -20,7 +20,8 @@ from src.parser.json.actor_parser import ActorParser from src.parser.json.checksum_parser import ChecksumParser from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, datetime_from_str, \ - raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error, parse_field_or_log_error + raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error, parse_field_or_log_error, \ + parse_field_or_no_assertion from src.parser.logger import Logger @@ -80,8 +81,7 @@ def parse_creators(self, creators_list_from_dict: List[str]) -> List[Actor]: logger = Logger() creators = [] for creator_str in creators_list_from_dict: - creators = append_parsed_field_or_log_error(logger, creators, creator_str, - self.actor_parser.parse_actor_or_no_assertion) + creators = append_parsed_field_or_log_error(logger, creators, creator_str, lambda x: parse_field_or_no_assertion(x, self.actor_parser.parse_actor)) raise_parsing_error_if_logger_has_messages(logger) return creators diff --git a/src/parser/json/dict_parsing_functions.py b/src/parser/json/dict_parsing_functions.py index 93c34c907..2a0815954 100644 --- a/src/parser/json/dict_parsing_functions.py +++ b/src/parser/json/dict_parsing_functions.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. from datetime import datetime -from typing import Any, Callable, Dict, List +from typing import Any, Callable, Dict, List, Union +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.error import SPDXParsingError from src.parser.logger import Logger @@ -67,3 +69,17 @@ def raise_parsing_error_if_logger_has_messages(logger: Logger, parsed_object_nam raise SPDXParsingError([f"Error while parsing {parsed_object_name}: {logger.get_messages()}"]) else: raise SPDXParsingError(logger.get_messages()) + +def parse_field_or_no_assertion_or_none(field: str, method_for_field: Callable=lambda x: x) -> Union[SpdxNoAssertion, SpdxNone, Any]: + if field == SpdxNoAssertion().__str__(): + return SpdxNoAssertion() + elif field == SpdxNone().__str__(): + return SpdxNone() + else: + return method_for_field(field) + +def parse_field_or_no_assertion(field: str, method_for_field: Callable = lambda x: x) -> Union[SpdxNoAssertion, Any]: + if field == SpdxNoAssertion().__str__(): + return SpdxNoAssertion() + else: + return method_for_field(field) diff --git a/src/parser/json/extracted_licensing_info_parser.py b/src/parser/json/extracted_licensing_info_parser.py index 4fc3a67c3..e59e0e4a2 100644 --- a/src/parser/json/extracted_licensing_info_parser.py +++ b/src/parser/json/extracted_licensing_info_parser.py @@ -13,7 +13,7 @@ from src.model.extracted_licensing_info import ExtractedLicensingInfo from src.model.spdx_no_assertion import SpdxNoAssertion from src.parser.json.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ - append_parsed_field_or_log_error, construct_or_raise_parsing_error, parse_field_or_log_error + append_parsed_field_or_log_error, construct_or_raise_parsing_error, parse_field_or_no_assertion from src.parser.logger import Logger @@ -28,8 +28,8 @@ def parse_extracted_licensing_infos(self, extracted_licensing_info_dicts: List[D extracted_licensing_infos = [] for extracted_licensing_info_dict in extracted_licensing_info_dicts: extracted_licensing_infos = append_parsed_field_or_log_error(self.logger, extracted_licensing_infos, - extracted_licensing_info_dict, - self.parse_extracted_licensing_info) + extracted_licensing_info_dict, + self.parse_extracted_licensing_info) raise_parsing_error_if_logger_has_messages(self.logger) return extracted_licensing_infos @@ -37,23 +37,14 @@ def parse_extracted_licensing_infos(self, extracted_licensing_info_dicts: List[D def parse_extracted_licensing_info(self, extracted_licensing_info_dict: Dict) -> ExtractedLicensingInfo: license_id: Optional[str] = extracted_licensing_info_dict.get("licenseId") extracted_text: Optional[str] = extracted_licensing_info_dict.get("extractedText") - license_name: Optional[Union[str, SpdxNoAssertion]] = parse_field_or_log_error(self.logger, - extracted_licensing_info_dict.get( - "name"), - self.parse_extracted_licensing_info_name) + license_name: Optional[Union[str, SpdxNoAssertion]] = parse_field_or_no_assertion( + extracted_licensing_info_dict.get("name")) cross_references: List[str] = extracted_licensing_info_dict.get("seeAlsos", []) comment: Optional[str] = extracted_licensing_info_dict.get("comment") extracted_licensing_info = construct_or_raise_parsing_error(ExtractedLicensingInfo, - dict(license_id=license_id, - extracted_text=extracted_text, - comment=comment, - license_name=license_name, - cross_references=cross_references)) + dict(license_id=license_id, + extracted_text=extracted_text, + comment=comment, + license_name=license_name, + cross_references=cross_references)) return extracted_licensing_info - - @staticmethod - def parse_extracted_licensing_info_name(extracted_licensing_info_name_or_no_assertion) -> Union[str, SpdxNoAssertion]: - if extracted_licensing_info_name_or_no_assertion == SpdxNoAssertion().__str__(): - return SpdxNoAssertion() - else: - return extracted_licensing_info_name_or_no_assertion diff --git a/src/parser/json/file_parser.py b/src/parser/json/file_parser.py index 3982ddc5d..b190eaa7c 100644 --- a/src/parser/json/file_parser.py +++ b/src/parser/json/file_parser.py @@ -17,7 +17,8 @@ from src.model.spdx_none import SpdxNone from src.parser.json.checksum_parser import ChecksumParser from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, \ - raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error, parse_field_or_log_error + raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error, parse_field_or_log_error, \ + parse_field_or_no_assertion_or_none from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -56,10 +57,10 @@ def parse_file(self, file_dict: Dict) -> Optional[File]: license_comments: Optional[str] = file_dict.get("licenseComments") license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger, file_dict.get("licenseConcluded"), self.license_expression_parser.parse_license_expression) + logger, file_dict.get("licenseConcluded"), lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expression)) license_info_in_files: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger, file_dict.get("licenseInfoInFiles"), self.license_expression_parser.parse_license_expression) + logger, file_dict.get("licenseInfoInFiles"), lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expressions)) notice_text: Optional[str] = file_dict.get("noticeText") raise_parsing_error_if_logger_has_messages(logger, "File") diff --git a/src/parser/json/license_expression_parser.py b/src/parser/json/license_expression_parser.py index 5c576da43..5fc0df4e1 100644 --- a/src/parser/json/license_expression_parser.py +++ b/src/parser/json/license_expression_parser.py @@ -11,8 +11,6 @@ from typing import Union, List from src.model.license_expression import LicenseExpression -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone from src.parser.error import SPDXParsingError from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error, append_parsed_field_or_log_error, \ raise_parsing_error_if_logger_has_messages @@ -20,22 +18,15 @@ class LicenseExpressionParser: - def parse_license_expression(self, license_expression_str_or_list: Union[str, List[str]]) -> Union[ - LicenseExpression, SpdxNoAssertion, SpdxNone, List[LicenseExpression]]: - if license_expression_str_or_list == SpdxNone().__str__(): - return SpdxNone() - if license_expression_str_or_list == SpdxNoAssertion().__str__(): - return SpdxNoAssertion() - elif isinstance(license_expression_str_or_list, list): - return self.parse_license_expressions(license_expression_str_or_list) + @staticmethod + def parse_license_expression(license_expression_str_or_list: str) -> LicenseExpression: + license_expression = construct_or_raise_parsing_error(LicenseExpression, + dict(expression_string=license_expression_str_or_list)) + return license_expression - else: - license_expression = construct_or_raise_parsing_error(LicenseExpression, - dict( - expression_string=license_expression_str_or_list)) - return license_expression - - def parse_license_expressions(self, license_expression_str_or_list: List[str]) -> List[LicenseExpression]: + def parse_license_expressions(self, license_expression_str_or_list: Union[str, List[str]]) -> Union[LicenseExpression, List[LicenseExpression]]: + if isinstance(license_expression_str_or_list, str): + return self.parse_license_expression(license_expression_str_or_list) license_expressions = [] logger = Logger() for license_expression_str in license_expression_str_or_list: diff --git a/src/parser/json/package_parser.py b/src/parser/json/package_parser.py index b528f9b32..e40db1afa 100644 --- a/src/parser/json/package_parser.py +++ b/src/parser/json/package_parser.py @@ -22,7 +22,7 @@ from src.parser.json.checksum_parser import ChecksumParser from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, datetime_from_str, \ raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, construct_or_raise_parsing_error, \ - parse_field_or_log_error + parse_field_or_log_error, parse_field_or_no_assertion_or_none, parse_field_or_no_assertion from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -62,7 +62,7 @@ def parse_package(self, package_dict: Dict) -> Package: comment: Optional[str] = package_dict.get("comment") copyright_text: Optional[str] = package_dict.get("copyrightText") description: Optional[str] = package_dict.get("description") - download_location: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = self.parse_download_location( + download_location: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = parse_field_or_no_assertion_or_none( package_dict.get("downloadLocation")) external_refs: List[ExternalPackageRef] = parse_field_or_log_error(logger, package_dict.get("externalRefs"), @@ -73,19 +73,22 @@ def parse_package(self, package_dict: Dict) -> Package: homepage: Optional[str] = package_dict.get("homepage") license_comments: Optional[str] = package_dict.get("licenseComments") license_concluded = parse_field_or_log_error(logger, package_dict.get("licenseConcluded"), - self.license_expression_parser.parse_license_expression, None) + lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expression), None) license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger, package_dict.get("licenseDeclared"), self.license_expression_parser.parse_license_expression) + logger, package_dict.get("licenseDeclared"), lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expression)) license_info_from_file: Optional[ Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger, package_dict.get( "licenseInfoFromFiles"), - self.license_expression_parser.parse_license_expression) + lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expressions)) originator: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error(logger, package_dict.get("originator"), - self.actor_parser.parse_actor_or_no_assertion) + lambda + x: parse_field_or_no_assertion( + x, + self.actor_parser.parse_actor)) package_file_name: Optional[str] = package_dict.get("packageFileName") package_verification_code: Optional[ @@ -100,7 +103,10 @@ def parse_package(self, package_dict: Dict) -> Package: summary: Optional[str] = package_dict.get("summary") supplier: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error(logger, package_dict.get("supplier"), - self.actor_parser.parse_actor_or_no_assertion) + lambda + x: parse_field_or_no_assertion( + x, + self.actor_parser.parse_actor)) valid_until_date: Optional[datetime] = parse_field_or_log_error(logger, package_dict.get("validUntilDate"), datetime_from_str) @@ -177,10 +183,3 @@ def parse_primary_package_purpose(primary_package_purpose: str) -> PackagePurpos except KeyError: raise SPDXParsingError([f"Invalid PrimaryPackagePurpose: {primary_package_purpose}"]) - @staticmethod - def parse_download_location(download_location: str) -> Union[str, SpdxNoAssertion, SpdxNone]: - if download_location == SpdxNone().__str__(): - return SpdxNone() - if download_location == SpdxNoAssertion().__str__(): - return SpdxNoAssertion() - return download_location diff --git a/src/parser/json/snippet_parser.py b/src/parser/json/snippet_parser.py index 4123451ee..9c7a78ed8 100644 --- a/src/parser/json/snippet_parser.py +++ b/src/parser/json/snippet_parser.py @@ -17,7 +17,7 @@ from src.model.spdx_none import SpdxNone from src.parser.error import SPDXParsingError from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error, parse_field_or_log_error, \ - raise_parsing_error_if_logger_has_messages, append_parsed_field_or_log_error + raise_parsing_error_if_logger_has_messages, append_parsed_field_or_log_error, parse_field_or_no_assertion_or_none from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -59,11 +59,19 @@ def parse_snippet(self, snippet_dict: Dict) -> Snippet: license_comment: Optional[str] = snippet_dict.get("licenseComments") concluded_license: Optional[Union[ LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger, snippet_dict.get( - "licenseConcluded"), self.license_expression_parser.parse_license_expression) + "licenseConcluded"), + lambda + x: parse_field_or_no_assertion_or_none( + x, + self.license_expression_parser.parse_license_expression)) license_info: Optional[Union[List[ LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger, snippet_dict.get( - "licenseInfoInSnippets"), self.license_expression_parser.parse_license_expression) + "licenseInfoInSnippets"), + lambda + x: parse_field_or_no_assertion_or_none( + x, + self.license_expression_parser.parse_license_expressions)) if logger.has_messages(): raise SPDXParsingError([f"Error while parsing snippet: {logger.get_messages()}"]) @@ -117,7 +125,7 @@ def validate_range_and_get_type(self, range_dict: Dict) -> RangeType: @staticmethod def validate_pointer_and_get_type(pointer: Dict) -> RangeType: if "offset" in pointer and "lineNumber" in pointer: - raise ValueError ('Couldn\'t determine type of pointer: "offset" and "lineNumber" provided as key.') + raise ValueError('Couldn\'t determine type of pointer: "offset" and "lineNumber" provided as key.') if "offset" not in pointer and "lineNumber" not in pointer: raise ValueError('Couldn\'t determine type of pointer: neither "offset" nor "lineNumber" provided as key.') return RangeType.BYTE if "offset" in pointer else RangeType.LINE diff --git a/tests/parser/test_dict_parsing_functions.py b/tests/parser/test_dict_parsing_functions.py index 4f1c91162..688f44df9 100644 --- a/tests/parser/test_dict_parsing_functions.py +++ b/tests/parser/test_dict_parsing_functions.py @@ -13,8 +13,11 @@ import pytest +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import datetime_from_str, json_str_to_enum_name +from src.parser.json.dict_parsing_functions import datetime_from_str, json_str_to_enum_name, \ + parse_field_or_no_assertion, parse_field_or_no_assertion_or_none def test_datetime_from_str(): @@ -35,6 +38,7 @@ def test_datetime_from_str_error(invalid_date_str, expected_message): TestCase().assertCountEqual(err.value.get_messages(), expected_message) + def test_json_str_to_enum(): json_str = "BLAKE2b-256" @@ -42,10 +46,26 @@ def test_json_str_to_enum(): assert enum_name == "BLAKE2B_256" + @pytest.mark.parametrize("invalid_json_str,expected_message", [(5, ["Type for enum must be str not int"])]) -def test_invalid_json_str_to_enum(invalid_json_str,expected_message): +def test_invalid_json_str_to_enum(invalid_json_str, expected_message): with pytest.raises(SPDXParsingError) as err: json_str_to_enum_name(invalid_json_str) TestCase().assertCountEqual(err.value.get_messages(), expected_message) + + +@pytest.mark.parametrize("input_str,expected_type", [("NOASSERTION", SpdxNoAssertion), ("example string", str)]) +def test_parse_field_or_no_assertion(input_str, expected_type): + resulting_value = parse_field_or_no_assertion(input_str, lambda x: x) + + assert type(resulting_value) == expected_type + + +@pytest.mark.parametrize("input_str,expected_type", + [("NOASSERTION", SpdxNoAssertion), ("NONE", SpdxNone), ("example string", str)]) +def test_parse_field_or_no_assertion_or_none(input_str, expected_type): + resulting_value = parse_field_or_no_assertion_or_none(input_str, lambda x: x) + + assert type(resulting_value) == expected_type diff --git a/tests/parser/test_extracted_licensing_info_parser.py b/tests/parser/test_extracted_licensing_info_parser.py index 2d5b4825f..47f70ef6b 100644 --- a/tests/parser/test_extracted_licensing_info_parser.py +++ b/tests/parser/test_extracted_licensing_info_parser.py @@ -58,12 +58,3 @@ def test_parse_invalid_extracted_licensing_info(): TestCase().assertCountEqual(err.value.get_messages(), [ "Error while constructing ExtractedLicensingInfo: ['SetterError " 'ExtractedLicensingInfo: type of argument "comment" must be one of (str, ' "NoneType); got int instead: 56']"]) - -def test_parse_extracted_licensing_info_name(): - extracted_licensing_info_parser = ExtractedLicensingInfoParser() - extracted_licensing_info_name_str = "NOASSERTION" - - extracted_licensing_info_name = extracted_licensing_info_parser.parse_extracted_licensing_info_name( - extracted_licensing_info_name_str) - - assert type(extracted_licensing_info_name) == SpdxNoAssertion diff --git a/tests/parser/test_license_expression_parser.py b/tests/parser/test_license_expression_parser.py index 6abac093c..89e23a59a 100644 --- a/tests/parser/test_license_expression_parser.py +++ b/tests/parser/test_license_expression_parser.py @@ -13,51 +13,14 @@ import pytest from src.model.license_expression import LicenseExpression -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone from src.parser.error import SPDXParsingError from src.parser.json.license_expression_parser import LicenseExpressionParser -def test_parse_license_expression(): - license_expression_parser = LicenseExpressionParser() - license_expression_str = "License-Ref1" - - license_expression = license_expression_parser.parse_license_expression( - license_expression_str_or_list=license_expression_str) - - assert license_expression.expression_string == "License-Ref1" - - -def test_parse_license_expression_no_assert(): - license_expression_parser = LicenseExpressionParser() - license_expression_str = "NOASSERTION" - - spdx_no_assertion = license_expression_parser.parse_license_expression( - license_expression_str_or_list=license_expression_str) - - assert type(spdx_no_assertion) == SpdxNoAssertion - - -def test_parse_license_expression_none(): - license_expression_parser = LicenseExpressionParser() - license_expression_str = "NONE" - - spdx_none = license_expression_parser.parse_license_expression( - license_expression_str_or_list=license_expression_str) - - assert type(spdx_none) == SpdxNone - - @pytest.mark.parametrize("invalid_license_expression,expected_message", [(56, ["Error while constructing LicenseExpression: ['SetterError LicenseExpression: " 'type of argument "expression_string" must be str; got int instead: 56\']'] - ), - (["First Expression", 4, 6], - ["Error while constructing LicenseExpression: ['SetterError LicenseExpression: " - 'type of argument "expression_string" must be str; got int instead: 4\']', - "Error while constructing LicenseExpression: ['SetterError LicenseExpression: " - 'type of argument "expression_string" must be str; got int instead: 6\']'])]) + ), ]) def test_parse_invalid_license_expression(invalid_license_expression, expected_message): license_expression_parser = LicenseExpressionParser() @@ -71,9 +34,24 @@ def test_parse_license_expressions(): license_expression_parser = LicenseExpressionParser() license_expressions_list = ["First License", "Second License", "Third License"] - license_expressions = license_expression_parser.parse_license_expression(license_expressions_list) + license_expressions = license_expression_parser.parse_license_expressions(license_expressions_list) assert len(license_expressions) == 3 TestCase().assertCountEqual(license_expressions, [LicenseExpression("First License"), LicenseExpression("Second License"), LicenseExpression("Third License")]) + + +@pytest.mark.parametrize("invalid_license_expressions,expected_message", [(["First Expression", 4, 6], + [ + "Error while constructing LicenseExpression: ['SetterError LicenseExpression: " + 'type of argument "expression_string" must be str; got int instead: 4\']', + "Error while constructing LicenseExpression: ['SetterError LicenseExpression: " + 'type of argument "expression_string" must be str; got int instead: 6\']'])]) +def test_parse_invalid_license_expressions(invalid_license_expressions, expected_message): + license_expression_parser = LicenseExpressionParser() + + with pytest.raises(SPDXParsingError) as err: + license_expression_parser.parse_license_expressions(invalid_license_expressions) + + TestCase().assertCountEqual(err.value.get_messages(), expected_message) From 829e8688fa228752017edefa0e0ce90a8327ca1b Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 22 Dec 2022 12:59:32 +0100 Subject: [PATCH 056/362] [issue-305, review] refactor parse_field_or_log_error Signed-off-by: Meret Behrens --- src/parser/json/annotation_parser.py | 17 ++------- src/parser/json/checksum_parser.py | 9 ----- src/parser/json/dict_parsing_functions.py | 22 +++++++++-- .../json/extracted_licensing_info_parser.py | 17 ++------- src/parser/json/file_parser.py | 25 +++++-------- src/parser/json/json_parser.py | 18 ++++++--- src/parser/json/package_parser.py | 37 ++++++++----------- src/parser/json/relationship_parser.py | 17 ++------- src/parser/json/snippet_parser.py | 25 +++---------- tests/parser/test_file_parser.py | 3 +- tests/parser/test_package_parser.py | 3 +- 11 files changed, 76 insertions(+), 117 deletions(-) diff --git a/src/parser/json/annotation_parser.py b/src/parser/json/annotation_parser.py index 5e09bab4a..0feec8e00 100644 --- a/src/parser/json/annotation_parser.py +++ b/src/parser/json/annotation_parser.py @@ -16,7 +16,8 @@ from src.parser.error import SPDXParsingError from src.parser.json.actor_parser import ActorParser from src.parser.json.dict_parsing_functions import datetime_from_str, construct_or_raise_parsing_error, \ - parse_field_or_log_error, append_parsed_field_or_log_error, raise_parsing_error_if_logger_has_messages + parse_field_or_log_error, append_parsed_field_or_log_error, raise_parsing_error_if_logger_has_messages, \ + parse_list_of_elements from src.parser.logger import Logger @@ -53,18 +54,8 @@ def parse_annotations_from_object(self, annotations: List[Annotation], element_l element_spdx_id: Optional[str] = element.get("SPDXID") element_annotations: List[Dict] = element.get("annotations", []) annotations.extend(parse_field_or_log_error(self.logger, element_annotations, - lambda x: self.parse_annotations(x, spdx_id=element_spdx_id), - default=[])) - - def parse_annotations(self, annotation_dicts: List[Dict], spdx_id: Optional[str] = None) -> List[Annotation]: - logger = Logger() - annotations = [] - for annotation_dict in annotation_dicts: - annotations = append_parsed_field_or_log_error(self.logger, annotations, annotation_dict, - lambda x: self.parse_annotation(x, spdx_id=spdx_id)) - raise_parsing_error_if_logger_has_messages(logger, "annotations") - - return annotations + lambda y: self.parse_annotation(y, spdx_id=element_spdx_id), + [], True)) def parse_annotation(self, annotation_dict: Dict, spdx_id: Optional[str] = None) -> Annotation: logger = Logger() diff --git a/src/parser/json/checksum_parser.py b/src/parser/json/checksum_parser.py index ec5e2408c..a02d79b01 100644 --- a/src/parser/json/checksum_parser.py +++ b/src/parser/json/checksum_parser.py @@ -23,15 +23,6 @@ class ChecksumParser: def __init__(self): self.logger = Logger() - def parse_checksums(self, checksum_dicts: List[Dict]) -> List[Checksum]: - checksums = [] - for checksum_dict in checksum_dicts: - checksums = append_parsed_field_or_log_error(self.logger, checksums, checksum_dict, - self.parse_checksum) - - raise_parsing_error_if_logger_has_messages(self.logger) - return checksums - @staticmethod def parse_checksum(checksum_dict: Dict) -> Checksum: logger = Logger() diff --git a/src/parser/json/dict_parsing_functions.py b/src/parser/json/dict_parsing_functions.py index 2a0815954..19ce4c5e6 100644 --- a/src/parser/json/dict_parsing_functions.py +++ b/src/parser/json/dict_parsing_functions.py @@ -43,11 +43,15 @@ def construct_or_raise_parsing_error(object_to_construct: Any, args_for_construc return constructed_object -def parse_field_or_log_error(logger: Logger, field: Any, parsing_method: Callable = lambda x: x, default=None) -> Any: +def parse_field_or_log_error(logger: Logger, field: Any, parsing_method: Callable = lambda x: x, default: Any = None, + field_is_list: bool = False) -> Any: if not field: return default try: - return parsing_method(field) + if field_is_list: + return parse_list_of_elements(field, parsing_method) + else: + return parsing_method(field) except SPDXParsingError as err: logger.extend(err.get_messages()) return default @@ -70,7 +74,7 @@ def raise_parsing_error_if_logger_has_messages(logger: Logger, parsed_object_nam else: raise SPDXParsingError(logger.get_messages()) -def parse_field_or_no_assertion_or_none(field: str, method_for_field: Callable=lambda x: x) -> Union[SpdxNoAssertion, SpdxNone, Any]: +def parse_field_or_no_assertion_or_none(field: str, method_for_field: Callable=lambda x: x) -> Any: if field == SpdxNoAssertion().__str__(): return SpdxNoAssertion() elif field == SpdxNone().__str__(): @@ -78,8 +82,18 @@ def parse_field_or_no_assertion_or_none(field: str, method_for_field: Callable=l else: return method_for_field(field) -def parse_field_or_no_assertion(field: str, method_for_field: Callable = lambda x: x) -> Union[SpdxNoAssertion, Any]: +def parse_field_or_no_assertion(field: str, method_for_field: Callable = lambda x: x) -> Any: if field == SpdxNoAssertion().__str__(): return SpdxNoAssertion() else: return method_for_field(field) + + +def parse_list_of_elements(list_of_elements: List[Dict], method_to_parse_element: Callable, logger=None) -> List[Any]: + if not logger: + logger = Logger() + parsed_elements = [] + for element_dict in list_of_elements: + parsed_elements = append_parsed_field_or_log_error(logger, parsed_elements, element_dict, method_to_parse_element) + raise_parsing_error_if_logger_has_messages(logger) + return parsed_elements diff --git a/src/parser/json/extracted_licensing_info_parser.py b/src/parser/json/extracted_licensing_info_parser.py index e59e0e4a2..dcb4360bb 100644 --- a/src/parser/json/extracted_licensing_info_parser.py +++ b/src/parser/json/extracted_licensing_info_parser.py @@ -12,8 +12,7 @@ from src.model.extracted_licensing_info import ExtractedLicensingInfo from src.model.spdx_no_assertion import SpdxNoAssertion -from src.parser.json.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ - append_parsed_field_or_log_error, construct_or_raise_parsing_error, parse_field_or_no_assertion +from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error, parse_field_or_no_assertion from src.parser.logger import Logger @@ -23,18 +22,8 @@ class ExtractedLicensingInfoParser: def __init__(self): self.logger = Logger() - def parse_extracted_licensing_infos(self, extracted_licensing_info_dicts: List[Dict]) -> List[ - ExtractedLicensingInfo]: - extracted_licensing_infos = [] - for extracted_licensing_info_dict in extracted_licensing_info_dicts: - extracted_licensing_infos = append_parsed_field_or_log_error(self.logger, extracted_licensing_infos, - extracted_licensing_info_dict, - self.parse_extracted_licensing_info) - - raise_parsing_error_if_logger_has_messages(self.logger) - return extracted_licensing_infos - - def parse_extracted_licensing_info(self, extracted_licensing_info_dict: Dict) -> ExtractedLicensingInfo: + @staticmethod + def parse_extracted_licensing_info(extracted_licensing_info_dict: Dict) -> ExtractedLicensingInfo: license_id: Optional[str] = extracted_licensing_info_dict.get("licenseId") extracted_text: Optional[str] = extracted_licensing_info_dict.get("extractedText") license_name: Optional[Union[str, SpdxNoAssertion]] = parse_field_or_no_assertion( diff --git a/src/parser/json/file_parser.py b/src/parser/json/file_parser.py index b190eaa7c..e9c236aee 100644 --- a/src/parser/json/file_parser.py +++ b/src/parser/json/file_parser.py @@ -16,9 +16,9 @@ from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone from src.parser.json.checksum_parser import ChecksumParser -from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, \ - raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error, parse_field_or_log_error, \ - parse_field_or_no_assertion_or_none +from src.parser.json.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ + construct_or_raise_parsing_error, parse_field_or_log_error, \ + parse_field_or_no_assertion_or_none, parse_list_of_elements from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -33,20 +33,12 @@ def __init__(self): self.checksum_parser = ChecksumParser() self.license_expression_parser = LicenseExpressionParser() - def parse_files(self, file_dicts: List[Dict]) -> List[File]: - files = [] - for file_dict in file_dicts: - files = append_parsed_field_or_log_error(self.logger, files, file_dict, self.parse_file) - raise_parsing_error_if_logger_has_messages(self.logger) - return files - def parse_file(self, file_dict: Dict) -> Optional[File]: logger = Logger() name: Optional[str] = file_dict.get("fileName") spdx_id: Optional[str] = file_dict.get("SPDXID") checksums_list: List[Dict] = file_dict.get("checksums") - checksums: List[Checksum] = parse_field_or_log_error(logger, checksums_list, - self.checksum_parser.parse_checksums) + checksums: List[Checksum] = parse_field_or_log_error(logger, checksums_list, self.checksum_parser.parse_checksum, field_is_list=True) attribution_texts: List[str] = file_dict.get("attributionTexts", []) comment: Optional[str] = file_dict.get("comment") @@ -57,10 +49,13 @@ def parse_file(self, file_dict: Dict) -> Optional[File]: license_comments: Optional[str] = file_dict.get("licenseComments") license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger, file_dict.get("licenseConcluded"), lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expression)) + logger, file_dict.get("licenseConcluded"), + lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expression)) - license_info_in_files: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger, file_dict.get("licenseInfoInFiles"), lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expressions)) + license_info_in_files: Optional[ + Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( + logger, file_dict.get("licenseInfoInFiles"), + lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expressions)) notice_text: Optional[str] = file_dict.get("noticeText") raise_parsing_error_if_logger_has_messages(logger, "File") diff --git a/src/parser/json/json_parser.py b/src/parser/json/json_parser.py index 1a8b72656..e81e71636 100644 --- a/src/parser/json/json_parser.py +++ b/src/parser/json/json_parser.py @@ -15,7 +15,7 @@ from src.parser.json.annotation_parser import AnnotationParser from src.parser.json.creation_info_parser import CreationInfoParser from src.parser.json.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ - construct_or_raise_parsing_error + construct_or_raise_parsing_error, parse_list_of_elements from src.parser.json.extracted_licensing_info_parser import ExtractedLicensingInfoParser from src.parser.json.file_parser import FileParser from src.parser.logger import Logger @@ -50,13 +50,21 @@ def parse(self, filename: str) -> Document: input_doc_as_dict = json.load(file) fields_to_parse = [("creation_info", input_doc_as_dict, self.creation_info_parser.parse_creation_info, False), - ("packages", input_doc_as_dict.get("packages"), self.package_parser.parse_packages, True), - ("files", input_doc_as_dict.get("files"), self.file_parser.parse_files, True), + ("packages", input_doc_as_dict.get("packages"), lambda x: parse_list_of_elements(x, + self.package_parser.parse_package, + self.package_parser.logger), True), + ("files", input_doc_as_dict.get("files"), lambda x: parse_list_of_elements(x, + self.file_parser.parse_file, + self.file_parser.logger), True), ("annotations", input_doc_as_dict, self.annotation_parser.parse_all_annotations, True), - ("snippets", input_doc_as_dict.get("snippets"), self.snippet_parser.parse_snippets, True), + ("snippets", input_doc_as_dict.get("snippets"), lambda x: parse_list_of_elements(x, + self.snippet_parser.parse_snippet, + self.snippet_parser.logger), True), ("relationships", input_doc_as_dict, self.relationship_parser.parse_all_relationships, True), ("extracted_licensing_info", input_doc_as_dict.get("hasExtractedLicensingInfos"), - self.extracted_licensing_info_parser.parse_extracted_licensing_infos, True)] + lambda x: parse_list_of_elements(x, + self.extracted_licensing_info_parser.parse_extracted_licensing_info, + self.extracted_licensing_info_parser.logger), True)] parsed_fields = {} diff --git a/src/parser/json/package_parser.py b/src/parser/json/package_parser.py index e40db1afa..bc73f9d90 100644 --- a/src/parser/json/package_parser.py +++ b/src/parser/json/package_parser.py @@ -22,7 +22,7 @@ from src.parser.json.checksum_parser import ChecksumParser from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, datetime_from_str, \ raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, construct_or_raise_parsing_error, \ - parse_field_or_log_error, parse_field_or_no_assertion_or_none, parse_field_or_no_assertion + parse_field_or_log_error, parse_field_or_no_assertion_or_none, parse_field_or_no_assertion, parse_list_of_elements from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -39,15 +39,6 @@ def __init__(self): self.license_expression_parser = LicenseExpressionParser() self.logger = Logger() - def parse_packages(self, package_dicts: List[Dict]) -> List[Package]: - packages = [] - for package_dict in package_dicts: - packages = append_parsed_field_or_log_error(self.logger, packages, package_dict, self.parse_package) - - raise_parsing_error_if_logger_has_messages(self.logger) - - return packages - def parse_package(self, package_dict: Dict) -> Package: logger = Logger() name: Optional[str] = package_dict.get("name") @@ -57,8 +48,8 @@ def parse_package(self, package_dict: Dict) -> Package: built_date: Optional[datetime] = parse_field_or_log_error(logger, package_dict.get("builtDate"), datetime_from_str) - checksums = parse_field_or_log_error(logger, package_dict.get("checksums"), - self.checksum_parser.parse_checksums) + checksums = parse_field_or_log_error(logger, package_dict.get("checksums"), self.checksum_parser.parse_checksum, + field_is_list=True) comment: Optional[str] = package_dict.get("comment") copyright_text: Optional[str] = package_dict.get("copyrightText") description: Optional[str] = package_dict.get("description") @@ -73,16 +64,22 @@ def parse_package(self, package_dict: Dict) -> Package: homepage: Optional[str] = package_dict.get("homepage") license_comments: Optional[str] = package_dict.get("licenseComments") license_concluded = parse_field_or_log_error(logger, package_dict.get("licenseConcluded"), - lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expression), None) + lambda x: parse_field_or_no_assertion_or_none(x, + self.license_expression_parser.parse_license_expression), + None) license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger, package_dict.get("licenseDeclared"), lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expression)) + logger, package_dict.get("licenseDeclared"), + lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expression)) license_info_from_file: Optional[ Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger, package_dict.get( "licenseInfoFromFiles"), - lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expressions)) + lambda + x: parse_field_or_no_assertion_or_none( + x, + self.license_expression_parser.parse_license_expressions)) originator: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error(logger, package_dict.get("originator"), lambda @@ -101,12 +98,9 @@ def parse_package(self, package_dict: Dict) -> Package: datetime_from_str) source_info: Optional[str] = package_dict.get("sourceInfo") summary: Optional[str] = package_dict.get("summary") - supplier: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error(logger, - package_dict.get("supplier"), - lambda - x: parse_field_or_no_assertion( - x, - self.actor_parser.parse_actor)) + supplier: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error( + logger, package_dict.get("supplier"), + lambda x: parse_field_or_no_assertion(x, self.actor_parser.parse_actor)) valid_until_date: Optional[datetime] = parse_field_or_log_error(logger, package_dict.get("validUntilDate"), datetime_from_str) @@ -182,4 +176,3 @@ def parse_primary_package_purpose(primary_package_purpose: str) -> PackagePurpos return PackagePurpose[json_str_to_enum_name(primary_package_purpose)] except KeyError: raise SPDXParsingError([f"Invalid PrimaryPackagePurpose: {primary_package_purpose}"]) - diff --git a/src/parser/json/relationship_parser.py b/src/parser/json/relationship_parser.py index c19c41114..5c7642455 100644 --- a/src/parser/json/relationship_parser.py +++ b/src/parser/json/relationship_parser.py @@ -13,9 +13,9 @@ from src.model.relationship import Relationship, RelationshipType from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, \ - raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, construct_or_raise_parsing_error, \ - parse_field_or_log_error +from src.parser.json.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, \ + construct_or_raise_parsing_error, \ + parse_field_or_log_error, parse_list_of_elements from src.parser.logger import Logger @@ -29,7 +29,7 @@ def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]: relationships = [] relationship_dicts: List[Dict] = input_doc_dict.get("relationships", []) relationships.extend( - parse_field_or_log_error(self.logger, relationship_dicts, self.parse_relationships, [])) + parse_field_or_log_error(self.logger, relationship_dicts, self.parse_relationship, [], True)) document_describes: List[str] = input_doc_dict.get("documentDescribes", []) doc_spdx_id: Optional[str] = input_doc_dict.get("SPDXID") @@ -56,15 +56,6 @@ def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]: return relationships - def parse_relationships(self, relationship_dicts: List[Dict]) -> List[Relationship]: - logger = Logger() - relationships = [] - for relationship_dict in relationship_dicts: - relationships = append_parsed_field_or_log_error(logger, relationships, relationship_dict, - self.parse_relationship) - raise_parsing_error_if_logger_has_messages(logger) - return relationships - def parse_relationship(self, relationship_dict: Dict) -> Relationship: logger = Logger() spdx_element_id: Optional[str] = relationship_dict.get("spdxElementId") diff --git a/src/parser/json/snippet_parser.py b/src/parser/json/snippet_parser.py index 9c7a78ed8..97ab6632c 100644 --- a/src/parser/json/snippet_parser.py +++ b/src/parser/json/snippet_parser.py @@ -17,7 +17,7 @@ from src.model.spdx_none import SpdxNone from src.parser.error import SPDXParsingError from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error, parse_field_or_log_error, \ - raise_parsing_error_if_logger_has_messages, append_parsed_field_or_log_error, parse_field_or_no_assertion_or_none + parse_field_or_no_assertion_or_none from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger @@ -36,15 +36,6 @@ def __init__(self): self.logger = Logger() self.license_expression_parser = LicenseExpressionParser() - def parse_snippets(self, snippet_dicts: List[Dict]) -> List[Snippet]: - snippets = [] - for snippet_dict in snippet_dicts: - snippets = append_parsed_field_or_log_error(self.logger, snippets, snippet_dict, self.parse_snippet) - - raise_parsing_error_if_logger_has_messages(self.logger) - - return snippets - def parse_snippet(self, snippet_dict: Dict) -> Snippet: logger = Logger() spdx_id: Optional[str] = snippet_dict.get("SPDXID") @@ -59,19 +50,13 @@ def parse_snippet(self, snippet_dict: Dict) -> Snippet: license_comment: Optional[str] = snippet_dict.get("licenseComments") concluded_license: Optional[Union[ LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger, snippet_dict.get( - "licenseConcluded"), - lambda - x: parse_field_or_no_assertion_or_none( - x, - self.license_expression_parser.parse_license_expression)) + "licenseConcluded"), lambda x: parse_field_or_no_assertion_or_none(x, + self.license_expression_parser.parse_license_expression)) license_info: Optional[Union[List[ LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger, snippet_dict.get( - "licenseInfoInSnippets"), - lambda - x: parse_field_or_no_assertion_or_none( - x, - self.license_expression_parser.parse_license_expressions)) + "licenseInfoInSnippets"), lambda x: parse_field_or_no_assertion_or_none(x, + self.license_expression_parser.parse_license_expressions)) if logger.has_messages(): raise SPDXParsingError([f"Error while parsing snippet: {logger.get_messages()}"]) diff --git a/tests/parser/test_file_parser.py b/tests/parser/test_file_parser.py index 9735fdf15..5c70c91c8 100644 --- a/tests/parser/test_file_parser.py +++ b/tests/parser/test_file_parser.py @@ -16,6 +16,7 @@ from src.model.file import FileType from src.model.license_expression import LicenseExpression from src.parser.error import SPDXParsingError +from src.parser.json.dict_parsing_functions import parse_list_of_elements from src.parser.json.file_parser import FileParser @@ -102,7 +103,7 @@ def test_parse_invalid_files(): ] with pytest.raises(SPDXParsingError) as err: - file_parser.parse_files(files) + parse_list_of_elements(files, file_parser.parse_file) TestCase().assertCountEqual(err.value.get_messages(), [ "Error while constructing File: ['SetterError File: type of argument " '"checksums" must be a list; got NoneType instead: None\']', 'Error while constructing File: [\'SetterError File: type of argument "name" ' "must be str; got NoneType instead: None']", diff --git a/tests/parser/test_package_parser.py b/tests/parser/test_package_parser.py index 0577b599e..084525465 100644 --- a/tests/parser/test_package_parser.py +++ b/tests/parser/test_package_parser.py @@ -18,6 +18,7 @@ from src.model.license_expression import LicenseExpression from src.model.package import PackageVerificationCode, ExternalPackageRef, ExternalPackageRefCategory, PackagePurpose from src.parser.error import SPDXParsingError +from src.parser.json.dict_parsing_functions import parse_list_of_elements from src.parser.json.package_parser import PackageParser @@ -179,7 +180,7 @@ def test_parse_packages(): ] with pytest.raises(SPDXParsingError) as err: - package_parser.parse_packages(packages_list) + parse_list_of_elements(packages_list, package_parser.parse_package) TestCase().assertCountEqual(err.value.get_messages(), ['Error while parsing Package: ["Error while parsing Checksum: ' From f3bca2d7601acf2dfa8a130912af02e4caf85066 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 28 Dec 2022 10:43:28 +0100 Subject: [PATCH 057/362] [issue-305, review] reformat, type hints, fix typos, error messages Signed-off-by: Meret Behrens --- src/parser/json/annotation_parser.py | 16 +++++------- src/parser/json/checksum_parser.py | 2 +- src/parser/json/dict_parsing_functions.py | 11 +++++--- src/parser/json/package_parser.py | 32 +++++++++-------------- src/parser/json/relationship_parser.py | 17 ++++++------ tests/parser/test_checksum_parser.py | 2 +- tests/parser/test_creation_info_parser.py | 2 +- tests/parser/test_file_parser.py | 2 +- tests/parser/test_package_parser.py | 13 +++++++-- tests/parser/test_relationship_parser.py | 4 +-- 10 files changed, 52 insertions(+), 49 deletions(-) diff --git a/src/parser/json/annotation_parser.py b/src/parser/json/annotation_parser.py index 0feec8e00..0dbf4e7fc 100644 --- a/src/parser/json/annotation_parser.py +++ b/src/parser/json/annotation_parser.py @@ -34,11 +34,8 @@ def parse_all_annotations(self, input_doc_dict: Dict) -> List[Annotation]: self.parse_annotations_from_object(annotations, [input_doc_dict]) reviews: List[Dict] = input_doc_dict.get("revieweds", []) for review in reviews: - annotations = append_parsed_field_or_log_error(self.logger, annotations, review, - lambda x: self.parse_review(x, - spdx_id=input_doc_dict.get( - "SPDXID"))) - + annotations = append_parsed_field_or_log_error( + self.logger, annotations, review, lambda x: self.parse_review(x, spdx_id=input_doc_dict.get("SPDXID"))) packages: List[Dict] = input_doc_dict.get("packages", []) self.parse_annotations_from_object(annotations, packages) files: List[Dict] = input_doc_dict.get("files", []) @@ -54,8 +51,9 @@ def parse_annotations_from_object(self, annotations: List[Annotation], element_l element_spdx_id: Optional[str] = element.get("SPDXID") element_annotations: List[Dict] = element.get("annotations", []) annotations.extend(parse_field_or_log_error(self.logger, element_annotations, - lambda y: self.parse_annotation(y, spdx_id=element_spdx_id), - [], True)) + + lambda y: self.parse_annotation(y, spdx_id=element_spdx_id), + [], True)) def parse_annotation(self, annotation_dict: Dict, spdx_id: Optional[str] = None) -> Annotation: logger = Logger() @@ -75,8 +73,8 @@ def parse_annotation(self, annotation_dict: Dict, spdx_id: Optional[str] = None) raise_parsing_error_if_logger_has_messages(logger, "Annotation") annotation_dict = construct_or_raise_parsing_error(Annotation, dict(spdx_id=spdx_id, annotation_type=annotation_type, - annotator=annotator, annotation_date=annotation_date, - annotation_comment=annotation_comment)) + annotator=annotator, annotation_date=annotation_date, + annotation_comment=annotation_comment)) return annotation_dict diff --git a/src/parser/json/checksum_parser.py b/src/parser/json/checksum_parser.py index a02d79b01..c86cb3e7d 100644 --- a/src/parser/json/checksum_parser.py +++ b/src/parser/json/checksum_parser.py @@ -30,7 +30,7 @@ def parse_checksum(checksum_dict: Dict) -> Checksum: try: checksum_algorithm = ChecksumAlgorithm[algorithm] except KeyError: - logger.append(f"Invalid Algorithm for checksum: {algorithm}") + logger.append(f"Invalid ChecksumAlgorithm: {algorithm}") checksum_algorithm = None checksum_value: Optional[str] = checksum_dict.get("checksumValue") raise_parsing_error_if_logger_has_messages(logger, "Checksum") diff --git a/src/parser/json/dict_parsing_functions.py b/src/parser/json/dict_parsing_functions.py index 19ce4c5e6..55262b61d 100644 --- a/src/parser/json/dict_parsing_functions.py +++ b/src/parser/json/dict_parsing_functions.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from datetime import datetime -from typing import Any, Callable, Dict, List, Union +from typing import Any, Callable, Dict, List, Union, Optional from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone @@ -74,7 +74,8 @@ def raise_parsing_error_if_logger_has_messages(logger: Logger, parsed_object_nam else: raise SPDXParsingError(logger.get_messages()) -def parse_field_or_no_assertion_or_none(field: str, method_for_field: Callable=lambda x: x) -> Any: + +def parse_field_or_no_assertion_or_none(field: Optional[str], method_for_field: Callable = lambda x: x) -> Any: if field == SpdxNoAssertion().__str__(): return SpdxNoAssertion() elif field == SpdxNone().__str__(): @@ -82,7 +83,8 @@ def parse_field_or_no_assertion_or_none(field: str, method_for_field: Callable=l else: return method_for_field(field) -def parse_field_or_no_assertion(field: str, method_for_field: Callable = lambda x: x) -> Any: + +def parse_field_or_no_assertion(field: Optional[str], method_for_field: Callable = lambda x: x) -> Any: if field == SpdxNoAssertion().__str__(): return SpdxNoAssertion() else: @@ -94,6 +96,7 @@ def parse_list_of_elements(list_of_elements: List[Dict], method_to_parse_element logger = Logger() parsed_elements = [] for element_dict in list_of_elements: - parsed_elements = append_parsed_field_or_log_error(logger, parsed_elements, element_dict, method_to_parse_element) + parsed_elements = append_parsed_field_or_log_error(logger, parsed_elements, element_dict, + method_to_parse_element) raise_parsing_error_if_logger_has_messages(logger) return parsed_elements diff --git a/src/parser/json/package_parser.py b/src/parser/json/package_parser.py index bc73f9d90..eb7118dc8 100644 --- a/src/parser/json/package_parser.py +++ b/src/parser/json/package_parser.py @@ -63,29 +63,23 @@ def parse_package(self, package_dict: Dict) -> Package: lambda x: x, True) homepage: Optional[str] = package_dict.get("homepage") license_comments: Optional[str] = package_dict.get("licenseComments") - license_concluded = parse_field_or_log_error(logger, package_dict.get("licenseConcluded"), - lambda x: parse_field_or_no_assertion_or_none(x, - self.license_expression_parser.parse_license_expression), - None) + license_concluded = parse_field_or_log_error( + logger, package_dict.get("licenseConcluded"), + lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expression), + None) license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( logger, package_dict.get("licenseDeclared"), lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expression)) - license_info_from_file: Optional[ - Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger, - package_dict.get( - "licenseInfoFromFiles"), - lambda - x: parse_field_or_no_assertion_or_none( - x, - self.license_expression_parser.parse_license_expressions)) - originator: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error(logger, - package_dict.get("originator"), - lambda - x: parse_field_or_no_assertion( - x, - self.actor_parser.parse_actor)) + license_info_from_file: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = \ + parse_field_or_log_error( + logger, package_dict.get("licenseInfoFromFiles"), + lambda x: parse_field_or_no_assertion_or_none(x, + self.license_expression_parser.parse_license_expressions)) + originator: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error( + logger, package_dict.get("originator"), + lambda x: parse_field_or_no_assertion(x, self.actor_parser.parse_actor)) package_file_name: Optional[str] = package_dict.get("packageFileName") package_verification_code: Optional[ @@ -155,7 +149,7 @@ def parse_external_ref_category(external_ref_category_str: str) -> ExternalPacka external_ref_category = ExternalPackageRefCategory[ json_str_to_enum_name(external_ref_category_str)] except KeyError: - raise SPDXParsingError([f"Invalid Category for ExternalPackageRef {external_ref_category_str}"]) + raise SPDXParsingError([f"Invalid ExternalPackageRefCategory: {external_ref_category_str}"]) return external_ref_category diff --git a/src/parser/json/relationship_parser.py b/src/parser/json/relationship_parser.py index 5c7642455..defecd3bc 100644 --- a/src/parser/json/relationship_parser.py +++ b/src/parser/json/relationship_parser.py @@ -41,10 +41,9 @@ def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]: package_dicts: List[Dict] = input_doc_dict.get("packages", []) - relationships.extend( - parse_field_or_log_error(self.logger, package_dicts, lambda x: self.parse_has_files(package_dicts=x, - existing_relationships=relationships), - [])) + relationships.extend(parse_field_or_log_error( + self.logger, package_dicts, + lambda x: self.parse_has_files(package_dicts=x, existing_relationships=relationships), [])) file_dicts: List[Dict] = input_doc_dict.get("files", []) @@ -144,13 +143,13 @@ def get_all_relationships_without_comments(existing_relationships: List[Relation def invert_relationship(self, relationship: Relationship) -> Relationship: return Relationship(related_spdx_element_id=relationship.spdx_element_id, spdx_element_id=relationship.related_spdx_element_id, - relationship_type=self.invvert_relationship_types[relationship.relationship_type], + relationship_type=self.invert_relationship_types[relationship.relationship_type], comment=relationship.comment) - invvert_relationship_types = {RelationshipType.DESCRIBES: RelationshipType.DESCRIBED_BY, - RelationshipType.DESCRIBED_BY: RelationshipType.DESCRIBES, - RelationshipType.CONTAINS: RelationshipType.CONTAINED_BY, - RelationshipType.CONTAINED_BY: RelationshipType.CONTAINS} + invert_relationship_types = {RelationshipType.DESCRIBES: RelationshipType.DESCRIBED_BY, + RelationshipType.DESCRIBED_BY: RelationshipType.DESCRIBES, + RelationshipType.CONTAINS: RelationshipType.CONTAINED_BY, + RelationshipType.CONTAINED_BY: RelationshipType.CONTAINS} @staticmethod def parse_file_dependencies(file_dicts: List[Dict]) -> List[ diff --git a/tests/parser/test_checksum_parser.py b/tests/parser/test_checksum_parser.py index cd27a8bed..11ad1610c 100644 --- a/tests/parser/test_checksum_parser.py +++ b/tests/parser/test_checksum_parser.py @@ -41,7 +41,7 @@ def test_parse_invalid_checksum(): checksum_parser.parse_checksum(checksum_dict) TestCase().assertCountEqual(err.value.get_messages(), - ["Error while parsing Checksum: ['Invalid Algorithm for checksum: SHA']"]) + ["Error while parsing Checksum: ['Invalid ChecksumAlgorithm: SHA']"]) def test_parse_incomplete_checksum(): diff --git a/tests/parser/test_creation_info_parser.py b/tests/parser/test_creation_info_parser.py index daa2785bb..8126b7fae 100644 --- a/tests/parser/test_creation_info_parser.py +++ b/tests/parser/test_creation_info_parser.py @@ -21,7 +21,7 @@ from src.parser.json.creation_info_parser import CreationInfoParser -def test_pares_creation_info(): +def test_parse_creation_info(): creation_info_parser = CreationInfoParser() doc_dict = { "spdxVersion": "2.3", diff --git a/tests/parser/test_file_parser.py b/tests/parser/test_file_parser.py index 5c70c91c8..e3ae52ea6 100644 --- a/tests/parser/test_file_parser.py +++ b/tests/parser/test_file_parser.py @@ -107,7 +107,7 @@ def test_parse_invalid_files(): TestCase().assertCountEqual(err.value.get_messages(), [ "Error while constructing File: ['SetterError File: type of argument " '"checksums" must be a list; got NoneType instead: None\']', 'Error while constructing File: [\'SetterError File: type of argument "name" ' "must be str; got NoneType instead: None']", - 'Error while parsing File: ["Error while parsing Checksum: [\'Invalid Algorithm for checksum: MD\']"]']) + 'Error while parsing File: ["Error while parsing Checksum: [\'Invalid ChecksumAlgorithm: MD\']"]']) def test_parse_file_types(): diff --git a/tests/parser/test_package_parser.py b/tests/parser/test_package_parser.py index 084525465..d5b9d9e7e 100644 --- a/tests/parser/test_package_parser.py +++ b/tests/parser/test_package_parser.py @@ -156,7 +156,7 @@ def test_parse_invalid_package(): package_parser.parse_package(package_dict) TestCase().assertCountEqual(err.value.get_messages(), [ - 'Error while parsing Package: ["Error while parsing Checksum: [\'Invalid Algorithm for checksum: SHA\']"]']) + 'Error while parsing Package: ["Error while parsing Checksum: [\'Invalid ChecksumAlgorithm: SHA\']"]']) def test_parse_packages(): @@ -184,7 +184,7 @@ def test_parse_packages(): TestCase().assertCountEqual(err.value.get_messages(), ['Error while parsing Package: ["Error while parsing Checksum: ' - '[\'Invalid Algorithm for checksum: SHA\']"]', + '[\'Invalid ChecksumAlgorithm: SHA\']"]', "Error while constructing Package: ['SetterError Package: type of argument " '"name" must be str; got int instead: 5\']']) @@ -200,3 +200,12 @@ def test_parse_external_ref(): TestCase().assertCountEqual(err.value.get_messages(), [ "Error while constructing ExternalPackageRef: ['SetterError " 'ExternalPackageRef: type of argument "category" must be ' "src.model.package.ExternalPackageRefCategory; got NoneType instead: None', " '\'SetterError ExternalPackageRef: type of argument "locator" must be str; ' "got NoneType instead: None']"]) + +def test_parse_invalid_external_package_ref_category(): + package_parser = PackageParser() + external_package_ref_category = "TEST" + + with pytest.raises(SPDXParsingError) as err: + package_parser.parse_external_ref_category(external_package_ref_category) + + TestCase().assertCountEqual(err.value.get_messages(), ["Invalid ExternalPackageRefCategory: TEST"]) diff --git a/tests/parser/test_relationship_parser.py b/tests/parser/test_relationship_parser.py index 2b814e1d2..d1afa642d 100644 --- a/tests/parser/test_relationship_parser.py +++ b/tests/parser/test_relationship_parser.py @@ -129,13 +129,13 @@ def test_parse_has_files_without_duplicating_relationships(): "hasFiles": ["SPDXRef-File1", "SPDXRef-File2"] }] } - created_relationships = [ + existing_relationships = [ Relationship(spdx_element_id="SPDXRef-Package", relationship_type=RelationshipType.CONTAINS, related_spdx_element_id="SPDXRef-File1", comment="This relationship has a comment."), Relationship(spdx_element_id="SPDXRef-File2", relationship_type=RelationshipType.CONTAINED_BY, related_spdx_element_id="SPDXRef-Package")] relationships = relationship_parser.parse_has_files(document_dict.get("packages"), - existing_relationships=created_relationships) + existing_relationships=existing_relationships) assert len(relationships) == 0 From 716becd903443a88e6ce20b7767c12186c457655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 29 Dec 2022 08:38:47 +0100 Subject: [PATCH 058/362] [issue-389] allow NONE and NOASSERTION in related_spdx_element_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/model/relationship.py | 10 ++++++---- src/parser/json/relationship_parser.py | 4 ++-- src/validation/relationship_validator.py | 14 +++++++++----- tests/model/test_relationship.py | 7 ++++--- tests/parser/test_relationship_parser.py | 5 +++-- tests/validation/test_relationship_validator.py | 8 ++++++-- 6 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/model/relationship.py b/src/model/relationship.py index 1b34fe051..88026c9fd 100644 --- a/src/model/relationship.py +++ b/src/model/relationship.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. from enum import auto, Enum -from typing import Optional +from typing import Optional, Union +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone from src.model.typing.dataclass_with_properties import dataclass_with_properties from src.model.typing.type_checks import check_types_and_set_values @@ -67,9 +69,9 @@ class RelationshipType(Enum): class Relationship: spdx_element_id: str relationship_type: RelationshipType - related_spdx_element_id: str + related_spdx_element_id: Union[str, SpdxNone, SpdxNoAssertion] comment: Optional[str] = None - def __init__(self, spdx_element_id: str, relationship_type: RelationshipType, related_spdx_element_id: str, - comment: Optional[str] = None): + def __init__(self, spdx_element_id: str, relationship_type: RelationshipType, + related_spdx_element_id: Union[str, SpdxNone, SpdxNoAssertion], comment: Optional[str] = None): check_types_and_set_values(self, locals()) diff --git a/src/parser/json/relationship_parser.py b/src/parser/json/relationship_parser.py index defecd3bc..e2c910e51 100644 --- a/src/parser/json/relationship_parser.py +++ b/src/parser/json/relationship_parser.py @@ -15,7 +15,7 @@ from src.parser.error import SPDXParsingError from src.parser.json.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, \ construct_or_raise_parsing_error, \ - parse_field_or_log_error, parse_list_of_elements + parse_field_or_log_error, parse_field_or_no_assertion_or_none from src.parser.logger import Logger @@ -58,7 +58,7 @@ def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]: def parse_relationship(self, relationship_dict: Dict) -> Relationship: logger = Logger() spdx_element_id: Optional[str] = relationship_dict.get("spdxElementId") - related_spdx_element: Optional[str] = relationship_dict.get("relatedSpdxElement") + related_spdx_element: Optional[str] = parse_field_or_no_assertion_or_none(relationship_dict.get("relatedSpdxElement")) relationship_type: Optional[RelationshipType] = parse_field_or_log_error(logger, relationship_dict.get( "relationshipType"), self.parse_relationship_type) relationship_comment: Optional[str] = relationship_dict.get("comment") diff --git a/src/validation/relationship_validator.py b/src/validation/relationship_validator.py index a9292253d..edf875469 100644 --- a/src/validation/relationship_validator.py +++ b/src/validation/relationship_validator.py @@ -9,10 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List +from typing import List, Union from src.model.document import Document from src.model.relationship import Relationship, RelationshipType +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone from src.validation.spdx_id_validators import validate_spdx_id from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType @@ -30,12 +32,14 @@ def validate_relationship(relationship: Relationship, document: Document, spdx_v context = ValidationContext(element_type=SpdxElementType.RELATIONSHIP, full_element=relationship) - first_id: str = relationship.spdx_element_id - second_id: str = relationship.related_spdx_element_id relationship_type: RelationshipType = relationship.relationship_type - for spdx_id in [first_id, second_id]: - messages: List[str] = validate_spdx_id(spdx_id, document, check_document=True) + messages: List[str] = validate_spdx_id(relationship.spdx_element_id, document, check_document=True) + for message in messages: + validation_messages.append(ValidationMessage(message, context)) + + if relationship.related_spdx_element_id not in [SpdxNone(), SpdxNoAssertion()]: + messages: List[str] = validate_spdx_id(relationship.related_spdx_element_id, document, check_document=True) for message in messages: validation_messages.append(ValidationMessage(message, context)) diff --git a/tests/model/test_relationship.py b/tests/model/test_relationship.py index 4d0a0a681..c3b99a689 100644 --- a/tests/model/test_relationship.py +++ b/tests/model/test_relationship.py @@ -1,19 +1,20 @@ import pytest from src.model.relationship import Relationship, RelationshipType +from src.model.spdx_no_assertion import SpdxNoAssertion def test_correct_initialization(): - relationship = Relationship("id", RelationshipType.OTHER, "other_id", "comment") + relationship = Relationship("id", RelationshipType.OTHER, SpdxNoAssertion(), "comment") assert relationship.spdx_element_id == "id" assert relationship.relationship_type == RelationshipType.OTHER - assert relationship.related_spdx_element_id == "other_id" + assert relationship.related_spdx_element_id == SpdxNoAssertion() assert relationship.comment == "comment" def test_wrong_type_in_spdx_element_id(): with pytest.raises(TypeError): - Relationship(42, RelationshipType.OTHER, "other_id") + Relationship(SpdxNoAssertion(), RelationshipType.OTHER, "other_id") def test_wrong_type_in_relationship_type(): diff --git a/tests/parser/test_relationship_parser.py b/tests/parser/test_relationship_parser.py index d1afa642d..27f2c4f09 100644 --- a/tests/parser/test_relationship_parser.py +++ b/tests/parser/test_relationship_parser.py @@ -13,6 +13,7 @@ import pytest from src.model.relationship import RelationshipType, Relationship +from src.model.spdx_no_assertion import SpdxNoAssertion from src.parser.error import SPDXParsingError from src.parser.json.relationship_parser import RelationshipParser @@ -23,7 +24,7 @@ def test_parse_relationship(): relationship_dict = { "spdxElementId": "SPDXRef-DOCUMENT", "relationshipType": "CONTAINS", - "relatedSpdxElement": "SPDXRef-Package", + "relatedSpdxElement": "NOASSERTION", "comment": "Comment." } @@ -31,7 +32,7 @@ def test_parse_relationship(): assert relationship.relationship_type == RelationshipType.CONTAINS assert relationship.spdx_element_id == "SPDXRef-DOCUMENT" - assert relationship.related_spdx_element_id == "SPDXRef-Package" + assert relationship.related_spdx_element_id == SpdxNoAssertion() assert relationship.comment == "Comment." diff --git a/tests/validation/test_relationship_validator.py b/tests/validation/test_relationship_validator.py index 1d56bd6af..2e2a08b95 100644 --- a/tests/validation/test_relationship_validator.py +++ b/tests/validation/test_relationship_validator.py @@ -15,15 +15,19 @@ from src.model.document import Document from src.model.relationship import Relationship, RelationshipType +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone from src.validation.relationship_validator import validate_relationship from src.validation.validation_message import ValidationMessage, SpdxElementType, ValidationContext from tests.valid_defaults import get_document, get_package, get_relationship, get_file -def test_valid_relationship(): +@pytest.mark.parametrize("related_spdx_element", + ["SPDXRef-Package", SpdxNoAssertion(), SpdxNone()]) +def test_valid_relationship(related_spdx_element): document: Document = get_document(packages=[get_package(spdx_id="SPDXRef-Package")]) - relationship = Relationship("SPDXRef-DOCUMENT", RelationshipType.AMENDS, "SPDXRef-Package", comment="comment") + relationship = Relationship("SPDXRef-DOCUMENT", RelationshipType.AMENDS, related_spdx_element, comment="comment") validation_messages: List[ValidationMessage] = validate_relationship(relationship, document, "2.3") assert validation_messages == [] From b62b0fc7c57b9fe006cde6ddd364c909dd56d931 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 29 Dec 2022 14:16:44 +0100 Subject: [PATCH 059/362] [format] delete unused imports Signed-off-by: Meret Behrens --- src/parser/json/actor_parser.py | 3 +-- src/parser/json/annotation_parser.py | 3 +-- src/parser/json/checksum_parser.py | 7 +++---- src/parser/json/dict_parsing_functions.py | 2 +- src/parser/json/file_parser.py | 2 +- src/parser/json/package_parser.py | 2 +- src/validation/relationship_validator.py | 2 +- tests/parser/test_extracted_licensing_info_parser.py | 1 - tests/parser/test_json_parser.py | 1 - tests/validation/test_external_document_ref_validator.py | 6 ++---- 10 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/parser/json/actor_parser.py b/src/parser/json/actor_parser.py index cfd835d5b..31082e0fc 100644 --- a/src/parser/json/actor_parser.py +++ b/src/parser/json/actor_parser.py @@ -9,10 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. import re -from typing import Union, Pattern, Match, Optional +from typing import Pattern, Match, Optional from src.model.actor import Actor, ActorType -from src.model.spdx_no_assertion import SpdxNoAssertion from src.parser.error import SPDXParsingError from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error diff --git a/src/parser/json/annotation_parser.py b/src/parser/json/annotation_parser.py index 0dbf4e7fc..44bbe6242 100644 --- a/src/parser/json/annotation_parser.py +++ b/src/parser/json/annotation_parser.py @@ -16,8 +16,7 @@ from src.parser.error import SPDXParsingError from src.parser.json.actor_parser import ActorParser from src.parser.json.dict_parsing_functions import datetime_from_str, construct_or_raise_parsing_error, \ - parse_field_or_log_error, append_parsed_field_or_log_error, raise_parsing_error_if_logger_has_messages, \ - parse_list_of_elements + parse_field_or_log_error, append_parsed_field_or_log_error, raise_parsing_error_if_logger_has_messages from src.parser.logger import Logger diff --git a/src/parser/json/checksum_parser.py b/src/parser/json/checksum_parser.py index c86cb3e7d..d6f82c412 100644 --- a/src/parser/json/checksum_parser.py +++ b/src/parser/json/checksum_parser.py @@ -8,12 +8,11 @@ # 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 typing import Dict, List, Optional +from typing import Dict, Optional from src.model.checksum import Checksum, ChecksumAlgorithm -from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, \ - raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, construct_or_raise_parsing_error +from src.parser.json.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, \ + construct_or_raise_parsing_error from src.parser.logger import Logger diff --git a/src/parser/json/dict_parsing_functions.py b/src/parser/json/dict_parsing_functions.py index 55262b61d..e7b0dd707 100644 --- a/src/parser/json/dict_parsing_functions.py +++ b/src/parser/json/dict_parsing_functions.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from datetime import datetime -from typing import Any, Callable, Dict, List, Union, Optional +from typing import Any, Callable, Dict, List, Optional from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone diff --git a/src/parser/json/file_parser.py b/src/parser/json/file_parser.py index e9c236aee..8b92b0290 100644 --- a/src/parser/json/file_parser.py +++ b/src/parser/json/file_parser.py @@ -18,7 +18,7 @@ from src.parser.json.checksum_parser import ChecksumParser from src.parser.json.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ construct_or_raise_parsing_error, parse_field_or_log_error, \ - parse_field_or_no_assertion_or_none, parse_list_of_elements + parse_field_or_no_assertion_or_none from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger diff --git a/src/parser/json/package_parser.py b/src/parser/json/package_parser.py index eb7118dc8..5c2c53494 100644 --- a/src/parser/json/package_parser.py +++ b/src/parser/json/package_parser.py @@ -22,7 +22,7 @@ from src.parser.json.checksum_parser import ChecksumParser from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, datetime_from_str, \ raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, construct_or_raise_parsing_error, \ - parse_field_or_log_error, parse_field_or_no_assertion_or_none, parse_field_or_no_assertion, parse_list_of_elements + parse_field_or_log_error, parse_field_or_no_assertion_or_none, parse_field_or_no_assertion from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger diff --git a/src/validation/relationship_validator.py b/src/validation/relationship_validator.py index edf875469..87da7f9ba 100644 --- a/src/validation/relationship_validator.py +++ b/src/validation/relationship_validator.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List, Union +from typing import List from src.model.document import Document from src.model.relationship import Relationship, RelationshipType diff --git a/tests/parser/test_extracted_licensing_info_parser.py b/tests/parser/test_extracted_licensing_info_parser.py index 47f70ef6b..8b529aace 100644 --- a/tests/parser/test_extracted_licensing_info_parser.py +++ b/tests/parser/test_extracted_licensing_info_parser.py @@ -12,7 +12,6 @@ import pytest -from src.model.spdx_no_assertion import SpdxNoAssertion from src.parser.error import SPDXParsingError from src.parser.json.extracted_licensing_info_parser import ExtractedLicensingInfoParser diff --git a/tests/parser/test_json_parser.py b/tests/parser/test_json_parser.py index 3b6ef3133..65a24cc47 100644 --- a/tests/parser/test_json_parser.py +++ b/tests/parser/test_json_parser.py @@ -13,7 +13,6 @@ import pytest from src.model.document import Document -from src.parser.error import SPDXParsingError from src.parser.json.json_parser import JsonParser def test_parse_json_file_not_found(): diff --git a/tests/validation/test_external_document_ref_validator.py b/tests/validation/test_external_document_ref_validator.py index 1c2e5cf52..84ead91ad 100644 --- a/tests/validation/test_external_document_ref_validator.py +++ b/tests/validation/test_external_document_ref_validator.py @@ -11,12 +11,10 @@ from typing import List -import pytest - from src.model.external_document_ref import ExternalDocumentRef from src.validation.external_document_ref_validator import validate_external_document_ref -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.valid_defaults import get_checksum, get_external_document_ref +from src.validation.validation_message import ValidationMessage +from tests.valid_defaults import get_checksum def test_valid_external_document_ref(): From 64dd01511beccb1529bdace8805eb59404f7d0a7 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 29 Dec 2022 14:07:42 +0100 Subject: [PATCH 060/362] [issue-381] move datetime conversion from parser to src level Signed-off-by: Meret Behrens --- src/datetime_conversions.py | 31 +++++++++++++++++++++ src/parser/json/annotation_parser.py | 3 +- src/parser/json/creation_info_parser.py | 3 +- src/parser/json/dict_parsing_functions.py | 12 -------- src/parser/json/package_parser.py | 3 +- tests/parser/test_dict_parsing_functions.py | 3 +- 6 files changed, 39 insertions(+), 16 deletions(-) create mode 100644 src/datetime_conversions.py diff --git a/src/datetime_conversions.py b/src/datetime_conversions.py new file mode 100644 index 000000000..3c49d9391 --- /dev/null +++ b/src/datetime_conversions.py @@ -0,0 +1,31 @@ +# 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 datetime import datetime + +from src.parser.error import SPDXParsingError + + +def datetime_from_str(date_str: str) -> datetime: + if not isinstance(date_str, str): + raise SPDXParsingError([f"Could not convert str to datetime, invalid type: {type(date_str).__name__}"]) + try: + date = datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%SZ") + except ValueError: + raise SPDXParsingError( + [f'Could not convert str to datetime, format of {date_str} does not match "%Y-%m-%dT%H:%M:%SZ"']) + return date + +def datetime_to_iso_string(date: datetime) -> str: + """ + Return an ISO-8601 representation of a datetime object. + """ + return date.isoformat() + "Z" + diff --git a/src/parser/json/annotation_parser.py b/src/parser/json/annotation_parser.py index 44bbe6242..85bf4115d 100644 --- a/src/parser/json/annotation_parser.py +++ b/src/parser/json/annotation_parser.py @@ -15,8 +15,9 @@ from src.model.annotation import Annotation, AnnotationType from src.parser.error import SPDXParsingError from src.parser.json.actor_parser import ActorParser -from src.parser.json.dict_parsing_functions import datetime_from_str, construct_or_raise_parsing_error, \ +from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error, \ parse_field_or_log_error, append_parsed_field_or_log_error, raise_parsing_error_if_logger_has_messages +from src.datetime_conversions import datetime_from_str from src.parser.logger import Logger diff --git a/src/parser/json/creation_info_parser.py b/src/parser/json/creation_info_parser.py index 5f8eaab40..51c115d10 100644 --- a/src/parser/json/creation_info_parser.py +++ b/src/parser/json/creation_info_parser.py @@ -19,9 +19,10 @@ from src.parser.error import SPDXParsingError from src.parser.json.actor_parser import ActorParser from src.parser.json.checksum_parser import ChecksumParser -from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, datetime_from_str, \ +from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, \ raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error, parse_field_or_log_error, \ parse_field_or_no_assertion +from src.datetime_conversions import datetime_from_str from src.parser.logger import Logger diff --git a/src/parser/json/dict_parsing_functions.py b/src/parser/json/dict_parsing_functions.py index e7b0dd707..e413802f9 100644 --- a/src/parser/json/dict_parsing_functions.py +++ b/src/parser/json/dict_parsing_functions.py @@ -8,7 +8,6 @@ # 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 datetime import datetime from typing import Any, Callable, Dict, List, Optional from src.model.spdx_no_assertion import SpdxNoAssertion @@ -18,17 +17,6 @@ from src.parser.logger import Logger -def datetime_from_str(date_str: str) -> datetime: - if not isinstance(date_str, str): - raise SPDXParsingError([f"Could not convert str to datetime, invalid type: {type(date_str).__name__}"]) - try: - date = datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%SZ") - except ValueError: - raise SPDXParsingError( - [f'Could not convert str to datetime, format of {date_str} does not match "%Y-%m-%dT%H:%M:%SZ"']) - return date - - def json_str_to_enum_name(json_str: str) -> str: if not isinstance(json_str, str): raise SPDXParsingError([f"Type for enum must be str not {type(json_str).__name__}"]) diff --git a/src/parser/json/package_parser.py b/src/parser/json/package_parser.py index 5c2c53494..82c1e4d95 100644 --- a/src/parser/json/package_parser.py +++ b/src/parser/json/package_parser.py @@ -20,9 +20,10 @@ from src.parser.error import SPDXParsingError from src.parser.json.actor_parser import ActorParser from src.parser.json.checksum_parser import ChecksumParser -from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, datetime_from_str, \ +from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, \ raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, construct_or_raise_parsing_error, \ parse_field_or_log_error, parse_field_or_no_assertion_or_none, parse_field_or_no_assertion +from src.datetime_conversions import datetime_from_str from src.parser.json.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger diff --git a/tests/parser/test_dict_parsing_functions.py b/tests/parser/test_dict_parsing_functions.py index 688f44df9..3c8b872b9 100644 --- a/tests/parser/test_dict_parsing_functions.py +++ b/tests/parser/test_dict_parsing_functions.py @@ -16,8 +16,9 @@ from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import datetime_from_str, json_str_to_enum_name, \ +from src.parser.json.dict_parsing_functions import json_str_to_enum_name, \ parse_field_or_no_assertion, parse_field_or_no_assertion_or_none +from src.datetime_conversions import datetime_from_str def test_datetime_from_str(): From 0a3ed5d947e1dfade863e22a13e054605e598241 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 29 Dec 2022 16:27:09 +0100 Subject: [PATCH 061/362] [issue-381] add tag-value writer Signed-off-by: Meret Behrens --- src/model/actor.py | 7 + src/writer/tagvalue/__init__.py | 11 ++ src/writer/tagvalue/annotation_writer.py | 26 ++++ src/writer/tagvalue/checksum_writer.py | 31 +++++ src/writer/tagvalue/creation_info_writer.py | 46 ++++++ .../extracted_licensing_info_writer.py | 28 ++++ src/writer/tagvalue/file_writer.py | 42 ++++++ src/writer/tagvalue/package_writer.py | 85 ++++++++++++ src/writer/tagvalue/relationship_writer.py | 24 ++++ src/writer/tagvalue/snippet_writer.py | 34 +++++ src/writer/tagvalue/tagvalue_writer.py | 87 ++++++++++++ .../tagvalue_writer_helper_functions.py | 131 ++++++++++++++++++ tests/writer/tagvalue/__init__.py | 10 ++ .../expected_results/expected_tag_value.spdx | 54 ++++++++ tests/writer/tagvalue/test_package_writer.py | 54 ++++++++ tests/writer/tagvalue/test_tagvalue_writer.py | 63 +++++++++ 16 files changed, 733 insertions(+) create mode 100644 src/writer/tagvalue/__init__.py create mode 100644 src/writer/tagvalue/annotation_writer.py create mode 100644 src/writer/tagvalue/checksum_writer.py create mode 100644 src/writer/tagvalue/creation_info_writer.py create mode 100644 src/writer/tagvalue/extracted_licensing_info_writer.py create mode 100644 src/writer/tagvalue/file_writer.py create mode 100644 src/writer/tagvalue/package_writer.py create mode 100644 src/writer/tagvalue/relationship_writer.py create mode 100644 src/writer/tagvalue/snippet_writer.py create mode 100644 src/writer/tagvalue/tagvalue_writer.py create mode 100644 src/writer/tagvalue/tagvalue_writer_helper_functions.py create mode 100644 tests/writer/tagvalue/__init__.py create mode 100644 tests/writer/tagvalue/expected_results/expected_tag_value.spdx create mode 100644 tests/writer/tagvalue/test_package_writer.py create mode 100644 tests/writer/tagvalue/test_tagvalue_writer.py diff --git a/src/model/actor.py b/src/model/actor.py index fff81c811..a44da8939 100644 --- a/src/model/actor.py +++ b/src/model/actor.py @@ -29,3 +29,10 @@ class Actor: def __init__(self, actor_type: ActorType, name: str, email: Optional[str] = None): check_types_and_set_values(self, locals()) + + def to_serialized_string(self) -> str: + """ + All serialization formats use the same representation of an actor, so this method is included in the data model + """ + optional_email = f" ({self.email})" if self.email else "" + return "".join([f"{self.actor_type.name.title()}:", f" {self.name}", optional_email]) diff --git a/src/writer/tagvalue/__init__.py b/src/writer/tagvalue/__init__.py new file mode 100644 index 000000000..b0981f064 --- /dev/null +++ b/src/writer/tagvalue/__init__.py @@ -0,0 +1,11 @@ +# 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. + diff --git a/src/writer/tagvalue/annotation_writer.py b/src/writer/tagvalue/annotation_writer.py new file mode 100644 index 000000000..cb7c29ef4 --- /dev/null +++ b/src/writer/tagvalue/annotation_writer.py @@ -0,0 +1,26 @@ +# 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 typing import TextIO + +from src.datetime_conversions import datetime_to_iso_string +from src.model.annotation import Annotation +from src.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value + + +def write_annotation(annotation: Annotation, text_output: TextIO): + """ + Write the fields of a single annotation to text_output. + """ + write_value("Annotator", annotation.annotator.to_serialized_string(), text_output) + write_value("AnnotationDate", datetime_to_iso_string(annotation.annotation_date), text_output) + write_text_value("AnnotationComment", annotation.annotation_comment, text_output, True) + write_value("AnnotationType", annotation.annotation_type.name, text_output) + write_value("SPDXREF", annotation.spdx_id, text_output, True) diff --git a/src/writer/tagvalue/checksum_writer.py b/src/writer/tagvalue/checksum_writer.py new file mode 100644 index 000000000..c5803181e --- /dev/null +++ b/src/writer/tagvalue/checksum_writer.py @@ -0,0 +1,31 @@ +# 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.model.checksum import Checksum, ChecksumAlgorithm + + +def write_checksum_to_tag_value(checksum: Checksum) -> str: + algorithm_name: str = checksum.algorithm.name + # Convert underscores to dashes, and other Blake2b-specific casing rules + if "_" in algorithm_name: + algorithm_name = CHECKSUM_ALGORITHM_TO_TV.get(algorithm_name) + if algorithm_name is None: + raise ValueError(f"Missing conversion rule for converting {checksum.algorithm.name} to tag-value string") + return "{0}: {1}".format(algorithm_name, checksum.value) + + +CHECKSUM_ALGORITHM_TO_TV = { + ChecksumAlgorithm.BLAKE2B_256.name: "BLAKE2b-256", + ChecksumAlgorithm.BLAKE2B_384.name: "BLAKE2b-384", + ChecksumAlgorithm.BLAKE2B_512.name: "BLAKE2b-512", + ChecksumAlgorithm.SHA3_256.name: "SHA3-256", + ChecksumAlgorithm.SHA3_384.name: "SHA3-384", + ChecksumAlgorithm.SHA3_512.name: "SHA3-512" +} diff --git a/src/writer/tagvalue/creation_info_writer.py b/src/writer/tagvalue/creation_info_writer.py new file mode 100644 index 000000000..3e7cc20d3 --- /dev/null +++ b/src/writer/tagvalue/creation_info_writer.py @@ -0,0 +1,46 @@ +# 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 typing import TextIO + +from src.datetime_conversions import datetime_to_iso_string +from src.model.document import CreationInfo +from src.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, write_optional_heading, \ + write_separator + + +def write_creation_info(creation_info: CreationInfo, text_output: TextIO): + """ + Write the creation info to text_output. + """ + write_value("SPDXVersion", creation_info.spdx_version, text_output) + write_value("DataLicense", creation_info.data_license, text_output) + write_value("DocumentNamespace", creation_info.document_namespace, text_output, True) + write_value("DocumentName", creation_info.name, text_output, True) + write_value("LicenseListVersion", str(creation_info.spdx_version), text_output, True) + write_value("SPDXID", creation_info.spdx_id, text_output) + write_text_value("DocumentComment", creation_info.document_comment, text_output, True) + + write_optional_heading(creation_info.external_document_refs, "\n## External Document References\n", text_output) + for external_document_ref in creation_info.external_document_refs: + external_document_ref_str = " ".join([external_document_ref.document_ref_id, external_document_ref.document_uri, + external_document_ref.checksum.algorithm.name + ": " + external_document_ref.checksum.value]) + write_value("ExternalDocumentRef", external_document_ref_str, text_output) + write_separator(text_output) + + text_output.write("## Creation Information\n") + # Write sorted creators + for creator in creation_info.creators: + write_value("Creator", creator.to_serialized_string(), text_output) + + # write created + write_value("Created", datetime_to_iso_string(creation_info.created), text_output) + # possible comment + write_text_value("CreatorComment", creation_info.creator_comment, text_output, True) diff --git a/src/writer/tagvalue/extracted_licensing_info_writer.py b/src/writer/tagvalue/extracted_licensing_info_writer.py new file mode 100644 index 000000000..e77587379 --- /dev/null +++ b/src/writer/tagvalue/extracted_licensing_info_writer.py @@ -0,0 +1,28 @@ +# 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 typing import TextIO + +from src.model.extracted_licensing_info import ExtractedLicensingInfo +from src.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value + + +def write_extracted_licensing_info(extracted_licensing_info: ExtractedLicensingInfo, text_output: TextIO): + """ + Write extracted licenses fields to out. + """ + write_value("LicenseID", extracted_licensing_info.license_id, text_output) + write_value("LicenseName", extracted_licensing_info.license_name, text_output, True) + write_text_value("LicenseComment", extracted_licensing_info.comment, text_output, True) + + for cross_reference in sorted(extracted_licensing_info.cross_references): + write_value("LicenseCrossReference", cross_reference, text_output) + + write_text_value("ExtractedText", extracted_licensing_info.extracted_text, text_output) diff --git a/src/writer/tagvalue/file_writer.py b/src/writer/tagvalue/file_writer.py new file mode 100644 index 000000000..635f01815 --- /dev/null +++ b/src/writer/tagvalue/file_writer.py @@ -0,0 +1,42 @@ +# 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 typing import TextIO + +from src.model.file import File +from src.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, \ + write_field_or_none_or_no_assertion +from src.writer.tagvalue.checksum_writer import write_checksum_to_tag_value + + +def write_file(file: File, text_output: TextIO): + """ + Write all file information to output_text. + """ + text_output.write("## File Information\n") + write_value("FileName", file.name, text_output) + write_value("SPDXID", file.spdx_id, text_output, True) + for file_type in file.file_type: + write_value("FileType", file_type.name, text_output) + for file_checksum in file.checksums: + write_value("FileChecksum", write_checksum_to_tag_value(file_checksum), text_output) + write_field_or_none_or_no_assertion("LicenseConcluded", file.concluded_license, text_output, True) + write_field_or_none_or_no_assertion("LicenseInfoInFile", file.license_info_in_file, text_output, True) + write_field_or_none_or_no_assertion("FileCopyrightText", file.copyright_text, text_output, True) + write_text_value("LicenseComments", file.license_comment, text_output, True) + + for attribution_text in file.attribution_texts: + write_text_value("FileAttributionText", attribution_text, text_output) + + write_text_value("FileComment", file.comment, text_output, True) + write_text_value("FileNotice", file.notice, text_output, True) + + for contributor in sorted(file.contributors): + write_value("FileContributor", contributor, text_output, True) diff --git a/src/writer/tagvalue/package_writer.py b/src/writer/tagvalue/package_writer.py new file mode 100644 index 000000000..216ba075b --- /dev/null +++ b/src/writer/tagvalue/package_writer.py @@ -0,0 +1,85 @@ +# 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 typing import TextIO + +from src.datetime_conversions import datetime_to_iso_string +from src.model.package import Package, PackageVerificationCode +from src.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, \ + write_field_or_none_or_no_assertion, transform_enum_name_to_tv, write_actor_or_no_assertion +from src.writer.tagvalue.checksum_writer import write_checksum_to_tag_value + + +def write_package(package: Package, text_output: TextIO): + """ + Write all package information to text_output. + """ + text_output.write("## Package Information\n") + + write_value("PackageName", package.name, text_output, True) + write_value("SPDXID", package.spdx_id, text_output, True) + write_value("PackageVersion", package.version, text_output, True) + write_value("PackageDownloadLocation", package.download_location, text_output) + write_value("FilesAnalyzed", package.files_analyzed, text_output, True) + write_text_value("PackageSummary", package.summary, text_output, True) + for attribution_text in package.attribution_texts: + write_text_value("PackageAttributionText", attribution_text, text_output) + + write_text_value("PackageSourceInfo", package.source_info, text_output, True) + write_value("PackageFileName", package.file_name, text_output, True) + write_actor_or_no_assertion("PackageSupplier", package.supplier, text_output, True) + write_actor_or_no_assertion("PackageOriginator", package.originator, text_output, True) + + for package_checksum in package.checksums: + write_value("PackageChecksum", write_checksum_to_tag_value(package_checksum), text_output, True) + + if package.verification_code: + package_verification_code = write_package_verification_code(package.verification_code) + write_value("PackageVerificationCode", package_verification_code, text_output, True) + + write_text_value("PackageDescription", package.description, text_output, True) + write_text_value("PackageComment", package.comment, text_output, True) + + write_field_or_none_or_no_assertion("PackageLicenseDeclared", package.license_declared, text_output, True) + write_field_or_none_or_no_assertion("PackageLicenseConcluded", package.license_concluded, text_output, True) + write_field_or_none_or_no_assertion("PackageLicenseInfoFromFiles", package.license_info_from_files, text_output, + True) + + write_text_value("PackageLicenseComments", package.license_comment, text_output, True) + write_field_or_none_or_no_assertion("PackageCopyrightText", package.copyright_text, text_output, True) + + write_value("PackageHomePage", package.homepage, text_output, True) + + for external_reference in package.external_references: + external_reference_str = " ".join( + [transform_enum_name_to_tv(external_reference.category.name), external_reference.reference_type, + external_reference.locator] + ) + write_value("ExternalRef", external_reference_str, text_output, True) + if external_reference.comment: + write_text_value("ExternalRefComment", external_reference.comment, text_output) + + write_value("PrimaryPackagePurpose", transform_enum_name_to_tv(package.primary_package_purpose.name), + text_output, True) + + if package.built_date: + write_value("BuiltDate", datetime_to_iso_string(package.built_date), text_output) + if package.release_date: + write_value("ReleaseDate", datetime_to_iso_string(package.release_date), text_output) + if package.valid_until_date: + write_value("ValidUntilDate", datetime_to_iso_string(package.valid_until_date), text_output) + + +def write_package_verification_code(verification_code: PackageVerificationCode): + if not verification_code.excluded_files: + return verification_code.value + + excluded_files_str = " (excludes: " + " ".join(verification_code.excluded_files) + ")" + return verification_code.value + excluded_files_str diff --git a/src/writer/tagvalue/relationship_writer.py b/src/writer/tagvalue/relationship_writer.py new file mode 100644 index 000000000..4740b4503 --- /dev/null +++ b/src/writer/tagvalue/relationship_writer.py @@ -0,0 +1,24 @@ +# 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 typing import TextIO + +from src.model.relationship import Relationship +from src.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value + + +def write_relationship(relationship: Relationship, text_output: TextIO): + """ + Write a relationship to text_output. + """ + write_value("Relationship", " ".join( + [relationship.spdx_element_id, relationship.relationship_type.name, relationship.related_spdx_element_id]), + text_output) + write_text_value("RelationshipComment", relationship.comment, text_output, True) diff --git a/src/writer/tagvalue/snippet_writer.py b/src/writer/tagvalue/snippet_writer.py new file mode 100644 index 000000000..d59e233f0 --- /dev/null +++ b/src/writer/tagvalue/snippet_writer.py @@ -0,0 +1,34 @@ +# 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 typing import TextIO + +from src.model.snippet import Snippet +from src.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, write_range, \ + write_field_or_none_or_no_assertion + + +def write_snippet(snippet: Snippet, output_text: TextIO): + """ + Write snippet fields to out. + """ + output_text.write("## Snippet Information\n") + write_value("SnippetSPDXID", snippet.spdx_id, output_text) + write_value("SnippetFromFileSPDXID", snippet.file_spdx_id, output_text) + write_text_value("SnippetCopyrightText", snippet.copyright_text, output_text, True) + write_range("SnippetByteRange", snippet.byte_range, output_text) + write_range("SnippetLineRange", snippet.line_range, output_text, True) + write_value("SnippetName", snippet.name, output_text, True) + write_text_value("SnippetComment", snippet.comment, output_text, True) + write_text_value("SnippetLicenseComments", snippet.license_comment, output_text, True) + for attribution_text in snippet.attribution_texts: + write_text_value("SnippetAttributionText", attribution_text, output_text) + write_field_or_none_or_no_assertion("SnippetLicenseConcluded", snippet.concluded_license, output_text, True) + write_field_or_none_or_no_assertion("LicenseInfoInSnippet", snippet.license_info_in_snippet, output_text, True) diff --git a/src/writer/tagvalue/tagvalue_writer.py b/src/writer/tagvalue/tagvalue_writer.py new file mode 100644 index 000000000..216bfeb49 --- /dev/null +++ b/src/writer/tagvalue/tagvalue_writer.py @@ -0,0 +1,87 @@ +# 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 typing import TextIO + +from src.model.document import Document +from src.writer.tagvalue.annotation_writer import write_annotation +from src.writer.tagvalue.creation_info_writer import write_creation_info +from src.writer.tagvalue.extracted_licensing_info_writer import write_extracted_licensing_info +from src.writer.tagvalue.file_writer import write_file +from src.writer.tagvalue.package_writer import write_package +from src.writer.tagvalue.relationship_writer import write_relationship +from src.writer.tagvalue.snippet_writer import write_snippet +from src.writer.tagvalue.tagvalue_writer_helper_functions import write_separator, scan_relationships, \ + determine_files_containing_snippets, write_optional_heading, write_list_of_elements + + +def write_document_to_file(document: Document, file_name: str): + with open(file_name, "w") as out: + write_document(document, out) + + +def write_document(document: Document, text_output: TextIO): + """ + Write a SPDX tag value document. + - document - src.document instance. + - text_output - file like object that will be written to. + """ + + text_output.write("## Document Information\n") + # Write out creation info + write_creation_info(document.creation_info, text_output) + write_separator(text_output) + + # Write sorted annotations + write_optional_heading(document.annotations, "## Annotations\n", text_output) + write_list_of_elements(document.annotations, write_annotation, text_output, True) + + relationships_to_write, contained_files_by_package_id = scan_relationships(document.relationships, + document.packages, document.files) + contained_snippets_by_file_id = determine_files_containing_snippets(document.snippets, document.files) + packaged_file_ids = [file.spdx_id for files_list in contained_files_by_package_id.values() + for file in files_list] + filed_snippet_ids = [snippet.spdx_id for snippets_list in contained_snippets_by_file_id.values() + for snippet in snippets_list] + + # Write Relationships + write_optional_heading(relationships_to_write, "## Relationships\n", text_output) + write_list_of_elements(relationships_to_write, write_relationship, text_output) + write_separator(text_output) + + # Write snippet info + for snippet in document.snippets: + if snippet.spdx_id not in filed_snippet_ids: + write_snippet(snippet, text_output) + write_separator(text_output) + + # Write file info + for file in document.files: + if file.spdx_id not in packaged_file_ids: + write_file(file, text_output) + write_separator(text_output) + if file.spdx_id in contained_snippets_by_file_id: + write_list_of_elements(contained_snippets_by_file_id[file.spdx_id], write_snippet, text_output, True) + + # Write package info + for package in document.packages: + write_package(package, text_output) + write_separator(text_output) + if package.spdx_id in contained_files_by_package_id: + for file in contained_files_by_package_id[package.spdx_id]: + write_file(file, text_output) + write_separator(text_output) + if file.spdx_id in contained_snippets_by_file_id: + write_list_of_elements(contained_snippets_by_file_id[file.spdx_id], write_snippet, text_output, True) + break + + write_optional_heading(document.extracted_licensing_info, "## License Information\n", text_output) + write_list_of_elements(document.extracted_licensing_info, write_extracted_licensing_info, text_output, True) diff --git a/src/writer/tagvalue/tagvalue_writer_helper_functions.py b/src/writer/tagvalue/tagvalue_writer_helper_functions.py new file mode 100644 index 000000000..95ca5654e --- /dev/null +++ b/src/writer/tagvalue/tagvalue_writer_helper_functions.py @@ -0,0 +1,131 @@ +# 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 typing import TextIO, Tuple, List, Dict, Any, Union, Callable + +from src.model.actor import Actor +from src.model.file import File +from src.model.license_expression import LicenseExpression +from src.model.package import Package +from src.model.relationship import Relationship +from src.model.snippet import Snippet +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone + + +def write_separator(out: TextIO): + out.write("\n") + + +def write_value(tag: str, value: Union[bool, str, SpdxNone, SpdxNoAssertion], out: TextIO, optional: bool = False): + if optional and not value: + return + out.write(f"{tag}: {value}\n") + + +def write_range(tag: str, value: Tuple[int, int], out: TextIO, optional: bool = False): + if optional and not value: + return + out.write(f"{tag}: {value[0]}:{value[1]}\n") + + +def write_text_value(tag: str, value: str, out: TextIO, optional: bool = False): + if optional and not value: + return + if "\n" in value: + out.write(f"{tag}: {value}\n") + else: + write_value(tag, value, out, True) + + +def transform_enum_name_to_tv(enum_str: str) -> str: + return enum_str.replace("_", "-") + + +def write_optional_heading(optional_field: Any, heading: str, text_output: TextIO): + if not optional_field: + return + text_output.write(heading) + + +def write_list_of_elements(list_of_elements: List[Any], write_method: Callable[[Any, TextIO], None], + text_output: TextIO, with_separator: bool = False): + for element in list_of_elements: + write_method(element, text_output) + if with_separator: + write_separator(text_output) + + +def write_actor_or_no_assertion(tag: str, element_to_write: Any, text_output: TextIO, optional: bool): + if optional and not element_to_write: + return + if isinstance(element_to_write, SpdxNoAssertion): + write_value(tag, element_to_write, text_output) + return + if isinstance(element_to_write, Actor): + write_value(tag, element_to_write.to_serialized_string(), text_output) + return + else: + write_value(tag, element_to_write, text_output) + + +def write_field_or_none_or_no_assertion(tag: str, element_to_write: Union[ + List[LicenseExpression], LicenseExpression, SpdxNoAssertion, SpdxNone], text_output: TextIO, + optional: bool = False): + if optional and not element_to_write: + return + if isinstance(element_to_write, (SpdxNone, SpdxNoAssertion)): + write_value(tag, element_to_write, text_output) + return + if isinstance(element_to_write, LicenseExpression): + write_value(tag, element_to_write.expression_string, text_output) + return + if isinstance(element_to_write, str): + write_value(tag, element_to_write, text_output) + return + if isinstance(element_to_write, list): + for element in element_to_write: + write_value(tag, element.expression_string, text_output) + + +def scan_relationships(relationships: List[Relationship], packages: List[Package], files: List[File]) \ + -> Tuple[List, Dict]: + contained_files_by_package_id = dict() + relationships_to_write = [] + files_by_spdx_id = {file.spdx_id: file for file in files} + packages_spdx_ids = [package.spdx_id for package in packages] + for relationship in relationships: + if relationship.relationship_type == "CONTAINS" and \ + relationship.spdx_element_id in packages_spdx_ids and \ + relationship.related_spdx_element in files_by_spdx_id.keys(): + contained_files_by_package_id.setdefault(relationship.spdx_element_id, []).append( + files_by_spdx_id[relationship.related_spdx_element]) + if relationship.has_comment: + relationships_to_write.append(relationship) + elif relationship.relationship_type == "CONTAINED_BY" and \ + relationship.related_spdx_element in packages_spdx_ids and \ + relationship.spdx_element_id in files_by_spdx_id: + contained_files_by_package_id.setdefault(relationship.related_spdx_element, []).append( + files_by_spdx_id[relationship.spdx_element_id]) + if relationship.has_comment: + relationships_to_write.append(relationship) + else: + relationships_to_write.append(relationship) + + return relationships_to_write, contained_files_by_package_id + + +def determine_files_containing_snippets(snippets: List[Snippet], files: List[File]) -> Dict: + contained_snippets_by_file_id = dict() + for snippet in snippets: + if snippet.file_spdx_id in [file.spdx_id for file in files]: + contained_snippets_by_file_id.setdefault(snippet.file_spdx_id, []).append(snippet) + + return contained_snippets_by_file_id diff --git a/tests/writer/tagvalue/__init__.py b/tests/writer/tagvalue/__init__.py new file mode 100644 index 000000000..c30b311b3 --- /dev/null +++ b/tests/writer/tagvalue/__init__.py @@ -0,0 +1,10 @@ +# 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. diff --git a/tests/writer/tagvalue/expected_results/expected_tag_value.spdx b/tests/writer/tagvalue/expected_results/expected_tag_value.spdx new file mode 100644 index 000000000..799178b40 --- /dev/null +++ b/tests/writer/tagvalue/expected_results/expected_tag_value.spdx @@ -0,0 +1,54 @@ +## Document Information +SPDXVersion: spdxVersion +DataLicense: dataLicense +DocumentNamespace: documentNamespace +DocumentName: documentName +SPDXID: documentId +DocumentComment: comment + +## External Document References +ExternalDocumentRef: docRefId externalDocumentUri SHA1: externalRefSha1 + +## Creation Information +Creator: Tool: tools-python (tools-python@github.com) +Created: 2022-12-01T00:00:00Z + +## Annotations +Annotator: Person: reviewerName +AnnotationDate: 2022-12-02T00:00:00Z +AnnotationComment: reviewComment +AnnotationType: REVIEW +SPDXREF: documentId + +Annotator: Tool: toolName +AnnotationDate: 2022-12-03T00:00:00Z +AnnotationComment: otherComment +AnnotationType: OTHER +SPDXREF: fileId + +## Relationships +Relationship: documentId DESCRIBES packageId +Relationship: documentId DESCRIBES fileId +RelationshipComment: relationshipComment +Relationship: relationshipOriginId AMENDS relationShipTargetId + +## Snippet Information +SnippetSPDXID: snippetId +SnippetFromFileSPDXID: snippetFileId +SnippetByteRange: 1:2 + +## File Information +FileName: fileName +SPDXID: fileId +FileChecksum: SHA1: fileSha1 + +## Package Information +PackageName: packageName +SPDXID: packageId +PackageDownloadLocation: NONE +FilesAnalyzed: True + + +## License Information +LicenseID: licenseId +ExtractedText: licenseText diff --git a/tests/writer/tagvalue/test_package_writer.py b/tests/writer/tagvalue/test_package_writer.py new file mode 100644 index 000000000..1fbd469ea --- /dev/null +++ b/tests/writer/tagvalue/test_package_writer.py @@ -0,0 +1,54 @@ +# 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 datetime import datetime +from unittest.mock import patch, mock_open, call + +from src.model.license_expression import LicenseExpression +from src.model.package import PackagePurpose +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone +from src.writer.tagvalue.package_writer import write_package +from tests.valid_defaults import get_package, get_package_verification_code, get_actor, get_checksum, \ + get_external_package_ref + + +def test_package_writer(): + package = get_package("SPDXRef-Package", "package name", "www.download.com", "version", "file_name", SpdxNoAssertion(), + get_actor(), True, + get_package_verification_code(), [get_checksum()], "https://homepage.com", "source_info", None, + [LicenseExpression("expression")], + SpdxNone(), "comment on license", "copyright", "summary", "description", "comment", + [get_external_package_ref()], ["text"], PackagePurpose.OTHER, datetime(2022, 1, 1), None, None) + + m = mock_open() + with patch('{}.open'.format(__name__), m, create=True): + with open('foo', 'w') as h: + write_package(package, h) + + m.assert_called_once_with('foo', 'w') + handle = m() + handle.write.assert_has_calls( + [call('## Package Information\n'), call('PackageName: package name\n'), call('SPDXID: SPDXRef-Package\n'), + call('PackageVersion: version\n'), call('PackageDownloadLocation: www.download.com\n'), + call('FilesAnalyzed: True\n'), call('PackageSummary: summary\n'), call('PackageAttributionText: text\n'), + call('PackageSourceInfo: source_info\n'), call('PackageFileName: file_name\n'), + call('PackageSupplier: NOASSERTION\n'), + call("PackageOriginator: Person: person name\n"), + call('PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c\n'), + call('PackageVerificationCode: 85ed0817af83a24ad8da68c2b5094de69833983c\n'), + call('PackageDescription: description\n'), call('PackageComment: comment\n'), + call('PackageLicenseDeclared: NONE\n'), + call("PackageLicenseInfoFromFiles: expression\n"), + call('PackageLicenseComments: comment on license\n'), call('PackageCopyrightText: copyright\n'), + call('PackageHomePage: https://homepage.com\n'), + call('ExternalRef: SECURITY cpe22Type cpe:/o:canonical:ubuntu_linux:10.04:-:lts\n'), + call('ExternalRefComment: external package ref comment\n'), call('PrimaryPackagePurpose: OTHER\n'), + call('ReleaseDate: 2022-01-01T00:00:00Z\n')]) diff --git a/tests/writer/tagvalue/test_tagvalue_writer.py b/tests/writer/tagvalue/test_tagvalue_writer.py new file mode 100644 index 000000000..98b786b7e --- /dev/null +++ b/tests/writer/tagvalue/test_tagvalue_writer.py @@ -0,0 +1,63 @@ +# 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. + +import os +from datetime import datetime + +import pytest + +from src.model.actor import Actor, ActorType +from src.model.annotation import Annotation, AnnotationType +from src.model.checksum import ChecksumAlgorithm, Checksum +from src.model.document import CreationInfo, Document +from src.model.external_document_ref import ExternalDocumentRef +from src.model.extracted_licensing_info import ExtractedLicensingInfo +from src.model.file import File +from src.model.package import Package +from src.model.relationship import RelationshipType, Relationship +from src.model.snippet import Snippet +from src.model.spdx_none import SpdxNone +from src.writer.tagvalue.tagvalue_writer import write_document_to_file + + +@pytest.fixture +def temporary_file_path() -> str: + temporary_file_path = "temp_test_tag_value_writer_output.spdx" + yield temporary_file_path + os.remove(temporary_file_path) + + +def test_write_tag_value(temporary_file_path: str): + creation_info = CreationInfo("spdxVersion", "documentId", "documentName", "documentNamespace", + [Actor(ActorType.TOOL, "tools-python", "tools-python@github.com")], + datetime(2022, 12, 1), document_comment="comment", data_license="dataLicense", + external_document_refs=[ExternalDocumentRef("docRefId", "externalDocumentUri", + Checksum(ChecksumAlgorithm.SHA1, + "externalRefSha1"))]) + package = Package("packageId", "packageName", SpdxNone()) + file = File("fileName", "fileId", [Checksum(ChecksumAlgorithm.SHA1, "fileSha1")]) + snippet = Snippet("snippetId", "snippetFileId", (1, 2)) + annotations = [ + Annotation("documentId", AnnotationType.REVIEW, Actor(ActorType.PERSON, "reviewerName"), datetime(2022, 12, 2), + "reviewComment"), + Annotation("fileId", AnnotationType.OTHER, Actor(ActorType.TOOL, "toolName"), datetime(2022, 12, 3), + "otherComment")] + extracted_licensing_info = [ExtractedLicensingInfo("licenseId", "licenseText")] + relationships = [Relationship(creation_info.spdx_id, RelationshipType.DESCRIBES, "packageId"), + Relationship(creation_info.spdx_id, RelationshipType.DESCRIBES, "fileId", "relationshipComment"), + Relationship("relationshipOriginId", RelationshipType.AMENDS, "relationShipTargetId")] + document = Document(creation_info, annotations=annotations, extracted_licensing_info=extracted_licensing_info, + relationships=relationships, packages=[package], files=[file], snippets=[snippet]) + + write_document_to_file(document, temporary_file_path) + + # without a tag-value parser we can only test that no errors occur while writing + # as soon as the tag-value parser is implemented (https://github.com/spdx/tools-python/issues/382) we can test for equality between the temporary file and the expected file in ./expected_results From 3d1b14ed544f344e9700081942a24100fda8a843 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 29 Dec 2022 16:37:46 +0100 Subject: [PATCH 062/362] [issue-381, fix] check if primary package purpose is present Signed-off-by: Meret Behrens --- src/writer/tagvalue/package_writer.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/writer/tagvalue/package_writer.py b/src/writer/tagvalue/package_writer.py index 216ba075b..dde9de6ce 100644 --- a/src/writer/tagvalue/package_writer.py +++ b/src/writer/tagvalue/package_writer.py @@ -66,8 +66,9 @@ def write_package(package: Package, text_output: TextIO): if external_reference.comment: write_text_value("ExternalRefComment", external_reference.comment, text_output) - write_value("PrimaryPackagePurpose", transform_enum_name_to_tv(package.primary_package_purpose.name), - text_output, True) + if package.primary_package_purpose: + write_value("PrimaryPackagePurpose", transform_enum_name_to_tv(package.primary_package_purpose.name), + text_output) if package.built_date: write_value("BuiltDate", datetime_to_iso_string(package.built_date), text_output) From 7eaf365f61b08177fa7b04512ca01c1d7051f162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 30 Dec 2022 15:18:54 +0100 Subject: [PATCH 063/362] [issue-381] remove unnecessary comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/writer/tagvalue/__init__.py | 11 ----------- src/writer/tagvalue/annotation_writer.py | 3 --- src/writer/tagvalue/creation_info_writer.py | 7 ------- .../tagvalue/extracted_licensing_info_writer.py | 3 --- src/writer/tagvalue/file_writer.py | 3 --- src/writer/tagvalue/package_writer.py | 3 --- src/writer/tagvalue/relationship_writer.py | 3 --- src/writer/tagvalue/snippet_writer.py | 3 --- src/writer/tagvalue/tagvalue_writer.py | 12 ------------ tests/writer/tagvalue/__init__.py | 10 ---------- 10 files changed, 58 deletions(-) diff --git a/src/writer/tagvalue/__init__.py b/src/writer/tagvalue/__init__.py index b0981f064..e69de29bb 100644 --- a/src/writer/tagvalue/__init__.py +++ b/src/writer/tagvalue/__init__.py @@ -1,11 +0,0 @@ -# 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. - diff --git a/src/writer/tagvalue/annotation_writer.py b/src/writer/tagvalue/annotation_writer.py index cb7c29ef4..2da82e87e 100644 --- a/src/writer/tagvalue/annotation_writer.py +++ b/src/writer/tagvalue/annotation_writer.py @@ -16,9 +16,6 @@ def write_annotation(annotation: Annotation, text_output: TextIO): - """ - Write the fields of a single annotation to text_output. - """ write_value("Annotator", annotation.annotator.to_serialized_string(), text_output) write_value("AnnotationDate", datetime_to_iso_string(annotation.annotation_date), text_output) write_text_value("AnnotationComment", annotation.annotation_comment, text_output, True) diff --git a/src/writer/tagvalue/creation_info_writer.py b/src/writer/tagvalue/creation_info_writer.py index 3e7cc20d3..4aba5b958 100644 --- a/src/writer/tagvalue/creation_info_writer.py +++ b/src/writer/tagvalue/creation_info_writer.py @@ -17,9 +17,6 @@ def write_creation_info(creation_info: CreationInfo, text_output: TextIO): - """ - Write the creation info to text_output. - """ write_value("SPDXVersion", creation_info.spdx_version, text_output) write_value("DataLicense", creation_info.data_license, text_output) write_value("DocumentNamespace", creation_info.document_namespace, text_output, True) @@ -36,11 +33,7 @@ def write_creation_info(creation_info: CreationInfo, text_output: TextIO): write_separator(text_output) text_output.write("## Creation Information\n") - # Write sorted creators for creator in creation_info.creators: write_value("Creator", creator.to_serialized_string(), text_output) - - # write created write_value("Created", datetime_to_iso_string(creation_info.created), text_output) - # possible comment write_text_value("CreatorComment", creation_info.creator_comment, text_output, True) diff --git a/src/writer/tagvalue/extracted_licensing_info_writer.py b/src/writer/tagvalue/extracted_licensing_info_writer.py index e77587379..949d15f70 100644 --- a/src/writer/tagvalue/extracted_licensing_info_writer.py +++ b/src/writer/tagvalue/extracted_licensing_info_writer.py @@ -15,9 +15,6 @@ def write_extracted_licensing_info(extracted_licensing_info: ExtractedLicensingInfo, text_output: TextIO): - """ - Write extracted licenses fields to out. - """ write_value("LicenseID", extracted_licensing_info.license_id, text_output) write_value("LicenseName", extracted_licensing_info.license_name, text_output, True) write_text_value("LicenseComment", extracted_licensing_info.comment, text_output, True) diff --git a/src/writer/tagvalue/file_writer.py b/src/writer/tagvalue/file_writer.py index 635f01815..aaf49b410 100644 --- a/src/writer/tagvalue/file_writer.py +++ b/src/writer/tagvalue/file_writer.py @@ -17,9 +17,6 @@ def write_file(file: File, text_output: TextIO): - """ - Write all file information to output_text. - """ text_output.write("## File Information\n") write_value("FileName", file.name, text_output) write_value("SPDXID", file.spdx_id, text_output, True) diff --git a/src/writer/tagvalue/package_writer.py b/src/writer/tagvalue/package_writer.py index dde9de6ce..9e8b57e9d 100644 --- a/src/writer/tagvalue/package_writer.py +++ b/src/writer/tagvalue/package_writer.py @@ -18,9 +18,6 @@ def write_package(package: Package, text_output: TextIO): - """ - Write all package information to text_output. - """ text_output.write("## Package Information\n") write_value("PackageName", package.name, text_output, True) diff --git a/src/writer/tagvalue/relationship_writer.py b/src/writer/tagvalue/relationship_writer.py index 4740b4503..7b7b3489a 100644 --- a/src/writer/tagvalue/relationship_writer.py +++ b/src/writer/tagvalue/relationship_writer.py @@ -15,9 +15,6 @@ def write_relationship(relationship: Relationship, text_output: TextIO): - """ - Write a relationship to text_output. - """ write_value("Relationship", " ".join( [relationship.spdx_element_id, relationship.relationship_type.name, relationship.related_spdx_element_id]), text_output) diff --git a/src/writer/tagvalue/snippet_writer.py b/src/writer/tagvalue/snippet_writer.py index d59e233f0..d9fc466b6 100644 --- a/src/writer/tagvalue/snippet_writer.py +++ b/src/writer/tagvalue/snippet_writer.py @@ -16,9 +16,6 @@ def write_snippet(snippet: Snippet, output_text: TextIO): - """ - Write snippet fields to out. - """ output_text.write("## Snippet Information\n") write_value("SnippetSPDXID", snippet.spdx_id, output_text) write_value("SnippetFromFileSPDXID", snippet.file_spdx_id, output_text) diff --git a/src/writer/tagvalue/tagvalue_writer.py b/src/writer/tagvalue/tagvalue_writer.py index 216bfeb49..bfc87e147 100644 --- a/src/writer/tagvalue/tagvalue_writer.py +++ b/src/writer/tagvalue/tagvalue_writer.py @@ -29,18 +29,10 @@ def write_document_to_file(document: Document, file_name: str): def write_document(document: Document, text_output: TextIO): - """ - Write a SPDX tag value document. - - document - src.document instance. - - text_output - file like object that will be written to. - """ - text_output.write("## Document Information\n") - # Write out creation info write_creation_info(document.creation_info, text_output) write_separator(text_output) - # Write sorted annotations write_optional_heading(document.annotations, "## Annotations\n", text_output) write_list_of_elements(document.annotations, write_annotation, text_output, True) @@ -52,18 +44,15 @@ def write_document(document: Document, text_output: TextIO): filed_snippet_ids = [snippet.spdx_id for snippets_list in contained_snippets_by_file_id.values() for snippet in snippets_list] - # Write Relationships write_optional_heading(relationships_to_write, "## Relationships\n", text_output) write_list_of_elements(relationships_to_write, write_relationship, text_output) write_separator(text_output) - # Write snippet info for snippet in document.snippets: if snippet.spdx_id not in filed_snippet_ids: write_snippet(snippet, text_output) write_separator(text_output) - # Write file info for file in document.files: if file.spdx_id not in packaged_file_ids: write_file(file, text_output) @@ -71,7 +60,6 @@ def write_document(document: Document, text_output: TextIO): if file.spdx_id in contained_snippets_by_file_id: write_list_of_elements(contained_snippets_by_file_id[file.spdx_id], write_snippet, text_output, True) - # Write package info for package in document.packages: write_package(package, text_output) write_separator(text_output) diff --git a/tests/writer/tagvalue/__init__.py b/tests/writer/tagvalue/__init__.py index c30b311b3..e69de29bb 100644 --- a/tests/writer/tagvalue/__init__.py +++ b/tests/writer/tagvalue/__init__.py @@ -1,10 +0,0 @@ -# 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 d534a537f4c711691182ed3db1eb794c7907df6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 30 Dec 2022 16:01:40 +0100 Subject: [PATCH 064/362] [issue-381] refactoring and renaming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/writer/tagvalue/checksum_writer.py | 2 +- src/writer/tagvalue/tagvalue_writer.py | 14 ++++----- .../tagvalue_writer_helper_functions.py | 31 +++++++------------ 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/writer/tagvalue/checksum_writer.py b/src/writer/tagvalue/checksum_writer.py index c5803181e..7a31cad6a 100644 --- a/src/writer/tagvalue/checksum_writer.py +++ b/src/writer/tagvalue/checksum_writer.py @@ -18,7 +18,7 @@ def write_checksum_to_tag_value(checksum: Checksum) -> str: algorithm_name = CHECKSUM_ALGORITHM_TO_TV.get(algorithm_name) if algorithm_name is None: raise ValueError(f"Missing conversion rule for converting {checksum.algorithm.name} to tag-value string") - return "{0}: {1}".format(algorithm_name, checksum.value) + return f"{algorithm_name}: {checksum.value}" CHECKSUM_ALGORITHM_TO_TV = { diff --git a/src/writer/tagvalue/tagvalue_writer.py b/src/writer/tagvalue/tagvalue_writer.py index bfc87e147..8ced15b03 100644 --- a/src/writer/tagvalue/tagvalue_writer.py +++ b/src/writer/tagvalue/tagvalue_writer.py @@ -20,7 +20,7 @@ from src.writer.tagvalue.relationship_writer import write_relationship from src.writer.tagvalue.snippet_writer import write_snippet from src.writer.tagvalue.tagvalue_writer_helper_functions import write_separator, scan_relationships, \ - determine_files_containing_snippets, write_optional_heading, write_list_of_elements + get_file_ids_with_contained_snippets, write_optional_heading, write_list_of_elements def write_document_to_file(document: Document, file_name: str): @@ -38,10 +38,10 @@ def write_document(document: Document, text_output: TextIO): relationships_to_write, contained_files_by_package_id = scan_relationships(document.relationships, document.packages, document.files) - contained_snippets_by_file_id = determine_files_containing_snippets(document.snippets, document.files) + file_ids_with_contained_snippets = get_file_ids_with_contained_snippets(document.snippets, document.files) packaged_file_ids = [file.spdx_id for files_list in contained_files_by_package_id.values() for file in files_list] - filed_snippet_ids = [snippet.spdx_id for snippets_list in contained_snippets_by_file_id.values() + filed_snippet_ids = [snippet.spdx_id for snippets_list in file_ids_with_contained_snippets.values() for snippet in snippets_list] write_optional_heading(relationships_to_write, "## Relationships\n", text_output) @@ -57,8 +57,8 @@ def write_document(document: Document, text_output: TextIO): if file.spdx_id not in packaged_file_ids: write_file(file, text_output) write_separator(text_output) - if file.spdx_id in contained_snippets_by_file_id: - write_list_of_elements(contained_snippets_by_file_id[file.spdx_id], write_snippet, text_output, True) + if file.spdx_id in file_ids_with_contained_snippets: + write_list_of_elements(file_ids_with_contained_snippets[file.spdx_id], write_snippet, text_output, True) for package in document.packages: write_package(package, text_output) @@ -67,8 +67,8 @@ def write_document(document: Document, text_output: TextIO): for file in contained_files_by_package_id[package.spdx_id]: write_file(file, text_output) write_separator(text_output) - if file.spdx_id in contained_snippets_by_file_id: - write_list_of_elements(contained_snippets_by_file_id[file.spdx_id], write_snippet, text_output, True) + if file.spdx_id in file_ids_with_contained_snippets: + write_list_of_elements(file_ids_with_contained_snippets[file.spdx_id], write_snippet, text_output, True) break write_optional_heading(document.extracted_licensing_info, "## License Information\n", text_output) diff --git a/src/writer/tagvalue/tagvalue_writer_helper_functions.py b/src/writer/tagvalue/tagvalue_writer_helper_functions.py index 95ca5654e..e0984dddd 100644 --- a/src/writer/tagvalue/tagvalue_writer_helper_functions.py +++ b/src/writer/tagvalue/tagvalue_writer_helper_functions.py @@ -50,9 +50,8 @@ def transform_enum_name_to_tv(enum_str: str) -> str: def write_optional_heading(optional_field: Any, heading: str, text_output: TextIO): - if not optional_field: - return - text_output.write(heading) + if optional_field: + text_output.write(heading) def write_list_of_elements(list_of_elements: List[Any], write_method: Callable[[Any, TextIO], None], @@ -66,12 +65,8 @@ def write_list_of_elements(list_of_elements: List[Any], write_method: Callable[[ def write_actor_or_no_assertion(tag: str, element_to_write: Any, text_output: TextIO, optional: bool): if optional and not element_to_write: return - if isinstance(element_to_write, SpdxNoAssertion): - write_value(tag, element_to_write, text_output) - return if isinstance(element_to_write, Actor): write_value(tag, element_to_write.to_serialized_string(), text_output) - return else: write_value(tag, element_to_write, text_output) @@ -81,16 +76,11 @@ def write_field_or_none_or_no_assertion(tag: str, element_to_write: Union[ optional: bool = False): if optional and not element_to_write: return - if isinstance(element_to_write, (SpdxNone, SpdxNoAssertion)): + if isinstance(element_to_write, (SpdxNone, SpdxNoAssertion, str)): write_value(tag, element_to_write, text_output) - return - if isinstance(element_to_write, LicenseExpression): + elif isinstance(element_to_write, LicenseExpression): write_value(tag, element_to_write.expression_string, text_output) - return - if isinstance(element_to_write, str): - write_value(tag, element_to_write, text_output) - return - if isinstance(element_to_write, list): + elif isinstance(element_to_write, list): for element in element_to_write: write_value(tag, element.expression_string, text_output) @@ -122,10 +112,11 @@ def scan_relationships(relationships: List[Relationship], packages: List[Package return relationships_to_write, contained_files_by_package_id -def determine_files_containing_snippets(snippets: List[Snippet], files: List[File]) -> Dict: - contained_snippets_by_file_id = dict() +def get_file_ids_with_contained_snippets(snippets: List[Snippet], files: List[File]) -> Dict: + file_ids_with_contained_snippets = dict() + file_spdx_ids: List[str] = [file.spdx_id for file in files] for snippet in snippets: - if snippet.file_spdx_id in [file.spdx_id for file in files]: - contained_snippets_by_file_id.setdefault(snippet.file_spdx_id, []).append(snippet) + if snippet.file_spdx_id in file_spdx_ids: + file_ids_with_contained_snippets.setdefault(snippet.file_spdx_id, []).append(snippet) - return contained_snippets_by_file_id + return file_ids_with_contained_snippets From a0db2b1a743a0adaca19bb0ea8be7b8e3b503265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 30 Dec 2022 16:26:06 +0100 Subject: [PATCH 065/362] [issue-381] remove "optional" handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We do not want to take invalid files into account when writing. For such cases, validation should be performed before the writing process. Signed-off-by: Armin Tänzer --- src/writer/tagvalue/annotation_writer.py | 4 +- src/writer/tagvalue/creation_info_writer.py | 10 ++--- .../extracted_licensing_info_writer.py | 4 +- src/writer/tagvalue/file_writer.py | 16 ++++---- src/writer/tagvalue/package_writer.py | 41 +++++++++---------- src/writer/tagvalue/relationship_writer.py | 2 +- src/writer/tagvalue/snippet_writer.py | 14 +++---- src/writer/tagvalue/tagvalue_writer.py | 8 ++-- .../tagvalue_writer_helper_functions.py | 31 +++++--------- 9 files changed, 60 insertions(+), 70 deletions(-) diff --git a/src/writer/tagvalue/annotation_writer.py b/src/writer/tagvalue/annotation_writer.py index 2da82e87e..5a789c96f 100644 --- a/src/writer/tagvalue/annotation_writer.py +++ b/src/writer/tagvalue/annotation_writer.py @@ -18,6 +18,6 @@ def write_annotation(annotation: Annotation, text_output: TextIO): write_value("Annotator", annotation.annotator.to_serialized_string(), text_output) write_value("AnnotationDate", datetime_to_iso_string(annotation.annotation_date), text_output) - write_text_value("AnnotationComment", annotation.annotation_comment, text_output, True) + write_text_value("AnnotationComment", annotation.annotation_comment, text_output) write_value("AnnotationType", annotation.annotation_type.name, text_output) - write_value("SPDXREF", annotation.spdx_id, text_output, True) + write_value("SPDXREF", annotation.spdx_id, text_output) diff --git a/src/writer/tagvalue/creation_info_writer.py b/src/writer/tagvalue/creation_info_writer.py index 4aba5b958..f889bac2c 100644 --- a/src/writer/tagvalue/creation_info_writer.py +++ b/src/writer/tagvalue/creation_info_writer.py @@ -19,11 +19,11 @@ def write_creation_info(creation_info: CreationInfo, text_output: TextIO): write_value("SPDXVersion", creation_info.spdx_version, text_output) write_value("DataLicense", creation_info.data_license, text_output) - write_value("DocumentNamespace", creation_info.document_namespace, text_output, True) - write_value("DocumentName", creation_info.name, text_output, True) - write_value("LicenseListVersion", str(creation_info.spdx_version), text_output, True) + write_value("DocumentNamespace", creation_info.document_namespace, text_output) + write_value("DocumentName", creation_info.name, text_output) + write_value("LicenseListVersion", str(creation_info.spdx_version), text_output) write_value("SPDXID", creation_info.spdx_id, text_output) - write_text_value("DocumentComment", creation_info.document_comment, text_output, True) + write_text_value("DocumentComment", creation_info.document_comment, text_output) write_optional_heading(creation_info.external_document_refs, "\n## External Document References\n", text_output) for external_document_ref in creation_info.external_document_refs: @@ -36,4 +36,4 @@ def write_creation_info(creation_info: CreationInfo, text_output: TextIO): for creator in creation_info.creators: write_value("Creator", creator.to_serialized_string(), text_output) write_value("Created", datetime_to_iso_string(creation_info.created), text_output) - write_text_value("CreatorComment", creation_info.creator_comment, text_output, True) + write_text_value("CreatorComment", creation_info.creator_comment, text_output) diff --git a/src/writer/tagvalue/extracted_licensing_info_writer.py b/src/writer/tagvalue/extracted_licensing_info_writer.py index 949d15f70..90d6e7087 100644 --- a/src/writer/tagvalue/extracted_licensing_info_writer.py +++ b/src/writer/tagvalue/extracted_licensing_info_writer.py @@ -16,8 +16,8 @@ def write_extracted_licensing_info(extracted_licensing_info: ExtractedLicensingInfo, text_output: TextIO): write_value("LicenseID", extracted_licensing_info.license_id, text_output) - write_value("LicenseName", extracted_licensing_info.license_name, text_output, True) - write_text_value("LicenseComment", extracted_licensing_info.comment, text_output, True) + write_value("LicenseName", extracted_licensing_info.license_name, text_output) + write_text_value("LicenseComment", extracted_licensing_info.comment, text_output) for cross_reference in sorted(extracted_licensing_info.cross_references): write_value("LicenseCrossReference", cross_reference, text_output) diff --git a/src/writer/tagvalue/file_writer.py b/src/writer/tagvalue/file_writer.py index aaf49b410..c2aafe348 100644 --- a/src/writer/tagvalue/file_writer.py +++ b/src/writer/tagvalue/file_writer.py @@ -19,21 +19,21 @@ def write_file(file: File, text_output: TextIO): text_output.write("## File Information\n") write_value("FileName", file.name, text_output) - write_value("SPDXID", file.spdx_id, text_output, True) + write_value("SPDXID", file.spdx_id, text_output) for file_type in file.file_type: write_value("FileType", file_type.name, text_output) for file_checksum in file.checksums: write_value("FileChecksum", write_checksum_to_tag_value(file_checksum), text_output) - write_field_or_none_or_no_assertion("LicenseConcluded", file.concluded_license, text_output, True) - write_field_or_none_or_no_assertion("LicenseInfoInFile", file.license_info_in_file, text_output, True) - write_field_or_none_or_no_assertion("FileCopyrightText", file.copyright_text, text_output, True) - write_text_value("LicenseComments", file.license_comment, text_output, True) + write_field_or_none_or_no_assertion("LicenseConcluded", file.concluded_license, text_output) + write_field_or_none_or_no_assertion("LicenseInfoInFile", file.license_info_in_file, text_output) + write_field_or_none_or_no_assertion("FileCopyrightText", file.copyright_text, text_output) + write_text_value("LicenseComments", file.license_comment, text_output) for attribution_text in file.attribution_texts: write_text_value("FileAttributionText", attribution_text, text_output) - write_text_value("FileComment", file.comment, text_output, True) - write_text_value("FileNotice", file.notice, text_output, True) + write_text_value("FileComment", file.comment, text_output) + write_text_value("FileNotice", file.notice, text_output) for contributor in sorted(file.contributors): - write_value("FileContributor", contributor, text_output, True) + write_value("FileContributor", contributor, text_output) diff --git a/src/writer/tagvalue/package_writer.py b/src/writer/tagvalue/package_writer.py index 9e8b57e9d..f597ee137 100644 --- a/src/writer/tagvalue/package_writer.py +++ b/src/writer/tagvalue/package_writer.py @@ -20,46 +20,45 @@ def write_package(package: Package, text_output: TextIO): text_output.write("## Package Information\n") - write_value("PackageName", package.name, text_output, True) - write_value("SPDXID", package.spdx_id, text_output, True) - write_value("PackageVersion", package.version, text_output, True) + write_value("PackageName", package.name, text_output) + write_value("SPDXID", package.spdx_id, text_output) + write_value("PackageVersion", package.version, text_output) write_value("PackageDownloadLocation", package.download_location, text_output) - write_value("FilesAnalyzed", package.files_analyzed, text_output, True) - write_text_value("PackageSummary", package.summary, text_output, True) + write_value("FilesAnalyzed", package.files_analyzed, text_output) + write_text_value("PackageSummary", package.summary, text_output) for attribution_text in package.attribution_texts: write_text_value("PackageAttributionText", attribution_text, text_output) - write_text_value("PackageSourceInfo", package.source_info, text_output, True) - write_value("PackageFileName", package.file_name, text_output, True) - write_actor_or_no_assertion("PackageSupplier", package.supplier, text_output, True) - write_actor_or_no_assertion("PackageOriginator", package.originator, text_output, True) + write_text_value("PackageSourceInfo", package.source_info, text_output) + write_value("PackageFileName", package.file_name, text_output) + write_actor_or_no_assertion("PackageSupplier", package.supplier, text_output) + write_actor_or_no_assertion("PackageOriginator", package.originator, text_output) for package_checksum in package.checksums: - write_value("PackageChecksum", write_checksum_to_tag_value(package_checksum), text_output, True) + write_value("PackageChecksum", write_checksum_to_tag_value(package_checksum), text_output) if package.verification_code: package_verification_code = write_package_verification_code(package.verification_code) - write_value("PackageVerificationCode", package_verification_code, text_output, True) + write_value("PackageVerificationCode", package_verification_code, text_output) - write_text_value("PackageDescription", package.description, text_output, True) - write_text_value("PackageComment", package.comment, text_output, True) + write_text_value("PackageDescription", package.description, text_output) + write_text_value("PackageComment", package.comment, text_output) - write_field_or_none_or_no_assertion("PackageLicenseDeclared", package.license_declared, text_output, True) - write_field_or_none_or_no_assertion("PackageLicenseConcluded", package.license_concluded, text_output, True) - write_field_or_none_or_no_assertion("PackageLicenseInfoFromFiles", package.license_info_from_files, text_output, - True) + write_field_or_none_or_no_assertion("PackageLicenseDeclared", package.license_declared, text_output) + write_field_or_none_or_no_assertion("PackageLicenseConcluded", package.license_concluded, text_output) + write_field_or_none_or_no_assertion("PackageLicenseInfoFromFiles", package.license_info_from_files, text_output) - write_text_value("PackageLicenseComments", package.license_comment, text_output, True) - write_field_or_none_or_no_assertion("PackageCopyrightText", package.copyright_text, text_output, True) + write_text_value("PackageLicenseComments", package.license_comment, text_output) + write_field_or_none_or_no_assertion("PackageCopyrightText", package.copyright_text, text_output) - write_value("PackageHomePage", package.homepage, text_output, True) + write_value("PackageHomePage", package.homepage, text_output) for external_reference in package.external_references: external_reference_str = " ".join( [transform_enum_name_to_tv(external_reference.category.name), external_reference.reference_type, external_reference.locator] ) - write_value("ExternalRef", external_reference_str, text_output, True) + write_value("ExternalRef", external_reference_str, text_output) if external_reference.comment: write_text_value("ExternalRefComment", external_reference.comment, text_output) diff --git a/src/writer/tagvalue/relationship_writer.py b/src/writer/tagvalue/relationship_writer.py index 7b7b3489a..e066fe52b 100644 --- a/src/writer/tagvalue/relationship_writer.py +++ b/src/writer/tagvalue/relationship_writer.py @@ -18,4 +18,4 @@ def write_relationship(relationship: Relationship, text_output: TextIO): write_value("Relationship", " ".join( [relationship.spdx_element_id, relationship.relationship_type.name, relationship.related_spdx_element_id]), text_output) - write_text_value("RelationshipComment", relationship.comment, text_output, True) + write_text_value("RelationshipComment", relationship.comment, text_output) diff --git a/src/writer/tagvalue/snippet_writer.py b/src/writer/tagvalue/snippet_writer.py index d9fc466b6..17750d889 100644 --- a/src/writer/tagvalue/snippet_writer.py +++ b/src/writer/tagvalue/snippet_writer.py @@ -19,13 +19,13 @@ def write_snippet(snippet: Snippet, output_text: TextIO): output_text.write("## Snippet Information\n") write_value("SnippetSPDXID", snippet.spdx_id, output_text) write_value("SnippetFromFileSPDXID", snippet.file_spdx_id, output_text) - write_text_value("SnippetCopyrightText", snippet.copyright_text, output_text, True) + write_text_value("SnippetCopyrightText", snippet.copyright_text, output_text) write_range("SnippetByteRange", snippet.byte_range, output_text) - write_range("SnippetLineRange", snippet.line_range, output_text, True) - write_value("SnippetName", snippet.name, output_text, True) - write_text_value("SnippetComment", snippet.comment, output_text, True) - write_text_value("SnippetLicenseComments", snippet.license_comment, output_text, True) + write_range("SnippetLineRange", snippet.line_range, output_text) + write_value("SnippetName", snippet.name, output_text) + write_text_value("SnippetComment", snippet.comment, output_text) + write_text_value("SnippetLicenseComments", snippet.license_comment, output_text) for attribution_text in snippet.attribution_texts: write_text_value("SnippetAttributionText", attribution_text, output_text) - write_field_or_none_or_no_assertion("SnippetLicenseConcluded", snippet.concluded_license, output_text, True) - write_field_or_none_or_no_assertion("LicenseInfoInSnippet", snippet.license_info_in_snippet, output_text, True) + write_field_or_none_or_no_assertion("SnippetLicenseConcluded", snippet.concluded_license, output_text) + write_field_or_none_or_no_assertion("LicenseInfoInSnippet", snippet.license_info_in_snippet, output_text) diff --git a/src/writer/tagvalue/tagvalue_writer.py b/src/writer/tagvalue/tagvalue_writer.py index 8ced15b03..2aca71b38 100644 --- a/src/writer/tagvalue/tagvalue_writer.py +++ b/src/writer/tagvalue/tagvalue_writer.py @@ -34,7 +34,7 @@ def write_document(document: Document, text_output: TextIO): write_separator(text_output) write_optional_heading(document.annotations, "## Annotations\n", text_output) - write_list_of_elements(document.annotations, write_annotation, text_output, True) + write_list_of_elements(document.annotations, write_annotation, text_output) relationships_to_write, contained_files_by_package_id = scan_relationships(document.relationships, document.packages, document.files) @@ -58,7 +58,7 @@ def write_document(document: Document, text_output: TextIO): write_file(file, text_output) write_separator(text_output) if file.spdx_id in file_ids_with_contained_snippets: - write_list_of_elements(file_ids_with_contained_snippets[file.spdx_id], write_snippet, text_output, True) + write_list_of_elements(file_ids_with_contained_snippets[file.spdx_id], write_snippet, text_output) for package in document.packages: write_package(package, text_output) @@ -68,8 +68,8 @@ def write_document(document: Document, text_output: TextIO): write_file(file, text_output) write_separator(text_output) if file.spdx_id in file_ids_with_contained_snippets: - write_list_of_elements(file_ids_with_contained_snippets[file.spdx_id], write_snippet, text_output, True) + write_list_of_elements(file_ids_with_contained_snippets[file.spdx_id], write_snippet, text_output) break write_optional_heading(document.extracted_licensing_info, "## License Information\n", text_output) - write_list_of_elements(document.extracted_licensing_info, write_extracted_licensing_info, text_output, True) + write_list_of_elements(document.extracted_licensing_info, write_extracted_licensing_info, text_output) diff --git a/src/writer/tagvalue/tagvalue_writer_helper_functions.py b/src/writer/tagvalue/tagvalue_writer_helper_functions.py index e0984dddd..e725d8601 100644 --- a/src/writer/tagvalue/tagvalue_writer_helper_functions.py +++ b/src/writer/tagvalue/tagvalue_writer_helper_functions.py @@ -24,25 +24,21 @@ def write_separator(out: TextIO): out.write("\n") -def write_value(tag: str, value: Union[bool, str, SpdxNone, SpdxNoAssertion], out: TextIO, optional: bool = False): - if optional and not value: - return - out.write(f"{tag}: {value}\n") +def write_value(tag: str, value: Union[bool, str, SpdxNone, SpdxNoAssertion], out: TextIO): + if value: + out.write(f"{tag}: {value}\n") -def write_range(tag: str, value: Tuple[int, int], out: TextIO, optional: bool = False): - if optional and not value: - return - out.write(f"{tag}: {value[0]}:{value[1]}\n") +def write_range(tag: str, value: Tuple[int, int], out: TextIO): + if value: + out.write(f"{tag}: {value[0]}:{value[1]}\n") -def write_text_value(tag: str, value: str, out: TextIO, optional: bool = False): - if optional and not value: - return - if "\n" in value: +def write_text_value(tag: str, value: str, out: TextIO): + if value and "\n" in value: out.write(f"{tag}: {value}\n") else: - write_value(tag, value, out, True) + write_value(tag, value, out) def transform_enum_name_to_tv(enum_str: str) -> str: @@ -62,9 +58,7 @@ def write_list_of_elements(list_of_elements: List[Any], write_method: Callable[[ write_separator(text_output) -def write_actor_or_no_assertion(tag: str, element_to_write: Any, text_output: TextIO, optional: bool): - if optional and not element_to_write: - return +def write_actor_or_no_assertion(tag: str, element_to_write: Any, text_output: TextIO): if isinstance(element_to_write, Actor): write_value(tag, element_to_write.to_serialized_string(), text_output) else: @@ -72,10 +66,7 @@ def write_actor_or_no_assertion(tag: str, element_to_write: Any, text_output: Te def write_field_or_none_or_no_assertion(tag: str, element_to_write: Union[ - List[LicenseExpression], LicenseExpression, SpdxNoAssertion, SpdxNone], text_output: TextIO, - optional: bool = False): - if optional and not element_to_write: - return + List[LicenseExpression], LicenseExpression, SpdxNoAssertion, SpdxNone], text_output: TextIO): if isinstance(element_to_write, (SpdxNone, SpdxNoAssertion, str)): write_value(tag, element_to_write, text_output) elif isinstance(element_to_write, LicenseExpression): From cc7b37df3478e3f6f3e8ec48a9acd8bba0f8df33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 30 Dec 2022 16:45:00 +0100 Subject: [PATCH 066/362] [issue-381] change handling of writer methods and change type hints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit also rename write_package_verification_code, erase a line in the test example and an erroneous break Signed-off-by: Armin Tänzer --- src/writer/tagvalue/file_writer.py | 8 ++++---- src/writer/tagvalue/package_writer.py | 18 +++++++++--------- src/writer/tagvalue/snippet_writer.py | 6 +++--- src/writer/tagvalue/tagvalue_writer.py | 1 - .../tagvalue_writer_helper_functions.py | 16 ++++++++-------- .../expected_results/expected_tag_value.spdx | 1 - 6 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/writer/tagvalue/file_writer.py b/src/writer/tagvalue/file_writer.py index c2aafe348..aa3ee5d65 100644 --- a/src/writer/tagvalue/file_writer.py +++ b/src/writer/tagvalue/file_writer.py @@ -12,7 +12,7 @@ from src.model.file import File from src.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, \ - write_field_or_none_or_no_assertion + write_license_expression from src.writer.tagvalue.checksum_writer import write_checksum_to_tag_value @@ -24,9 +24,9 @@ def write_file(file: File, text_output: TextIO): write_value("FileType", file_type.name, text_output) for file_checksum in file.checksums: write_value("FileChecksum", write_checksum_to_tag_value(file_checksum), text_output) - write_field_or_none_or_no_assertion("LicenseConcluded", file.concluded_license, text_output) - write_field_or_none_or_no_assertion("LicenseInfoInFile", file.license_info_in_file, text_output) - write_field_or_none_or_no_assertion("FileCopyrightText", file.copyright_text, text_output) + write_license_expression("LicenseConcluded", file.concluded_license, text_output) + write_license_expression("LicenseInfoInFile", file.license_info_in_file, text_output) + write_text_value("FileCopyrightText", file.copyright_text, text_output) write_text_value("LicenseComments", file.license_comment, text_output) for attribution_text in file.attribution_texts: diff --git a/src/writer/tagvalue/package_writer.py b/src/writer/tagvalue/package_writer.py index f597ee137..628d33d9b 100644 --- a/src/writer/tagvalue/package_writer.py +++ b/src/writer/tagvalue/package_writer.py @@ -13,7 +13,7 @@ from src.datetime_conversions import datetime_to_iso_string from src.model.package import Package, PackageVerificationCode from src.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, \ - write_field_or_none_or_no_assertion, transform_enum_name_to_tv, write_actor_or_no_assertion + write_license_expression, transform_enum_name_to_tv, write_actor from src.writer.tagvalue.checksum_writer import write_checksum_to_tag_value @@ -31,25 +31,25 @@ def write_package(package: Package, text_output: TextIO): write_text_value("PackageSourceInfo", package.source_info, text_output) write_value("PackageFileName", package.file_name, text_output) - write_actor_or_no_assertion("PackageSupplier", package.supplier, text_output) - write_actor_or_no_assertion("PackageOriginator", package.originator, text_output) + write_actor("PackageSupplier", package.supplier, text_output) + write_actor("PackageOriginator", package.originator, text_output) for package_checksum in package.checksums: write_value("PackageChecksum", write_checksum_to_tag_value(package_checksum), text_output) if package.verification_code: - package_verification_code = write_package_verification_code(package.verification_code) + package_verification_code = get_package_verification_code_string(package.verification_code) write_value("PackageVerificationCode", package_verification_code, text_output) write_text_value("PackageDescription", package.description, text_output) write_text_value("PackageComment", package.comment, text_output) - write_field_or_none_or_no_assertion("PackageLicenseDeclared", package.license_declared, text_output) - write_field_or_none_or_no_assertion("PackageLicenseConcluded", package.license_concluded, text_output) - write_field_or_none_or_no_assertion("PackageLicenseInfoFromFiles", package.license_info_from_files, text_output) + write_license_expression("PackageLicenseDeclared", package.license_declared, text_output) + write_license_expression("PackageLicenseConcluded", package.license_concluded, text_output) + write_license_expression("PackageLicenseInfoFromFiles", package.license_info_from_files, text_output) write_text_value("PackageLicenseComments", package.license_comment, text_output) - write_field_or_none_or_no_assertion("PackageCopyrightText", package.copyright_text, text_output) + write_text_value("PackageCopyrightText", package.copyright_text, text_output) write_value("PackageHomePage", package.homepage, text_output) @@ -74,7 +74,7 @@ def write_package(package: Package, text_output: TextIO): write_value("ValidUntilDate", datetime_to_iso_string(package.valid_until_date), text_output) -def write_package_verification_code(verification_code: PackageVerificationCode): +def get_package_verification_code_string(verification_code: PackageVerificationCode) -> str: if not verification_code.excluded_files: return verification_code.value diff --git a/src/writer/tagvalue/snippet_writer.py b/src/writer/tagvalue/snippet_writer.py index 17750d889..07a10757e 100644 --- a/src/writer/tagvalue/snippet_writer.py +++ b/src/writer/tagvalue/snippet_writer.py @@ -12,7 +12,7 @@ from src.model.snippet import Snippet from src.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, write_range, \ - write_field_or_none_or_no_assertion + write_license_expression def write_snippet(snippet: Snippet, output_text: TextIO): @@ -27,5 +27,5 @@ def write_snippet(snippet: Snippet, output_text: TextIO): write_text_value("SnippetLicenseComments", snippet.license_comment, output_text) for attribution_text in snippet.attribution_texts: write_text_value("SnippetAttributionText", attribution_text, output_text) - write_field_or_none_or_no_assertion("SnippetLicenseConcluded", snippet.concluded_license, output_text) - write_field_or_none_or_no_assertion("LicenseInfoInSnippet", snippet.license_info_in_snippet, output_text) + write_license_expression("SnippetLicenseConcluded", snippet.concluded_license, output_text) + write_license_expression("LicenseInfoInSnippet", snippet.license_info_in_snippet, output_text) diff --git a/src/writer/tagvalue/tagvalue_writer.py b/src/writer/tagvalue/tagvalue_writer.py index 2aca71b38..50d70dcef 100644 --- a/src/writer/tagvalue/tagvalue_writer.py +++ b/src/writer/tagvalue/tagvalue_writer.py @@ -69,7 +69,6 @@ def write_document(document: Document, text_output: TextIO): write_separator(text_output) if file.spdx_id in file_ids_with_contained_snippets: write_list_of_elements(file_ids_with_contained_snippets[file.spdx_id], write_snippet, text_output) - break write_optional_heading(document.extracted_licensing_info, "## License Information\n", text_output) write_list_of_elements(document.extracted_licensing_info, write_extracted_licensing_info, text_output) diff --git a/src/writer/tagvalue/tagvalue_writer_helper_functions.py b/src/writer/tagvalue/tagvalue_writer_helper_functions.py index e725d8601..967281551 100644 --- a/src/writer/tagvalue/tagvalue_writer_helper_functions.py +++ b/src/writer/tagvalue/tagvalue_writer_helper_functions.py @@ -8,7 +8,7 @@ # 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 typing import TextIO, Tuple, List, Dict, Any, Union, Callable +from typing import TextIO, Tuple, List, Dict, Any, Union, Callable, Optional from src.model.actor import Actor from src.model.file import File @@ -24,18 +24,18 @@ def write_separator(out: TextIO): out.write("\n") -def write_value(tag: str, value: Union[bool, str, SpdxNone, SpdxNoAssertion], out: TextIO): +def write_value(tag: str, value: Optional[Union[bool, str, SpdxNone, SpdxNoAssertion]], out: TextIO): if value: out.write(f"{tag}: {value}\n") -def write_range(tag: str, value: Tuple[int, int], out: TextIO): +def write_range(tag: str, value: Optional[Tuple[int, int]], out: TextIO): if value: out.write(f"{tag}: {value[0]}:{value[1]}\n") -def write_text_value(tag: str, value: str, out: TextIO): - if value and "\n" in value: +def write_text_value(tag: str, value: Optional[Union[str, SpdxNone, SpdxNoAssertion]], out: TextIO): + if isinstance(value, str) and "\n" in value: out.write(f"{tag}: {value}\n") else: write_value(tag, value, out) @@ -58,15 +58,15 @@ def write_list_of_elements(list_of_elements: List[Any], write_method: Callable[[ write_separator(text_output) -def write_actor_or_no_assertion(tag: str, element_to_write: Any, text_output: TextIO): +def write_actor(tag: str, element_to_write: Optional[Union[Actor, SpdxNoAssertion]], text_output: TextIO): if isinstance(element_to_write, Actor): write_value(tag, element_to_write.to_serialized_string(), text_output) else: write_value(tag, element_to_write, text_output) -def write_field_or_none_or_no_assertion(tag: str, element_to_write: Union[ - List[LicenseExpression], LicenseExpression, SpdxNoAssertion, SpdxNone], text_output: TextIO): +def write_license_expression(tag: str, element_to_write: Optional[Union[ + List[LicenseExpression], LicenseExpression, SpdxNoAssertion, SpdxNone]], text_output: TextIO): if isinstance(element_to_write, (SpdxNone, SpdxNoAssertion, str)): write_value(tag, element_to_write, text_output) elif isinstance(element_to_write, LicenseExpression): diff --git a/tests/writer/tagvalue/expected_results/expected_tag_value.spdx b/tests/writer/tagvalue/expected_results/expected_tag_value.spdx index 799178b40..89c20e964 100644 --- a/tests/writer/tagvalue/expected_results/expected_tag_value.spdx +++ b/tests/writer/tagvalue/expected_results/expected_tag_value.spdx @@ -48,7 +48,6 @@ SPDXID: packageId PackageDownloadLocation: NONE FilesAnalyzed: True - ## License Information LicenseID: licenseId ExtractedText: licenseText From 3635c6e9afed4122710272ce477de0a357a3427d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Mon, 2 Jan 2023 10:06:13 +0100 Subject: [PATCH 067/362] [issue-381] adjust ordering of the output to follow the specification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/writer/tagvalue/annotation_writer.py | 2 +- src/writer/tagvalue/creation_info_writer.py | 11 ++-- .../extracted_licensing_info_writer.py | 6 ++- src/writer/tagvalue/file_writer.py | 12 +++-- src/writer/tagvalue/package_writer.py | 33 ++++++------ src/writer/tagvalue/snippet_writer.py | 14 +++-- src/writer/tagvalue/tagvalue_writer.py | 27 +++++----- .../expected_results/expected_tag_value.spdx | 53 ++++++++++--------- tests/writer/tagvalue/test_package_writer.py | 31 +++++++---- tests/writer/tagvalue/test_tagvalue_writer.py | 2 +- 10 files changed, 109 insertions(+), 82 deletions(-) diff --git a/src/writer/tagvalue/annotation_writer.py b/src/writer/tagvalue/annotation_writer.py index 5a789c96f..5cb7b8b46 100644 --- a/src/writer/tagvalue/annotation_writer.py +++ b/src/writer/tagvalue/annotation_writer.py @@ -18,6 +18,6 @@ def write_annotation(annotation: Annotation, text_output: TextIO): write_value("Annotator", annotation.annotator.to_serialized_string(), text_output) write_value("AnnotationDate", datetime_to_iso_string(annotation.annotation_date), text_output) - write_text_value("AnnotationComment", annotation.annotation_comment, text_output) write_value("AnnotationType", annotation.annotation_type.name, text_output) write_value("SPDXREF", annotation.spdx_id, text_output) + write_text_value("AnnotationComment", annotation.annotation_comment, text_output) diff --git a/src/writer/tagvalue/creation_info_writer.py b/src/writer/tagvalue/creation_info_writer.py index f889bac2c..0cf50b23e 100644 --- a/src/writer/tagvalue/creation_info_writer.py +++ b/src/writer/tagvalue/creation_info_writer.py @@ -19,10 +19,9 @@ def write_creation_info(creation_info: CreationInfo, text_output: TextIO): write_value("SPDXVersion", creation_info.spdx_version, text_output) write_value("DataLicense", creation_info.data_license, text_output) - write_value("DocumentNamespace", creation_info.document_namespace, text_output) - write_value("DocumentName", creation_info.name, text_output) - write_value("LicenseListVersion", str(creation_info.spdx_version), text_output) write_value("SPDXID", creation_info.spdx_id, text_output) + write_value("DocumentName", creation_info.name, text_output) + write_value("DocumentNamespace", creation_info.document_namespace, text_output) write_text_value("DocumentComment", creation_info.document_comment, text_output) write_optional_heading(creation_info.external_document_refs, "\n## External Document References\n", text_output) @@ -33,7 +32,13 @@ def write_creation_info(creation_info: CreationInfo, text_output: TextIO): write_separator(text_output) text_output.write("## Creation Information\n") + write_value("LicenseListVersion", str(creation_info.spdx_version), text_output) for creator in creation_info.creators: write_value("Creator", creator.to_serialized_string(), text_output) write_value("Created", datetime_to_iso_string(creation_info.created), text_output) write_text_value("CreatorComment", creation_info.creator_comment, text_output) + + + + + diff --git a/src/writer/tagvalue/extracted_licensing_info_writer.py b/src/writer/tagvalue/extracted_licensing_info_writer.py index 90d6e7087..4cc86a0f4 100644 --- a/src/writer/tagvalue/extracted_licensing_info_writer.py +++ b/src/writer/tagvalue/extracted_licensing_info_writer.py @@ -16,10 +16,12 @@ def write_extracted_licensing_info(extracted_licensing_info: ExtractedLicensingInfo, text_output: TextIO): write_value("LicenseID", extracted_licensing_info.license_id, text_output) + write_text_value("ExtractedText", extracted_licensing_info.extracted_text, text_output) write_value("LicenseName", extracted_licensing_info.license_name, text_output) - write_text_value("LicenseComment", extracted_licensing_info.comment, text_output) for cross_reference in sorted(extracted_licensing_info.cross_references): write_value("LicenseCrossReference", cross_reference, text_output) - write_text_value("ExtractedText", extracted_licensing_info.extracted_text, text_output) + write_text_value("LicenseComment", extracted_licensing_info.comment, text_output) + + diff --git a/src/writer/tagvalue/file_writer.py b/src/writer/tagvalue/file_writer.py index aa3ee5d65..67780d146 100644 --- a/src/writer/tagvalue/file_writer.py +++ b/src/writer/tagvalue/file_writer.py @@ -18,22 +18,26 @@ def write_file(file: File, text_output: TextIO): text_output.write("## File Information\n") + write_value("FileName", file.name, text_output) write_value("SPDXID", file.spdx_id, text_output) + for file_type in file.file_type: write_value("FileType", file_type.name, text_output) + for file_checksum in file.checksums: write_value("FileChecksum", write_checksum_to_tag_value(file_checksum), text_output) + write_license_expression("LicenseConcluded", file.concluded_license, text_output) write_license_expression("LicenseInfoInFile", file.license_info_in_file, text_output) - write_text_value("FileCopyrightText", file.copyright_text, text_output) write_text_value("LicenseComments", file.license_comment, text_output) - - for attribution_text in file.attribution_texts: - write_text_value("FileAttributionText", attribution_text, text_output) + write_text_value("FileCopyrightText", file.copyright_text, text_output) write_text_value("FileComment", file.comment, text_output) write_text_value("FileNotice", file.notice, text_output) for contributor in sorted(file.contributors): write_value("FileContributor", contributor, text_output) + + for attribution_text in file.attribution_texts: + write_text_value("FileAttributionText", attribution_text, text_output) diff --git a/src/writer/tagvalue/package_writer.py b/src/writer/tagvalue/package_writer.py index 628d33d9b..78bb90dc7 100644 --- a/src/writer/tagvalue/package_writer.py +++ b/src/writer/tagvalue/package_writer.py @@ -23,35 +23,31 @@ def write_package(package: Package, text_output: TextIO): write_value("PackageName", package.name, text_output) write_value("SPDXID", package.spdx_id, text_output) write_value("PackageVersion", package.version, text_output) - write_value("PackageDownloadLocation", package.download_location, text_output) - write_value("FilesAnalyzed", package.files_analyzed, text_output) - write_text_value("PackageSummary", package.summary, text_output) - for attribution_text in package.attribution_texts: - write_text_value("PackageAttributionText", attribution_text, text_output) - - write_text_value("PackageSourceInfo", package.source_info, text_output) write_value("PackageFileName", package.file_name, text_output) write_actor("PackageSupplier", package.supplier, text_output) write_actor("PackageOriginator", package.originator, text_output) + write_value("PackageDownloadLocation", package.download_location, text_output) - for package_checksum in package.checksums: - write_value("PackageChecksum", write_checksum_to_tag_value(package_checksum), text_output) - + write_value("FilesAnalyzed", package.files_analyzed, text_output) if package.verification_code: package_verification_code = get_package_verification_code_string(package.verification_code) write_value("PackageVerificationCode", package_verification_code, text_output) - write_text_value("PackageDescription", package.description, text_output) - write_text_value("PackageComment", package.comment, text_output) + for package_checksum in package.checksums: + write_value("PackageChecksum", write_checksum_to_tag_value(package_checksum), text_output) + + write_value("PackageHomePage", package.homepage, text_output) + write_text_value("PackageSourceInfo", package.source_info, text_output) - write_license_expression("PackageLicenseDeclared", package.license_declared, text_output) write_license_expression("PackageLicenseConcluded", package.license_concluded, text_output) write_license_expression("PackageLicenseInfoFromFiles", package.license_info_from_files, text_output) - + write_license_expression("PackageLicenseDeclared", package.license_declared, text_output) write_text_value("PackageLicenseComments", package.license_comment, text_output) write_text_value("PackageCopyrightText", package.copyright_text, text_output) - write_value("PackageHomePage", package.homepage, text_output) + write_text_value("PackageSummary", package.summary, text_output) + write_text_value("PackageDescription", package.description, text_output) + write_text_value("PackageComment", package.comment, text_output) for external_reference in package.external_references: external_reference_str = " ".join( @@ -62,14 +58,17 @@ def write_package(package: Package, text_output: TextIO): if external_reference.comment: write_text_value("ExternalRefComment", external_reference.comment, text_output) + for attribution_text in package.attribution_texts: + write_text_value("PackageAttributionText", attribution_text, text_output) + if package.primary_package_purpose: write_value("PrimaryPackagePurpose", transform_enum_name_to_tv(package.primary_package_purpose.name), text_output) - if package.built_date: - write_value("BuiltDate", datetime_to_iso_string(package.built_date), text_output) if package.release_date: write_value("ReleaseDate", datetime_to_iso_string(package.release_date), text_output) + if package.built_date: + write_value("BuiltDate", datetime_to_iso_string(package.built_date), text_output) if package.valid_until_date: write_value("ValidUntilDate", datetime_to_iso_string(package.valid_until_date), text_output) diff --git a/src/writer/tagvalue/snippet_writer.py b/src/writer/tagvalue/snippet_writer.py index 07a10757e..b4a4740e6 100644 --- a/src/writer/tagvalue/snippet_writer.py +++ b/src/writer/tagvalue/snippet_writer.py @@ -17,15 +17,19 @@ def write_snippet(snippet: Snippet, output_text: TextIO): output_text.write("## Snippet Information\n") + write_value("SnippetSPDXID", snippet.spdx_id, output_text) write_value("SnippetFromFileSPDXID", snippet.file_spdx_id, output_text) - write_text_value("SnippetCopyrightText", snippet.copyright_text, output_text) write_range("SnippetByteRange", snippet.byte_range, output_text) write_range("SnippetLineRange", snippet.line_range, output_text) - write_value("SnippetName", snippet.name, output_text) - write_text_value("SnippetComment", snippet.comment, output_text) + + write_license_expression("SnippetLicenseConcluded", snippet.concluded_license, output_text) + write_license_expression("LicenseInfoInSnippet", snippet.license_info_in_snippet, output_text) write_text_value("SnippetLicenseComments", snippet.license_comment, output_text) + write_text_value("SnippetCopyrightText", snippet.copyright_text, output_text) + + write_text_value("SnippetComment", snippet.comment, output_text) + write_value("SnippetName", snippet.name, output_text) + for attribution_text in snippet.attribution_texts: write_text_value("SnippetAttributionText", attribution_text, output_text) - write_license_expression("SnippetLicenseConcluded", snippet.concluded_license, output_text) - write_license_expression("LicenseInfoInSnippet", snippet.license_info_in_snippet, output_text) diff --git a/src/writer/tagvalue/tagvalue_writer.py b/src/writer/tagvalue/tagvalue_writer.py index 50d70dcef..0f609602f 100644 --- a/src/writer/tagvalue/tagvalue_writer.py +++ b/src/writer/tagvalue/tagvalue_writer.py @@ -29,13 +29,6 @@ def write_document_to_file(document: Document, file_name: str): def write_document(document: Document, text_output: TextIO): - text_output.write("## Document Information\n") - write_creation_info(document.creation_info, text_output) - write_separator(text_output) - - write_optional_heading(document.annotations, "## Annotations\n", text_output) - write_list_of_elements(document.annotations, write_annotation, text_output) - relationships_to_write, contained_files_by_package_id = scan_relationships(document.relationships, document.packages, document.files) file_ids_with_contained_snippets = get_file_ids_with_contained_snippets(document.snippets, document.files) @@ -44,8 +37,8 @@ def write_document(document: Document, text_output: TextIO): filed_snippet_ids = [snippet.spdx_id for snippets_list in file_ids_with_contained_snippets.values() for snippet in snippets_list] - write_optional_heading(relationships_to_write, "## Relationships\n", text_output) - write_list_of_elements(relationships_to_write, write_relationship, text_output) + text_output.write("## Document Information\n") + write_creation_info(document.creation_info, text_output) write_separator(text_output) for snippet in document.snippets: @@ -58,7 +51,8 @@ def write_document(document: Document, text_output: TextIO): write_file(file, text_output) write_separator(text_output) if file.spdx_id in file_ids_with_contained_snippets: - write_list_of_elements(file_ids_with_contained_snippets[file.spdx_id], write_snippet, text_output) + write_list_of_elements(file_ids_with_contained_snippets[file.spdx_id], write_snippet, text_output, + with_separator=True) for package in document.packages: write_package(package, text_output) @@ -68,7 +62,16 @@ def write_document(document: Document, text_output: TextIO): write_file(file, text_output) write_separator(text_output) if file.spdx_id in file_ids_with_contained_snippets: - write_list_of_elements(file_ids_with_contained_snippets[file.spdx_id], write_snippet, text_output) + write_list_of_elements(file_ids_with_contained_snippets[file.spdx_id], write_snippet, text_output, + with_separator=True) write_optional_heading(document.extracted_licensing_info, "## License Information\n", text_output) - write_list_of_elements(document.extracted_licensing_info, write_extracted_licensing_info, text_output) + write_list_of_elements(document.extracted_licensing_info, write_extracted_licensing_info, text_output, + with_separator=True) + + write_optional_heading(relationships_to_write, "## Relationships\n", text_output) + write_list_of_elements(relationships_to_write, write_relationship, text_output) + write_separator(text_output) + + write_optional_heading(document.annotations, "## Annotations\n", text_output) + write_list_of_elements(document.annotations, write_annotation, text_output, with_separator=True) diff --git a/tests/writer/tagvalue/expected_results/expected_tag_value.spdx b/tests/writer/tagvalue/expected_results/expected_tag_value.spdx index 89c20e964..ae6eb22c4 100644 --- a/tests/writer/tagvalue/expected_results/expected_tag_value.spdx +++ b/tests/writer/tagvalue/expected_results/expected_tag_value.spdx @@ -1,47 +1,29 @@ ## Document Information SPDXVersion: spdxVersion DataLicense: dataLicense -DocumentNamespace: documentNamespace -DocumentName: documentName SPDXID: documentId +DocumentName: documentName +DocumentNamespace: documentNamespace DocumentComment: comment ## External Document References ExternalDocumentRef: docRefId externalDocumentUri SHA1: externalRefSha1 ## Creation Information +LicenseListVersion: spdxVersion Creator: Tool: tools-python (tools-python@github.com) Created: 2022-12-01T00:00:00Z -## Annotations -Annotator: Person: reviewerName -AnnotationDate: 2022-12-02T00:00:00Z -AnnotationComment: reviewComment -AnnotationType: REVIEW -SPDXREF: documentId - -Annotator: Tool: toolName -AnnotationDate: 2022-12-03T00:00:00Z -AnnotationComment: otherComment -AnnotationType: OTHER -SPDXREF: fileId - -## Relationships -Relationship: documentId DESCRIBES packageId -Relationship: documentId DESCRIBES fileId -RelationshipComment: relationshipComment -Relationship: relationshipOriginId AMENDS relationShipTargetId - -## Snippet Information -SnippetSPDXID: snippetId -SnippetFromFileSPDXID: snippetFileId -SnippetByteRange: 1:2 - ## File Information FileName: fileName SPDXID: fileId FileChecksum: SHA1: fileSha1 +## Snippet Information +SnippetSPDXID: snippetId +SnippetFromFileSPDXID: fileId +SnippetByteRange: 1:2 + ## Package Information PackageName: packageName SPDXID: packageId @@ -51,3 +33,22 @@ FilesAnalyzed: True ## License Information LicenseID: licenseId ExtractedText: licenseText + +## Relationships +Relationship: documentId DESCRIBES packageId +Relationship: documentId DESCRIBES fileId +RelationshipComment: relationshipComment +Relationship: relationshipOriginId AMENDS relationShipTargetId + +## Annotations +Annotator: Person: reviewerName +AnnotationDate: 2022-12-02T00:00:00Z +AnnotationType: REVIEW +SPDXREF: documentId +AnnotationComment: reviewComment + +Annotator: Tool: toolName +AnnotationDate: 2022-12-03T00:00:00Z +AnnotationType: OTHER +SPDXREF: fileId +AnnotationComment: otherComment diff --git a/tests/writer/tagvalue/test_package_writer.py b/tests/writer/tagvalue/test_package_writer.py index 1fbd469ea..052b24dd7 100644 --- a/tests/writer/tagvalue/test_package_writer.py +++ b/tests/writer/tagvalue/test_package_writer.py @@ -36,19 +36,28 @@ def test_package_writer(): m.assert_called_once_with('foo', 'w') handle = m() handle.write.assert_has_calls( - [call('## Package Information\n'), call('PackageName: package name\n'), call('SPDXID: SPDXRef-Package\n'), - call('PackageVersion: version\n'), call('PackageDownloadLocation: www.download.com\n'), - call('FilesAnalyzed: True\n'), call('PackageSummary: summary\n'), call('PackageAttributionText: text\n'), - call('PackageSourceInfo: source_info\n'), call('PackageFileName: file_name\n'), + [call('## Package Information\n'), + call('PackageName: package name\n'), + call('SPDXID: SPDXRef-Package\n'), + call('PackageVersion: version\n'), + call('PackageFileName: file_name\n'), call('PackageSupplier: NOASSERTION\n'), - call("PackageOriginator: Person: person name\n"), - call('PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c\n'), + call('PackageOriginator: Person: person name\n'), + call('PackageDownloadLocation: www.download.com\n'), + call('FilesAnalyzed: True\n'), call('PackageVerificationCode: 85ed0817af83a24ad8da68c2b5094de69833983c\n'), - call('PackageDescription: description\n'), call('PackageComment: comment\n'), - call('PackageLicenseDeclared: NONE\n'), - call("PackageLicenseInfoFromFiles: expression\n"), - call('PackageLicenseComments: comment on license\n'), call('PackageCopyrightText: copyright\n'), + call('PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c\n'), call('PackageHomePage: https://homepage.com\n'), + call('PackageSourceInfo: source_info\n'), + call('PackageLicenseInfoFromFiles: expression\n'), + call('PackageLicenseDeclared: NONE\n'), + call('PackageLicenseComments: comment on license\n'), + call('PackageCopyrightText: copyright\n'), + call('PackageSummary: summary\n'), + call('PackageDescription: description\n'), + call('PackageComment: comment\n'), call('ExternalRef: SECURITY cpe22Type cpe:/o:canonical:ubuntu_linux:10.04:-:lts\n'), - call('ExternalRefComment: external package ref comment\n'), call('PrimaryPackagePurpose: OTHER\n'), + call('ExternalRefComment: external package ref comment\n'), + call('PackageAttributionText: text\n'), + call('PrimaryPackagePurpose: OTHER\n'), call('ReleaseDate: 2022-01-01T00:00:00Z\n')]) diff --git a/tests/writer/tagvalue/test_tagvalue_writer.py b/tests/writer/tagvalue/test_tagvalue_writer.py index 98b786b7e..d7a1a9968 100644 --- a/tests/writer/tagvalue/test_tagvalue_writer.py +++ b/tests/writer/tagvalue/test_tagvalue_writer.py @@ -44,7 +44,7 @@ def test_write_tag_value(temporary_file_path: str): "externalRefSha1"))]) package = Package("packageId", "packageName", SpdxNone()) file = File("fileName", "fileId", [Checksum(ChecksumAlgorithm.SHA1, "fileSha1")]) - snippet = Snippet("snippetId", "snippetFileId", (1, 2)) + snippet = Snippet("snippetId", "fileId", (1, 2)) annotations = [ Annotation("documentId", AnnotationType.REVIEW, Actor(ActorType.PERSON, "reviewerName"), datetime(2022, 12, 2), "reviewComment"), From fee40944a3bded49e345a3ece4ea4d05385d7592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Mon, 2 Jan 2023 10:50:32 +0100 Subject: [PATCH 068/362] [issue-381] change error type of datetime_from_str() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/datetime_conversions.py | 9 ++---- src/parser/json/dict_parsing_functions.py | 6 +++- src/writer/tagvalue/creation_info_writer.py | 5 --- .../extracted_licensing_info_writer.py | 2 -- tests/parser/test_dict_parsing_functions.py | 19 ------------ tests/test_datetime_conversions.py | 31 +++++++++++++++++++ 6 files changed, 39 insertions(+), 33 deletions(-) create mode 100644 tests/test_datetime_conversions.py diff --git a/src/datetime_conversions.py b/src/datetime_conversions.py index 3c49d9391..7829693cf 100644 --- a/src/datetime_conversions.py +++ b/src/datetime_conversions.py @@ -15,12 +15,9 @@ def datetime_from_str(date_str: str) -> datetime: if not isinstance(date_str, str): - raise SPDXParsingError([f"Could not convert str to datetime, invalid type: {type(date_str).__name__}"]) - try: - date = datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%SZ") - except ValueError: - raise SPDXParsingError( - [f'Could not convert str to datetime, format of {date_str} does not match "%Y-%m-%dT%H:%M:%SZ"']) + raise TypeError(f"Could not convert str to datetime, invalid type: {type(date_str).__name__}") + + date = datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%SZ") # raises ValueError if format does not match return date def datetime_to_iso_string(date: datetime) -> str: diff --git a/src/parser/json/dict_parsing_functions.py b/src/parser/json/dict_parsing_functions.py index e413802f9..2f7359924 100644 --- a/src/parser/json/dict_parsing_functions.py +++ b/src/parser/json/dict_parsing_functions.py @@ -42,7 +42,9 @@ def parse_field_or_log_error(logger: Logger, field: Any, parsing_method: Callabl return parsing_method(field) except SPDXParsingError as err: logger.extend(err.get_messages()) - return default + except (TypeError, ValueError) as err: + logger.extend(err.args[0]) + return default def append_parsed_field_or_log_error(logger: Logger, list_to_append_to: List[Any], field: Any, @@ -52,6 +54,8 @@ def append_parsed_field_or_log_error(logger: Logger, list_to_append_to: List[Any list_to_append_to.append(parsed_element) except SPDXParsingError as err: logger.extend(err.get_messages()) + except (TypeError, ValueError) as err: + logger.extend(err.args[0]) return list_to_append_to diff --git a/src/writer/tagvalue/creation_info_writer.py b/src/writer/tagvalue/creation_info_writer.py index 0cf50b23e..d2c515332 100644 --- a/src/writer/tagvalue/creation_info_writer.py +++ b/src/writer/tagvalue/creation_info_writer.py @@ -37,8 +37,3 @@ def write_creation_info(creation_info: CreationInfo, text_output: TextIO): write_value("Creator", creator.to_serialized_string(), text_output) write_value("Created", datetime_to_iso_string(creation_info.created), text_output) write_text_value("CreatorComment", creation_info.creator_comment, text_output) - - - - - diff --git a/src/writer/tagvalue/extracted_licensing_info_writer.py b/src/writer/tagvalue/extracted_licensing_info_writer.py index 4cc86a0f4..eed0e07f2 100644 --- a/src/writer/tagvalue/extracted_licensing_info_writer.py +++ b/src/writer/tagvalue/extracted_licensing_info_writer.py @@ -23,5 +23,3 @@ def write_extracted_licensing_info(extracted_licensing_info: ExtractedLicensingI write_value("LicenseCrossReference", cross_reference, text_output) write_text_value("LicenseComment", extracted_licensing_info.comment, text_output) - - diff --git a/tests/parser/test_dict_parsing_functions.py b/tests/parser/test_dict_parsing_functions.py index 3c8b872b9..c77458f94 100644 --- a/tests/parser/test_dict_parsing_functions.py +++ b/tests/parser/test_dict_parsing_functions.py @@ -21,25 +21,6 @@ from src.datetime_conversions import datetime_from_str -def test_datetime_from_str(): - date_str = "2010-03-04T05:45:11Z" - - date = datetime_from_str(date_str) - - assert date == datetime(2010, 3, 4, 5, 45, 11) - - -@pytest.mark.parametrize("invalid_date_str,expected_message", - [(5, ["Could not convert str to datetime, invalid type: int"]), - ("2010-02-03", ['Could not convert str to datetime, format of 2010-02-03 does not match ' - '"%Y-%m-%dT%H:%M:%SZ"'])]) -def test_datetime_from_str_error(invalid_date_str, expected_message): - with pytest.raises(SPDXParsingError) as err: - datetime_from_str(invalid_date_str) - - TestCase().assertCountEqual(err.value.get_messages(), expected_message) - - def test_json_str_to_enum(): json_str = "BLAKE2b-256" diff --git a/tests/test_datetime_conversions.py b/tests/test_datetime_conversions.py new file mode 100644 index 000000000..6ec233cda --- /dev/null +++ b/tests/test_datetime_conversions.py @@ -0,0 +1,31 @@ +# 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 datetime import datetime + +import pytest + +from src.datetime_conversions import datetime_from_str + + +def test_datetime_from_str(): + date_str = "2010-03-04T05:45:11Z" + + date = datetime_from_str(date_str) + + assert date == datetime(2010, 3, 4, 5, 45, 11) + + +@pytest.mark.parametrize("invalid_date_str, error_type, expected_message", + [(5, TypeError, "Could not convert str to datetime, invalid type: int"), + ("2010-02-03", ValueError, "time data '2010-02-03' does not match format '%Y-%m-%dT%H:%M:%SZ'")]) +def test_datetime_from_str_error(invalid_date_str, error_type, expected_message): + with pytest.raises(error_type, match=expected_message): + datetime_from_str(invalid_date_str) From d694c13467e648c7aa6f6c93c0104a0fb70b6a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Mon, 2 Jan 2023 12:54:12 +0100 Subject: [PATCH 069/362] convert related_spdx_element_id to str in tv writer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit as this can also be NONE or NOASSERTION Signed-off-by: Armin Tänzer --- src/writer/tagvalue/relationship_writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/writer/tagvalue/relationship_writer.py b/src/writer/tagvalue/relationship_writer.py index e066fe52b..15aeb3cd4 100644 --- a/src/writer/tagvalue/relationship_writer.py +++ b/src/writer/tagvalue/relationship_writer.py @@ -16,6 +16,6 @@ def write_relationship(relationship: Relationship, text_output: TextIO): write_value("Relationship", " ".join( - [relationship.spdx_element_id, relationship.relationship_type.name, relationship.related_spdx_element_id]), + [relationship.spdx_element_id, relationship.relationship_type.name, str(relationship.related_spdx_element_id)]), text_output) write_text_value("RelationshipComment", relationship.comment, text_output) From 56372ada650e7f3eb8ce16ca11802f8ff6100488 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Wed, 21 Dec 2022 15:21:42 +0100 Subject: [PATCH 070/362] [bugfix] Fix snippet copyright text type Signed-off-by: Nicolaus Weidner --- src/model/snippet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/snippet.py b/src/model/snippet.py index 329de5c25..690647fad 100644 --- a/src/model/snippet.py +++ b/src/model/snippet.py @@ -27,7 +27,7 @@ class Snippet: concluded_license: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None license_info_in_snippet: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = None license_comment: Optional[str] = None - copyright_text: Optional[str] = None + copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None comment: Optional[str] = None name: Optional[str] = None attribution_texts: List[str] = field(default_factory=list) From 1a99fabff3f13aedcc3dd8d8a6d65b6ac1084fc3 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Tue, 13 Dec 2022 17:32:54 +0100 Subject: [PATCH 071/362] [issue-359] Recover datetime utils from git history, add tests Signed-off-by: Nicolaus Weidner --- src/datetime_conversions.py | 2 -- tests/test_datetime_conversions.py | 6 +++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/datetime_conversions.py b/src/datetime_conversions.py index 7829693cf..62fafe866 100644 --- a/src/datetime_conversions.py +++ b/src/datetime_conversions.py @@ -10,8 +10,6 @@ # limitations under the License. from datetime import datetime -from src.parser.error import SPDXParsingError - def datetime_from_str(date_str: str) -> datetime: if not isinstance(date_str, str): diff --git a/tests/test_datetime_conversions.py b/tests/test_datetime_conversions.py index 6ec233cda..e70e1dc86 100644 --- a/tests/test_datetime_conversions.py +++ b/tests/test_datetime_conversions.py @@ -12,7 +12,11 @@ import pytest -from src.datetime_conversions import datetime_from_str +from src.datetime_conversions import datetime_from_str, datetime_to_iso_string + + +def test_datetime_to_iso_string(): + assert datetime_to_iso_string(datetime(2022, 12, 13, 1, 2, 3)) == "2022-12-13T01:02:03Z" def test_datetime_from_str(): From c31991fc430ae1d2ebe760069fa24fb8b28b11f1 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Wed, 14 Dec 2022 23:42:06 +0100 Subject: [PATCH 072/362] [issue-359] Add Json property conversion setup for CreationInfo properties Signed-off-by: Nicolaus Weidner --- src/jsonschema/__init__.py | 0 src/jsonschema/checksum_properties.py | 37 ++++++++++++++ src/jsonschema/common_conversions.py | 20 ++++++++ src/jsonschema/creation_info_properties.py | 37 ++++++++++++++ src/jsonschema/document_properties.py | 48 +++++++++++++++++++ .../external_document_ref_properties.py | 35 ++++++++++++++ src/jsonschema/json_property.py | 20 ++++++++ src/writer/__init__.py | 0 src/writer/casing_tools.py | 16 +++++++ tests/model/test_actor.py | 13 +++++ 10 files changed, 226 insertions(+) create mode 100644 src/jsonschema/__init__.py create mode 100644 src/jsonschema/checksum_properties.py create mode 100644 src/jsonschema/common_conversions.py create mode 100644 src/jsonschema/creation_info_properties.py create mode 100644 src/jsonschema/document_properties.py create mode 100644 src/jsonschema/external_document_ref_properties.py create mode 100644 src/jsonschema/json_property.py create mode 100644 src/writer/__init__.py create mode 100644 src/writer/casing_tools.py diff --git a/src/jsonschema/__init__.py b/src/jsonschema/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/jsonschema/checksum_properties.py b/src/jsonschema/checksum_properties.py new file mode 100644 index 000000000..98a28166a --- /dev/null +++ b/src/jsonschema/checksum_properties.py @@ -0,0 +1,37 @@ +# 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 auto +from typing import Any + +from src.jsonschema.json_property import JsonProperty +from src.model.checksum import Checksum, ChecksumAlgorithm +from src.writer.casing_tools import snake_case_to_camel_case + + +class ChecksumProperty(JsonProperty): + ALGORITHM = auto() + CHECKSUM_VALUE = auto() + + def json_property_name(self) -> str: + return snake_case_to_camel_case(self.name) + + def get_property_value(self, checksum: Checksum) -> Any: + if self == ChecksumProperty.ALGORITHM: + return algorithm_to_json_string(checksum.algorithm) + elif self == ChecksumProperty.CHECKSUM_VALUE: + return checksum.value + + +def algorithm_to_json_string(algorithm: ChecksumAlgorithm) -> str: + name_with_dash: str = algorithm.name.replace("_", "-") + if "BLAKE2B" in name_with_dash: + return name_with_dash.replace("BLAKE2B", "BLAKE2b") + return name_with_dash diff --git a/src/jsonschema/common_conversions.py b/src/jsonschema/common_conversions.py new file mode 100644 index 000000000..a9102dd06 --- /dev/null +++ b/src/jsonschema/common_conversions.py @@ -0,0 +1,20 @@ +# 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 typing import Any, Dict, Type + +from src.jsonschema.json_property import JsonProperty + + +def convert_complex_type(instance: Any, property_type: Type[JsonProperty]) -> Dict: + result = {} + for property_name in property_type: + result[property_name.json_property_name()] = property_name.get_property_value(instance) + return result diff --git a/src/jsonschema/creation_info_properties.py b/src/jsonschema/creation_info_properties.py new file mode 100644 index 000000000..6451f1fb7 --- /dev/null +++ b/src/jsonschema/creation_info_properties.py @@ -0,0 +1,37 @@ +# 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 auto +from typing import Any + +from src.datetime_conversions import datetime_to_iso_string +from src.jsonschema.json_property import JsonProperty +from src.model.document import CreationInfo +from src.writer.casing_tools import snake_case_to_camel_case + + +class CreationInfoProperty(JsonProperty): + CREATED = auto() + CREATORS = auto() + LICENSE_LIST_VERSION = auto() + COMMENT = auto() + + def json_property_name(self) -> str: + return snake_case_to_camel_case(self.name) + + def get_property_value(self, creation_info: CreationInfo) -> Any: + if self == CreationInfoProperty.CREATED: + return datetime_to_iso_string(creation_info.created) + elif self == CreationInfoProperty.CREATORS: + return [creator.to_serialized_string() for creator in creation_info.creators] + elif self == CreationInfoProperty.LICENSE_LIST_VERSION: + return str(creation_info.license_list_version) + elif self == CreationInfoProperty.COMMENT: + return creation_info.creator_comment diff --git a/src/jsonschema/document_properties.py b/src/jsonschema/document_properties.py new file mode 100644 index 000000000..06938b1f5 --- /dev/null +++ b/src/jsonschema/document_properties.py @@ -0,0 +1,48 @@ +# 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 auto +from typing import Any + +from src.jsonschema.common_conversions import convert_complex_type +from src.jsonschema.external_document_ref_properties import ExternalDocumentRefProperty +from src.jsonschema.json_property import JsonProperty +from src.model.document import CreationInfo +from src.writer.casing_tools import snake_case_to_camel_case + + +class DocumentProperty(JsonProperty): + SPDX_VERSION = auto() + SPDX_ID = auto() + NAME = auto() + DOCUMENT_NAMESPACE = auto() + DATA_LICENSE = auto() + EXTERNAL_DOCUMENT_REFS = auto() + COMMENT = auto() + + def json_property_name(self) -> str: + if self == DocumentProperty.SPDX_ID: + return "SPDXID" + return snake_case_to_camel_case(self.name) + + def get_property_value(self, creation_info: CreationInfo) -> Any: + if self == DocumentProperty.SPDX_VERSION: + return creation_info.spdx_version + elif self == DocumentProperty.SPDX_ID: + return creation_info.spdx_id + elif self == DocumentProperty.NAME: + return creation_info.name + elif self == DocumentProperty.DATA_LICENSE: + return creation_info.data_license + elif self == DocumentProperty.EXTERNAL_DOCUMENT_REFS: + return [convert_complex_type(external_document_ref, ExternalDocumentRefProperty) for external_document_ref + in creation_info.external_document_refs] + elif self == DocumentProperty.COMMENT: + return creation_info.document_comment diff --git a/src/jsonschema/external_document_ref_properties.py b/src/jsonschema/external_document_ref_properties.py new file mode 100644 index 000000000..2c22afc3d --- /dev/null +++ b/src/jsonschema/external_document_ref_properties.py @@ -0,0 +1,35 @@ +# 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 auto +from typing import Any + +from src.jsonschema.checksum_properties import ChecksumProperty +from src.jsonschema.common_conversions import convert_complex_type +from src.jsonschema.json_property import JsonProperty +from src.model.external_document_ref import ExternalDocumentRef +from src.writer.casing_tools import snake_case_to_camel_case + + +class ExternalDocumentRefProperty(JsonProperty): + EXTERNAL_DOCUMENT_ID = auto() + SPDX_DOCUMENT = auto() + CHECKSUM = auto() + + def json_property_name(self) -> str: + return snake_case_to_camel_case(self.name) + + def get_property_value(self, external_document_ref: ExternalDocumentRef) -> Any: + if self == ExternalDocumentRefProperty.EXTERNAL_DOCUMENT_ID: + return external_document_ref.document_ref_id + elif self == ExternalDocumentRefProperty.SPDX_DOCUMENT: + return external_document_ref.document_uri + elif self == ExternalDocumentRefProperty.CHECKSUM: + return convert_complex_type(external_document_ref.checksum, ChecksumProperty) diff --git a/src/jsonschema/json_property.py b/src/jsonschema/json_property.py new file mode 100644 index 000000000..28c7ef29e --- /dev/null +++ b/src/jsonschema/json_property.py @@ -0,0 +1,20 @@ +# 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 +from typing import Any + + +class JsonProperty(Enum): + def json_property_name(self) -> str: + raise NotImplementedError("Must be implemented") + + def get_property_value(self, instance: Any) -> Any: + raise NotImplementedError("Must be implemented") diff --git a/src/writer/__init__.py b/src/writer/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/writer/casing_tools.py b/src/writer/casing_tools.py new file mode 100644 index 000000000..b14543093 --- /dev/null +++ b/src/writer/casing_tools.py @@ -0,0 +1,16 @@ +# 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 re import sub + + +def snake_case_to_camel_case(snake_case_string: str) -> str: + each_word_capitalized = sub(r"[_\-]+", " ", snake_case_string).title().replace(" ", "") + return each_word_capitalized[0].lower() + each_word_capitalized[1:] diff --git a/tests/model/test_actor.py b/tests/model/test_actor.py index f1eacf5b1..c39ac744a 100644 --- a/tests/model/test_actor.py +++ b/tests/model/test_actor.py @@ -36,3 +36,16 @@ def test_wrong_type_in_email_after_initializing(): with pytest.raises(TypeError): actor = Actor(ActorType.PERSON, "name") actor.email = [] + + +@pytest.mark.parametrize("actor,expected_string", [(Actor(ActorType.PERSON, "personName"), "Person: personName"), + (Actor(ActorType.PERSON, "personName", "personEmail"), + "Person: personName (personEmail)"), + (Actor(ActorType.ORGANIZATION, "orgName"), "Organization: orgName"), + (Actor(ActorType.ORGANIZATION, "orgName", "orgEmail"), + "Organization: orgName (orgEmail)"), + (Actor(ActorType.TOOL, "toolName"), "Tool: toolName"), + (Actor(ActorType.TOOL, "toolName", "toolEmail"), + "Tool: toolName (toolEmail)")]) +def test_serialization(actor: Actor, expected_string: str): + assert actor.to_serialized_string() == expected_string From 92eb25a822e949fac16a3162eae115c039c97c73 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 15 Dec 2022 11:10:57 +0100 Subject: [PATCH 073/362] [issue-359] Remove logic from json property enums (will be included in separate converters), refactor document_properties.py to include all document properties instead of just top-level ones Signed-off-by: Nicolaus Weidner --- src/jsonschema/checksum_properties.py | 19 ------- src/jsonschema/creation_info_properties.py | 17 ------ src/jsonschema/document_properties.py | 52 ++++++------------- .../external_document_ref_properties.py | 16 ------ src/jsonschema/json_property.py | 11 ++-- 5 files changed, 22 insertions(+), 93 deletions(-) diff --git a/src/jsonschema/checksum_properties.py b/src/jsonschema/checksum_properties.py index 98a28166a..6b974d5bb 100644 --- a/src/jsonschema/checksum_properties.py +++ b/src/jsonschema/checksum_properties.py @@ -9,29 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. from enum import auto -from typing import Any from src.jsonschema.json_property import JsonProperty -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.writer.casing_tools import snake_case_to_camel_case class ChecksumProperty(JsonProperty): ALGORITHM = auto() CHECKSUM_VALUE = auto() - - def json_property_name(self) -> str: - return snake_case_to_camel_case(self.name) - - def get_property_value(self, checksum: Checksum) -> Any: - if self == ChecksumProperty.ALGORITHM: - return algorithm_to_json_string(checksum.algorithm) - elif self == ChecksumProperty.CHECKSUM_VALUE: - return checksum.value - - -def algorithm_to_json_string(algorithm: ChecksumAlgorithm) -> str: - name_with_dash: str = algorithm.name.replace("_", "-") - if "BLAKE2B" in name_with_dash: - return name_with_dash.replace("BLAKE2B", "BLAKE2b") - return name_with_dash diff --git a/src/jsonschema/creation_info_properties.py b/src/jsonschema/creation_info_properties.py index 6451f1fb7..ccd52f58c 100644 --- a/src/jsonschema/creation_info_properties.py +++ b/src/jsonschema/creation_info_properties.py @@ -9,12 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. from enum import auto -from typing import Any -from src.datetime_conversions import datetime_to_iso_string from src.jsonschema.json_property import JsonProperty -from src.model.document import CreationInfo -from src.writer.casing_tools import snake_case_to_camel_case class CreationInfoProperty(JsonProperty): @@ -22,16 +18,3 @@ class CreationInfoProperty(JsonProperty): CREATORS = auto() LICENSE_LIST_VERSION = auto() COMMENT = auto() - - def json_property_name(self) -> str: - return snake_case_to_camel_case(self.name) - - def get_property_value(self, creation_info: CreationInfo) -> Any: - if self == CreationInfoProperty.CREATED: - return datetime_to_iso_string(creation_info.created) - elif self == CreationInfoProperty.CREATORS: - return [creator.to_serialized_string() for creator in creation_info.creators] - elif self == CreationInfoProperty.LICENSE_LIST_VERSION: - return str(creation_info.license_list_version) - elif self == CreationInfoProperty.COMMENT: - return creation_info.creator_comment diff --git a/src/jsonschema/document_properties.py b/src/jsonschema/document_properties.py index 06938b1f5..444a966bd 100644 --- a/src/jsonschema/document_properties.py +++ b/src/jsonschema/document_properties.py @@ -1,21 +1,16 @@ -# 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. +# 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 auto -from typing import Any -from src.jsonschema.common_conversions import convert_complex_type -from src.jsonschema.external_document_ref_properties import ExternalDocumentRefProperty from src.jsonschema.json_property import JsonProperty -from src.model.document import CreationInfo -from src.writer.casing_tools import snake_case_to_camel_case class DocumentProperty(JsonProperty): @@ -26,23 +21,10 @@ class DocumentProperty(JsonProperty): DATA_LICENSE = auto() EXTERNAL_DOCUMENT_REFS = auto() COMMENT = auto() - - def json_property_name(self) -> str: - if self == DocumentProperty.SPDX_ID: - return "SPDXID" - return snake_case_to_camel_case(self.name) - - def get_property_value(self, creation_info: CreationInfo) -> Any: - if self == DocumentProperty.SPDX_VERSION: - return creation_info.spdx_version - elif self == DocumentProperty.SPDX_ID: - return creation_info.spdx_id - elif self == DocumentProperty.NAME: - return creation_info.name - elif self == DocumentProperty.DATA_LICENSE: - return creation_info.data_license - elif self == DocumentProperty.EXTERNAL_DOCUMENT_REFS: - return [convert_complex_type(external_document_ref, ExternalDocumentRefProperty) for external_document_ref - in creation_info.external_document_refs] - elif self == DocumentProperty.COMMENT: - return creation_info.document_comment + CREATION_INFO = auto() + PACKAGES = auto() + FILES = auto() + SNIPPETS = auto() + ANNOTATIONS = auto() + RELATIONSHIPS = auto() + HAS_EXTRACTED_LICENSING_INFO = auto() diff --git a/src/jsonschema/external_document_ref_properties.py b/src/jsonschema/external_document_ref_properties.py index 2c22afc3d..fd2c1eb3a 100644 --- a/src/jsonschema/external_document_ref_properties.py +++ b/src/jsonschema/external_document_ref_properties.py @@ -9,27 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. from enum import auto -from typing import Any -from src.jsonschema.checksum_properties import ChecksumProperty -from src.jsonschema.common_conversions import convert_complex_type from src.jsonschema.json_property import JsonProperty -from src.model.external_document_ref import ExternalDocumentRef -from src.writer.casing_tools import snake_case_to_camel_case class ExternalDocumentRefProperty(JsonProperty): EXTERNAL_DOCUMENT_ID = auto() SPDX_DOCUMENT = auto() CHECKSUM = auto() - - def json_property_name(self) -> str: - return snake_case_to_camel_case(self.name) - - def get_property_value(self, external_document_ref: ExternalDocumentRef) -> Any: - if self == ExternalDocumentRefProperty.EXTERNAL_DOCUMENT_ID: - return external_document_ref.document_ref_id - elif self == ExternalDocumentRefProperty.SPDX_DOCUMENT: - return external_document_ref.document_uri - elif self == ExternalDocumentRefProperty.CHECKSUM: - return convert_complex_type(external_document_ref.checksum, ChecksumProperty) diff --git a/src/jsonschema/json_property.py b/src/jsonschema/json_property.py index 28c7ef29e..fcdcd7167 100644 --- a/src/jsonschema/json_property.py +++ b/src/jsonschema/json_property.py @@ -9,12 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. from enum import Enum -from typing import Any class JsonProperty(Enum): - def json_property_name(self) -> str: - raise NotImplementedError("Must be implemented") - - def get_property_value(self, instance: Any) -> Any: - raise NotImplementedError("Must be implemented") + """ + Parent class for all json property classes. Not meant to be instantiated directly, only to have a common parent + type that can be used in type hints. + """ + pass From 0219f3c4f4a6bb75b08bd1624f72ce73aea63acf Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 15 Dec 2022 11:57:10 +0100 Subject: [PATCH 074/362] [issue-359] Add abstract base converter class and checksum converter Signed-off-by: Nicolaus Weidner --- src/jsonschema/checksum_converter.py | 42 +++++++++++ src/jsonschema/common_conversions.py | 20 ------ src/jsonschema/converter.py | 45 ++++++++++++ tests/jsonschema/__init__.py | 0 tests/jsonschema/test_checksum_converter.py | 43 ++++++++++++ tests/jsonschema/test_converter.py | 78 +++++++++++++++++++++ 6 files changed, 208 insertions(+), 20 deletions(-) create mode 100644 src/jsonschema/checksum_converter.py delete mode 100644 src/jsonschema/common_conversions.py create mode 100644 src/jsonschema/converter.py create mode 100644 tests/jsonschema/__init__.py create mode 100644 tests/jsonschema/test_checksum_converter.py create mode 100644 tests/jsonschema/test_converter.py diff --git a/src/jsonschema/checksum_converter.py b/src/jsonschema/checksum_converter.py new file mode 100644 index 000000000..9a2ec945f --- /dev/null +++ b/src/jsonschema/checksum_converter.py @@ -0,0 +1,42 @@ +# 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 typing import Type + +from src.jsonschema.checksum_properties import ChecksumProperty +from src.jsonschema.converter import TypedConverter +from src.jsonschema.json_property import JsonProperty +from src.model.checksum import Checksum, ChecksumAlgorithm +from src.writer.casing_tools import snake_case_to_camel_case + + +class ChecksumConverter(TypedConverter): + + def get_data_model_type(self) -> Type[Checksum]: + return Checksum + + def get_json_type(self) -> Type[JsonProperty]: + return ChecksumProperty + + def json_property_name(self, checksum_property: ChecksumProperty) -> str: + return snake_case_to_camel_case(checksum_property.name) + + def get_property_value(self, checksum: Checksum, checksum_property: ChecksumProperty) -> str: + if checksum_property == ChecksumProperty.ALGORITHM: + return algorithm_to_json_string(checksum.algorithm) + elif checksum_property == ChecksumProperty.CHECKSUM_VALUE: + return checksum.value + + +def algorithm_to_json_string(algorithm: ChecksumAlgorithm) -> str: + name_with_dash: str = algorithm.name.replace("_", "-") + if "BLAKE2B" in name_with_dash: + return name_with_dash.replace("BLAKE2B", "BLAKE2b") + return name_with_dash diff --git a/src/jsonschema/common_conversions.py b/src/jsonschema/common_conversions.py deleted file mode 100644 index a9102dd06..000000000 --- a/src/jsonschema/common_conversions.py +++ /dev/null @@ -1,20 +0,0 @@ -# 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 typing import Any, Dict, Type - -from src.jsonschema.json_property import JsonProperty - - -def convert_complex_type(instance: Any, property_type: Type[JsonProperty]) -> Dict: - result = {} - for property_name in property_type: - result[property_name.json_property_name()] = property_name.get_property_value(instance) - return result diff --git a/src/jsonschema/converter.py b/src/jsonschema/converter.py new file mode 100644 index 000000000..cb703c3bb --- /dev/null +++ b/src/jsonschema/converter.py @@ -0,0 +1,45 @@ +# 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 abc import ABC, abstractmethod +from typing import Any, Type, Dict + +from src.jsonschema.json_property import JsonProperty + +MISSING_IMPLEMENTATION_MESSAGE = "Must be implemented" + + +class TypedConverter(ABC): + @abstractmethod + def json_property_name(self, property_thing: JsonProperty) -> str: + raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE) + + @abstractmethod + def get_property_value(self, instance: Any, property_thing: JsonProperty) -> Any: + raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE) + + @abstractmethod + def get_json_type(self) -> Type[JsonProperty]: + raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE) + + @abstractmethod + def get_data_model_type(self) -> Type: + raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE) + + def convert(self, instance: Any) -> Dict: + if not isinstance(instance, self.get_data_model_type()): + raise TypeError( + f"Converter of type {self.__class__} can only convert objects of type " + f"{self.get_data_model_type()}. Received {type(instance)} instead.") + + result = {} + for property_name in self.get_json_type(): + result[self.json_property_name(property_name)] = self.get_property_value(instance, property_name) + return result diff --git a/tests/jsonschema/__init__.py b/tests/jsonschema/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/jsonschema/test_checksum_converter.py b/tests/jsonschema/test_checksum_converter.py new file mode 100644 index 000000000..088f853f1 --- /dev/null +++ b/tests/jsonschema/test_checksum_converter.py @@ -0,0 +1,43 @@ +# 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. +import pytest + +from src.jsonschema.checksum_converter import ChecksumConverter +from src.jsonschema.checksum_properties import ChecksumProperty +from src.model.checksum import Checksum, ChecksumAlgorithm + + +@pytest.fixture +def converter() -> ChecksumConverter: + return ChecksumConverter() + + +@pytest.mark.parametrize("checksum_property,expected", [(ChecksumProperty.ALGORITHM, "algorithm"), + (ChecksumProperty.CHECKSUM_VALUE, "checksumValue")]) +def test_json_property_names(converter: ChecksumConverter, checksum_property: ChecksumProperty, expected: str): + assert converter.json_property_name(checksum_property) == expected + + +def test_successful_conversion(converter: ChecksumConverter): + checksum = Checksum(ChecksumAlgorithm.SHA1, "123") + + converted_dict = converter.convert(checksum) + + assert converted_dict[converter.json_property_name(ChecksumProperty.ALGORITHM)] == "SHA1" + assert converted_dict[converter.json_property_name(ChecksumProperty.CHECKSUM_VALUE)] == "123" + + +def test_json_type(converter: ChecksumConverter): + assert converter.get_json_type() == ChecksumProperty + + +def test_data_model_type(converter: ChecksumConverter): + assert converter.get_data_model_type() == Checksum diff --git a/tests/jsonschema/test_converter.py b/tests/jsonschema/test_converter.py new file mode 100644 index 000000000..433037c4b --- /dev/null +++ b/tests/jsonschema/test_converter.py @@ -0,0 +1,78 @@ +# 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 auto +from typing import Type, Any + +import pytest + +from src.jsonschema.converter import TypedConverter +from src.jsonschema.json_property import JsonProperty +from src.model.checksum import Checksum, ChecksumAlgorithm +from src.model.typing.dataclass_with_properties import dataclass_with_properties +from src.model.typing.type_checks import check_types_and_set_values + + +class TestPropertyType(JsonProperty): + FIRST_NAME = auto() + SECOND_NAME = auto() + + +@dataclass_with_properties +class TestDataModelType: + first_property: str + second_property: int + third_property: int + + def __init__(self, first_property: str, second_property: int, third_property: int): + check_types_and_set_values(self, locals()) + + +class TestConverter(TypedConverter): + def json_property_name(self, test_property: TestPropertyType) -> str: + if test_property == TestPropertyType.FIRST_NAME: + return "jsonFirstName" + else: + return "jsonSecondName" + + def get_property_value(self, instance: TestDataModelType, test_property: TestPropertyType) -> Any: + if test_property == TestPropertyType.FIRST_NAME: + return instance.first_property + elif test_property == TestPropertyType.SECOND_NAME: + return instance.second_property + instance.third_property + + def get_json_type(self) -> Type[JsonProperty]: + return TestPropertyType + + def get_data_model_type(self) -> Type: + return TestDataModelType + + +def test_conversion(): + converter = TestConverter() + test_instance = TestDataModelType("firstPropertyValue", 1, 2) + + converted_dict = converter.convert(test_instance) + + assert converted_dict.get("jsonFirstName") == "firstPropertyValue" + assert converted_dict.get("jsonSecondName") == 3 + + +def test_wrong_type(): + converter = TestConverter() + checksum = Checksum(ChecksumAlgorithm.SHA1, "123") + + with pytest.raises(TypeError) as error: + converter.convert(checksum) + + error_message: str = error.value.args[0] + assert TestConverter.__name__ in error_message + assert TestDataModelType.__name__ in error_message + assert Checksum.__name__ in error_message From 30c453bd6a28b7d74a36a781b06323f71d242105 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 15 Dec 2022 13:36:19 +0100 Subject: [PATCH 075/362] [issue-359] Add CreationInfo converter Signed-off-by: Nicolaus Weidner --- src/jsonschema/creation_info_converter.py | 39 +++++++++++++ .../test_creation_info_converter.py | 57 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 src/jsonschema/creation_info_converter.py create mode 100644 tests/jsonschema/test_creation_info_converter.py diff --git a/src/jsonschema/creation_info_converter.py b/src/jsonschema/creation_info_converter.py new file mode 100644 index 000000000..8483fb2a8 --- /dev/null +++ b/src/jsonschema/creation_info_converter.py @@ -0,0 +1,39 @@ +# 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 typing import Type, Any + +from src.datetime_conversions import datetime_to_iso_string +from src.jsonschema.converter import TypedConverter +from src.jsonschema.creation_info_properties import CreationInfoProperty +from src.jsonschema.json_property import JsonProperty +from src.model.document import CreationInfo +from src.writer.casing_tools import snake_case_to_camel_case + + +class CreationInfoConverter(TypedConverter): + def get_data_model_type(self) -> Type[CreationInfo]: + return CreationInfo + + def get_json_type(self) -> Type[JsonProperty]: + return CreationInfoProperty + + def json_property_name(self, creation_info_property: CreationInfoProperty) -> str: + return snake_case_to_camel_case(creation_info_property.name) + + def get_property_value(self, creation_info: CreationInfo, creation_info_property: CreationInfoProperty) -> Any: + if creation_info_property == CreationInfoProperty.CREATED: + return datetime_to_iso_string(creation_info.created) + elif creation_info_property == CreationInfoProperty.CREATORS: + return [creator.to_serialized_string() for creator in creation_info.creators] + elif creation_info_property == CreationInfoProperty.LICENSE_LIST_VERSION: + return str(creation_info.license_list_version) + elif creation_info_property == CreationInfoProperty.COMMENT: + return creation_info.creator_comment diff --git a/tests/jsonschema/test_creation_info_converter.py b/tests/jsonschema/test_creation_info_converter.py new file mode 100644 index 000000000..8b1cb4b2d --- /dev/null +++ b/tests/jsonschema/test_creation_info_converter.py @@ -0,0 +1,57 @@ +# 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 datetime import datetime + +import pytest + +from src.datetime_conversions import datetime_to_iso_string +from src.jsonschema.creation_info_converter import CreationInfoConverter +from src.jsonschema.creation_info_properties import CreationInfoProperty +from src.model.actor import Actor, ActorType +from src.model.document import CreationInfo +from src.model.version import Version + + +@pytest.fixture +def converter() -> CreationInfoConverter: + return CreationInfoConverter() + + +@pytest.mark.parametrize("creation_info_property,expected", + [(CreationInfoProperty.CREATED, "created"), (CreationInfoProperty.CREATORS, "creators"), + (CreationInfoProperty.LICENSE_LIST_VERSION, "licenseListVersion"), + (CreationInfoProperty.COMMENT, "comment")]) +def test_json_property_names(converter: CreationInfoConverter, creation_info_property: CreationInfoProperty, + expected: str): + assert converter.json_property_name(creation_info_property) == expected + + +def test_successful_conversion(converter: CreationInfoConverter): + creators = [Actor(ActorType.PERSON, "personName"), Actor(ActorType.TOOL, "toolName")] + created = datetime(2022, 12, 1) + creation_info = CreationInfo("irrelevant", "irrelevant", "irrelevant", "irrelevant", creators=creators, + created=created, creator_comment="comment", license_list_version=Version(1, 2)) + + converted_dict = converter.convert(creation_info) + + assert converted_dict[converter.json_property_name(CreationInfoProperty.CREATED)] == datetime_to_iso_string(created) + assert converted_dict[converter.json_property_name(CreationInfoProperty.CREATORS)] == ["Person: personName", + "Tool: toolName"] + assert converted_dict[converter.json_property_name(CreationInfoProperty.LICENSE_LIST_VERSION)] == "1.2" + assert converted_dict[converter.json_property_name(CreationInfoProperty.COMMENT)] == "comment" + + +def test_json_type(converter: CreationInfoConverter): + assert converter.get_json_type() == CreationInfoProperty + + +def test_data_model_type(converter: CreationInfoConverter): + assert converter.get_data_model_type() == CreationInfo From 02084a71aa26d867359c17fa6e07f5cefd94471d Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 15 Dec 2022 14:14:17 +0100 Subject: [PATCH 076/362] [issue-359] Add external document ref converter Signed-off-by: Nicolaus Weidner --- .../external_document_ref_converter.py | 43 ++++++++++++++ .../test_external_document_ref_converter.py | 59 +++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 src/jsonschema/external_document_ref_converter.py create mode 100644 tests/jsonschema/test_external_document_ref_converter.py diff --git a/src/jsonschema/external_document_ref_converter.py b/src/jsonschema/external_document_ref_converter.py new file mode 100644 index 000000000..41eb939da --- /dev/null +++ b/src/jsonschema/external_document_ref_converter.py @@ -0,0 +1,43 @@ +# 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 typing import Type, Any + +from src.jsonschema.checksum_converter import ChecksumConverter +from src.jsonschema.converter import TypedConverter +from src.jsonschema.external_document_ref_properties import ExternalDocumentRefProperty +from src.jsonschema.json_property import JsonProperty +from src.model.external_document_ref import ExternalDocumentRef +from src.writer.casing_tools import snake_case_to_camel_case + + +class ExternalDocumentRefConverter(TypedConverter): + checksum_converter: ChecksumConverter + + def __init__(self): + self.checksum_converter = ChecksumConverter() + + def json_property_name(self, property_thing: ExternalDocumentRefProperty) -> str: + return snake_case_to_camel_case(property_thing.name) + + def get_property_value(self, external_document_ref: ExternalDocumentRef, + property_thing: ExternalDocumentRefProperty) -> Any: + if property_thing == ExternalDocumentRefProperty.EXTERNAL_DOCUMENT_ID: + return external_document_ref.document_ref_id + elif property_thing == ExternalDocumentRefProperty.SPDX_DOCUMENT: + return external_document_ref.document_uri + elif property_thing == ExternalDocumentRefProperty.CHECKSUM: + return self.checksum_converter.convert(external_document_ref.checksum) + + def get_json_type(self) -> Type[JsonProperty]: + return ExternalDocumentRefProperty + + def get_data_model_type(self) -> Type[ExternalDocumentRef]: + return ExternalDocumentRef diff --git a/tests/jsonschema/test_external_document_ref_converter.py b/tests/jsonschema/test_external_document_ref_converter.py new file mode 100644 index 000000000..0694b95ea --- /dev/null +++ b/tests/jsonschema/test_external_document_ref_converter.py @@ -0,0 +1,59 @@ +# 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 unittest import mock +from unittest.mock import MagicMock + +import pytest + +from src.jsonschema.external_document_ref_converter import ExternalDocumentRefConverter +from src.jsonschema.external_document_ref_properties import ExternalDocumentRefProperty +from src.model.checksum import Checksum, ChecksumAlgorithm +from src.model.external_document_ref import ExternalDocumentRef + + +@pytest.fixture +@mock.patch('src.jsonschema.checksum_converter.ChecksumConverter', autospec=True) +def converter(checksum_converter_magic_mock: MagicMock) -> ExternalDocumentRefConverter: + mocked_checksum_converter = checksum_converter_magic_mock() + converter = ExternalDocumentRefConverter() + converter.checksum_converter = mocked_checksum_converter + return converter + + +@pytest.mark.parametrize("external_document_ref_property,expected", + [(ExternalDocumentRefProperty.EXTERNAL_DOCUMENT_ID, "externalDocumentId"), + (ExternalDocumentRefProperty.SPDX_DOCUMENT, "spdxDocument"), + (ExternalDocumentRefProperty.CHECKSUM, "checksum")]) +def test_json_property_names(converter: ExternalDocumentRefConverter, + external_document_ref_property: ExternalDocumentRefProperty, expected: str): + assert converter.json_property_name(external_document_ref_property) == expected + + +def test_successful_conversion(converter: ExternalDocumentRefConverter): + converter.checksum_converter.convert.return_value = "mock_converted_checksum" + checksum = Checksum(ChecksumAlgorithm.SHA1, "123") + external_document_ref = ExternalDocumentRef("document_ref_id", "document_uri", checksum) + + converted_dict = converter.convert(external_document_ref) + + assert converted_dict[ + converter.json_property_name(ExternalDocumentRefProperty.EXTERNAL_DOCUMENT_ID)] == "document_ref_id" + assert converted_dict[converter.json_property_name(ExternalDocumentRefProperty.SPDX_DOCUMENT)] == "document_uri" + assert converted_dict[ + converter.json_property_name(ExternalDocumentRefProperty.CHECKSUM)] == "mock_converted_checksum" + + +def test_json_type(converter: ExternalDocumentRefConverter): + assert converter.get_json_type() == ExternalDocumentRefProperty + + +def test_data_model_type(converter: ExternalDocumentRefConverter): + assert converter.get_data_model_type() == ExternalDocumentRef From 09142126df0e4d5fc9730bf48feac651330699bc Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 15 Dec 2022 16:37:04 +0100 Subject: [PATCH 077/362] [issue-359] Enable passing the full document to converters to account for (upcoming) more complex conversion cases Signed-off-by: Nicolaus Weidner --- src/jsonschema/checksum_converter.py | 4 +++- src/jsonschema/converter.py | 12 +++++++++--- src/jsonschema/creation_info_converter.py | 5 +++-- src/jsonschema/external_document_ref_converter.py | 5 +++-- tests/jsonschema/test_converter.py | 4 +++- 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/jsonschema/checksum_converter.py b/src/jsonschema/checksum_converter.py index 9a2ec945f..695f4df7b 100644 --- a/src/jsonschema/checksum_converter.py +++ b/src/jsonschema/checksum_converter.py @@ -14,6 +14,7 @@ from src.jsonschema.converter import TypedConverter from src.jsonschema.json_property import JsonProperty from src.model.checksum import Checksum, ChecksumAlgorithm +from src.model.document import Document from src.writer.casing_tools import snake_case_to_camel_case @@ -28,7 +29,8 @@ def get_json_type(self) -> Type[JsonProperty]: def json_property_name(self, checksum_property: ChecksumProperty) -> str: return snake_case_to_camel_case(checksum_property.name) - def get_property_value(self, checksum: Checksum, checksum_property: ChecksumProperty) -> str: + def _get_property_value(self, checksum: Checksum, checksum_property: ChecksumProperty, + _document: Document = None) -> str: if checksum_property == ChecksumProperty.ALGORITHM: return algorithm_to_json_string(checksum.algorithm) elif checksum_property == ChecksumProperty.CHECKSUM_VALUE: diff --git a/src/jsonschema/converter.py b/src/jsonschema/converter.py index cb703c3bb..a4e03f3c3 100644 --- a/src/jsonschema/converter.py +++ b/src/jsonschema/converter.py @@ -12,6 +12,7 @@ from typing import Any, Type, Dict from src.jsonschema.json_property import JsonProperty +from src.model.document import Document MISSING_IMPLEMENTATION_MESSAGE = "Must be implemented" @@ -22,7 +23,7 @@ def json_property_name(self, property_thing: JsonProperty) -> str: raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE) @abstractmethod - def get_property_value(self, instance: Any, property_thing: JsonProperty) -> Any: + def _get_property_value(self, instance: Any, property_thing: JsonProperty, document: Document = None) -> Any: raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE) @abstractmethod @@ -33,13 +34,18 @@ def get_json_type(self) -> Type[JsonProperty]: def get_data_model_type(self) -> Type: raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE) - def convert(self, instance: Any) -> Dict: + def requires_full_document(self) -> bool: + return False + + def convert(self, instance: Any, document: Document = None) -> Dict: if not isinstance(instance, self.get_data_model_type()): raise TypeError( f"Converter of type {self.__class__} can only convert objects of type " f"{self.get_data_model_type()}. Received {type(instance)} instead.") + if self.requires_full_document() and not document: + raise ValueError(f"Converter of type {self.__class__} requires the full document") result = {} for property_name in self.get_json_type(): - result[self.json_property_name(property_name)] = self.get_property_value(instance, property_name) + result[self.json_property_name(property_name)] = self._get_property_value(instance, property_name, document) return result diff --git a/src/jsonschema/creation_info_converter.py b/src/jsonschema/creation_info_converter.py index 8483fb2a8..656126203 100644 --- a/src/jsonschema/creation_info_converter.py +++ b/src/jsonschema/creation_info_converter.py @@ -14,7 +14,7 @@ from src.jsonschema.converter import TypedConverter from src.jsonschema.creation_info_properties import CreationInfoProperty from src.jsonschema.json_property import JsonProperty -from src.model.document import CreationInfo +from src.model.document import CreationInfo, Document from src.writer.casing_tools import snake_case_to_camel_case @@ -28,7 +28,8 @@ def get_json_type(self) -> Type[JsonProperty]: def json_property_name(self, creation_info_property: CreationInfoProperty) -> str: return snake_case_to_camel_case(creation_info_property.name) - def get_property_value(self, creation_info: CreationInfo, creation_info_property: CreationInfoProperty) -> Any: + def _get_property_value(self, creation_info: CreationInfo, creation_info_property: CreationInfoProperty, + _document: Document = None) -> Any: if creation_info_property == CreationInfoProperty.CREATED: return datetime_to_iso_string(creation_info.created) elif creation_info_property == CreationInfoProperty.CREATORS: diff --git a/src/jsonschema/external_document_ref_converter.py b/src/jsonschema/external_document_ref_converter.py index 41eb939da..ccb5e0be3 100644 --- a/src/jsonschema/external_document_ref_converter.py +++ b/src/jsonschema/external_document_ref_converter.py @@ -14,6 +14,7 @@ from src.jsonschema.converter import TypedConverter from src.jsonschema.external_document_ref_properties import ExternalDocumentRefProperty from src.jsonschema.json_property import JsonProperty +from src.model.document import Document from src.model.external_document_ref import ExternalDocumentRef from src.writer.casing_tools import snake_case_to_camel_case @@ -27,8 +28,8 @@ def __init__(self): def json_property_name(self, property_thing: ExternalDocumentRefProperty) -> str: return snake_case_to_camel_case(property_thing.name) - def get_property_value(self, external_document_ref: ExternalDocumentRef, - property_thing: ExternalDocumentRefProperty) -> Any: + def _get_property_value(self, external_document_ref: ExternalDocumentRef, + property_thing: ExternalDocumentRefProperty, _document: Document = None) -> Any: if property_thing == ExternalDocumentRefProperty.EXTERNAL_DOCUMENT_ID: return external_document_ref.document_ref_id elif property_thing == ExternalDocumentRefProperty.SPDX_DOCUMENT: diff --git a/tests/jsonschema/test_converter.py b/tests/jsonschema/test_converter.py index 433037c4b..add64a706 100644 --- a/tests/jsonschema/test_converter.py +++ b/tests/jsonschema/test_converter.py @@ -16,6 +16,7 @@ from src.jsonschema.converter import TypedConverter from src.jsonschema.json_property import JsonProperty from src.model.checksum import Checksum, ChecksumAlgorithm +from src.model.document import Document from src.model.typing.dataclass_with_properties import dataclass_with_properties from src.model.typing.type_checks import check_types_and_set_values @@ -42,7 +43,8 @@ def json_property_name(self, test_property: TestPropertyType) -> str: else: return "jsonSecondName" - def get_property_value(self, instance: TestDataModelType, test_property: TestPropertyType) -> Any: + def _get_property_value(self, instance: TestDataModelType, test_property: TestPropertyType, + _document: Document = None) -> Any: if test_property == TestPropertyType.FIRST_NAME: return instance.first_property elif test_property == TestPropertyType.SECOND_NAME: From 1a721b31c15a759af785353d74a32503709afdea Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 15 Dec 2022 22:02:11 +0100 Subject: [PATCH 078/362] [issue-359] Add annotation properties and converter Signed-off-by: Nicolaus Weidner --- src/jsonschema/annotation_converter.py | 41 ++++++++++++++ src/jsonschema/annotation_properties.py | 20 +++++++ tests/jsonschema/test_annotation_converter.py | 56 +++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 src/jsonschema/annotation_converter.py create mode 100644 src/jsonschema/annotation_properties.py create mode 100644 tests/jsonschema/test_annotation_converter.py diff --git a/src/jsonschema/annotation_converter.py b/src/jsonschema/annotation_converter.py new file mode 100644 index 000000000..a8f30d5a1 --- /dev/null +++ b/src/jsonschema/annotation_converter.py @@ -0,0 +1,41 @@ +# 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 typing import Type, Any + +from src.datetime_conversions import datetime_to_iso_string +from src.jsonschema.annotation_properties import AnnotationProperty +from src.jsonschema.converter import TypedConverter +from src.jsonschema.json_property import JsonProperty +from src.model.annotation import Annotation +from src.model.document import Document +from src.writer.casing_tools import snake_case_to_camel_case + + +class AnnotationConverter(TypedConverter): + def json_property_name(self, annotation_property: AnnotationProperty) -> str: + return snake_case_to_camel_case(annotation_property.name) + + def _get_property_value(self, annotation: Annotation, annotation_property: AnnotationProperty, + document: Document = None) -> Any: + if annotation_property == AnnotationProperty.ANNOTATION_DATE: + return datetime_to_iso_string(annotation.annotation_date) + elif annotation_property == AnnotationProperty.ANNOTATION_TYPE: + return annotation.annotation_type.name + elif annotation_property == AnnotationProperty.ANNOTATOR: + return annotation.annotator.to_serialized_string() + elif annotation_property == AnnotationProperty.COMMENT: + return annotation.annotation_comment + + def get_json_type(self) -> Type[JsonProperty]: + return AnnotationProperty + + def get_data_model_type(self) -> Type[Annotation]: + return Annotation diff --git a/src/jsonschema/annotation_properties.py b/src/jsonschema/annotation_properties.py new file mode 100644 index 000000000..5215a2819 --- /dev/null +++ b/src/jsonschema/annotation_properties.py @@ -0,0 +1,20 @@ +# 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 auto + +from src.jsonschema.json_property import JsonProperty + + +class AnnotationProperty(JsonProperty): + ANNOTATION_DATE = auto() + ANNOTATION_TYPE = auto() + ANNOTATOR = auto() + COMMENT = auto() diff --git a/tests/jsonschema/test_annotation_converter.py b/tests/jsonschema/test_annotation_converter.py new file mode 100644 index 000000000..1f64ba336 --- /dev/null +++ b/tests/jsonschema/test_annotation_converter.py @@ -0,0 +1,56 @@ +# 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 datetime import datetime + +import pytest + +from src.datetime_conversions import datetime_to_iso_string +from src.jsonschema.annotation_converter import AnnotationConverter +from src.jsonschema.annotation_properties import AnnotationProperty +from src.model.actor import Actor, ActorType +from src.model.annotation import Annotation, AnnotationType + + +@pytest.fixture +def converter() -> AnnotationConverter: + return AnnotationConverter() + + +@pytest.mark.parametrize("annotation_property,expected", [(AnnotationProperty.ANNOTATION_DATE, "annotationDate"), + (AnnotationProperty.ANNOTATION_TYPE, "annotationType"), + (AnnotationProperty.ANNOTATOR, "annotator"), + (AnnotationProperty.COMMENT, "comment")]) +def test_json_property_names(converter: AnnotationConverter, annotation_property: AnnotationProperty, expected: str): + assert converter.json_property_name(annotation_property) == expected + + +def test_json_type(converter: AnnotationConverter): + assert converter.get_json_type() == AnnotationProperty + + +def test_data_model_type(converter: AnnotationConverter): + assert converter.get_data_model_type() == Annotation + + +def test_successful_conversion(converter: AnnotationConverter): + date = datetime(2022, 12, 1) + annotator = Actor(ActorType.PERSON, "actorName") + annotation = Annotation("spdxId", AnnotationType.REVIEW, annotator, + date, "comment") + + converted_dict = converter.convert(annotation) + + assert converted_dict[converter.json_property_name(AnnotationProperty.ANNOTATION_DATE)] == datetime_to_iso_string( + date) + assert converted_dict[converter.json_property_name(AnnotationProperty.ANNOTATION_TYPE)] == "REVIEW" + assert converted_dict[ + converter.json_property_name(AnnotationProperty.ANNOTATOR)] == annotator.to_serialized_string() + assert converted_dict[converter.json_property_name(AnnotationProperty.COMMENT)] == "comment" From 2846e47815100e33edc9587ffd61377ce0da4ad0 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 15 Dec 2022 22:27:44 +0100 Subject: [PATCH 079/362] [issue-359] Add external package ref properties and converter Signed-off-by: Nicolaus Weidner --- .../external_package_ref_converter.py | 40 +++++++++++++++ .../external_package_ref_properties.py | 20 ++++++++ .../test_external_package_ref_converter.py | 50 +++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 src/jsonschema/external_package_ref_converter.py create mode 100644 src/jsonschema/external_package_ref_properties.py create mode 100644 tests/jsonschema/test_external_package_ref_converter.py diff --git a/src/jsonschema/external_package_ref_converter.py b/src/jsonschema/external_package_ref_converter.py new file mode 100644 index 000000000..531861105 --- /dev/null +++ b/src/jsonschema/external_package_ref_converter.py @@ -0,0 +1,40 @@ +# 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 typing import Type, Any + +from src.jsonschema.converter import TypedConverter +from src.jsonschema.external_package_ref_properties import ExternalPackageRefProperty +from src.jsonschema.json_property import JsonProperty +from src.model.document import Document +from src.model.package import ExternalPackageRef +from src.writer.casing_tools import snake_case_to_camel_case + + +class ExternalPackageRefConverter(TypedConverter): + def json_property_name(self, external_ref_property: ExternalPackageRefProperty) -> str: + return snake_case_to_camel_case(external_ref_property.name) + + def _get_property_value(self, external_ref: ExternalPackageRef, external_ref_property: ExternalPackageRefProperty, + document: Document = None) -> Any: + if external_ref_property == ExternalPackageRefProperty.COMMENT: + return external_ref.comment + elif external_ref_property == ExternalPackageRefProperty.REFERENCE_CATEGORY: + return external_ref.category.name + elif external_ref_property == ExternalPackageRefProperty.REFERENCE_LOCATOR: + return external_ref.locator + elif external_ref_property == ExternalPackageRefProperty.REFERENCE_TYPE: + return external_ref.reference_type + + def get_json_type(self) -> Type[JsonProperty]: + return ExternalPackageRefProperty + + def get_data_model_type(self) -> Type[ExternalPackageRef]: + return ExternalPackageRef diff --git a/src/jsonschema/external_package_ref_properties.py b/src/jsonschema/external_package_ref_properties.py new file mode 100644 index 000000000..d59348821 --- /dev/null +++ b/src/jsonschema/external_package_ref_properties.py @@ -0,0 +1,20 @@ +# 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 auto + +from src.jsonschema.json_property import JsonProperty + + +class ExternalPackageRefProperty(JsonProperty): + COMMENT = auto() + REFERENCE_CATEGORY = auto() + REFERENCE_LOCATOR = auto() + REFERENCE_TYPE = auto() diff --git a/tests/jsonschema/test_external_package_ref_converter.py b/tests/jsonschema/test_external_package_ref_converter.py new file mode 100644 index 000000000..44094c00d --- /dev/null +++ b/tests/jsonschema/test_external_package_ref_converter.py @@ -0,0 +1,50 @@ +# 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. +import pytest + +from src.jsonschema.external_package_ref_converter import ExternalPackageRefConverter +from src.jsonschema.external_package_ref_properties import ExternalPackageRefProperty +from src.model.package import ExternalPackageRef, ExternalPackageRefCategory + + +@pytest.fixture +def converter() -> ExternalPackageRefConverter: + return ExternalPackageRefConverter() + + +@pytest.mark.parametrize("external_package_ref_property,expected", + [(ExternalPackageRefProperty.COMMENT, "comment"), + (ExternalPackageRefProperty.REFERENCE_CATEGORY, "referenceCategory"), + (ExternalPackageRefProperty.REFERENCE_LOCATOR, "referenceLocator"), + (ExternalPackageRefProperty.REFERENCE_TYPE, "referenceType")]) +def test_json_property_names(converter: ExternalPackageRefConverter, + external_package_ref_property: ExternalPackageRefProperty, expected: str): + assert converter.json_property_name(external_package_ref_property) == expected + + +def test_json_type(converter: ExternalPackageRefConverter): + assert converter.get_json_type() == ExternalPackageRefProperty + + +def test_data_model_type(converter: ExternalPackageRefConverter): + assert converter.get_data_model_type() == ExternalPackageRef + + +def test_successful_conversion(converter: ExternalPackageRefConverter): + external_package_ref = ExternalPackageRef(ExternalPackageRefCategory.PACKAGE_MANAGER, "type", "locator", "comment") + + converted_dict = converter.convert(external_package_ref) + + assert converted_dict[converter.json_property_name(ExternalPackageRefProperty.COMMENT)] == "comment" + assert converted_dict[ + converter.json_property_name(ExternalPackageRefProperty.REFERENCE_CATEGORY)] == "PACKAGE_MANAGER" + assert converted_dict[converter.json_property_name(ExternalPackageRefProperty.REFERENCE_LOCATOR)] == "locator" + assert converted_dict[converter.json_property_name(ExternalPackageRefProperty.REFERENCE_TYPE)] == "type" From 1880b32a249b4ad0118717e5b28a3b8d4fac4df4 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 15 Dec 2022 22:44:39 +0100 Subject: [PATCH 080/362] [issue-359] Add package verification code properties and converter Signed-off-by: Nicolaus Weidner --- .../package_verification_code_converter.py | 37 ++++++++++++++ .../package_verification_code_properties.py | 18 +++++++ ...est_package_verification_code_converter.py | 49 +++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 src/jsonschema/package_verification_code_converter.py create mode 100644 src/jsonschema/package_verification_code_properties.py create mode 100644 tests/jsonschema/test_package_verification_code_converter.py diff --git a/src/jsonschema/package_verification_code_converter.py b/src/jsonschema/package_verification_code_converter.py new file mode 100644 index 000000000..61a5b7400 --- /dev/null +++ b/src/jsonschema/package_verification_code_converter.py @@ -0,0 +1,37 @@ +# 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 typing import Type, Any + +from src.jsonschema.converter import TypedConverter +from src.jsonschema.json_property import JsonProperty +from src.jsonschema.package_verification_code_properties import PackageVerificationCodeProperty +from src.model.document import Document +from src.model.package import PackageVerificationCode +from src.writer.casing_tools import snake_case_to_camel_case + + +class PackageVerificationCodeConverter(TypedConverter): + def json_property_name(self, verification_code_property: PackageVerificationCodeProperty) -> str: + return snake_case_to_camel_case(verification_code_property.name) + + def _get_property_value(self, verification_code: PackageVerificationCode, + verification_code_property: PackageVerificationCodeProperty, + document: Document = None) -> Any: + if verification_code_property == PackageVerificationCodeProperty.PACKAGE_VERIFICATION_CODE_EXCLUDED_FILES: + return verification_code.excluded_files + elif verification_code_property == PackageVerificationCodeProperty.PACKAGE_VERIFICATION_CODE_VALUE: + return verification_code.value + + def get_json_type(self) -> Type[JsonProperty]: + return PackageVerificationCodeProperty + + def get_data_model_type(self) -> Type[PackageVerificationCode]: + return PackageVerificationCode diff --git a/src/jsonschema/package_verification_code_properties.py b/src/jsonschema/package_verification_code_properties.py new file mode 100644 index 000000000..ee202f51a --- /dev/null +++ b/src/jsonschema/package_verification_code_properties.py @@ -0,0 +1,18 @@ +# 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 auto + +from src.jsonschema.json_property import JsonProperty + + +class PackageVerificationCodeProperty(JsonProperty): + PACKAGE_VERIFICATION_CODE_EXCLUDED_FILES = auto() + PACKAGE_VERIFICATION_CODE_VALUE = auto() diff --git a/tests/jsonschema/test_package_verification_code_converter.py b/tests/jsonschema/test_package_verification_code_converter.py new file mode 100644 index 000000000..926c6e428 --- /dev/null +++ b/tests/jsonschema/test_package_verification_code_converter.py @@ -0,0 +1,49 @@ +# 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. +import pytest + +from src.jsonschema.package_verification_code_converter import PackageVerificationCodeConverter +from src.jsonschema.package_verification_code_properties import PackageVerificationCodeProperty +from src.model.package import PackageVerificationCode + + +@pytest.fixture +def converter() -> PackageVerificationCodeConverter: + return PackageVerificationCodeConverter() + + +@pytest.mark.parametrize("package_verification_code_property,expected", + [(PackageVerificationCodeProperty.PACKAGE_VERIFICATION_CODE_EXCLUDED_FILES, + "packageVerificationCodeExcludedFiles"), + (PackageVerificationCodeProperty.PACKAGE_VERIFICATION_CODE_VALUE, + "packageVerificationCodeValue")]) +def test_json_property_names(converter: PackageVerificationCodeConverter, + package_verification_code_property: PackageVerificationCodeProperty, expected: str): + assert converter.json_property_name(package_verification_code_property) == expected + + +def test_json_type(converter: PackageVerificationCodeConverter): + assert converter.get_json_type() == PackageVerificationCodeProperty + + +def test_data_model_type(converter: PackageVerificationCodeConverter): + assert converter.get_data_model_type() == PackageVerificationCode + + +def test_successful_conversion(converter: PackageVerificationCodeConverter): + package_verification_code = PackageVerificationCode("value", ["file1", "file2"]) + + converted_dict = converter.convert(package_verification_code) + + assert converted_dict[converter.json_property_name( + PackageVerificationCodeProperty.PACKAGE_VERIFICATION_CODE_EXCLUDED_FILES)] == ["file1", "file2"] + assert converted_dict[ + converter.json_property_name(PackageVerificationCodeProperty.PACKAGE_VERIFICATION_CODE_VALUE)] == "value" From 88605e76e897b793086f5b507f129b9899f3cabd Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Wed, 21 Dec 2022 13:52:35 +0100 Subject: [PATCH 081/362] [issue-359] Add __str__ method to LicenseExpression for easy serialization Signed-off-by: Nicolaus Weidner --- src/model/license_expression.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/model/license_expression.py b/src/model/license_expression.py index 1f73ffc33..226b5e438 100644 --- a/src/model/license_expression.py +++ b/src/model/license_expression.py @@ -21,3 +21,6 @@ class LicenseExpression: def __init__(self, expression_string: str): check_types_and_set_values(self, locals()) + + def __str__(self): + return self.expression_string From d3a0d6e68aa9bc567be518651f16b7e0fe8afb20 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Mon, 19 Dec 2022 17:49:29 +0100 Subject: [PATCH 082/362] [issue-359] add package converter Signed-off-by: Nicolaus Weidner --- src/jsonschema/package_converter.py | 124 +++++++++++++++++ src/jsonschema/package_properties.py | 44 ++++++ src/jsonschema/relationship_filters.py | 43 ++++++ tests/jsonschema/test_package_converter.py | 148 +++++++++++++++++++++ 4 files changed, 359 insertions(+) create mode 100644 src/jsonschema/package_converter.py create mode 100644 src/jsonschema/package_properties.py create mode 100644 src/jsonschema/relationship_filters.py create mode 100644 tests/jsonschema/test_package_converter.py diff --git a/src/jsonschema/package_converter.py b/src/jsonschema/package_converter.py new file mode 100644 index 000000000..ff140251d --- /dev/null +++ b/src/jsonschema/package_converter.py @@ -0,0 +1,124 @@ +# 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 typing import Type, Any + +from src.datetime_conversions import datetime_to_iso_string +from src.jsonschema.annotation_converter import AnnotationConverter +from src.jsonschema.checksum_converter import ChecksumConverter +from src.jsonschema.converter import TypedConverter +from src.jsonschema.external_package_ref_converter import ExternalPackageRefConverter +from src.jsonschema.json_property import JsonProperty +from src.jsonschema.package_properties import PackageProperty +from src.jsonschema.package_verification_code_converter import PackageVerificationCodeConverter +from src.jsonschema.relationship_filters import find_package_contains_file_relationships, \ + find_file_contained_by_package_relationships +from src.model.actor import Actor +from src.model.document import Document +from src.model.package import Package +from src.writer.casing_tools import snake_case_to_camel_case + + +class PackageConverter(TypedConverter): + annotation_converter: AnnotationConverter + checksum_converter: ChecksumConverter + external_package_ref_converter: ExternalPackageRefConverter + package_verification_code_converter: PackageVerificationCodeConverter + + def __init__(self): + self.annotation_converter = AnnotationConverter() + self.checksum_converter = ChecksumConverter() + self.external_package_ref_converter = ExternalPackageRefConverter() + self.package_verification_code_converter = PackageVerificationCodeConverter() + + def json_property_name(self, package_property: PackageProperty) -> str: + if package_property == PackageProperty.SPDX_ID: + return "SPDXID" + return snake_case_to_camel_case(package_property.name) + + def _get_property_value(self, package: Package, package_property: PackageProperty, + document: Document = None) -> Any: + if package_property == PackageProperty.SPDX_ID: + return package.spdx_id + elif package_property == PackageProperty.ANNOTATIONS: + package_annotations = filter(lambda annotation: annotation.spdx_id == package.spdx_id, document.annotations) + return [self.annotation_converter.convert(annotation, document) for annotation in package_annotations] + elif package_property == PackageProperty.ATTRIBUTION_TEXTS: + return package.attribution_texts + elif package_property == PackageProperty.BUILT_DATE: + return datetime_to_iso_string(package.built_date) + elif package_property == PackageProperty.CHECKSUMS: + return [self.checksum_converter.convert(checksum, document) for checksum in package.checksums] + elif package_property == PackageProperty.COMMENT: + return package.comment + elif package_property == PackageProperty.COPYRIGHT_TEXT: + return str(package.copyright_text) + elif package_property == PackageProperty.DESCRIPTION: + return package.description + elif package_property == PackageProperty.DOWNLOAD_LOCATION: + return str(package.download_location) + elif package_property == PackageProperty.EXTERNAL_REFS: + return [self.external_package_ref_converter.convert(external_ref) for external_ref in + package.external_references] + elif package_property == PackageProperty.FILES_ANALYZED: + return package.files_analyzed + elif package_property == PackageProperty.HAS_FILES: + package_contains_file_ids = [relationship.related_spdx_element_id for relationship in + find_package_contains_file_relationships(document, package)] + file_contained_in_package_ids = [relationship.spdx_element_id for relationship in + find_file_contained_by_package_relationships(document, package)] + return package_contains_file_ids + file_contained_in_package_ids + elif package_property == PackageProperty.HOMEPAGE: + return str(package.homepage) + elif package_property == PackageProperty.LICENSE_COMMENTS: + return package.license_comment + elif package_property == PackageProperty.LICENSE_CONCLUDED: + return str(package.license_concluded) + elif package_property == PackageProperty.LICENSE_DECLARED: + return str(package.license_declared) + elif package_property == PackageProperty.LICENSE_INFO_FROM_FILES: + if isinstance(package.license_info_from_files, list): + return [str(license_expression) for license_expression in package.license_info_from_files] + return str(package.license_info_from_files) + elif package_property == PackageProperty.NAME: + return package.name + elif package_property == PackageProperty.ORIGINATOR: + if isinstance(package.originator, Actor): + return package.originator.to_serialized_string() + return str(package.originator) + elif package_property == PackageProperty.PACKAGE_FILE_NAME: + return package.file_name + elif package_property == PackageProperty.PACKAGE_VERIFICATION_CODE: + return self.package_verification_code_converter.convert(package.verification_code) + elif package_property == PackageProperty.PRIMARY_PACKAGE_PURPOSE: + return package.primary_package_purpose.name + elif package_property == PackageProperty.RELEASE_DATE: + return datetime_to_iso_string(package.release_date) + elif package_property == PackageProperty.SOURCE_INFO: + return package.source_info + elif package_property == PackageProperty.SUMMARY: + return package.summary + elif package_property == PackageProperty.SUPPLIER: + if isinstance(package.supplier, Actor): + return package.supplier.to_serialized_string() + return str(package.supplier) + elif package_property == PackageProperty.VALID_UNTIL_DATE: + return datetime_to_iso_string(package.valid_until_date) + elif package_property == PackageProperty.VERSION_INFO: + return package.version + + def get_json_type(self) -> Type[JsonProperty]: + return PackageProperty + + def get_data_model_type(self) -> Type[Package]: + return Package + + def requires_full_document(self) -> bool: + return True diff --git a/src/jsonschema/package_properties.py b/src/jsonschema/package_properties.py new file mode 100644 index 000000000..467ef5fc1 --- /dev/null +++ b/src/jsonschema/package_properties.py @@ -0,0 +1,44 @@ +# 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 auto + +from src.jsonschema.json_property import JsonProperty + + +class PackageProperty(JsonProperty): + SPDX_ID = auto() + ANNOTATIONS = auto() + ATTRIBUTION_TEXTS = auto() + BUILT_DATE = auto() + CHECKSUMS = auto() + COMMENT = auto() + COPYRIGHT_TEXT = auto() + DESCRIPTION = auto() + DOWNLOAD_LOCATION = auto() + EXTERNAL_REFS = auto() + FILES_ANALYZED = auto() + HAS_FILES = auto() + HOMEPAGE = auto() + LICENSE_COMMENTS = auto() + LICENSE_CONCLUDED = auto() + LICENSE_DECLARED = auto() + LICENSE_INFO_FROM_FILES = auto() + NAME = auto() + ORIGINATOR = auto() + PACKAGE_FILE_NAME = auto() + PACKAGE_VERIFICATION_CODE = auto() + PRIMARY_PACKAGE_PURPOSE = auto() + RELEASE_DATE = auto() + SOURCE_INFO = auto() + SUMMARY = auto() + SUPPLIER = auto() + VALID_UNTIL_DATE = auto() + VERSION_INFO = auto() diff --git a/src/jsonschema/relationship_filters.py b/src/jsonschema/relationship_filters.py new file mode 100644 index 000000000..4d388b5f5 --- /dev/null +++ b/src/jsonschema/relationship_filters.py @@ -0,0 +1,43 @@ +# 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 typing import List + +from src.model.document import Document +from src.model.package import Package +from src.model.relationship import Relationship, RelationshipType + + +def find_package_contains_file_relationships(document: Document, package: Package) -> List[Relationship]: + file_ids_in_document = [file.spdx_id for file in document.files] + package_contains_relationships = filter_by_type_and_origin(document.relationships, RelationshipType.CONTAINS, + package.spdx_id) + return [relationship for relationship in package_contains_relationships if + relationship.related_spdx_element_id in file_ids_in_document] + + +def find_file_contained_by_package_relationships(document: Document, package: Package) -> List[Relationship]: + file_ids_in_document = [file.spdx_id for file in document.files] + contained_by_package_relationships = filter_by_type_and_target(document.relationships, + RelationshipType.CONTAINED_BY, package.spdx_id) + return [relationship for relationship in contained_by_package_relationships if + relationship.spdx_element_id in file_ids_in_document] + + +def filter_by_type_and_target(relationships: List[Relationship], relationship_type: RelationshipType, + target_id: str) -> List[Relationship]: + return [relationship for relationship in relationships if + relationship.relationship_type == relationship_type and relationship.related_spdx_element_id == target_id] + + +def filter_by_type_and_origin(relationships: List[Relationship], relationship_type: RelationshipType, + origin_id: str) -> List[Relationship]: + return [relationship for relationship in relationships if + relationship.relationship_type == relationship_type and relationship.spdx_element_id == origin_id] diff --git a/tests/jsonschema/test_package_converter.py b/tests/jsonschema/test_package_converter.py new file mode 100644 index 000000000..a1aeb3abe --- /dev/null +++ b/tests/jsonschema/test_package_converter.py @@ -0,0 +1,148 @@ +# 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 datetime import datetime +from unittest import mock +from unittest.mock import MagicMock + +import pytest + +from src.jsonschema.package_converter import PackageConverter +from src.jsonschema.package_properties import PackageProperty +from src.model.actor import Actor, ActorType +from src.model.annotation import Annotation, AnnotationType +from src.model.checksum import Checksum, ChecksumAlgorithm +from src.model.document import Document, CreationInfo +from src.model.license_expression import LicenseExpression +from src.model.package import Package, PackageVerificationCode, ExternalPackageRef, ExternalPackageRefCategory, \ + PackagePurpose + + +@pytest.fixture +@mock.patch('src.jsonschema.checksum_converter.ChecksumConverter', autospec=True) +@mock.patch('src.jsonschema.annotation_converter.AnnotationConverter', autospec=True) +@mock.patch('src.jsonschema.package_verification_code_converter.PackageVerificationCodeConverter', autospec=True) +@mock.patch('src.jsonschema.external_package_ref_converter.ExternalPackageRefConverter', autospec=True) +def converter(checksum_converter_mock: MagicMock, annotation_converter_mock: MagicMock, + verification_code_converter_mock: MagicMock, + package_ref_converter_mock: MagicMock) -> PackageConverter: + converter = PackageConverter() + converter.checksum_converter = checksum_converter_mock() + converter.annotation_converter = annotation_converter_mock() + converter.package_verification_code_converter = verification_code_converter_mock() + converter.external_package_ref_converter = package_ref_converter_mock() + return converter + + +@pytest.mark.parametrize("external_package_ref_property,expected", + [(PackageProperty.SPDX_ID, "SPDXID"), + (PackageProperty.ANNOTATIONS, "annotations"), + (PackageProperty.ATTRIBUTION_TEXTS, "attributionTexts"), + (PackageProperty.BUILT_DATE, "builtDate"), + (PackageProperty.CHECKSUMS, "checksums"), + (PackageProperty.COMMENT, "comment"), + (PackageProperty.COPYRIGHT_TEXT, "copyrightText"), + (PackageProperty.DESCRIPTION, "description"), + (PackageProperty.DOWNLOAD_LOCATION, "downloadLocation"), + (PackageProperty.EXTERNAL_REFS, "externalRefs"), + (PackageProperty.FILES_ANALYZED, "filesAnalyzed"), + (PackageProperty.HAS_FILES, "hasFiles"), + (PackageProperty.HOMEPAGE, "homepage"), + (PackageProperty.LICENSE_COMMENTS, "licenseComments"), + (PackageProperty.LICENSE_CONCLUDED, "licenseConcluded"), + (PackageProperty.LICENSE_DECLARED, "licenseDeclared"), + (PackageProperty.LICENSE_INFO_FROM_FILES, "licenseInfoFromFiles"), + (PackageProperty.NAME, "name"), + (PackageProperty.ORIGINATOR, "originator"), + (PackageProperty.PACKAGE_FILE_NAME, "packageFileName"), + (PackageProperty.PACKAGE_VERIFICATION_CODE, "packageVerificationCode"), + (PackageProperty.PRIMARY_PACKAGE_PURPOSE, "primaryPackagePurpose"), + (PackageProperty.RELEASE_DATE, "releaseDate"), + (PackageProperty.SOURCE_INFO, "sourceInfo"), + (PackageProperty.SUMMARY, "summary"), + (PackageProperty.SUPPLIER, "supplier"), + (PackageProperty.VALID_UNTIL_DATE, "validUntilDate"), + (PackageProperty.VERSION_INFO, "versionInfo")]) +def test_json_property_names(converter: PackageConverter, + external_package_ref_property: PackageProperty, expected: str): + assert converter.json_property_name(external_package_ref_property) == expected + + +def test_json_type(converter: PackageConverter): + assert converter.get_json_type() == PackageProperty + + +def test_data_model_type(converter: PackageConverter): + assert converter.get_data_model_type() == Package + + +def test_successful_conversion(converter: PackageConverter): + converter.checksum_converter.convert.return_value = "mock_converted_checksum" + converter.annotation_converter.convert.return_value = "mock_converted_annotation" + converter.package_verification_code_converter.convert.return_value = "mock_converted_verification_code" + converter.external_package_ref_converter.convert.return_value = "mock_package_ref" + package = Package(spdx_id="packageId", name="name", download_location="downloadLocation", version="version", + file_name="fileName", supplier=Actor(ActorType.PERSON, "supplierName"), + originator=Actor(ActorType.PERSON, "originatorName"), files_analyzed=True, + verification_code=PackageVerificationCode("value"), + checksums=[Checksum(ChecksumAlgorithm.SHA1, "sha1"), + Checksum(ChecksumAlgorithm.BLAKE2B_256, "blake")], homepage="homepage", + source_info="sourceInfo", license_concluded=LicenseExpression("licenseExpression1"), + license_info_from_files=[LicenseExpression("licenseExpression2"), + LicenseExpression("licenseExpression3")], + license_declared=LicenseExpression("licenseExpression4"), license_comment="licenseComment", + copyright_text="copyrightText", summary="summary", description="description", comment="comment", + external_references=[ + ExternalPackageRef(ExternalPackageRefCategory.PACKAGE_MANAGER, "referenceType", + "referenceLocator")], + attribution_texts=["attributionText1", "attributionText2"], + primary_package_purpose=PackagePurpose.APPLICATION, release_date=datetime(2022, 12, 1), + built_date=datetime(2022, 12, 2), valid_until_date=datetime(2022, 12, 3)) + + creation_info = CreationInfo("spdxVersion", "documentID", "documentName", "documentNamespace", [], + datetime(2022, 12, 4)) + annotation = Annotation(package.spdx_id, AnnotationType.REVIEW, Actor(ActorType.TOOL, "toolName"), + datetime(2022, 12, 5), + "review comment") + document = Document(creation_info, packages=[package], annotations=[annotation]) + + converted_dict = converter.convert(package, document) + + assert converted_dict[converter.json_property_name(PackageProperty.SPDX_ID)] == "packageId" + assert converted_dict[converter.json_property_name(PackageProperty.ANNOTATIONS)] == ["mock_converted_annotation"] + assert converted_dict[converter.json_property_name(PackageProperty.ATTRIBUTION_TEXTS)] == ["attributionText1", + "attributionText2"] + assert converted_dict[converter.json_property_name(PackageProperty.NAME)] == "name" + assert converted_dict[converter.json_property_name(PackageProperty.DOWNLOAD_LOCATION)] == "downloadLocation" + assert converted_dict[converter.json_property_name(PackageProperty.VERSION_INFO)] == "version" + assert converted_dict[converter.json_property_name(PackageProperty.PACKAGE_FILE_NAME)] == "fileName" + assert converted_dict[converter.json_property_name(PackageProperty.SUPPLIER)] == "Person: supplierName" + assert converted_dict[converter.json_property_name(PackageProperty.ORIGINATOR)] == "Person: originatorName" + assert converted_dict[converter.json_property_name(PackageProperty.FILES_ANALYZED)] + assert converted_dict[converter.json_property_name( + PackageProperty.PACKAGE_VERIFICATION_CODE)] == "mock_converted_verification_code" + assert converted_dict[converter.json_property_name(PackageProperty.CHECKSUMS)] == ["mock_converted_checksum", + "mock_converted_checksum"] + assert converted_dict[converter.json_property_name(PackageProperty.HOMEPAGE)] == "homepage" + assert converted_dict[converter.json_property_name(PackageProperty.SOURCE_INFO)] == "sourceInfo" + assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_CONCLUDED)] == "licenseExpression1" + assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_INFO_FROM_FILES)] == [ + "licenseExpression2", "licenseExpression3"] + assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_DECLARED)] == "licenseExpression4" + assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_COMMENTS)] == "licenseComment" + assert converted_dict[converter.json_property_name(PackageProperty.COPYRIGHT_TEXT)] == "copyrightText" + assert converted_dict[converter.json_property_name(PackageProperty.SUMMARY)] == "summary" + assert converted_dict[converter.json_property_name(PackageProperty.DESCRIPTION)] == "description" + assert converted_dict[converter.json_property_name(PackageProperty.COMMENT)] == "comment" + assert converted_dict[converter.json_property_name(PackageProperty.EXTERNAL_REFS)] == ["mock_package_ref"] + assert converted_dict[converter.json_property_name(PackageProperty.PRIMARY_PACKAGE_PURPOSE)] == "APPLICATION" + assert converted_dict[converter.json_property_name(PackageProperty.RELEASE_DATE)] == "2022-12-01T00:00:00Z" + assert converted_dict[converter.json_property_name(PackageProperty.BUILT_DATE)] == "2022-12-02T00:00:00Z" + assert converted_dict[converter.json_property_name(PackageProperty.VALID_UNTIL_DATE)] == "2022-12-03T00:00:00Z" From 8b35256ed262ffbe038bcc1d0d341c87dec215b6 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Mon, 19 Dec 2022 17:59:27 +0100 Subject: [PATCH 083/362] [issue-359] add relationship converter Signed-off-by: Nicolaus Weidner --- src/jsonschema/relationship_converter.py | 40 +++++++++++++++ src/jsonschema/relationship_properties.py | 20 ++++++++ .../jsonschema/test_relationship_converter.py | 49 +++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 src/jsonschema/relationship_converter.py create mode 100644 src/jsonschema/relationship_properties.py create mode 100644 tests/jsonschema/test_relationship_converter.py diff --git a/src/jsonschema/relationship_converter.py b/src/jsonschema/relationship_converter.py new file mode 100644 index 000000000..1860b6a06 --- /dev/null +++ b/src/jsonschema/relationship_converter.py @@ -0,0 +1,40 @@ +# 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 typing import Type, Any + +from src.jsonschema.converter import TypedConverter +from src.jsonschema.json_property import JsonProperty +from src.jsonschema.relationship_properties import RelationshipProperty +from src.model.document import Document +from src.model.relationship import Relationship +from src.writer.casing_tools import snake_case_to_camel_case + + +class RelationshipConverter(TypedConverter): + def json_property_name(self, relationship_property: RelationshipProperty) -> str: + return snake_case_to_camel_case(relationship_property.name) + + def _get_property_value(self, relationship: Relationship, relationship_property: RelationshipProperty, + document: Document = None) -> Any: + if relationship_property == RelationshipProperty.SPDX_ELEMENT_ID: + return relationship.spdx_element_id + elif relationship_property == RelationshipProperty.COMMENT: + return relationship.comment + elif relationship_property == RelationshipProperty.RELATED_SPDX_ELEMENT: + return relationship.related_spdx_element_id + elif relationship_property == RelationshipProperty.RELATIONSHIP_TYPE: + return relationship.relationship_type.name + + def get_json_type(self) -> Type[JsonProperty]: + return RelationshipProperty + + def get_data_model_type(self) -> Type[Relationship]: + return Relationship diff --git a/src/jsonschema/relationship_properties.py b/src/jsonschema/relationship_properties.py new file mode 100644 index 000000000..bd6b787d6 --- /dev/null +++ b/src/jsonschema/relationship_properties.py @@ -0,0 +1,20 @@ +# 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 auto + +from src.jsonschema.json_property import JsonProperty + + +class RelationshipProperty(JsonProperty): + SPDX_ELEMENT_ID = auto() + COMMENT = auto() + RELATED_SPDX_ELEMENT = auto() + RELATIONSHIP_TYPE = auto() diff --git a/tests/jsonschema/test_relationship_converter.py b/tests/jsonschema/test_relationship_converter.py new file mode 100644 index 000000000..6c6a51a57 --- /dev/null +++ b/tests/jsonschema/test_relationship_converter.py @@ -0,0 +1,49 @@ +# 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. +import pytest + +from src.jsonschema.relationship_converter import RelationshipConverter +from src.jsonschema.relationship_properties import RelationshipProperty +from src.model.relationship import Relationship, RelationshipType + + +@pytest.fixture +def converter() -> RelationshipConverter: + return RelationshipConverter() + + +@pytest.mark.parametrize("relationship_property,expected", + [(RelationshipProperty.SPDX_ELEMENT_ID, "spdxElementId"), + (RelationshipProperty.COMMENT, "comment"), + (RelationshipProperty.RELATED_SPDX_ELEMENT, "relatedSpdxElement"), + (RelationshipProperty.RELATIONSHIP_TYPE, "relationshipType")]) +def test_json_property_names(converter: RelationshipConverter, relationship_property: RelationshipProperty, + expected: str): + assert converter.json_property_name(relationship_property) == expected + + +def test_json_type(converter: RelationshipConverter): + assert converter.get_json_type() == RelationshipProperty + + +def test_data_model_type(converter: RelationshipConverter): + assert converter.get_data_model_type() == Relationship + + +def test_successful_conversion(converter: RelationshipConverter): + relationship = Relationship("spdxElementId", RelationshipType.COPY_OF, "relatedElementId", "comment") + + converted_dict = converter.convert(relationship) + + assert converted_dict[converter.json_property_name(RelationshipProperty.SPDX_ELEMENT_ID)] == "spdxElementId" + assert converted_dict[converter.json_property_name(RelationshipProperty.COMMENT)] == "comment" + assert converted_dict[converter.json_property_name(RelationshipProperty.RELATED_SPDX_ELEMENT)] == "relatedElementId" + assert converted_dict[converter.json_property_name(RelationshipProperty.RELATIONSHIP_TYPE)] == "COPY_OF" From 5b266904bda6c834b858adc0dcbcfe9a4037c385 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Mon, 19 Dec 2022 23:00:57 +0100 Subject: [PATCH 084/362] [issue-359] add extracted licensing info converter Signed-off-by: Nicolaus Weidner --- .../extracted_licensing_info_converter.py | 43 +++++++++++++++ .../extracted_licensing_info_properties.py | 21 +++++++ ...test_extracted_licensing_info_converter.py | 55 +++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 src/jsonschema/extracted_licensing_info_converter.py create mode 100644 src/jsonschema/extracted_licensing_info_properties.py create mode 100644 tests/jsonschema/test_extracted_licensing_info_converter.py diff --git a/src/jsonschema/extracted_licensing_info_converter.py b/src/jsonschema/extracted_licensing_info_converter.py new file mode 100644 index 000000000..f15629c5a --- /dev/null +++ b/src/jsonschema/extracted_licensing_info_converter.py @@ -0,0 +1,43 @@ +# 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 typing import Type, Any + +from src.jsonschema.converter import TypedConverter +from src.jsonschema.extracted_licensing_info_properties import ExtractedLicensingInfoProperty +from src.jsonschema.json_property import JsonProperty +from src.model.document import Document +from src.model.extracted_licensing_info import ExtractedLicensingInfo +from src.writer.casing_tools import snake_case_to_camel_case + + +class ExtractedLicensingInfoConverter(TypedConverter): + def json_property_name(self, extracted_licensing_info_property: ExtractedLicensingInfoProperty) -> str: + return snake_case_to_camel_case(extracted_licensing_info_property.name) + + def _get_property_value(self, extracted_licensing_info: ExtractedLicensingInfo, + extracted_licensing_info_property: ExtractedLicensingInfoProperty, + document: Document = None) -> Any: + if extracted_licensing_info_property == ExtractedLicensingInfoProperty.COMMENT: + return extracted_licensing_info.comment + elif extracted_licensing_info_property == ExtractedLicensingInfoProperty.EXTRACTED_TEXT: + return extracted_licensing_info.extracted_text + elif extracted_licensing_info_property == ExtractedLicensingInfoProperty.LICENSE_ID: + return extracted_licensing_info.license_id + elif extracted_licensing_info_property == ExtractedLicensingInfoProperty.NAME: + return extracted_licensing_info.license_name + elif extracted_licensing_info_property == ExtractedLicensingInfoProperty.SEE_ALSOS: + return extracted_licensing_info.cross_references + + def get_json_type(self) -> Type[JsonProperty]: + return ExtractedLicensingInfoProperty + + def get_data_model_type(self) -> Type[ExtractedLicensingInfo]: + return ExtractedLicensingInfo diff --git a/src/jsonschema/extracted_licensing_info_properties.py b/src/jsonschema/extracted_licensing_info_properties.py new file mode 100644 index 000000000..0bfcda02a --- /dev/null +++ b/src/jsonschema/extracted_licensing_info_properties.py @@ -0,0 +1,21 @@ +# 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 auto + +from src.jsonschema.json_property import JsonProperty + + +class ExtractedLicensingInfoProperty(JsonProperty): + COMMENT = auto() + EXTRACTED_TEXT = auto() + LICENSE_ID = auto() + NAME = auto() + SEE_ALSOS = auto() diff --git a/tests/jsonschema/test_extracted_licensing_info_converter.py b/tests/jsonschema/test_extracted_licensing_info_converter.py new file mode 100644 index 000000000..518a6f2b3 --- /dev/null +++ b/tests/jsonschema/test_extracted_licensing_info_converter.py @@ -0,0 +1,55 @@ +# 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. +import pytest + +from src.jsonschema.extracted_licensing_info_converter import ExtractedLicensingInfoConverter +from src.jsonschema.extracted_licensing_info_properties import ExtractedLicensingInfoProperty +from src.model.extracted_licensing_info import ExtractedLicensingInfo + + +@pytest.fixture +def converter() -> ExtractedLicensingInfoConverter: + return ExtractedLicensingInfoConverter() + + +@pytest.mark.parametrize("extracted_licensing_info_property,expected", + [(ExtractedLicensingInfoProperty.LICENSE_ID, "licenseId"), + (ExtractedLicensingInfoProperty.EXTRACTED_TEXT, "extractedText"), + (ExtractedLicensingInfoProperty.NAME, "name"), + (ExtractedLicensingInfoProperty.COMMENT, "comment"), + (ExtractedLicensingInfoProperty.SEE_ALSOS, "seeAlsos")]) +def test_json_property_names(converter: ExtractedLicensingInfoConverter, + extracted_licensing_info_property: ExtractedLicensingInfoProperty, expected: str): + assert converter.json_property_name(extracted_licensing_info_property) == expected + + +def test_json_type(converter: ExtractedLicensingInfoConverter): + assert converter.get_json_type() == ExtractedLicensingInfoProperty + + +def test_data_model_type(converter: ExtractedLicensingInfoConverter): + assert converter.get_data_model_type() == ExtractedLicensingInfo + + +def test_successful_conversion(converter: ExtractedLicensingInfoConverter): + extracted_licensing_info = ExtractedLicensingInfo(license_id="licenseId", extracted_text="Extracted text", + license_name="license name", + cross_references=["reference1", "reference2"], comment="comment") + + converted_dict = converter.convert(extracted_licensing_info) + + assert converted_dict[converter.json_property_name(ExtractedLicensingInfoProperty.LICENSE_ID)] == "licenseId" + assert converted_dict[ + converter.json_property_name(ExtractedLicensingInfoProperty.EXTRACTED_TEXT)] == "Extracted text" + assert converted_dict[converter.json_property_name(ExtractedLicensingInfoProperty.NAME)] == "license name" + assert converted_dict[converter.json_property_name(ExtractedLicensingInfoProperty.SEE_ALSOS)] == ["reference1", + "reference2"] + assert converted_dict[converter.json_property_name(ExtractedLicensingInfoProperty.COMMENT)] == "comment" From 1a51fc27ce1facb004cffc6f57e5fe0cbb4ff08a Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Tue, 20 Dec 2022 10:41:14 +0100 Subject: [PATCH 085/362] [issue-359] add file converter Signed-off-by: Nicolaus Weidner --- src/jsonschema/file_converter.py | 80 +++++++++++++++++++ src/jsonschema/file_properties.py | 31 +++++++ tests/jsonschema/test_file_converter.py | 102 ++++++++++++++++++++++++ 3 files changed, 213 insertions(+) create mode 100644 src/jsonschema/file_converter.py create mode 100644 src/jsonschema/file_properties.py create mode 100644 tests/jsonschema/test_file_converter.py diff --git a/src/jsonschema/file_converter.py b/src/jsonschema/file_converter.py new file mode 100644 index 000000000..19d96b180 --- /dev/null +++ b/src/jsonschema/file_converter.py @@ -0,0 +1,80 @@ +# 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 typing import Type, Any + +from src.jsonschema.annotation_converter import AnnotationConverter +from src.jsonschema.checksum_converter import ChecksumConverter +from src.jsonschema.converter import TypedConverter +from src.jsonschema.file_properties import FileProperty +from src.jsonschema.json_property import JsonProperty +from src.model.document import Document +from src.model.file import File +from src.writer.casing_tools import snake_case_to_camel_case + + +class FileConverter(TypedConverter): + annotation_converter: AnnotationConverter + checksum_converter: ChecksumConverter + + def __init__(self): + self.annotation_converter = AnnotationConverter() + self.checksum_converter = ChecksumConverter() + + def json_property_name(self, file_property: FileProperty) -> str: + if file_property == FileProperty.SPDX_ID: + return "SPDXID" + return snake_case_to_camel_case(file_property.name) + + def _get_property_value(self, file: Any, file_property: FileProperty, document: Document = None) -> Any: + if file_property == FileProperty.SPDX_ID: + return file.spdx_id + elif file_property == FileProperty.ANNOTATIONS: + file_annotations = filter(lambda annotation: annotation.spdx_id == file.spdx_id, document.annotations) + return [self.annotation_converter.convert(annotation) for annotation in file_annotations] + elif file_property == FileProperty.ARTIFACT_OFS: + # Deprecated property, automatically converted during parsing + pass + elif file_property == FileProperty.ATTRIBUTION_TEXTS: + return file.attribution_texts + elif file_property == FileProperty.CHECKSUMS: + return [self.checksum_converter.convert(checksum) for checksum in file.checksums] + elif file_property == FileProperty.COMMENT: + return file.comment + elif file_property == FileProperty.COPYRIGHT_TEXT: + return file.copyright_text + elif file_property == FileProperty.FILE_CONTRIBUTORS: + return file.contributors + elif file_property == FileProperty.FILE_DEPENDENCIES: + # Deprecated property, automatically converted during parsing + pass + elif file_property == FileProperty.FILE_NAME: + return file.name + elif file_property == FileProperty.FILE_TYPES: + return [file_type.name for file_type in file.file_type] + elif file_property == FileProperty.LICENSE_COMMENTS: + return file.license_comment + elif file_property == FileProperty.LICENSE_CONCLUDED: + return str(file.concluded_license) + elif file_property == FileProperty.LICENSE_INFO_IN_FILES: + if isinstance(file.license_info_in_file, list): + return [str(license_expression) for license_expression in file.license_info_in_file] + return str(file.license_info_in_file) + elif file_property == FileProperty.NOTICE_TEXT: + return file.notice + + def get_json_type(self) -> Type[JsonProperty]: + return FileProperty + + def get_data_model_type(self) -> Type[File]: + return File + + def requires_full_document(self) -> bool: + return True diff --git a/src/jsonschema/file_properties.py b/src/jsonschema/file_properties.py new file mode 100644 index 000000000..02cc8a25b --- /dev/null +++ b/src/jsonschema/file_properties.py @@ -0,0 +1,31 @@ +# 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 auto + +from src.jsonschema.json_property import JsonProperty + + +class FileProperty(JsonProperty): + SPDX_ID = auto() + ANNOTATIONS = auto() + ARTIFACT_OFS = auto() + ATTRIBUTION_TEXTS = auto() + CHECKSUMS = auto() + COMMENT = auto() + COPYRIGHT_TEXT = auto() + FILE_CONTRIBUTORS = auto() + FILE_DEPENDENCIES = auto() + FILE_NAME = auto() + FILE_TYPES = auto() + LICENSE_COMMENTS = auto() + LICENSE_CONCLUDED = auto() + LICENSE_INFO_IN_FILES = auto() + NOTICE_TEXT = auto() diff --git a/tests/jsonschema/test_file_converter.py b/tests/jsonschema/test_file_converter.py new file mode 100644 index 000000000..a7846b756 --- /dev/null +++ b/tests/jsonschema/test_file_converter.py @@ -0,0 +1,102 @@ +# 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 datetime import datetime +from unittest import mock +from unittest.mock import MagicMock + +import pytest + +from src.jsonschema.file_converter import FileConverter +from src.jsonschema.file_properties import FileProperty +from src.model.actor import Actor, ActorType +from src.model.annotation import Annotation, AnnotationType +from src.model.checksum import Checksum, ChecksumAlgorithm +from src.model.document import CreationInfo, Document +from src.model.file import File, FileType +from src.model.license_expression import LicenseExpression + + +@pytest.fixture +@mock.patch('src.jsonschema.checksum_converter.ChecksumConverter', autospec=True) +@mock.patch('src.jsonschema.annotation_converter.AnnotationConverter', autospec=True) +def converter(checksum_converter_mock: MagicMock, annotation_converter_mock: MagicMock) -> FileConverter: + converter = FileConverter() + converter.checksum_converter = checksum_converter_mock() + converter.annotation_converter = annotation_converter_mock() + return converter + + +@pytest.mark.parametrize("file_property,expected", + [(FileProperty.SPDX_ID, "SPDXID"), + (FileProperty.ANNOTATIONS, "annotations"), + (FileProperty.ARTIFACT_OFS, "artifactOfs"), + (FileProperty.ATTRIBUTION_TEXTS, "attributionTexts"), + (FileProperty.CHECKSUMS, "checksums"), + (FileProperty.COMMENT, "comment"), + (FileProperty.COPYRIGHT_TEXT, "copyrightText"), + (FileProperty.FILE_CONTRIBUTORS, "fileContributors"), + (FileProperty.FILE_DEPENDENCIES, "fileDependencies"), + (FileProperty.FILE_NAME, "fileName"), + (FileProperty.FILE_TYPES, "fileTypes"), + (FileProperty.LICENSE_COMMENTS, "licenseComments"), + (FileProperty.LICENSE_CONCLUDED, "licenseConcluded"), + (FileProperty.LICENSE_INFO_IN_FILES, "licenseInfoInFiles"), + (FileProperty.NOTICE_TEXT, "noticeText")]) +def test_json_property_names(converter: FileConverter, file_property: FileProperty, expected: str): + assert converter.json_property_name(file_property) == expected + + +def test_json_type(converter: FileConverter): + assert converter.get_json_type() == FileProperty + + +def test_data_model_type(converter: FileConverter): + assert converter.get_data_model_type() == File + + +def test_successful_conversion(converter: FileConverter): + converter.checksum_converter.convert.return_value = "mock_converted_checksum" + converter.annotation_converter.convert.return_value = "mock_converted_annotation" + file = File(name="name", spdx_id="spdxId", + checksums=[Checksum(ChecksumAlgorithm.SHA224, "sha224"), Checksum(ChecksumAlgorithm.MD2, "md2")], + file_type=[FileType.SPDX, FileType.OTHER], concluded_license=LicenseExpression("licenseExpression1"), + license_info_in_file=[LicenseExpression("licenseExpression2"), LicenseExpression("licenseExpression3")], + license_comment="licenseComment", copyright_text="copyrightText", comment="comment", notice="notice", + contributors=["contributor1", "contributor2"], + attribution_texts=["attributionText1", "attributionText2"]) + + creation_info = CreationInfo("spdxVersion", "documentID", "documentName", "documentNamespace", [], + datetime(2022, 12, 4)) + + annotations = [Annotation(file.spdx_id, AnnotationType.REVIEW, Actor(ActorType.PERSON, "annotatorName"), + datetime(2022, 12, 5), "review comment")] + document = Document(creation_info, files=[file], annotations=annotations) + + converted_dict = converter.convert(file, document) + + assert converted_dict[converter.json_property_name(FileProperty.SPDX_ID)] == "spdxId" + assert converted_dict[converter.json_property_name(FileProperty.ANNOTATIONS)] == ["mock_converted_annotation"] + assert converted_dict[converter.json_property_name(FileProperty.ATTRIBUTION_TEXTS)] == ["attributionText1", + "attributionText2"] + assert converted_dict[converter.json_property_name(FileProperty.CHECKSUMS)] == ["mock_converted_checksum", + "mock_converted_checksum"] + assert converted_dict[converter.json_property_name(FileProperty.COMMENT)] == "comment" + assert converted_dict[ + converter.json_property_name(FileProperty.COPYRIGHT_TEXT)] == "copyrightText" + assert converted_dict[converter.json_property_name(FileProperty.FILE_CONTRIBUTORS)] == ["contributor1", + "contributor2"] + assert converted_dict[converter.json_property_name(FileProperty.FILE_NAME)] == "name" + assert converted_dict[converter.json_property_name(FileProperty.FILE_TYPES)] == ["SPDX", "OTHER"] + assert converted_dict[converter.json_property_name(FileProperty.LICENSE_COMMENTS)] == "licenseComment" + assert converted_dict[converter.json_property_name(FileProperty.LICENSE_CONCLUDED)] == "licenseExpression1" + assert converted_dict[converter.json_property_name(FileProperty.LICENSE_INFO_IN_FILES)] == ["licenseExpression2", + "licenseExpression3"] + assert converted_dict[converter.json_property_name(FileProperty.NOTICE_TEXT)] == "notice" From 8d7d47825cb357e829c3d8670ce16a6eb21a6774 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Tue, 20 Dec 2022 12:11:23 +0100 Subject: [PATCH 086/362] [issue-359] add snippet converter Signed-off-by: Nicolaus Weidner --- src/jsonschema/snippet_converter.py | 88 +++++++++++++++++++++ src/jsonschema/snippet_properties.py | 27 +++++++ tests/jsonschema/test_snippet_converter.py | 92 ++++++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 src/jsonschema/snippet_converter.py create mode 100644 src/jsonschema/snippet_properties.py create mode 100644 tests/jsonschema/test_snippet_converter.py diff --git a/src/jsonschema/snippet_converter.py b/src/jsonschema/snippet_converter.py new file mode 100644 index 000000000..b0581e2c6 --- /dev/null +++ b/src/jsonschema/snippet_converter.py @@ -0,0 +1,88 @@ +# 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 typing import Type, Any, Tuple, Dict + +from src.jsonschema.annotation_converter import AnnotationConverter +from src.jsonschema.converter import TypedConverter +from src.jsonschema.json_property import JsonProperty +from src.jsonschema.snippet_properties import SnippetProperty +from src.model.document import Document +from src.model.snippet import Snippet +from src.writer.casing_tools import snake_case_to_camel_case + + +class SnippetConverter(TypedConverter): + annotation_converter: AnnotationConverter + + def __init__(self): + self.annotation_converter = AnnotationConverter() + + def json_property_name(self, snippet_property: SnippetProperty) -> str: + if snippet_property == SnippetProperty.SPDX_ID: + return "SPDXID" + return snake_case_to_camel_case(snippet_property.name) + + def _get_property_value(self, snippet: Snippet, snippet_property: SnippetProperty, + document: Document = None) -> Any: + if snippet_property == SnippetProperty.SPDX_ID: + return snippet.spdx_id + elif snippet_property == SnippetProperty.ANNOTATIONS: + snippet_annotations = filter(lambda annotation: annotation.spdx_id == snippet.spdx_id, document.annotations) + return [self.annotation_converter.convert(annotation) for annotation in snippet_annotations] + elif snippet_property == SnippetProperty.ATTRIBUTION_TEXTS: + return snippet.attribution_texts + elif snippet_property == SnippetProperty.COMMENT: + return snippet.comment + elif snippet_property == SnippetProperty.COPYRIGHT_TEXT: + return snippet.copyright_text + elif snippet_property == SnippetProperty.LICENSE_COMMENTS: + return snippet.license_comment + elif snippet_property == SnippetProperty.LICENSE_CONCLUDED: + return str(snippet.concluded_license) + elif snippet_property == SnippetProperty.LICENSE_INFO_IN_SNIPPETS: + if isinstance(snippet.license_info_in_snippet, list): + return [str(license_expression) for license_expression in snippet.license_info_in_snippet] + return str(snippet.license_info_in_snippet) + elif snippet_property == SnippetProperty.NAME: + return snippet.name + elif snippet_property == SnippetProperty.RANGES: + ranges = [convert_byte_range_to_dict(snippet.byte_range, snippet.file_spdx_id)] + if snippet.line_range: + ranges.append(convert_line_range_to_dict(snippet.line_range, snippet.file_spdx_id)) + return ranges + elif snippet_property == SnippetProperty.SNIPPET_FROM_FILE: + return snippet.file_spdx_id + + def get_json_type(self) -> Type[JsonProperty]: + return SnippetProperty + + def get_data_model_type(self) -> Type[Snippet]: + return Snippet + + def requires_full_document(self) -> bool: + return True + + +def convert_line_range_to_dict(line_range: Tuple[int, int], file_id: str) -> Dict: + return _convert_range_to_dict(line_range, file_id, "lineNumber") + + +def convert_byte_range_to_dict(byte_range: Tuple[int, int], file_id: str) -> Dict: + return _convert_range_to_dict(byte_range, file_id, "offset") + + +def _convert_range_to_dict(int_range: Tuple[int, int], file_id: str, pointer_property: str) -> Dict: + return {"startPointer": _pointer(file_id, int_range[0], pointer_property), + "endPointer": _pointer(file_id, int_range[1], pointer_property)} + + +def _pointer(reference: str, target: int, pointer_property: str) -> Dict: + return {"reference": reference, pointer_property: target} diff --git a/src/jsonschema/snippet_properties.py b/src/jsonschema/snippet_properties.py new file mode 100644 index 000000000..679326b26 --- /dev/null +++ b/src/jsonschema/snippet_properties.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 enum import auto + +from src.jsonschema.json_property import JsonProperty + + +class SnippetProperty(JsonProperty): + SPDX_ID = auto() + ANNOTATIONS = auto() + ATTRIBUTION_TEXTS = auto() + COMMENT = auto() + COPYRIGHT_TEXT = auto() + LICENSE_COMMENTS = auto() + LICENSE_CONCLUDED = auto() + LICENSE_INFO_IN_SNIPPETS = auto() + NAME = auto() + RANGES = auto() + SNIPPET_FROM_FILE = auto() diff --git a/tests/jsonschema/test_snippet_converter.py b/tests/jsonschema/test_snippet_converter.py new file mode 100644 index 000000000..693b008ac --- /dev/null +++ b/tests/jsonschema/test_snippet_converter.py @@ -0,0 +1,92 @@ +# 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 datetime import datetime +from unittest import mock +from unittest.mock import MagicMock + +import pytest + +from src.jsonschema.snippet_converter import SnippetConverter +from src.jsonschema.snippet_properties import SnippetProperty +from src.model.actor import Actor, ActorType +from src.model.annotation import Annotation, AnnotationType +from src.model.document import Document, CreationInfo +from src.model.license_expression import LicenseExpression +from src.model.snippet import Snippet + + +@pytest.fixture +@mock.patch('src.jsonschema.annotation_converter.AnnotationConverter', autospec=True) +def converter(annotation_converter_mock: MagicMock) -> SnippetConverter: + converter = SnippetConverter() + converter.annotation_converter = annotation_converter_mock() + return converter + + +@pytest.mark.parametrize("snippet_property,expected", + [(SnippetProperty.SPDX_ID, "SPDXID"), + (SnippetProperty.ANNOTATIONS, "annotations"), + (SnippetProperty.ATTRIBUTION_TEXTS, "attributionTexts"), + (SnippetProperty.COMMENT, "comment"), + (SnippetProperty.COPYRIGHT_TEXT, "copyrightText"), + (SnippetProperty.LICENSE_COMMENTS, "licenseComments"), + (SnippetProperty.LICENSE_CONCLUDED, "licenseConcluded"), + (SnippetProperty.LICENSE_INFO_IN_SNIPPETS, "licenseInfoInSnippets"), + (SnippetProperty.NAME, "name"), + (SnippetProperty.RANGES, "ranges"), + (SnippetProperty.SNIPPET_FROM_FILE, "snippetFromFile")]) +def test_json_property_names(converter: SnippetConverter, snippet_property: SnippetProperty, + expected: str): + assert converter.json_property_name(snippet_property) == expected + + +def test_json_type(converter: SnippetConverter): + assert converter.get_json_type() == SnippetProperty + + +def test_data_model_type(converter: SnippetConverter): + assert converter.get_data_model_type() == Snippet + + +def test_successful_conversion(converter: SnippetConverter): + converter.annotation_converter.convert.return_value = "mock_converted_annotation" + file_spdx_id = "fileSpdxId" + snippet = Snippet("spdxId", file_spdx_id=file_spdx_id, byte_range=(1, 2), line_range=(3, 4), + concluded_license=LicenseExpression("licenseExpression1"), + license_info_in_snippet=[LicenseExpression("licenseExpression2"), + LicenseExpression("licenseExpression3")], + license_comment="licenseComment", copyright_text="copyrightText", comment="comment", name="name", + attribution_texts=["attributionText1", "attributionText2"]) + + annotation = Annotation(snippet.spdx_id, AnnotationType.OTHER, Actor(ActorType.PERSON, "annotatorName"), + datetime(2022, 12, 5), "other comment") + creation_info = CreationInfo("spdxVersion", "documentId", "documentName", "documentNamespace", [], + datetime(2022, 12, 4)) + document = Document(creation_info, snippets=[snippet], annotations=[annotation]) + converted_dict = converter.convert(snippet, document) + + assert converted_dict[converter.json_property_name(SnippetProperty.SPDX_ID)] == "spdxId" + assert converted_dict[converter.json_property_name(SnippetProperty.ANNOTATIONS)] == ["mock_converted_annotation"] + assert converted_dict[converter.json_property_name(SnippetProperty.ATTRIBUTION_TEXTS)] == ["attributionText1", + "attributionText2"] + assert converted_dict[converter.json_property_name(SnippetProperty.COMMENT)] == "comment" + assert converted_dict[converter.json_property_name(SnippetProperty.COPYRIGHT_TEXT)] == "copyrightText" + assert converted_dict[converter.json_property_name(SnippetProperty.LICENSE_COMMENTS)] == "licenseComment" + assert converted_dict[converter.json_property_name(SnippetProperty.LICENSE_CONCLUDED)] == "licenseExpression1" + assert converted_dict[converter.json_property_name(SnippetProperty.LICENSE_INFO_IN_SNIPPETS)] == [ + "licenseExpression2", "licenseExpression3"] + assert converted_dict[converter.json_property_name(SnippetProperty.NAME)] == "name" + assert converted_dict[converter.json_property_name(SnippetProperty.RANGES)] == [ + {"startPointer": {"reference": file_spdx_id, "offset": 1}, + "endPointer": {"reference": file_spdx_id, "offset": 2}}, + {"startPointer": {"reference": file_spdx_id, "lineNumber": 3}, + "endPointer": {"reference": file_spdx_id, "lineNumber": 4}}] + assert converted_dict[converter.json_property_name(SnippetProperty.SNIPPET_FROM_FILE)] == file_spdx_id From cab138d9422fd4425c4e04885a82cdffb724bd60 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Tue, 20 Dec 2022 17:34:11 +0100 Subject: [PATCH 087/362] [issue-359] add document converter Signed-off-by: Nicolaus Weidner --- src/jsonschema/document_converter.py | 120 +++++++++++++++++++ src/jsonschema/document_properties.py | 15 +-- tests/jsonschema/test_document_converter.py | 125 ++++++++++++++++++++ 3 files changed, 253 insertions(+), 7 deletions(-) create mode 100644 src/jsonschema/document_converter.py create mode 100644 tests/jsonschema/test_document_converter.py diff --git a/src/jsonschema/document_converter.py b/src/jsonschema/document_converter.py new file mode 100644 index 000000000..3e0ad398f --- /dev/null +++ b/src/jsonschema/document_converter.py @@ -0,0 +1,120 @@ +# 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 typing import Type, Any + +from src.jsonschema.annotation_converter import AnnotationConverter +from src.jsonschema.converter import TypedConverter +from src.jsonschema.creation_info_converter import CreationInfoConverter +from src.jsonschema.document_properties import DocumentProperty +from src.jsonschema.external_document_ref_converter import ExternalDocumentRefConverter +from src.jsonschema.extracted_licensing_info_converter import ExtractedLicensingInfoConverter +from src.jsonschema.file_converter import FileConverter +from src.jsonschema.json_property import JsonProperty +from src.jsonschema.package_converter import PackageConverter +from src.jsonschema.relationship_converter import RelationshipConverter +from src.jsonschema.relationship_filters import filter_by_type_and_origin, filter_by_type_and_target, \ + find_package_contains_file_relationships, \ + find_file_contained_by_package_relationships +from src.jsonschema.snippet_converter import SnippetConverter +from src.model.document import Document +from src.model.relationship import RelationshipType +from src.writer.casing_tools import snake_case_to_camel_case + + +class DocumentConverter(TypedConverter): + creation_info_converter: CreationInfoConverter + external_document_ref_converter: ExternalDocumentRefConverter + package_converter: PackageConverter + file_converter: FileConverter + snippet_converter: SnippetConverter + annotation_converter: AnnotationConverter + relationship_converter: RelationshipConverter + extracted_licensing_info_converter: ExtractedLicensingInfoConverter + + def __init__(self): + self.external_document_ref_converter = ExternalDocumentRefConverter() + self.creation_info_converter = CreationInfoConverter() + self.package_converter = PackageConverter() + self.file_converter = FileConverter() + self.snippet_converter = SnippetConverter() + self.annotation_converter = AnnotationConverter() + self.relationship_converter = RelationshipConverter() + self.extracted_licensing_info_converter = ExtractedLicensingInfoConverter() + + def get_json_type(self) -> Type[JsonProperty]: + return DocumentProperty + + def get_data_model_type(self) -> Type[Document]: + return Document + + def json_property_name(self, document_property: DocumentProperty) -> str: + if document_property == DocumentProperty.SPDX_ID: + return "SPDXID" + return snake_case_to_camel_case(document_property.name) + + def _get_property_value(self, document: Document, document_property: DocumentProperty, + _document: Document = None) -> Any: + if document_property == DocumentProperty.SPDX_ID: + return document.creation_info.spdx_id + elif document_property == DocumentProperty.ANNOTATIONS: + # annotations referencing files, packages or snippets will be added to those elements directly + element_ids = [file.spdx_id for file in document.files] + element_ids.extend([package.spdx_id for package in document.packages]) + element_ids.extend([snippet.spdx_id for snippet in document.snippets]) + document_annotations = filter(lambda annotation: annotation.spdx_id not in element_ids, + document.annotations) + return [self.annotation_converter.convert(annotation) for annotation in document_annotations] + elif document_property == DocumentProperty.COMMENT: + return document.creation_info.document_comment + elif document_property == DocumentProperty.CREATION_INFO: + return self.creation_info_converter.convert(document.creation_info) + elif document_property == DocumentProperty.DATA_LICENSE: + return document.creation_info.data_license + elif document_property == DocumentProperty.EXTERNAL_DOCUMENT_REFS: + return [self.external_document_ref_converter.convert(external_document_ref) for + external_document_ref in document.creation_info.external_document_refs] + elif document_property == DocumentProperty.HAS_EXTRACTED_LICENSING_INFO: + return [self.extracted_licensing_info_converter.convert(licensing_info) for licensing_info in + document.extracted_licensing_info] + elif document_property == DocumentProperty.NAME: + return document.creation_info.name + elif document_property == DocumentProperty.SPDX_VERSION: + return document.creation_info.spdx_version + elif document_property == DocumentProperty.DOCUMENT_NAMESPACE: + return document.creation_info.document_namespace + elif document_property == DocumentProperty.DOCUMENT_DESCRIBES: + describes_ids = [relationship.related_spdx_element_id for relationship in + filter_by_type_and_origin(document.relationships, RelationshipType.DESCRIBES, + document.creation_info.spdx_id)] + described_by_ids = [relationship.spdx_element_id for relationship in + filter_by_type_and_target(document.relationships, RelationshipType.DESCRIBED_BY, + document.creation_info.spdx_id)] + return describes_ids + described_by_ids + elif document_property == DocumentProperty.PACKAGES: + return [self.package_converter.convert(package, document) for package in document.packages] + elif document_property == DocumentProperty.FILES: + return [self.file_converter.convert(file, document) for file in document.files] + elif document_property == DocumentProperty.SNIPPETS: + return [self.snippet_converter.convert(snippet, document) for snippet in document.snippets] + elif document_property == DocumentProperty.RELATIONSHIPS: + already_covered_relationships = filter_by_type_and_origin(document.relationships, + RelationshipType.DESCRIBES, + document.creation_info.spdx_id) + already_covered_relationships.extend( + filter_by_type_and_target(document.relationships, RelationshipType.DESCRIBED_BY, + document.creation_info.spdx_id)) + for package in document.packages: + already_covered_relationships.extend(find_package_contains_file_relationships(document, package)) + already_covered_relationships.extend(find_file_contained_by_package_relationships(document, package)) + relationships_to_ignore = [relationship for relationship in already_covered_relationships if + relationship.comment is None] + return [self.relationship_converter.convert(relationship) for relationship in document.relationships if + relationship not in relationships_to_ignore] diff --git a/src/jsonschema/document_properties.py b/src/jsonschema/document_properties.py index 444a966bd..e684fe80f 100644 --- a/src/jsonschema/document_properties.py +++ b/src/jsonschema/document_properties.py @@ -14,17 +14,18 @@ class DocumentProperty(JsonProperty): - SPDX_VERSION = auto() SPDX_ID = auto() - NAME = auto() - DOCUMENT_NAMESPACE = auto() - DATA_LICENSE = auto() - EXTERNAL_DOCUMENT_REFS = auto() + ANNOTATIONS = auto() COMMENT = auto() CREATION_INFO = auto() + DATA_LICENSE = auto() + EXTERNAL_DOCUMENT_REFS = auto() + HAS_EXTRACTED_LICENSING_INFO = auto() + NAME = auto() + SPDX_VERSION = auto() + DOCUMENT_NAMESPACE = auto() + DOCUMENT_DESCRIBES = auto() PACKAGES = auto() FILES = auto() SNIPPETS = auto() - ANNOTATIONS = auto() RELATIONSHIPS = auto() - HAS_EXTRACTED_LICENSING_INFO = auto() diff --git a/tests/jsonschema/test_document_converter.py b/tests/jsonschema/test_document_converter.py new file mode 100644 index 000000000..73088366e --- /dev/null +++ b/tests/jsonschema/test_document_converter.py @@ -0,0 +1,125 @@ +# 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 datetime import datetime +from unittest import mock +from unittest.mock import MagicMock + +import pytest + +from src.jsonschema.document_converter import DocumentConverter +from src.jsonschema.document_properties import DocumentProperty +from src.model.actor import Actor, ActorType +from src.model.annotation import Annotation, AnnotationType +from src.model.checksum import Checksum, ChecksumAlgorithm +from src.model.document import Document, CreationInfo +from src.model.external_document_ref import ExternalDocumentRef +from src.model.extracted_licensing_info import ExtractedLicensingInfo +from src.model.file import File +from src.model.package import Package +from src.model.relationship import Relationship, RelationshipType +from src.model.snippet import Snippet +from src.model.spdx_none import SpdxNone + + +@pytest.fixture +@mock.patch('src.jsonschema.creation_info_converter.CreationInfoConverter', autospec=True) +@mock.patch('src.jsonschema.external_document_ref_converter.ExternalDocumentRefConverter', autospec=True) +@mock.patch('src.jsonschema.package_converter.PackageConverter', autospec=True) +@mock.patch('src.jsonschema.annotation_converter.AnnotationConverter', autospec=True) +@mock.patch('src.jsonschema.extracted_licensing_info_converter.ExtractedLicensingInfoConverter', autospec=True) +@mock.patch('src.jsonschema.file_converter.FileConverter', autospec=True) +@mock.patch('src.jsonschema.snippet_converter.SnippetConverter', autospec=True) +@mock.patch('src.jsonschema.relationship_converter.RelationshipConverter', autospec=True) +def converter(external_ref_converter_mock: MagicMock, creation_info_converter_mock: MagicMock, + package_converter_mock: MagicMock, annotation_converter_mock: MagicMock, + extracted_licensing_info_converter_mock: MagicMock, file_converter_mock: MagicMock, + snippet_converter_mock: MagicMock, relationship_converter_mock: MagicMock) -> DocumentConverter: + converter = DocumentConverter() + converter.creation_info_converter = creation_info_converter_mock() + converter.external_document_ref_converter = external_ref_converter_mock() + converter.package_converter = package_converter_mock() + converter.annotation_converter = annotation_converter_mock() + converter.extracted_licensing_info_converter = extracted_licensing_info_converter_mock() + converter.file_converter = file_converter_mock() + converter.snippet_converter = snippet_converter_mock() + converter.relationship_converter = relationship_converter_mock() + return converter + + +@pytest.mark.parametrize("document_property,expected", + [(DocumentProperty.SPDX_VERSION, "spdxVersion"), (DocumentProperty.SPDX_ID, "SPDXID"), + (DocumentProperty.NAME, "name"), (DocumentProperty.DOCUMENT_NAMESPACE, "documentNamespace"), + (DocumentProperty.DATA_LICENSE, "dataLicense"), + (DocumentProperty.EXTERNAL_DOCUMENT_REFS, "externalDocumentRefs"), + (DocumentProperty.COMMENT, "comment"), (DocumentProperty.CREATION_INFO, "creationInfo"), + (DocumentProperty.PACKAGES, "packages"), (DocumentProperty.FILES, "files"), + (DocumentProperty.SNIPPETS, "snippets"), (DocumentProperty.ANNOTATIONS, "annotations"), + (DocumentProperty.RELATIONSHIPS, "relationships"), + (DocumentProperty.HAS_EXTRACTED_LICENSING_INFO, "hasExtractedLicensingInfo")]) +def test_json_property_names(converter: DocumentConverter, document_property: DocumentProperty, + expected: str): + assert converter.json_property_name(document_property) == expected + + +def test_successful_conversion(converter: DocumentConverter): + creation_info = CreationInfo("spdxVersion", "spdxId", "name", "namespace", [], datetime.today(), + document_comment="comment", data_license="dataLicense", external_document_refs=[ + ExternalDocumentRef("docRefId", "externalDocumentUri", Checksum(ChecksumAlgorithm.SHA1, "sha1"))]) + package = Package("packageID", "packageName", SpdxNone()) + file = File("fileName", "fileId", []) + snippet = Snippet("snippetId", "snippetFileId", (1, 2)) + document = Document(creation_info, annotations=[ + Annotation("annotationId", AnnotationType.REVIEW, Actor(ActorType.PERSON, "reviewerName"), + datetime(2022, 12, 1), "reviewComment")], + extracted_licensing_info=[ExtractedLicensingInfo("licenseId", "licenseText")], relationships=[ + Relationship(creation_info.spdx_id, RelationshipType.DESCRIBES, "describedElementId"), + Relationship("relationshipOriginId", RelationshipType.AMENDS, "relationShipTargetId")], packages=[package], + files=[file], snippets=[snippet]) + converter.external_document_ref_converter.convert.return_value = "mock_converted_external_ref" + converter.creation_info_converter.convert.return_value = "mock_converted_creation_info" + converter.package_converter.convert.return_value = "mock_converted_package" + converter.annotation_converter.convert.return_value = "mock_converted_annotation" + converter.extracted_licensing_info_converter.convert.return_value = "mock_converted_extracted_licensing_info" + converter.package_converter.convert.return_value = "mock_converted_package" + converter.file_converter.convert.return_value = "mock_converted_file" + converter.snippet_converter.convert.return_value = "mock_converted_snippet" + converter.relationship_converter.convert.return_value = "mock_converted_relationship" + + converted_dict = converter.convert(document) + + assert converted_dict[converter.json_property_name(DocumentProperty.SPDX_ID)] == "spdxId" + assert converted_dict[converter.json_property_name(DocumentProperty.ANNOTATIONS)] == ["mock_converted_annotation"] + assert converted_dict[converter.json_property_name(DocumentProperty.COMMENT)] == "comment" + assert converted_dict[ + converter.json_property_name(DocumentProperty.CREATION_INFO)] == "mock_converted_creation_info" + assert converted_dict[converter.json_property_name(DocumentProperty.DATA_LICENSE)] == "dataLicense" + assert converted_dict[ + converter.json_property_name( + DocumentProperty.EXTERNAL_DOCUMENT_REFS)] == ["mock_converted_external_ref"] + assert converted_dict[converter.json_property_name(DocumentProperty.HAS_EXTRACTED_LICENSING_INFO)] == [ + "mock_converted_extracted_licensing_info"] + assert converted_dict[converter.json_property_name(DocumentProperty.NAME)] == "name" + assert converted_dict[converter.json_property_name(DocumentProperty.SPDX_VERSION)] == "spdxVersion" + assert converted_dict[converter.json_property_name(DocumentProperty.DOCUMENT_NAMESPACE)] == "namespace" + assert converted_dict[converter.json_property_name(DocumentProperty.DOCUMENT_DESCRIBES)] == ["describedElementId"] + assert converted_dict[converter.json_property_name(DocumentProperty.PACKAGES)] == ["mock_converted_package"] + assert converted_dict[converter.json_property_name(DocumentProperty.FILES)] == ["mock_converted_file"] + assert converted_dict[converter.json_property_name(DocumentProperty.SNIPPETS)] == ["mock_converted_snippet"] + assert converted_dict[converter.json_property_name(DocumentProperty.RELATIONSHIPS)] == [ + "mock_converted_relationship"] + + +def test_json_type(converter: DocumentConverter): + assert converter.get_json_type() == DocumentProperty + + +def test_data_model_type(converter: DocumentConverter): + assert converter.get_data_model_type() == Document From 5964055042526c9b00a6913136868874d3058a82 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Tue, 20 Dec 2022 23:33:03 +0100 Subject: [PATCH 088/362] [issue-359] json converters no longer set dictionary properties if the value is None Signed-off-by: Nicolaus Weidner --- src/jsonschema/converter.py | 5 +++- src/jsonschema/creation_info_converter.py | 3 +- src/jsonschema/file_converter.py | 7 +++-- src/jsonschema/optional_utils.py | 18 ++++++++++++ src/jsonschema/package_converter.py | 25 ++++++++-------- src/jsonschema/snippet_converter.py | 7 +++-- .../test_creation_info_converter.py | 9 ++++++ tests/jsonschema/test_file_converter.py | 18 ++++++++++++ tests/jsonschema/test_package_converter.py | 29 +++++++++++++++++++ tests/jsonschema/test_snippet_converter.py | 15 ++++++++++ 10 files changed, 116 insertions(+), 20 deletions(-) create mode 100644 src/jsonschema/optional_utils.py diff --git a/src/jsonschema/converter.py b/src/jsonschema/converter.py index a4e03f3c3..edecf3be5 100644 --- a/src/jsonschema/converter.py +++ b/src/jsonschema/converter.py @@ -47,5 +47,8 @@ def convert(self, instance: Any, document: Document = None) -> Dict: result = {} for property_name in self.get_json_type(): - result[self.json_property_name(property_name)] = self._get_property_value(instance, property_name, document) + property_value = self._get_property_value(instance, property_name, document) + if property_value is None: + continue + result[self.json_property_name(property_name)] = property_value return result diff --git a/src/jsonschema/creation_info_converter.py b/src/jsonschema/creation_info_converter.py index 656126203..36dec0215 100644 --- a/src/jsonschema/creation_info_converter.py +++ b/src/jsonschema/creation_info_converter.py @@ -14,6 +14,7 @@ from src.jsonschema.converter import TypedConverter from src.jsonschema.creation_info_properties import CreationInfoProperty from src.jsonschema.json_property import JsonProperty +from src.jsonschema.optional_utils import apply_if_present from src.model.document import CreationInfo, Document from src.writer.casing_tools import snake_case_to_camel_case @@ -35,6 +36,6 @@ def _get_property_value(self, creation_info: CreationInfo, creation_info_propert elif creation_info_property == CreationInfoProperty.CREATORS: return [creator.to_serialized_string() for creator in creation_info.creators] elif creation_info_property == CreationInfoProperty.LICENSE_LIST_VERSION: - return str(creation_info.license_list_version) + return apply_if_present(str, creation_info.license_list_version) elif creation_info_property == CreationInfoProperty.COMMENT: return creation_info.creator_comment diff --git a/src/jsonschema/file_converter.py b/src/jsonschema/file_converter.py index 19d96b180..e2309ee8b 100644 --- a/src/jsonschema/file_converter.py +++ b/src/jsonschema/file_converter.py @@ -15,6 +15,7 @@ from src.jsonschema.converter import TypedConverter from src.jsonschema.file_properties import FileProperty from src.jsonschema.json_property import JsonProperty +from src.jsonschema.optional_utils import apply_if_present from src.model.document import Document from src.model.file import File from src.writer.casing_tools import snake_case_to_camel_case @@ -49,7 +50,7 @@ def _get_property_value(self, file: Any, file_property: FileProperty, document: elif file_property == FileProperty.COMMENT: return file.comment elif file_property == FileProperty.COPYRIGHT_TEXT: - return file.copyright_text + return apply_if_present(str, file.copyright_text) elif file_property == FileProperty.FILE_CONTRIBUTORS: return file.contributors elif file_property == FileProperty.FILE_DEPENDENCIES: @@ -62,11 +63,11 @@ def _get_property_value(self, file: Any, file_property: FileProperty, document: elif file_property == FileProperty.LICENSE_COMMENTS: return file.license_comment elif file_property == FileProperty.LICENSE_CONCLUDED: - return str(file.concluded_license) + return apply_if_present(str, file.concluded_license) elif file_property == FileProperty.LICENSE_INFO_IN_FILES: if isinstance(file.license_info_in_file, list): return [str(license_expression) for license_expression in file.license_info_in_file] - return str(file.license_info_in_file) + return apply_if_present(str, file.license_info_in_file) elif file_property == FileProperty.NOTICE_TEXT: return file.notice diff --git a/src/jsonschema/optional_utils.py b/src/jsonschema/optional_utils.py new file mode 100644 index 000000000..d30fd7607 --- /dev/null +++ b/src/jsonschema/optional_utils.py @@ -0,0 +1,18 @@ +# 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 typing import Callable, TypeVar, Optional + +T = TypeVar("T") +S = TypeVar("S") + + +def apply_if_present(function: Callable[[T], S], optional_value: Optional[T]) -> Optional[S]: + return function(optional_value) if optional_value else None diff --git a/src/jsonschema/package_converter.py b/src/jsonschema/package_converter.py index ff140251d..3e016330b 100644 --- a/src/jsonschema/package_converter.py +++ b/src/jsonschema/package_converter.py @@ -16,6 +16,7 @@ from src.jsonschema.converter import TypedConverter from src.jsonschema.external_package_ref_converter import ExternalPackageRefConverter from src.jsonschema.json_property import JsonProperty +from src.jsonschema.optional_utils import apply_if_present from src.jsonschema.package_properties import PackageProperty from src.jsonschema.package_verification_code_converter import PackageVerificationCodeConverter from src.jsonschema.relationship_filters import find_package_contains_file_relationships, \ @@ -53,13 +54,13 @@ def _get_property_value(self, package: Package, package_property: PackagePropert elif package_property == PackageProperty.ATTRIBUTION_TEXTS: return package.attribution_texts elif package_property == PackageProperty.BUILT_DATE: - return datetime_to_iso_string(package.built_date) + return apply_if_present(datetime_to_iso_string, package.built_date) elif package_property == PackageProperty.CHECKSUMS: return [self.checksum_converter.convert(checksum, document) for checksum in package.checksums] elif package_property == PackageProperty.COMMENT: return package.comment elif package_property == PackageProperty.COPYRIGHT_TEXT: - return str(package.copyright_text) + return apply_if_present(str, package.copyright_text) elif package_property == PackageProperty.DESCRIPTION: return package.description elif package_property == PackageProperty.DOWNLOAD_LOCATION: @@ -76,31 +77,31 @@ def _get_property_value(self, package: Package, package_property: PackagePropert find_file_contained_by_package_relationships(document, package)] return package_contains_file_ids + file_contained_in_package_ids elif package_property == PackageProperty.HOMEPAGE: - return str(package.homepage) + return apply_if_present(str, package.homepage) elif package_property == PackageProperty.LICENSE_COMMENTS: return package.license_comment elif package_property == PackageProperty.LICENSE_CONCLUDED: - return str(package.license_concluded) + return apply_if_present(str, package.license_concluded) elif package_property == PackageProperty.LICENSE_DECLARED: - return str(package.license_declared) + return apply_if_present(str, package.license_declared) elif package_property == PackageProperty.LICENSE_INFO_FROM_FILES: if isinstance(package.license_info_from_files, list): return [str(license_expression) for license_expression in package.license_info_from_files] - return str(package.license_info_from_files) + return apply_if_present(str, package.license_info_from_files) elif package_property == PackageProperty.NAME: return package.name elif package_property == PackageProperty.ORIGINATOR: if isinstance(package.originator, Actor): return package.originator.to_serialized_string() - return str(package.originator) + return apply_if_present(str, package.originator) elif package_property == PackageProperty.PACKAGE_FILE_NAME: return package.file_name elif package_property == PackageProperty.PACKAGE_VERIFICATION_CODE: - return self.package_verification_code_converter.convert(package.verification_code) + return apply_if_present(self.package_verification_code_converter.convert, package.verification_code) elif package_property == PackageProperty.PRIMARY_PACKAGE_PURPOSE: - return package.primary_package_purpose.name + return package.primary_package_purpose.name if package.primary_package_purpose is not None else None elif package_property == PackageProperty.RELEASE_DATE: - return datetime_to_iso_string(package.release_date) + return apply_if_present(datetime_to_iso_string, package.release_date) elif package_property == PackageProperty.SOURCE_INFO: return package.source_info elif package_property == PackageProperty.SUMMARY: @@ -108,9 +109,9 @@ def _get_property_value(self, package: Package, package_property: PackagePropert elif package_property == PackageProperty.SUPPLIER: if isinstance(package.supplier, Actor): return package.supplier.to_serialized_string() - return str(package.supplier) + return apply_if_present(str, package.supplier) elif package_property == PackageProperty.VALID_UNTIL_DATE: - return datetime_to_iso_string(package.valid_until_date) + return apply_if_present(datetime_to_iso_string, package.valid_until_date) elif package_property == PackageProperty.VERSION_INFO: return package.version diff --git a/src/jsonschema/snippet_converter.py b/src/jsonschema/snippet_converter.py index b0581e2c6..d726d63b9 100644 --- a/src/jsonschema/snippet_converter.py +++ b/src/jsonschema/snippet_converter.py @@ -13,6 +13,7 @@ from src.jsonschema.annotation_converter import AnnotationConverter from src.jsonschema.converter import TypedConverter from src.jsonschema.json_property import JsonProperty +from src.jsonschema.optional_utils import apply_if_present from src.jsonschema.snippet_properties import SnippetProperty from src.model.document import Document from src.model.snippet import Snippet @@ -42,15 +43,15 @@ def _get_property_value(self, snippet: Snippet, snippet_property: SnippetPropert elif snippet_property == SnippetProperty.COMMENT: return snippet.comment elif snippet_property == SnippetProperty.COPYRIGHT_TEXT: - return snippet.copyright_text + return apply_if_present(str, snippet.copyright_text) elif snippet_property == SnippetProperty.LICENSE_COMMENTS: return snippet.license_comment elif snippet_property == SnippetProperty.LICENSE_CONCLUDED: - return str(snippet.concluded_license) + return apply_if_present(str, snippet.concluded_license) elif snippet_property == SnippetProperty.LICENSE_INFO_IN_SNIPPETS: if isinstance(snippet.license_info_in_snippet, list): return [str(license_expression) for license_expression in snippet.license_info_in_snippet] - return str(snippet.license_info_in_snippet) + return apply_if_present(str, snippet.license_info_in_snippet) elif snippet_property == SnippetProperty.NAME: return snippet.name elif snippet_property == SnippetProperty.RANGES: diff --git a/tests/jsonschema/test_creation_info_converter.py b/tests/jsonschema/test_creation_info_converter.py index 8b1cb4b2d..f27081dca 100644 --- a/tests/jsonschema/test_creation_info_converter.py +++ b/tests/jsonschema/test_creation_info_converter.py @@ -49,6 +49,15 @@ def test_successful_conversion(converter: CreationInfoConverter): assert converted_dict[converter.json_property_name(CreationInfoProperty.COMMENT)] == "comment" +def test_null_values(converter: CreationInfoConverter): + creation_info = CreationInfo("irrelevant", "irrelevant", "irrelevant", "irrelevant", [], datetime(2022, 12, 1)) + + converted_dict = converter.convert(creation_info) + + assert converter.json_property_name(CreationInfoProperty.LICENSE_LIST_VERSION) not in converted_dict + assert converter.json_property_name(CreationInfoProperty.COMMENT) not in converted_dict + + def test_json_type(converter: CreationInfoConverter): assert converter.get_json_type() == CreationInfoProperty diff --git a/tests/jsonschema/test_file_converter.py b/tests/jsonschema/test_file_converter.py index a7846b756..1acd74918 100644 --- a/tests/jsonschema/test_file_converter.py +++ b/tests/jsonschema/test_file_converter.py @@ -100,3 +100,21 @@ def test_successful_conversion(converter: FileConverter): assert converted_dict[converter.json_property_name(FileProperty.LICENSE_INFO_IN_FILES)] == ["licenseExpression2", "licenseExpression3"] assert converted_dict[converter.json_property_name(FileProperty.NOTICE_TEXT)] == "notice" + + +def test_null_values(converter: FileConverter): + file = File(name="name", spdx_id="spdxId", + checksums=[Checksum(ChecksumAlgorithm.SHA224, "sha224"), Checksum(ChecksumAlgorithm.MD2, "md2")]) + + creation_info = CreationInfo("spdxVersion", "documentID", "documentName", "documentNamespace", [], + datetime(2022, 12, 4)) + + document = Document(creation_info, files=[file]) + + converted_dict = converter.convert(file, document) + + assert converter.json_property_name(FileProperty.COPYRIGHT_TEXT) not in converted_dict + assert converter.json_property_name(FileProperty.LICENSE_CONCLUDED) not in converted_dict + assert converter.json_property_name(FileProperty.LICENSE_COMMENTS) not in converted_dict + assert converter.json_property_name(FileProperty.COMMENT) not in converted_dict + assert converter.json_property_name(FileProperty.NOTICE_TEXT) not in converted_dict diff --git a/tests/jsonschema/test_package_converter.py b/tests/jsonschema/test_package_converter.py index a1aeb3abe..324b64ccb 100644 --- a/tests/jsonschema/test_package_converter.py +++ b/tests/jsonschema/test_package_converter.py @@ -146,3 +146,32 @@ def test_successful_conversion(converter: PackageConverter): assert converted_dict[converter.json_property_name(PackageProperty.RELEASE_DATE)] == "2022-12-01T00:00:00Z" assert converted_dict[converter.json_property_name(PackageProperty.BUILT_DATE)] == "2022-12-02T00:00:00Z" assert converted_dict[converter.json_property_name(PackageProperty.VALID_UNTIL_DATE)] == "2022-12-03T00:00:00Z" + + +def test_null_values(converter: PackageConverter): + package = Package(spdx_id="packageId", name="name", download_location="downloadLocation") + + creation_info = CreationInfo("spdxVersion", "documentID", "documentName", "documentNamespace", [], + datetime(2022, 12, 4)) + document = Document(creation_info, packages=[package]) + + converted_dict = converter.convert(package, document) + + assert converter.json_property_name(PackageProperty.VERSION_INFO) not in converted_dict + assert converter.json_property_name(PackageProperty.PACKAGE_FILE_NAME) not in converted_dict + assert converter.json_property_name(PackageProperty.SUPPLIER) not in converted_dict + assert converter.json_property_name(PackageProperty.ORIGINATOR) not in converted_dict + assert converter.json_property_name(PackageProperty.PACKAGE_VERIFICATION_CODE) not in converted_dict + assert converter.json_property_name(PackageProperty.HOMEPAGE) not in converted_dict + assert converter.json_property_name(PackageProperty.SOURCE_INFO) not in converted_dict + assert converter.json_property_name(PackageProperty.LICENSE_CONCLUDED) not in converted_dict + assert converter.json_property_name(PackageProperty.LICENSE_DECLARED) not in converted_dict + assert converter.json_property_name(PackageProperty.LICENSE_COMMENTS) not in converted_dict + assert converter.json_property_name(PackageProperty.COPYRIGHT_TEXT) not in converted_dict + assert converter.json_property_name(PackageProperty.SUMMARY) not in converted_dict + assert converter.json_property_name(PackageProperty.DESCRIPTION) not in converted_dict + assert converter.json_property_name(PackageProperty.COMMENT) not in converted_dict + assert converter.json_property_name(PackageProperty.PRIMARY_PACKAGE_PURPOSE) not in converted_dict + assert converter.json_property_name(PackageProperty.BUILT_DATE) not in converted_dict + assert converter.json_property_name(PackageProperty.RELEASE_DATE) not in converted_dict + assert converter.json_property_name(PackageProperty.VALID_UNTIL_DATE) not in converted_dict diff --git a/tests/jsonschema/test_snippet_converter.py b/tests/jsonschema/test_snippet_converter.py index 693b008ac..6008d63b0 100644 --- a/tests/jsonschema/test_snippet_converter.py +++ b/tests/jsonschema/test_snippet_converter.py @@ -90,3 +90,18 @@ def test_successful_conversion(converter: SnippetConverter): {"startPointer": {"reference": file_spdx_id, "lineNumber": 3}, "endPointer": {"reference": file_spdx_id, "lineNumber": 4}}] assert converted_dict[converter.json_property_name(SnippetProperty.SNIPPET_FROM_FILE)] == file_spdx_id + + +def test_null_values(converter: SnippetConverter): + snippet = Snippet("spdxId", file_spdx_id="fileId", byte_range=(1, 2)) + + creation_info = CreationInfo("spdxVersion", "documentId", "documentName", "documentNamespace", [], + datetime(2022, 12, 4)) + document = Document(creation_info, snippets=[snippet]) + converted_dict = converter.convert(snippet, document) + + assert converter.json_property_name(SnippetProperty.LICENSE_CONCLUDED) not in converted_dict + assert converter.json_property_name(SnippetProperty.LICENSE_COMMENTS) not in converted_dict + assert converter.json_property_name(SnippetProperty.COPYRIGHT_TEXT) not in converted_dict + assert converter.json_property_name(SnippetProperty.COMMENT) not in converted_dict + assert converter.json_property_name(SnippetProperty.NAME) not in converted_dict From 5239bb978ef48610923262214ddcbd31464ad19f Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Wed, 21 Dec 2022 09:26:19 +0100 Subject: [PATCH 089/362] [issue-359] Improve converter typing and some parameter names Signed-off-by: Nicolaus Weidner --- src/jsonschema/annotation_converter.py | 2 +- src/jsonschema/checksum_converter.py | 2 +- src/jsonschema/converter.py | 14 ++++++++------ src/jsonschema/creation_info_converter.py | 2 +- src/jsonschema/document_converter.py | 2 +- src/jsonschema/external_document_ref_converter.py | 15 ++++++++------- src/jsonschema/external_package_ref_converter.py | 2 +- .../extracted_licensing_info_converter.py | 2 +- src/jsonschema/file_converter.py | 2 +- src/jsonschema/package_converter.py | 2 +- .../package_verification_code_converter.py | 2 +- src/jsonschema/relationship_converter.py | 2 +- src/jsonschema/snippet_converter.py | 2 +- 13 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/jsonschema/annotation_converter.py b/src/jsonschema/annotation_converter.py index a8f30d5a1..503da69e9 100644 --- a/src/jsonschema/annotation_converter.py +++ b/src/jsonschema/annotation_converter.py @@ -19,7 +19,7 @@ from src.writer.casing_tools import snake_case_to_camel_case -class AnnotationConverter(TypedConverter): +class AnnotationConverter(TypedConverter[Annotation]): def json_property_name(self, annotation_property: AnnotationProperty) -> str: return snake_case_to_camel_case(annotation_property.name) diff --git a/src/jsonschema/checksum_converter.py b/src/jsonschema/checksum_converter.py index 695f4df7b..d68710234 100644 --- a/src/jsonschema/checksum_converter.py +++ b/src/jsonschema/checksum_converter.py @@ -18,7 +18,7 @@ from src.writer.casing_tools import snake_case_to_camel_case -class ChecksumConverter(TypedConverter): +class ChecksumConverter(TypedConverter[Checksum]): def get_data_model_type(self) -> Type[Checksum]: return Checksum diff --git a/src/jsonschema/converter.py b/src/jsonschema/converter.py index edecf3be5..1d6d348fa 100644 --- a/src/jsonschema/converter.py +++ b/src/jsonschema/converter.py @@ -9,21 +9,23 @@ # See the License for the specific language governing permissions and # limitations under the License. from abc import ABC, abstractmethod -from typing import Any, Type, Dict +from typing import Any, Type, Dict, TypeVar, Generic from src.jsonschema.json_property import JsonProperty from src.model.document import Document MISSING_IMPLEMENTATION_MESSAGE = "Must be implemented" +T = TypeVar("T") -class TypedConverter(ABC): + +class TypedConverter(ABC, Generic[T]): @abstractmethod - def json_property_name(self, property_thing: JsonProperty) -> str: + def json_property_name(self, json_property: JsonProperty) -> str: raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE) @abstractmethod - def _get_property_value(self, instance: Any, property_thing: JsonProperty, document: Document = None) -> Any: + def _get_property_value(self, instance: T, json_property: JsonProperty, document: Document = None) -> Any: raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE) @abstractmethod @@ -31,13 +33,13 @@ def get_json_type(self) -> Type[JsonProperty]: raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE) @abstractmethod - def get_data_model_type(self) -> Type: + def get_data_model_type(self) -> Type[T]: raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE) def requires_full_document(self) -> bool: return False - def convert(self, instance: Any, document: Document = None) -> Dict: + def convert(self, instance: T, document: Document = None) -> Dict: if not isinstance(instance, self.get_data_model_type()): raise TypeError( f"Converter of type {self.__class__} can only convert objects of type " diff --git a/src/jsonschema/creation_info_converter.py b/src/jsonschema/creation_info_converter.py index 36dec0215..04734ea8c 100644 --- a/src/jsonschema/creation_info_converter.py +++ b/src/jsonschema/creation_info_converter.py @@ -19,7 +19,7 @@ from src.writer.casing_tools import snake_case_to_camel_case -class CreationInfoConverter(TypedConverter): +class CreationInfoConverter(TypedConverter[CreationInfo]): def get_data_model_type(self) -> Type[CreationInfo]: return CreationInfo diff --git a/src/jsonschema/document_converter.py b/src/jsonschema/document_converter.py index 3e0ad398f..98f5b784a 100644 --- a/src/jsonschema/document_converter.py +++ b/src/jsonschema/document_converter.py @@ -29,7 +29,7 @@ from src.writer.casing_tools import snake_case_to_camel_case -class DocumentConverter(TypedConverter): +class DocumentConverter(TypedConverter[Document]): creation_info_converter: CreationInfoConverter external_document_ref_converter: ExternalDocumentRefConverter package_converter: PackageConverter diff --git a/src/jsonschema/external_document_ref_converter.py b/src/jsonschema/external_document_ref_converter.py index ccb5e0be3..6ac16d56e 100644 --- a/src/jsonschema/external_document_ref_converter.py +++ b/src/jsonschema/external_document_ref_converter.py @@ -19,22 +19,23 @@ from src.writer.casing_tools import snake_case_to_camel_case -class ExternalDocumentRefConverter(TypedConverter): +class ExternalDocumentRefConverter(TypedConverter[ExternalDocumentRef]): checksum_converter: ChecksumConverter def __init__(self): self.checksum_converter = ChecksumConverter() - def json_property_name(self, property_thing: ExternalDocumentRefProperty) -> str: - return snake_case_to_camel_case(property_thing.name) + def json_property_name(self, external_document_ref_property: ExternalDocumentRefProperty) -> str: + return snake_case_to_camel_case(external_document_ref_property.name) def _get_property_value(self, external_document_ref: ExternalDocumentRef, - property_thing: ExternalDocumentRefProperty, _document: Document = None) -> Any: - if property_thing == ExternalDocumentRefProperty.EXTERNAL_DOCUMENT_ID: + external_document_ref_property: ExternalDocumentRefProperty, + _document: Document = None) -> Any: + if external_document_ref_property == ExternalDocumentRefProperty.EXTERNAL_DOCUMENT_ID: return external_document_ref.document_ref_id - elif property_thing == ExternalDocumentRefProperty.SPDX_DOCUMENT: + elif external_document_ref_property == ExternalDocumentRefProperty.SPDX_DOCUMENT: return external_document_ref.document_uri - elif property_thing == ExternalDocumentRefProperty.CHECKSUM: + elif external_document_ref_property == ExternalDocumentRefProperty.CHECKSUM: return self.checksum_converter.convert(external_document_ref.checksum) def get_json_type(self) -> Type[JsonProperty]: diff --git a/src/jsonschema/external_package_ref_converter.py b/src/jsonschema/external_package_ref_converter.py index 531861105..55e1ccabf 100644 --- a/src/jsonschema/external_package_ref_converter.py +++ b/src/jsonschema/external_package_ref_converter.py @@ -18,7 +18,7 @@ from src.writer.casing_tools import snake_case_to_camel_case -class ExternalPackageRefConverter(TypedConverter): +class ExternalPackageRefConverter(TypedConverter[ExternalPackageRef]): def json_property_name(self, external_ref_property: ExternalPackageRefProperty) -> str: return snake_case_to_camel_case(external_ref_property.name) diff --git a/src/jsonschema/extracted_licensing_info_converter.py b/src/jsonschema/extracted_licensing_info_converter.py index f15629c5a..25643ec3d 100644 --- a/src/jsonschema/extracted_licensing_info_converter.py +++ b/src/jsonschema/extracted_licensing_info_converter.py @@ -18,7 +18,7 @@ from src.writer.casing_tools import snake_case_to_camel_case -class ExtractedLicensingInfoConverter(TypedConverter): +class ExtractedLicensingInfoConverter(TypedConverter[ExtractedLicensingInfo]): def json_property_name(self, extracted_licensing_info_property: ExtractedLicensingInfoProperty) -> str: return snake_case_to_camel_case(extracted_licensing_info_property.name) diff --git a/src/jsonschema/file_converter.py b/src/jsonschema/file_converter.py index e2309ee8b..305239637 100644 --- a/src/jsonschema/file_converter.py +++ b/src/jsonschema/file_converter.py @@ -21,7 +21,7 @@ from src.writer.casing_tools import snake_case_to_camel_case -class FileConverter(TypedConverter): +class FileConverter(TypedConverter[File]): annotation_converter: AnnotationConverter checksum_converter: ChecksumConverter diff --git a/src/jsonschema/package_converter.py b/src/jsonschema/package_converter.py index 3e016330b..3253ec801 100644 --- a/src/jsonschema/package_converter.py +++ b/src/jsonschema/package_converter.py @@ -27,7 +27,7 @@ from src.writer.casing_tools import snake_case_to_camel_case -class PackageConverter(TypedConverter): +class PackageConverter(TypedConverter[Package]): annotation_converter: AnnotationConverter checksum_converter: ChecksumConverter external_package_ref_converter: ExternalPackageRefConverter diff --git a/src/jsonschema/package_verification_code_converter.py b/src/jsonschema/package_verification_code_converter.py index 61a5b7400..f8bdc68df 100644 --- a/src/jsonschema/package_verification_code_converter.py +++ b/src/jsonschema/package_verification_code_converter.py @@ -18,7 +18,7 @@ from src.writer.casing_tools import snake_case_to_camel_case -class PackageVerificationCodeConverter(TypedConverter): +class PackageVerificationCodeConverter(TypedConverter[PackageVerificationCode]): def json_property_name(self, verification_code_property: PackageVerificationCodeProperty) -> str: return snake_case_to_camel_case(verification_code_property.name) diff --git a/src/jsonschema/relationship_converter.py b/src/jsonschema/relationship_converter.py index 1860b6a06..ea2806765 100644 --- a/src/jsonschema/relationship_converter.py +++ b/src/jsonschema/relationship_converter.py @@ -18,7 +18,7 @@ from src.writer.casing_tools import snake_case_to_camel_case -class RelationshipConverter(TypedConverter): +class RelationshipConverter(TypedConverter[Relationship]): def json_property_name(self, relationship_property: RelationshipProperty) -> str: return snake_case_to_camel_case(relationship_property.name) diff --git a/src/jsonschema/snippet_converter.py b/src/jsonschema/snippet_converter.py index d726d63b9..f907072fa 100644 --- a/src/jsonschema/snippet_converter.py +++ b/src/jsonschema/snippet_converter.py @@ -20,7 +20,7 @@ from src.writer.casing_tools import snake_case_to_camel_case -class SnippetConverter(TypedConverter): +class SnippetConverter(TypedConverter[Snippet]): annotation_converter: AnnotationConverter def __init__(self): From e8bab6c4dccda60c1a5de0591130db76d63a4cdd Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Wed, 21 Dec 2022 11:07:50 +0100 Subject: [PATCH 090/362] [issue-359] Extract creation info fixture for reuse in tests Signed-off-by: Nicolaus Weidner --- tests/fixtures.py | 28 +++++++++++++++++++ .../test_creation_info_converter.py | 9 +++--- tests/jsonschema/test_document_converter.py | 11 +++++--- tests/jsonschema/test_file_converter.py | 15 ++++------ tests/jsonschema/test_package_converter.py | 11 +++----- tests/jsonschema/test_snippet_converter.py | 11 +++----- 6 files changed, 54 insertions(+), 31 deletions(-) create mode 100644 tests/fixtures.py diff --git a/tests/fixtures.py b/tests/fixtures.py new file mode 100644 index 000000000..c00af0e1a --- /dev/null +++ b/tests/fixtures.py @@ -0,0 +1,28 @@ +# 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 datetime import datetime + +from src.model.actor import Actor, ActorType +from src.model.document import CreationInfo +from src.model.version import Version + +"""Utility methods to create data model instances. All properties have valid defaults, so they don't need to be +specified unless relevant for the test.""" + + +def creation_info_fixture(spdx_version="spdxVersion", spdx_id="documentId", name="documentName", + namespace="documentNamespace", creators=None, created=datetime(2022, 12, 1), + creator_comment="creatorComment", data_license="CC0-1.0", external_document_refs=None, + license_list_version=Version(3, 19), document_comment="documentComment") -> CreationInfo: + creators = [Actor(ActorType.PERSON, "creatorName")] if creators is None else creators + external_document_refs = [] if external_document_refs is None else external_document_refs + return CreationInfo(spdx_version, spdx_id, name, namespace, creators, created, creator_comment, data_license, + external_document_refs, license_list_version, document_comment) diff --git a/tests/jsonschema/test_creation_info_converter.py b/tests/jsonschema/test_creation_info_converter.py index f27081dca..0fb500ebc 100644 --- a/tests/jsonschema/test_creation_info_converter.py +++ b/tests/jsonschema/test_creation_info_converter.py @@ -18,6 +18,7 @@ from src.model.actor import Actor, ActorType from src.model.document import CreationInfo from src.model.version import Version +from tests.fixtures import creation_info_fixture @pytest.fixture @@ -37,10 +38,10 @@ def test_json_property_names(converter: CreationInfoConverter, creation_info_pro def test_successful_conversion(converter: CreationInfoConverter): creators = [Actor(ActorType.PERSON, "personName"), Actor(ActorType.TOOL, "toolName")] created = datetime(2022, 12, 1) - creation_info = CreationInfo("irrelevant", "irrelevant", "irrelevant", "irrelevant", creators=creators, - created=created, creator_comment="comment", license_list_version=Version(1, 2)) - converted_dict = converter.convert(creation_info) + converted_dict = converter.convert( + creation_info_fixture(creators=creators, created=created, creator_comment="comment", + license_list_version=Version(1, 2))) assert converted_dict[converter.json_property_name(CreationInfoProperty.CREATED)] == datetime_to_iso_string(created) assert converted_dict[converter.json_property_name(CreationInfoProperty.CREATORS)] == ["Person: personName", @@ -50,7 +51,7 @@ def test_successful_conversion(converter: CreationInfoConverter): def test_null_values(converter: CreationInfoConverter): - creation_info = CreationInfo("irrelevant", "irrelevant", "irrelevant", "irrelevant", [], datetime(2022, 12, 1)) + creation_info = creation_info_fixture(license_list_version=None, creator_comment=None) converted_dict = converter.convert(creation_info) diff --git a/tests/jsonschema/test_document_converter.py b/tests/jsonschema/test_document_converter.py index 73088366e..d531ebc18 100644 --- a/tests/jsonschema/test_document_converter.py +++ b/tests/jsonschema/test_document_converter.py @@ -19,7 +19,7 @@ from src.model.actor import Actor, ActorType from src.model.annotation import Annotation, AnnotationType from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.document import Document, CreationInfo +from src.model.document import Document from src.model.external_document_ref import ExternalDocumentRef from src.model.extracted_licensing_info import ExtractedLicensingInfo from src.model.file import File @@ -27,6 +27,7 @@ from src.model.relationship import Relationship, RelationshipType from src.model.snippet import Snippet from src.model.spdx_none import SpdxNone +from tests.fixtures import creation_info_fixture @pytest.fixture @@ -70,9 +71,11 @@ def test_json_property_names(converter: DocumentConverter, document_property: Do def test_successful_conversion(converter: DocumentConverter): - creation_info = CreationInfo("spdxVersion", "spdxId", "name", "namespace", [], datetime.today(), - document_comment="comment", data_license="dataLicense", external_document_refs=[ - ExternalDocumentRef("docRefId", "externalDocumentUri", Checksum(ChecksumAlgorithm.SHA1, "sha1"))]) + creation_info = creation_info_fixture(spdx_version="spdxVersion", spdx_id="spdxId", name="name", + namespace="namespace", document_comment="comment", data_license="dataLicense", + external_document_refs=[ExternalDocumentRef("docRefId", "externalDocumentUri", + Checksum(ChecksumAlgorithm.SHA1, + "sha1"))]) package = Package("packageID", "packageName", SpdxNone()) file = File("fileName", "fileId", []) snippet = Snippet("snippetId", "snippetFileId", (1, 2)) diff --git a/tests/jsonschema/test_file_converter.py b/tests/jsonschema/test_file_converter.py index 1acd74918..5e971a328 100644 --- a/tests/jsonschema/test_file_converter.py +++ b/tests/jsonschema/test_file_converter.py @@ -19,9 +19,12 @@ from src.model.actor import Actor, ActorType from src.model.annotation import Annotation, AnnotationType from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.document import CreationInfo, Document +from src.model.document import Document from src.model.file import File, FileType from src.model.license_expression import LicenseExpression +from src.model.spdx_no_assertion import SpdxNoAssertion +from src.model.spdx_none import SpdxNone +from tests.fixtures import creation_info_fixture @pytest.fixture @@ -73,12 +76,9 @@ def test_successful_conversion(converter: FileConverter): contributors=["contributor1", "contributor2"], attribution_texts=["attributionText1", "attributionText2"]) - creation_info = CreationInfo("spdxVersion", "documentID", "documentName", "documentNamespace", [], - datetime(2022, 12, 4)) - annotations = [Annotation(file.spdx_id, AnnotationType.REVIEW, Actor(ActorType.PERSON, "annotatorName"), datetime(2022, 12, 5), "review comment")] - document = Document(creation_info, files=[file], annotations=annotations) + document = Document(creation_info_fixture(), files=[file], annotations=annotations) converted_dict = converter.convert(file, document) @@ -106,10 +106,7 @@ def test_null_values(converter: FileConverter): file = File(name="name", spdx_id="spdxId", checksums=[Checksum(ChecksumAlgorithm.SHA224, "sha224"), Checksum(ChecksumAlgorithm.MD2, "md2")]) - creation_info = CreationInfo("spdxVersion", "documentID", "documentName", "documentNamespace", [], - datetime(2022, 12, 4)) - - document = Document(creation_info, files=[file]) + document = Document(creation_info_fixture(), files=[file]) converted_dict = converter.convert(file, document) diff --git a/tests/jsonschema/test_package_converter.py b/tests/jsonschema/test_package_converter.py index 324b64ccb..6a4e5a2b3 100644 --- a/tests/jsonschema/test_package_converter.py +++ b/tests/jsonschema/test_package_converter.py @@ -19,10 +19,11 @@ from src.model.actor import Actor, ActorType from src.model.annotation import Annotation, AnnotationType from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.document import Document, CreationInfo +from src.model.document import Document from src.model.license_expression import LicenseExpression from src.model.package import Package, PackageVerificationCode, ExternalPackageRef, ExternalPackageRefCategory, \ PackagePurpose +from tests.fixtures import creation_info_fixture @pytest.fixture @@ -106,12 +107,10 @@ def test_successful_conversion(converter: PackageConverter): primary_package_purpose=PackagePurpose.APPLICATION, release_date=datetime(2022, 12, 1), built_date=datetime(2022, 12, 2), valid_until_date=datetime(2022, 12, 3)) - creation_info = CreationInfo("spdxVersion", "documentID", "documentName", "documentNamespace", [], - datetime(2022, 12, 4)) annotation = Annotation(package.spdx_id, AnnotationType.REVIEW, Actor(ActorType.TOOL, "toolName"), datetime(2022, 12, 5), "review comment") - document = Document(creation_info, packages=[package], annotations=[annotation]) + document = Document(creation_info_fixture(), packages=[package], annotations=[annotation]) converted_dict = converter.convert(package, document) @@ -151,9 +150,7 @@ def test_successful_conversion(converter: PackageConverter): def test_null_values(converter: PackageConverter): package = Package(spdx_id="packageId", name="name", download_location="downloadLocation") - creation_info = CreationInfo("spdxVersion", "documentID", "documentName", "documentNamespace", [], - datetime(2022, 12, 4)) - document = Document(creation_info, packages=[package]) + document = Document(creation_info_fixture(), packages=[package]) converted_dict = converter.convert(package, document) diff --git a/tests/jsonschema/test_snippet_converter.py b/tests/jsonschema/test_snippet_converter.py index 6008d63b0..e15d27a7e 100644 --- a/tests/jsonschema/test_snippet_converter.py +++ b/tests/jsonschema/test_snippet_converter.py @@ -18,9 +18,10 @@ from src.jsonschema.snippet_properties import SnippetProperty from src.model.actor import Actor, ActorType from src.model.annotation import Annotation, AnnotationType -from src.model.document import Document, CreationInfo +from src.model.document import Document from src.model.license_expression import LicenseExpression from src.model.snippet import Snippet +from tests.fixtures import creation_info_fixture @pytest.fixture @@ -68,9 +69,7 @@ def test_successful_conversion(converter: SnippetConverter): annotation = Annotation(snippet.spdx_id, AnnotationType.OTHER, Actor(ActorType.PERSON, "annotatorName"), datetime(2022, 12, 5), "other comment") - creation_info = CreationInfo("spdxVersion", "documentId", "documentName", "documentNamespace", [], - datetime(2022, 12, 4)) - document = Document(creation_info, snippets=[snippet], annotations=[annotation]) + document = Document(creation_info_fixture(), snippets=[snippet], annotations=[annotation]) converted_dict = converter.convert(snippet, document) assert converted_dict[converter.json_property_name(SnippetProperty.SPDX_ID)] == "spdxId" @@ -95,9 +94,7 @@ def test_successful_conversion(converter: SnippetConverter): def test_null_values(converter: SnippetConverter): snippet = Snippet("spdxId", file_spdx_id="fileId", byte_range=(1, 2)) - creation_info = CreationInfo("spdxVersion", "documentId", "documentName", "documentNamespace", [], - datetime(2022, 12, 4)) - document = Document(creation_info, snippets=[snippet]) + document = Document(creation_info_fixture(), snippets=[snippet]) converted_dict = converter.convert(snippet, document) assert converter.json_property_name(SnippetProperty.LICENSE_CONCLUDED) not in converted_dict From dc59e5d6a0aa1cfe9f7877e0c0a27845cc9794c9 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Wed, 21 Dec 2022 11:34:43 +0100 Subject: [PATCH 091/362] [issue-359] Extract file fixture for reuse in tests Signed-off-by: Nicolaus Weidner --- tests/fixtures.py | 19 +++++++++++++++++++ tests/jsonschema/test_document_converter.py | 6 ++---- tests/jsonschema/test_file_converter.py | 7 ++----- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index c00af0e1a..71e092fb0 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -11,7 +11,10 @@ from datetime import datetime from src.model.actor import Actor, ActorType +from src.model.checksum import Checksum, ChecksumAlgorithm from src.model.document import CreationInfo +from src.model.file import File, FileType +from src.model.license_expression import LicenseExpression from src.model.version import Version """Utility methods to create data model instances. All properties have valid defaults, so they don't need to be @@ -26,3 +29,19 @@ def creation_info_fixture(spdx_version="spdxVersion", spdx_id="documentId", name external_document_refs = [] if external_document_refs is None else external_document_refs return CreationInfo(spdx_version, spdx_id, name, namespace, creators, created, creator_comment, data_license, external_document_refs, license_list_version, document_comment) + + +def file_fixture(name="fileName", spdx_id="fileId", checksums=None, file_type=None, + concluded_license=LicenseExpression("concludedLicenseExpression"), license_info_in_file=None, + license_comment="licenseComment", copyright_text="copyrightText", comment="fileComment", + notice="fileNotice", contributors=None, attribution_texts=None) -> File: + checksums = [Checksum(ChecksumAlgorithm.SHA1, "sha1")] if checksums is None else checksums + file_type = [FileType.TEXT] if file_type is None else file_type + license_info_in_file = [ + LicenseExpression("licenseInfoInFileExpression")] if license_info_in_file is None else license_info_in_file + contributors = ["fileContributor"] if contributors is None else contributors + attribution_texts = ["fileAttributionText"] if attribution_texts is None else attribution_texts + return File(name=name, spdx_id=spdx_id, checksums=checksums, file_type=file_type, + concluded_license=concluded_license, license_info_in_file=license_info_in_file, + license_comment=license_comment, copyright_text=copyright_text, comment=comment, notice=notice, + contributors=contributors, attribution_texts=attribution_texts) diff --git a/tests/jsonschema/test_document_converter.py b/tests/jsonschema/test_document_converter.py index d531ebc18..2f4433105 100644 --- a/tests/jsonschema/test_document_converter.py +++ b/tests/jsonschema/test_document_converter.py @@ -22,12 +22,11 @@ from src.model.document import Document from src.model.external_document_ref import ExternalDocumentRef from src.model.extracted_licensing_info import ExtractedLicensingInfo -from src.model.file import File from src.model.package import Package from src.model.relationship import Relationship, RelationshipType from src.model.snippet import Snippet from src.model.spdx_none import SpdxNone -from tests.fixtures import creation_info_fixture +from tests.fixtures import creation_info_fixture, file_fixture @pytest.fixture @@ -77,7 +76,6 @@ def test_successful_conversion(converter: DocumentConverter): Checksum(ChecksumAlgorithm.SHA1, "sha1"))]) package = Package("packageID", "packageName", SpdxNone()) - file = File("fileName", "fileId", []) snippet = Snippet("snippetId", "snippetFileId", (1, 2)) document = Document(creation_info, annotations=[ Annotation("annotationId", AnnotationType.REVIEW, Actor(ActorType.PERSON, "reviewerName"), @@ -85,7 +83,7 @@ def test_successful_conversion(converter: DocumentConverter): extracted_licensing_info=[ExtractedLicensingInfo("licenseId", "licenseText")], relationships=[ Relationship(creation_info.spdx_id, RelationshipType.DESCRIBES, "describedElementId"), Relationship("relationshipOriginId", RelationshipType.AMENDS, "relationShipTargetId")], packages=[package], - files=[file], snippets=[snippet]) + files=[file_fixture()], snippets=[snippet]) converter.external_document_ref_converter.convert.return_value = "mock_converted_external_ref" converter.creation_info_converter.convert.return_value = "mock_converted_creation_info" converter.package_converter.convert.return_value = "mock_converted_package" diff --git a/tests/jsonschema/test_file_converter.py b/tests/jsonschema/test_file_converter.py index 5e971a328..f20159c83 100644 --- a/tests/jsonschema/test_file_converter.py +++ b/tests/jsonschema/test_file_converter.py @@ -22,9 +22,7 @@ from src.model.document import Document from src.model.file import File, FileType from src.model.license_expression import LicenseExpression -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone -from tests.fixtures import creation_info_fixture +from tests.fixtures import creation_info_fixture, file_fixture @pytest.fixture @@ -103,8 +101,7 @@ def test_successful_conversion(converter: FileConverter): def test_null_values(converter: FileConverter): - file = File(name="name", spdx_id="spdxId", - checksums=[Checksum(ChecksumAlgorithm.SHA224, "sha224"), Checksum(ChecksumAlgorithm.MD2, "md2")]) + file = file_fixture(copyright_text=None, concluded_license=None, license_comment=None, comment=None, notice=None) document = Document(creation_info_fixture(), files=[file]) From 3e19b0fd0b704ec5734eefc9b6b9f6d1b647c3de Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Wed, 21 Dec 2022 11:50:16 +0100 Subject: [PATCH 092/362] [issue-359] Extract package fixture for reuse in tests Signed-off-by: Nicolaus Weidner --- tests/fixtures.py | 29 +++++++++++++++++++++ tests/jsonschema/test_document_converter.py | 9 +++---- tests/jsonschema/test_package_converter.py | 8 ++++-- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index 71e092fb0..407e79875 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -15,6 +15,7 @@ from src.model.document import CreationInfo from src.model.file import File, FileType from src.model.license_expression import LicenseExpression +from src.model.package import Package, PackageVerificationCode, PackagePurpose from src.model.version import Version """Utility methods to create data model instances. All properties have valid defaults, so they don't need to be @@ -45,3 +46,31 @@ def file_fixture(name="fileName", spdx_id="fileId", checksums=None, file_type=No concluded_license=concluded_license, license_info_in_file=license_info_in_file, license_comment=license_comment, copyright_text=copyright_text, comment=comment, notice=notice, contributors=contributors, attribution_texts=attribution_texts) + + +def package_fixture(spdx_id="packageId", name="packageName", download_location="downloadLocation", + version="packageVersion", file_name="packageFileName", + supplier=Actor(ActorType.PERSON, "supplierName"), + originator=Actor(ActorType.PERSON, "originatorName"), files_analyzed=True, + verification_code=PackageVerificationCode("verificationCode"), checksums=None, + homepage="packageHomepage", source_info="sourceInfo", + license_concluded=LicenseExpression("packageLicenseConcluded"), license_info_from_files=None, + license_declared=LicenseExpression("packageLicenseDeclared"), + license_comment="packageLicenseComment", copyright_text="packageCopyrightText", + summary="packageSummary", description="packageDescription", comment="packageComment", + external_references=None, attribution_texts=None, primary_package_purpose=PackagePurpose.SOURCE, + release_date=datetime(2022, 12, 1), built_date=datetime(2022, 12, 2), + valid_until_date=datetime(2022, 12, 3)) -> Package: + checksums = [Checksum(ChecksumAlgorithm.SHA1, "packageSha1")] if checksums is None else checksums + license_info_from_files = [ + LicenseExpression("licenseInfoFromFile")] if license_info_from_files is None else license_info_from_files + external_references = [] if external_references is None else external_references + attribution_texts = ["packageAttributionText"] if attribution_texts is None else attribution_texts + return Package(spdx_id=spdx_id, name=name, download_location=download_location, version=version, + file_name=file_name, supplier=supplier, originator=originator, files_analyzed=files_analyzed, + verification_code=verification_code, checksums=checksums, homepage=homepage, source_info=source_info, + license_concluded=license_concluded, license_info_from_files=license_info_from_files, + license_declared=license_declared, license_comment=license_comment, copyright_text=copyright_text, + summary=summary, description=description, comment=comment, external_references=external_references, + attribution_texts=attribution_texts, primary_package_purpose=primary_package_purpose, + release_date=release_date, built_date=built_date, valid_until_date=valid_until_date) diff --git a/tests/jsonschema/test_document_converter.py b/tests/jsonschema/test_document_converter.py index 2f4433105..7622e1865 100644 --- a/tests/jsonschema/test_document_converter.py +++ b/tests/jsonschema/test_document_converter.py @@ -22,11 +22,9 @@ from src.model.document import Document from src.model.external_document_ref import ExternalDocumentRef from src.model.extracted_licensing_info import ExtractedLicensingInfo -from src.model.package import Package from src.model.relationship import Relationship, RelationshipType from src.model.snippet import Snippet -from src.model.spdx_none import SpdxNone -from tests.fixtures import creation_info_fixture, file_fixture +from tests.fixtures import creation_info_fixture, file_fixture, package_fixture @pytest.fixture @@ -75,15 +73,14 @@ def test_successful_conversion(converter: DocumentConverter): external_document_refs=[ExternalDocumentRef("docRefId", "externalDocumentUri", Checksum(ChecksumAlgorithm.SHA1, "sha1"))]) - package = Package("packageID", "packageName", SpdxNone()) snippet = Snippet("snippetId", "snippetFileId", (1, 2)) document = Document(creation_info, annotations=[ Annotation("annotationId", AnnotationType.REVIEW, Actor(ActorType.PERSON, "reviewerName"), datetime(2022, 12, 1), "reviewComment")], extracted_licensing_info=[ExtractedLicensingInfo("licenseId", "licenseText")], relationships=[ Relationship(creation_info.spdx_id, RelationshipType.DESCRIBES, "describedElementId"), - Relationship("relationshipOriginId", RelationshipType.AMENDS, "relationShipTargetId")], packages=[package], - files=[file_fixture()], snippets=[snippet]) + Relationship("relationshipOriginId", RelationshipType.AMENDS, "relationShipTargetId")], + packages=[package_fixture()], files=[file_fixture()], snippets=[snippet]) converter.external_document_ref_converter.convert.return_value = "mock_converted_external_ref" converter.creation_info_converter.convert.return_value = "mock_converted_creation_info" converter.package_converter.convert.return_value = "mock_converted_package" diff --git a/tests/jsonschema/test_package_converter.py b/tests/jsonschema/test_package_converter.py index 6a4e5a2b3..d7f633558 100644 --- a/tests/jsonschema/test_package_converter.py +++ b/tests/jsonschema/test_package_converter.py @@ -23,7 +23,7 @@ from src.model.license_expression import LicenseExpression from src.model.package import Package, PackageVerificationCode, ExternalPackageRef, ExternalPackageRefCategory, \ PackagePurpose -from tests.fixtures import creation_info_fixture +from tests.fixtures import creation_info_fixture, package_fixture @pytest.fixture @@ -148,7 +148,11 @@ def test_successful_conversion(converter: PackageConverter): def test_null_values(converter: PackageConverter): - package = Package(spdx_id="packageId", name="name", download_location="downloadLocation") + package = package_fixture(built_date=None, release_date=None, valid_until_date=None, homepage=None, + license_concluded=None, license_declared=None, originator=None, verification_code=None, + primary_package_purpose=None, supplier=None, version=None, file_name=None, + source_info=None, license_comment=None, copyright_text=None, summary=None, + description=None, comment=None) document = Document(creation_info_fixture(), packages=[package]) From 4a874e5578f36ad470373494f80fd7f867abc989 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Wed, 21 Dec 2022 11:57:38 +0100 Subject: [PATCH 093/362] [issue-359] Extract external document ref fixture for reuse in tests Signed-off-by: Nicolaus Weidner --- tests/fixtures.py | 10 +++++++++- tests/jsonschema/test_document_converter.py | 8 ++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index 407e79875..df7b5f96c 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -13,6 +13,7 @@ from src.model.actor import Actor, ActorType from src.model.checksum import Checksum, ChecksumAlgorithm from src.model.document import CreationInfo +from src.model.external_document_ref import ExternalDocumentRef from src.model.file import File, FileType from src.model.license_expression import LicenseExpression from src.model.package import Package, PackageVerificationCode, PackagePurpose @@ -27,7 +28,8 @@ def creation_info_fixture(spdx_version="spdxVersion", spdx_id="documentId", name creator_comment="creatorComment", data_license="CC0-1.0", external_document_refs=None, license_list_version=Version(3, 19), document_comment="documentComment") -> CreationInfo: creators = [Actor(ActorType.PERSON, "creatorName")] if creators is None else creators - external_document_refs = [] if external_document_refs is None else external_document_refs + external_document_refs = [ + external_document_ref_fixture()] if external_document_refs is None else external_document_refs return CreationInfo(spdx_version, spdx_id, name, namespace, creators, created, creator_comment, data_license, external_document_refs, license_list_version, document_comment) @@ -74,3 +76,9 @@ def package_fixture(spdx_id="packageId", name="packageName", download_location=" summary=summary, description=description, comment=comment, external_references=external_references, attribution_texts=attribution_texts, primary_package_purpose=primary_package_purpose, release_date=release_date, built_date=built_date, valid_until_date=valid_until_date) + + +def external_document_ref_fixture(document_ref_id="externalDocumentRefId", document_uri="externalDocumentUri", + checksum=Checksum(ChecksumAlgorithm.MD5, + "externalDocumentRefMd5")) -> ExternalDocumentRef: + return ExternalDocumentRef(document_ref_id=document_ref_id, document_uri=document_uri, checksum=checksum) diff --git a/tests/jsonschema/test_document_converter.py b/tests/jsonschema/test_document_converter.py index 7622e1865..42270be06 100644 --- a/tests/jsonschema/test_document_converter.py +++ b/tests/jsonschema/test_document_converter.py @@ -18,13 +18,11 @@ from src.jsonschema.document_properties import DocumentProperty from src.model.actor import Actor, ActorType from src.model.annotation import Annotation, AnnotationType -from src.model.checksum import Checksum, ChecksumAlgorithm from src.model.document import Document -from src.model.external_document_ref import ExternalDocumentRef from src.model.extracted_licensing_info import ExtractedLicensingInfo from src.model.relationship import Relationship, RelationshipType from src.model.snippet import Snippet -from tests.fixtures import creation_info_fixture, file_fixture, package_fixture +from tests.fixtures import creation_info_fixture, file_fixture, package_fixture, external_document_ref_fixture @pytest.fixture @@ -70,9 +68,7 @@ def test_json_property_names(converter: DocumentConverter, document_property: Do def test_successful_conversion(converter: DocumentConverter): creation_info = creation_info_fixture(spdx_version="spdxVersion", spdx_id="spdxId", name="name", namespace="namespace", document_comment="comment", data_license="dataLicense", - external_document_refs=[ExternalDocumentRef("docRefId", "externalDocumentUri", - Checksum(ChecksumAlgorithm.SHA1, - "sha1"))]) + external_document_refs=[external_document_ref_fixture()]) snippet = Snippet("snippetId", "snippetFileId", (1, 2)) document = Document(creation_info, annotations=[ Annotation("annotationId", AnnotationType.REVIEW, Actor(ActorType.PERSON, "reviewerName"), From 418b681ead2402185e421d564813f9000c4f0994 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Wed, 21 Dec 2022 12:01:22 +0100 Subject: [PATCH 094/362] [issue-359] Extract external package ref fixture for reuse in tests Signed-off-by: Nicolaus Weidner --- tests/fixtures.py | 12 ++++++++++-- tests/jsonschema/test_package_converter.py | 12 ++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index df7b5f96c..6d6d529b4 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -16,7 +16,8 @@ from src.model.external_document_ref import ExternalDocumentRef from src.model.file import File, FileType from src.model.license_expression import LicenseExpression -from src.model.package import Package, PackageVerificationCode, PackagePurpose +from src.model.package import Package, PackageVerificationCode, PackagePurpose, ExternalPackageRef, \ + ExternalPackageRefCategory from src.model.version import Version """Utility methods to create data model instances. All properties have valid defaults, so they don't need to be @@ -66,7 +67,7 @@ def package_fixture(spdx_id="packageId", name="packageName", download_location=" checksums = [Checksum(ChecksumAlgorithm.SHA1, "packageSha1")] if checksums is None else checksums license_info_from_files = [ LicenseExpression("licenseInfoFromFile")] if license_info_from_files is None else license_info_from_files - external_references = [] if external_references is None else external_references + external_references = [external_package_ref_fixture()] if external_references is None else external_references attribution_texts = ["packageAttributionText"] if attribution_texts is None else attribution_texts return Package(spdx_id=spdx_id, name=name, download_location=download_location, version=version, file_name=file_name, supplier=supplier, originator=originator, files_analyzed=files_analyzed, @@ -82,3 +83,10 @@ def external_document_ref_fixture(document_ref_id="externalDocumentRefId", docum checksum=Checksum(ChecksumAlgorithm.MD5, "externalDocumentRefMd5")) -> ExternalDocumentRef: return ExternalDocumentRef(document_ref_id=document_ref_id, document_uri=document_uri, checksum=checksum) + + +def external_package_ref_fixture(category=ExternalPackageRefCategory.PACKAGE_MANAGER, + reference_type="externalPackageRefType", + locator="externalPackageRefLocator", + comment="externalPackageRefComment") -> ExternalPackageRef: + return ExternalPackageRef(category=category, reference_type=reference_type, locator=locator, comment=comment) diff --git a/tests/jsonschema/test_package_converter.py b/tests/jsonschema/test_package_converter.py index d7f633558..6da05428d 100644 --- a/tests/jsonschema/test_package_converter.py +++ b/tests/jsonschema/test_package_converter.py @@ -21,9 +21,8 @@ from src.model.checksum import Checksum, ChecksumAlgorithm from src.model.document import Document from src.model.license_expression import LicenseExpression -from src.model.package import Package, PackageVerificationCode, ExternalPackageRef, ExternalPackageRefCategory, \ - PackagePurpose -from tests.fixtures import creation_info_fixture, package_fixture +from src.model.package import Package, PackageVerificationCode, PackagePurpose +from tests.fixtures import creation_info_fixture, package_fixture, external_package_ref_fixture @pytest.fixture @@ -100,16 +99,13 @@ def test_successful_conversion(converter: PackageConverter): LicenseExpression("licenseExpression3")], license_declared=LicenseExpression("licenseExpression4"), license_comment="licenseComment", copyright_text="copyrightText", summary="summary", description="description", comment="comment", - external_references=[ - ExternalPackageRef(ExternalPackageRefCategory.PACKAGE_MANAGER, "referenceType", - "referenceLocator")], + external_references=[external_package_ref_fixture()], attribution_texts=["attributionText1", "attributionText2"], primary_package_purpose=PackagePurpose.APPLICATION, release_date=datetime(2022, 12, 1), built_date=datetime(2022, 12, 2), valid_until_date=datetime(2022, 12, 3)) annotation = Annotation(package.spdx_id, AnnotationType.REVIEW, Actor(ActorType.TOOL, "toolName"), - datetime(2022, 12, 5), - "review comment") + datetime(2022, 12, 5), "review comment") document = Document(creation_info_fixture(), packages=[package], annotations=[annotation]) converted_dict = converter.convert(package, document) From 8f6a20c20efdd4526b98511ed41ebb1179957d4e Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Wed, 21 Dec 2022 12:26:11 +0100 Subject: [PATCH 095/362] [issue-359] Extract snippet fixture for reuse in tests Signed-off-by: Nicolaus Weidner --- tests/fixtures.py | 15 +++++++++++++++ tests/jsonschema/test_document_converter.py | 7 +++---- tests/jsonschema/test_snippet_converter.py | 5 +++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index 6d6d529b4..ae9d4b3d8 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -18,6 +18,7 @@ from src.model.license_expression import LicenseExpression from src.model.package import Package, PackageVerificationCode, PackagePurpose, ExternalPackageRef, \ ExternalPackageRefCategory +from src.model.snippet import Snippet from src.model.version import Version """Utility methods to create data model instances. All properties have valid defaults, so they don't need to be @@ -90,3 +91,17 @@ def external_package_ref_fixture(category=ExternalPackageRefCategory.PACKAGE_MAN locator="externalPackageRefLocator", comment="externalPackageRefComment") -> ExternalPackageRef: return ExternalPackageRef(category=category, reference_type=reference_type, locator=locator, comment=comment) + + +def snippet_fixture(spdx_id="snippetId", file_spdx_id="snippetFromFileId", byte_range=(1, 2), + line_range=(3, 4), concluded_license=LicenseExpression("snippetLicenseConcluded"), + license_info_in_snippet=None, license_comment="snippetLicenseComment", + copyright_text="licenseCopyrightText", comment="snippetComment", name="snippetName", + attribution_texts=None) -> Snippet: + license_info_in_snippet = [ + LicenseExpression("licenseInfoInSnippet")] if license_info_in_snippet is None else license_info_in_snippet + attribution_texts = ["snippetAttributionText"] if attribution_texts is None else attribution_texts + return Snippet(spdx_id=spdx_id, file_spdx_id=file_spdx_id, byte_range=byte_range, line_range=line_range, + concluded_license=concluded_license, license_info_in_snippet=license_info_in_snippet, + license_comment=license_comment, copyright_text=copyright_text, comment=comment, name=name, + attribution_texts=attribution_texts) diff --git a/tests/jsonschema/test_document_converter.py b/tests/jsonschema/test_document_converter.py index 42270be06..cac563dd5 100644 --- a/tests/jsonschema/test_document_converter.py +++ b/tests/jsonschema/test_document_converter.py @@ -21,8 +21,8 @@ from src.model.document import Document from src.model.extracted_licensing_info import ExtractedLicensingInfo from src.model.relationship import Relationship, RelationshipType -from src.model.snippet import Snippet -from tests.fixtures import creation_info_fixture, file_fixture, package_fixture, external_document_ref_fixture +from tests.fixtures import creation_info_fixture, file_fixture, package_fixture, external_document_ref_fixture, \ + snippet_fixture @pytest.fixture @@ -69,14 +69,13 @@ def test_successful_conversion(converter: DocumentConverter): creation_info = creation_info_fixture(spdx_version="spdxVersion", spdx_id="spdxId", name="name", namespace="namespace", document_comment="comment", data_license="dataLicense", external_document_refs=[external_document_ref_fixture()]) - snippet = Snippet("snippetId", "snippetFileId", (1, 2)) document = Document(creation_info, annotations=[ Annotation("annotationId", AnnotationType.REVIEW, Actor(ActorType.PERSON, "reviewerName"), datetime(2022, 12, 1), "reviewComment")], extracted_licensing_info=[ExtractedLicensingInfo("licenseId", "licenseText")], relationships=[ Relationship(creation_info.spdx_id, RelationshipType.DESCRIBES, "describedElementId"), Relationship("relationshipOriginId", RelationshipType.AMENDS, "relationShipTargetId")], - packages=[package_fixture()], files=[file_fixture()], snippets=[snippet]) + packages=[package_fixture()], files=[file_fixture()], snippets=[snippet_fixture()]) converter.external_document_ref_converter.convert.return_value = "mock_converted_external_ref" converter.creation_info_converter.convert.return_value = "mock_converted_creation_info" converter.package_converter.convert.return_value = "mock_converted_package" diff --git a/tests/jsonschema/test_snippet_converter.py b/tests/jsonschema/test_snippet_converter.py index e15d27a7e..564d0f118 100644 --- a/tests/jsonschema/test_snippet_converter.py +++ b/tests/jsonschema/test_snippet_converter.py @@ -21,7 +21,7 @@ from src.model.document import Document from src.model.license_expression import LicenseExpression from src.model.snippet import Snippet -from tests.fixtures import creation_info_fixture +from tests.fixtures import creation_info_fixture, snippet_fixture @pytest.fixture @@ -92,7 +92,8 @@ def test_successful_conversion(converter: SnippetConverter): def test_null_values(converter: SnippetConverter): - snippet = Snippet("spdxId", file_spdx_id="fileId", byte_range=(1, 2)) + snippet = snippet_fixture(concluded_license=None, license_comment=None, copyright_text=None, comment=None, + name=None) document = Document(creation_info_fixture(), snippets=[snippet]) converted_dict = converter.convert(snippet, document) From a288f29fc82ad7e1db2807be73d91c62880d5431 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Wed, 21 Dec 2022 13:33:55 +0100 Subject: [PATCH 096/362] [issue-359] Add SpdxNoAssertion and SpdxNone tests Signed-off-by: Nicolaus Weidner --- src/model/spdx_no_assertion.py | 8 ++--- src/model/spdx_none.py | 8 ++--- tests/jsonschema/test_file_converter.py | 28 ++++++++++++++- tests/jsonschema/test_package_converter.py | 40 ++++++++++++++++++++++ tests/jsonschema/test_snippet_converter.py | 24 +++++++++++++ 5 files changed, 99 insertions(+), 9 deletions(-) diff --git a/src/model/spdx_no_assertion.py b/src/model/spdx_no_assertion.py index 60bec6fd6..3a12ed36c 100644 --- a/src/model/spdx_no_assertion.py +++ b/src/model/spdx_no_assertion.py @@ -9,19 +9,19 @@ # See the License for the specific language governing permissions and # limitations under the License. +SPDX_NO_ASSERTION_STRING = "NOASSERTION" + class SpdxNoAssertion: """ Represents the SPDX NOASSERTION value. """ - _string_value: str = "NOASSERTION" - def __str__(self): - return self._string_value + return SPDX_NO_ASSERTION_STRING def __repr__(self): - return self._string_value + return SPDX_NO_ASSERTION_STRING def __eq__(self, other): return isinstance(other, SpdxNoAssertion) diff --git a/src/model/spdx_none.py b/src/model/spdx_none.py index 8e170ae4c..e4e97c534 100644 --- a/src/model/spdx_none.py +++ b/src/model/spdx_none.py @@ -9,19 +9,19 @@ # See the License for the specific language governing permissions and # limitations under the License. +SPDX_NONE_STRING = "NONE" + class SpdxNone: """ Represents the SPDX NONE value. """ - _string_value = "NONE" - def __str__(self): - return self._string_value + return SPDX_NONE_STRING def __repr__(self): - return self._string_value + return SPDX_NONE_STRING def __eq__(self, other): return isinstance(other, SpdxNone) diff --git a/tests/jsonschema/test_file_converter.py b/tests/jsonschema/test_file_converter.py index f20159c83..7cc6f0462 100644 --- a/tests/jsonschema/test_file_converter.py +++ b/tests/jsonschema/test_file_converter.py @@ -22,6 +22,8 @@ from src.model.document import Document from src.model.file import File, FileType from src.model.license_expression import LicenseExpression +from src.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING +from src.model.spdx_none import SpdxNone, SPDX_NONE_STRING from tests.fixtures import creation_info_fixture, file_fixture @@ -102,7 +104,6 @@ def test_successful_conversion(converter: FileConverter): def test_null_values(converter: FileConverter): file = file_fixture(copyright_text=None, concluded_license=None, license_comment=None, comment=None, notice=None) - document = Document(creation_info_fixture(), files=[file]) converted_dict = converter.convert(file, document) @@ -112,3 +113,28 @@ def test_null_values(converter: FileConverter): assert converter.json_property_name(FileProperty.LICENSE_COMMENTS) not in converted_dict assert converter.json_property_name(FileProperty.COMMENT) not in converted_dict assert converter.json_property_name(FileProperty.NOTICE_TEXT) not in converted_dict + + +def test_spdx_no_assertion(converter: FileConverter): + file = file_fixture(concluded_license=SpdxNoAssertion(), license_info_in_file=SpdxNoAssertion(), + copyright_text=SpdxNoAssertion()) + document = Document(creation_info_fixture(), files=[file]) + + converted_dict = converter.convert(file, document) + + assert converted_dict[ + converter.json_property_name(FileProperty.COPYRIGHT_TEXT)] == SPDX_NO_ASSERTION_STRING + assert converted_dict[converter.json_property_name(FileProperty.LICENSE_CONCLUDED)] == SPDX_NO_ASSERTION_STRING + assert converted_dict[converter.json_property_name(FileProperty.LICENSE_INFO_IN_FILES)] == SPDX_NO_ASSERTION_STRING + + +def test_spdx_none(converter: FileConverter): + file = file_fixture(concluded_license=SpdxNone(), license_info_in_file=SpdxNone(), copyright_text=SpdxNone()) + document = Document(creation_info_fixture(), files=[file]) + + converted_dict = converter.convert(file, document) + + assert converted_dict[ + converter.json_property_name(FileProperty.COPYRIGHT_TEXT)] == SPDX_NONE_STRING + assert converted_dict[converter.json_property_name(FileProperty.LICENSE_CONCLUDED)] == SPDX_NONE_STRING + assert converted_dict[converter.json_property_name(FileProperty.LICENSE_INFO_IN_FILES)] == SPDX_NONE_STRING diff --git a/tests/jsonschema/test_package_converter.py b/tests/jsonschema/test_package_converter.py index 6da05428d..ba769a616 100644 --- a/tests/jsonschema/test_package_converter.py +++ b/tests/jsonschema/test_package_converter.py @@ -22,6 +22,8 @@ from src.model.document import Document from src.model.license_expression import LicenseExpression from src.model.package import Package, PackageVerificationCode, PackagePurpose +from src.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING +from src.model.spdx_none import SpdxNone, SPDX_NONE_STRING from tests.fixtures import creation_info_fixture, package_fixture, external_package_ref_fixture @@ -172,3 +174,41 @@ def test_null_values(converter: PackageConverter): assert converter.json_property_name(PackageProperty.BUILT_DATE) not in converted_dict assert converter.json_property_name(PackageProperty.RELEASE_DATE) not in converted_dict assert converter.json_property_name(PackageProperty.VALID_UNTIL_DATE) not in converted_dict + + +def test_spdx_no_assertion(converter: PackageConverter): + package = package_fixture(download_location=SpdxNoAssertion(), supplier=SpdxNoAssertion(), + originator=SpdxNoAssertion(), homepage=SpdxNoAssertion(), + license_concluded=SpdxNoAssertion(), license_info_from_files=SpdxNoAssertion(), + license_declared=SpdxNoAssertion(), copyright_text=SpdxNoAssertion()) + + document = Document(creation_info_fixture(), packages=[package]) + + converted_dict = converter.convert(package, document) + + assert converted_dict[converter.json_property_name(PackageProperty.DOWNLOAD_LOCATION)] == SPDX_NO_ASSERTION_STRING + assert converted_dict[converter.json_property_name(PackageProperty.SUPPLIER)] == SPDX_NO_ASSERTION_STRING + assert converted_dict[converter.json_property_name(PackageProperty.ORIGINATOR)] == SPDX_NO_ASSERTION_STRING + assert converted_dict[converter.json_property_name(PackageProperty.HOMEPAGE)] == SPDX_NO_ASSERTION_STRING + assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_CONCLUDED)] == SPDX_NO_ASSERTION_STRING + assert converted_dict[ + converter.json_property_name(PackageProperty.LICENSE_INFO_FROM_FILES)] == SPDX_NO_ASSERTION_STRING + assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_DECLARED)] == SPDX_NO_ASSERTION_STRING + assert converted_dict[converter.json_property_name(PackageProperty.COPYRIGHT_TEXT)] == SPDX_NO_ASSERTION_STRING + + +def test_spdx_none(converter: PackageConverter): + package = package_fixture(download_location=SpdxNone(), homepage=SpdxNone(), + license_concluded=SpdxNone(), license_info_from_files=SpdxNone(), + license_declared=SpdxNone(), copyright_text=SpdxNone()) + + document = Document(creation_info_fixture(), packages=[package]) + + converted_dict = converter.convert(package, document) + + assert converted_dict[converter.json_property_name(PackageProperty.DOWNLOAD_LOCATION)] == SPDX_NONE_STRING + assert converted_dict[converter.json_property_name(PackageProperty.HOMEPAGE)] == SPDX_NONE_STRING + assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_CONCLUDED)] == SPDX_NONE_STRING + assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_INFO_FROM_FILES)] == SPDX_NONE_STRING + assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_DECLARED)] == SPDX_NONE_STRING + assert converted_dict[converter.json_property_name(PackageProperty.COPYRIGHT_TEXT)] == SPDX_NONE_STRING diff --git a/tests/jsonschema/test_snippet_converter.py b/tests/jsonschema/test_snippet_converter.py index 564d0f118..746476d3a 100644 --- a/tests/jsonschema/test_snippet_converter.py +++ b/tests/jsonschema/test_snippet_converter.py @@ -21,6 +21,8 @@ from src.model.document import Document from src.model.license_expression import LicenseExpression from src.model.snippet import Snippet +from src.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING +from src.model.spdx_none import SpdxNone, SPDX_NONE_STRING from tests.fixtures import creation_info_fixture, snippet_fixture @@ -103,3 +105,25 @@ def test_null_values(converter: SnippetConverter): assert converter.json_property_name(SnippetProperty.COPYRIGHT_TEXT) not in converted_dict assert converter.json_property_name(SnippetProperty.COMMENT) not in converted_dict assert converter.json_property_name(SnippetProperty.NAME) not in converted_dict + + +def test_spdx_no_assertion(converter: SnippetConverter): + snippet = snippet_fixture(concluded_license=SpdxNoAssertion(), license_info_in_snippet=SpdxNoAssertion()) + + document = Document(creation_info_fixture(), snippets=[snippet]) + converted_dict = converter.convert(snippet, document) + + assert converted_dict[converter.json_property_name(SnippetProperty.LICENSE_CONCLUDED)] == SPDX_NO_ASSERTION_STRING + assert converted_dict[ + converter.json_property_name(SnippetProperty.LICENSE_INFO_IN_SNIPPETS)] == SPDX_NO_ASSERTION_STRING + + +def test_spdx_none(converter: SnippetConverter): + snippet = snippet_fixture(concluded_license=SpdxNone(), license_info_in_snippet=SpdxNone()) + + document = Document(creation_info_fixture(), snippets=[snippet]) + converted_dict = converter.convert(snippet, document) + + assert converted_dict[converter.json_property_name(SnippetProperty.LICENSE_CONCLUDED)] == SPDX_NONE_STRING + assert converted_dict[ + converter.json_property_name(SnippetProperty.LICENSE_INFO_IN_SNIPPETS)] == SPDX_NONE_STRING From 794f29e61d7c344eb47a11f19d0956f8e2af70a6 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Wed, 21 Dec 2022 16:02:35 +0100 Subject: [PATCH 097/362] [issue-359] Don't write empty lists into the converted dictionary Signed-off-by: Nicolaus Weidner --- src/jsonschema/creation_info_converter.py | 2 +- src/jsonschema/document_converter.py | 16 ++++++++-------- .../extracted_licensing_info_converter.py | 2 +- src/jsonschema/file_converter.py | 12 ++++++------ src/jsonschema/package_converter.py | 13 +++++++------ .../package_verification_code_converter.py | 2 +- src/jsonschema/snippet_converter.py | 6 +++--- tests/jsonschema/test_creation_info_converter.py | 3 ++- tests/jsonschema/test_document_converter.py | 15 +++++++++++++++ .../test_extracted_licensing_info_converter.py | 12 ++++++++++++ tests/jsonschema/test_file_converter.py | 9 ++++++++- tests/jsonschema/test_package_converter.py | 9 ++++++++- .../test_package_verification_code_converter.py | 9 +++++++++ tests/jsonschema/test_snippet_converter.py | 5 ++++- 14 files changed, 85 insertions(+), 30 deletions(-) diff --git a/src/jsonschema/creation_info_converter.py b/src/jsonschema/creation_info_converter.py index 04734ea8c..656f56ee7 100644 --- a/src/jsonschema/creation_info_converter.py +++ b/src/jsonschema/creation_info_converter.py @@ -34,7 +34,7 @@ def _get_property_value(self, creation_info: CreationInfo, creation_info_propert if creation_info_property == CreationInfoProperty.CREATED: return datetime_to_iso_string(creation_info.created) elif creation_info_property == CreationInfoProperty.CREATORS: - return [creator.to_serialized_string() for creator in creation_info.creators] + return [creator.to_serialized_string() for creator in creation_info.creators] or None elif creation_info_property == CreationInfoProperty.LICENSE_LIST_VERSION: return apply_if_present(str, creation_info.license_list_version) elif creation_info_property == CreationInfoProperty.COMMENT: diff --git a/src/jsonschema/document_converter.py b/src/jsonschema/document_converter.py index 98f5b784a..d04e4e8e2 100644 --- a/src/jsonschema/document_converter.py +++ b/src/jsonschema/document_converter.py @@ -71,7 +71,7 @@ def _get_property_value(self, document: Document, document_property: DocumentPro element_ids.extend([snippet.spdx_id for snippet in document.snippets]) document_annotations = filter(lambda annotation: annotation.spdx_id not in element_ids, document.annotations) - return [self.annotation_converter.convert(annotation) for annotation in document_annotations] + return [self.annotation_converter.convert(annotation) for annotation in document_annotations] or None elif document_property == DocumentProperty.COMMENT: return document.creation_info.document_comment elif document_property == DocumentProperty.CREATION_INFO: @@ -80,10 +80,10 @@ def _get_property_value(self, document: Document, document_property: DocumentPro return document.creation_info.data_license elif document_property == DocumentProperty.EXTERNAL_DOCUMENT_REFS: return [self.external_document_ref_converter.convert(external_document_ref) for - external_document_ref in document.creation_info.external_document_refs] + external_document_ref in document.creation_info.external_document_refs] or None elif document_property == DocumentProperty.HAS_EXTRACTED_LICENSING_INFO: return [self.extracted_licensing_info_converter.convert(licensing_info) for licensing_info in - document.extracted_licensing_info] + document.extracted_licensing_info] or None elif document_property == DocumentProperty.NAME: return document.creation_info.name elif document_property == DocumentProperty.SPDX_VERSION: @@ -97,13 +97,13 @@ def _get_property_value(self, document: Document, document_property: DocumentPro described_by_ids = [relationship.spdx_element_id for relationship in filter_by_type_and_target(document.relationships, RelationshipType.DESCRIBED_BY, document.creation_info.spdx_id)] - return describes_ids + described_by_ids + return describes_ids + described_by_ids or None elif document_property == DocumentProperty.PACKAGES: - return [self.package_converter.convert(package, document) for package in document.packages] + return [self.package_converter.convert(package, document) for package in document.packages] or None elif document_property == DocumentProperty.FILES: - return [self.file_converter.convert(file, document) for file in document.files] + return [self.file_converter.convert(file, document) for file in document.files] or None elif document_property == DocumentProperty.SNIPPETS: - return [self.snippet_converter.convert(snippet, document) for snippet in document.snippets] + return [self.snippet_converter.convert(snippet, document) for snippet in document.snippets] or None elif document_property == DocumentProperty.RELATIONSHIPS: already_covered_relationships = filter_by_type_and_origin(document.relationships, RelationshipType.DESCRIBES, @@ -117,4 +117,4 @@ def _get_property_value(self, document: Document, document_property: DocumentPro relationships_to_ignore = [relationship for relationship in already_covered_relationships if relationship.comment is None] return [self.relationship_converter.convert(relationship) for relationship in document.relationships if - relationship not in relationships_to_ignore] + relationship not in relationships_to_ignore] or None diff --git a/src/jsonschema/extracted_licensing_info_converter.py b/src/jsonschema/extracted_licensing_info_converter.py index 25643ec3d..87f1312fa 100644 --- a/src/jsonschema/extracted_licensing_info_converter.py +++ b/src/jsonschema/extracted_licensing_info_converter.py @@ -34,7 +34,7 @@ def _get_property_value(self, extracted_licensing_info: ExtractedLicensingInfo, elif extracted_licensing_info_property == ExtractedLicensingInfoProperty.NAME: return extracted_licensing_info.license_name elif extracted_licensing_info_property == ExtractedLicensingInfoProperty.SEE_ALSOS: - return extracted_licensing_info.cross_references + return extracted_licensing_info.cross_references or None def get_json_type(self) -> Type[JsonProperty]: return ExtractedLicensingInfoProperty diff --git a/src/jsonschema/file_converter.py b/src/jsonschema/file_converter.py index 305239637..1b66a72d8 100644 --- a/src/jsonschema/file_converter.py +++ b/src/jsonschema/file_converter.py @@ -39,34 +39,34 @@ def _get_property_value(self, file: Any, file_property: FileProperty, document: return file.spdx_id elif file_property == FileProperty.ANNOTATIONS: file_annotations = filter(lambda annotation: annotation.spdx_id == file.spdx_id, document.annotations) - return [self.annotation_converter.convert(annotation) for annotation in file_annotations] + return [self.annotation_converter.convert(annotation) for annotation in file_annotations] or None elif file_property == FileProperty.ARTIFACT_OFS: # Deprecated property, automatically converted during parsing pass elif file_property == FileProperty.ATTRIBUTION_TEXTS: - return file.attribution_texts + return file.attribution_texts or None elif file_property == FileProperty.CHECKSUMS: - return [self.checksum_converter.convert(checksum) for checksum in file.checksums] + return [self.checksum_converter.convert(checksum) for checksum in file.checksums] or None elif file_property == FileProperty.COMMENT: return file.comment elif file_property == FileProperty.COPYRIGHT_TEXT: return apply_if_present(str, file.copyright_text) elif file_property == FileProperty.FILE_CONTRIBUTORS: - return file.contributors + return file.contributors or None elif file_property == FileProperty.FILE_DEPENDENCIES: # Deprecated property, automatically converted during parsing pass elif file_property == FileProperty.FILE_NAME: return file.name elif file_property == FileProperty.FILE_TYPES: - return [file_type.name for file_type in file.file_type] + return [file_type.name for file_type in file.file_type] or None elif file_property == FileProperty.LICENSE_COMMENTS: return file.license_comment elif file_property == FileProperty.LICENSE_CONCLUDED: return apply_if_present(str, file.concluded_license) elif file_property == FileProperty.LICENSE_INFO_IN_FILES: if isinstance(file.license_info_in_file, list): - return [str(license_expression) for license_expression in file.license_info_in_file] + return [str(license_expression) for license_expression in file.license_info_in_file] or None return apply_if_present(str, file.license_info_in_file) elif file_property == FileProperty.NOTICE_TEXT: return file.notice diff --git a/src/jsonschema/package_converter.py b/src/jsonschema/package_converter.py index 3253ec801..a15503e71 100644 --- a/src/jsonschema/package_converter.py +++ b/src/jsonschema/package_converter.py @@ -50,13 +50,14 @@ def _get_property_value(self, package: Package, package_property: PackagePropert return package.spdx_id elif package_property == PackageProperty.ANNOTATIONS: package_annotations = filter(lambda annotation: annotation.spdx_id == package.spdx_id, document.annotations) - return [self.annotation_converter.convert(annotation, document) for annotation in package_annotations] + return [self.annotation_converter.convert(annotation, document) for annotation in + package_annotations] or None elif package_property == PackageProperty.ATTRIBUTION_TEXTS: - return package.attribution_texts + return package.attribution_texts or None elif package_property == PackageProperty.BUILT_DATE: return apply_if_present(datetime_to_iso_string, package.built_date) elif package_property == PackageProperty.CHECKSUMS: - return [self.checksum_converter.convert(checksum, document) for checksum in package.checksums] + return [self.checksum_converter.convert(checksum, document) for checksum in package.checksums] or None elif package_property == PackageProperty.COMMENT: return package.comment elif package_property == PackageProperty.COPYRIGHT_TEXT: @@ -67,7 +68,7 @@ def _get_property_value(self, package: Package, package_property: PackagePropert return str(package.download_location) elif package_property == PackageProperty.EXTERNAL_REFS: return [self.external_package_ref_converter.convert(external_ref) for external_ref in - package.external_references] + package.external_references] or None elif package_property == PackageProperty.FILES_ANALYZED: return package.files_analyzed elif package_property == PackageProperty.HAS_FILES: @@ -75,7 +76,7 @@ def _get_property_value(self, package: Package, package_property: PackagePropert find_package_contains_file_relationships(document, package)] file_contained_in_package_ids = [relationship.spdx_element_id for relationship in find_file_contained_by_package_relationships(document, package)] - return package_contains_file_ids + file_contained_in_package_ids + return package_contains_file_ids + file_contained_in_package_ids or None elif package_property == PackageProperty.HOMEPAGE: return apply_if_present(str, package.homepage) elif package_property == PackageProperty.LICENSE_COMMENTS: @@ -86,7 +87,7 @@ def _get_property_value(self, package: Package, package_property: PackagePropert return apply_if_present(str, package.license_declared) elif package_property == PackageProperty.LICENSE_INFO_FROM_FILES: if isinstance(package.license_info_from_files, list): - return [str(license_expression) for license_expression in package.license_info_from_files] + return [str(license_expression) for license_expression in package.license_info_from_files] or None return apply_if_present(str, package.license_info_from_files) elif package_property == PackageProperty.NAME: return package.name diff --git a/src/jsonschema/package_verification_code_converter.py b/src/jsonschema/package_verification_code_converter.py index f8bdc68df..c312ef4b5 100644 --- a/src/jsonschema/package_verification_code_converter.py +++ b/src/jsonschema/package_verification_code_converter.py @@ -26,7 +26,7 @@ def _get_property_value(self, verification_code: PackageVerificationCode, verification_code_property: PackageVerificationCodeProperty, document: Document = None) -> Any: if verification_code_property == PackageVerificationCodeProperty.PACKAGE_VERIFICATION_CODE_EXCLUDED_FILES: - return verification_code.excluded_files + return verification_code.excluded_files or None elif verification_code_property == PackageVerificationCodeProperty.PACKAGE_VERIFICATION_CODE_VALUE: return verification_code.value diff --git a/src/jsonschema/snippet_converter.py b/src/jsonschema/snippet_converter.py index f907072fa..6e73f0849 100644 --- a/src/jsonschema/snippet_converter.py +++ b/src/jsonschema/snippet_converter.py @@ -37,9 +37,9 @@ def _get_property_value(self, snippet: Snippet, snippet_property: SnippetPropert return snippet.spdx_id elif snippet_property == SnippetProperty.ANNOTATIONS: snippet_annotations = filter(lambda annotation: annotation.spdx_id == snippet.spdx_id, document.annotations) - return [self.annotation_converter.convert(annotation) for annotation in snippet_annotations] + return [self.annotation_converter.convert(annotation) for annotation in snippet_annotations] or None elif snippet_property == SnippetProperty.ATTRIBUTION_TEXTS: - return snippet.attribution_texts + return snippet.attribution_texts or None elif snippet_property == SnippetProperty.COMMENT: return snippet.comment elif snippet_property == SnippetProperty.COPYRIGHT_TEXT: @@ -50,7 +50,7 @@ def _get_property_value(self, snippet: Snippet, snippet_property: SnippetPropert return apply_if_present(str, snippet.concluded_license) elif snippet_property == SnippetProperty.LICENSE_INFO_IN_SNIPPETS: if isinstance(snippet.license_info_in_snippet, list): - return [str(license_expression) for license_expression in snippet.license_info_in_snippet] + return [str(license_expression) for license_expression in snippet.license_info_in_snippet] or None return apply_if_present(str, snippet.license_info_in_snippet) elif snippet_property == SnippetProperty.NAME: return snippet.name diff --git a/tests/jsonschema/test_creation_info_converter.py b/tests/jsonschema/test_creation_info_converter.py index 0fb500ebc..6e236d798 100644 --- a/tests/jsonschema/test_creation_info_converter.py +++ b/tests/jsonschema/test_creation_info_converter.py @@ -51,12 +51,13 @@ def test_successful_conversion(converter: CreationInfoConverter): def test_null_values(converter: CreationInfoConverter): - creation_info = creation_info_fixture(license_list_version=None, creator_comment=None) + creation_info = creation_info_fixture(license_list_version=None, creator_comment=None, creators=[]) converted_dict = converter.convert(creation_info) assert converter.json_property_name(CreationInfoProperty.LICENSE_LIST_VERSION) not in converted_dict assert converter.json_property_name(CreationInfoProperty.COMMENT) not in converted_dict + assert converter.json_property_name(CreationInfoProperty.CREATORS) not in converted_dict def test_json_type(converter: CreationInfoConverter): diff --git a/tests/jsonschema/test_document_converter.py b/tests/jsonschema/test_document_converter.py index cac563dd5..b16ad4bbc 100644 --- a/tests/jsonschema/test_document_converter.py +++ b/tests/jsonschema/test_document_converter.py @@ -116,3 +116,18 @@ def test_json_type(converter: DocumentConverter): def test_data_model_type(converter: DocumentConverter): assert converter.get_data_model_type() == Document + + +def test_null_values(converter: DocumentConverter): + document = Document(creation_info_fixture(external_document_refs=[])) + + converted_dict = converter.convert(document) + + assert converter.json_property_name(DocumentProperty.ANNOTATIONS) not in converted_dict + assert converter.json_property_name(DocumentProperty.EXTERNAL_DOCUMENT_REFS) not in converted_dict + assert converter.json_property_name(DocumentProperty.HAS_EXTRACTED_LICENSING_INFO) not in converted_dict + assert converter.json_property_name(DocumentProperty.DOCUMENT_DESCRIBES) not in converted_dict + assert converter.json_property_name(DocumentProperty.PACKAGES) not in converted_dict + assert converter.json_property_name(DocumentProperty.FILES) not in converted_dict + assert converter.json_property_name(DocumentProperty.SNIPPETS) not in converted_dict + assert converter.json_property_name(DocumentProperty.RELATIONSHIPS) not in converted_dict diff --git a/tests/jsonschema/test_extracted_licensing_info_converter.py b/tests/jsonschema/test_extracted_licensing_info_converter.py index 518a6f2b3..d5085d889 100644 --- a/tests/jsonschema/test_extracted_licensing_info_converter.py +++ b/tests/jsonschema/test_extracted_licensing_info_converter.py @@ -53,3 +53,15 @@ def test_successful_conversion(converter: ExtractedLicensingInfoConverter): assert converted_dict[converter.json_property_name(ExtractedLicensingInfoProperty.SEE_ALSOS)] == ["reference1", "reference2"] assert converted_dict[converter.json_property_name(ExtractedLicensingInfoProperty.COMMENT)] == "comment" + + +def test_null_values(converter: ExtractedLicensingInfoConverter): + extracted_licensing_info = ExtractedLicensingInfo(cross_references=[]) + + converted_dict = converter.convert(extracted_licensing_info) + + assert converter.json_property_name(ExtractedLicensingInfoProperty.LICENSE_ID) not in converted_dict + assert converter.json_property_name(ExtractedLicensingInfoProperty.EXTRACTED_TEXT) not in converted_dict + assert converter.json_property_name(ExtractedLicensingInfoProperty.NAME) not in converted_dict + assert converter.json_property_name(ExtractedLicensingInfoProperty.SEE_ALSOS) not in converted_dict + assert converter.json_property_name(ExtractedLicensingInfoProperty.COMMENT) not in converted_dict diff --git a/tests/jsonschema/test_file_converter.py b/tests/jsonschema/test_file_converter.py index 7cc6f0462..d90bf6883 100644 --- a/tests/jsonschema/test_file_converter.py +++ b/tests/jsonschema/test_file_converter.py @@ -103,7 +103,8 @@ def test_successful_conversion(converter: FileConverter): def test_null_values(converter: FileConverter): - file = file_fixture(copyright_text=None, concluded_license=None, license_comment=None, comment=None, notice=None) + file = file_fixture(copyright_text=None, concluded_license=None, license_comment=None, comment=None, notice=None, + attribution_texts=[], checksums=[], contributors=[], file_type=[], license_info_in_file=[]) document = Document(creation_info_fixture(), files=[file]) converted_dict = converter.convert(file, document) @@ -113,6 +114,12 @@ def test_null_values(converter: FileConverter): assert converter.json_property_name(FileProperty.LICENSE_COMMENTS) not in converted_dict assert converter.json_property_name(FileProperty.COMMENT) not in converted_dict assert converter.json_property_name(FileProperty.NOTICE_TEXT) not in converted_dict + assert converter.json_property_name(FileProperty.ANNOTATIONS) not in converted_dict + assert converter.json_property_name(FileProperty.ATTRIBUTION_TEXTS) not in converted_dict + assert converter.json_property_name(FileProperty.CHECKSUMS) not in converted_dict + assert converter.json_property_name(FileProperty.FILE_CONTRIBUTORS) not in converted_dict + assert converter.json_property_name(FileProperty.FILE_TYPES) not in converted_dict + assert converter.json_property_name(FileProperty.LICENSE_INFO_IN_FILES) not in converted_dict def test_spdx_no_assertion(converter: FileConverter): diff --git a/tests/jsonschema/test_package_converter.py b/tests/jsonschema/test_package_converter.py index ba769a616..71e8c1ae7 100644 --- a/tests/jsonschema/test_package_converter.py +++ b/tests/jsonschema/test_package_converter.py @@ -150,7 +150,8 @@ def test_null_values(converter: PackageConverter): license_concluded=None, license_declared=None, originator=None, verification_code=None, primary_package_purpose=None, supplier=None, version=None, file_name=None, source_info=None, license_comment=None, copyright_text=None, summary=None, - description=None, comment=None) + description=None, comment=None, attribution_texts=[], checksums=[], + external_references=[], license_info_from_files=[]) document = Document(creation_info_fixture(), packages=[package]) @@ -174,6 +175,12 @@ def test_null_values(converter: PackageConverter): assert converter.json_property_name(PackageProperty.BUILT_DATE) not in converted_dict assert converter.json_property_name(PackageProperty.RELEASE_DATE) not in converted_dict assert converter.json_property_name(PackageProperty.VALID_UNTIL_DATE) not in converted_dict + assert converter.json_property_name(PackageProperty.ANNOTATIONS) not in converted_dict + assert converter.json_property_name(PackageProperty.ATTRIBUTION_TEXTS) not in converted_dict + assert converter.json_property_name(PackageProperty.CHECKSUMS) not in converted_dict + assert converter.json_property_name(PackageProperty.EXTERNAL_REFS) not in converted_dict + assert converter.json_property_name(PackageProperty.HAS_FILES) not in converted_dict + assert converter.json_property_name(PackageProperty.LICENSE_INFO_FROM_FILES) not in converted_dict def test_spdx_no_assertion(converter: PackageConverter): diff --git a/tests/jsonschema/test_package_verification_code_converter.py b/tests/jsonschema/test_package_verification_code_converter.py index 926c6e428..48d7d649b 100644 --- a/tests/jsonschema/test_package_verification_code_converter.py +++ b/tests/jsonschema/test_package_verification_code_converter.py @@ -47,3 +47,12 @@ def test_successful_conversion(converter: PackageVerificationCodeConverter): PackageVerificationCodeProperty.PACKAGE_VERIFICATION_CODE_EXCLUDED_FILES)] == ["file1", "file2"] assert converted_dict[ converter.json_property_name(PackageVerificationCodeProperty.PACKAGE_VERIFICATION_CODE_VALUE)] == "value" + + +def test_null_values(converter: PackageVerificationCodeConverter): + package_verification_code = PackageVerificationCode("value") + + converted_dict = converter.convert(package_verification_code) + + assert converter.json_property_name( + PackageVerificationCodeProperty.PACKAGE_VERIFICATION_CODE_EXCLUDED_FILES) not in converted_dict diff --git a/tests/jsonschema/test_snippet_converter.py b/tests/jsonschema/test_snippet_converter.py index 746476d3a..61262ce5d 100644 --- a/tests/jsonschema/test_snippet_converter.py +++ b/tests/jsonschema/test_snippet_converter.py @@ -95,7 +95,7 @@ def test_successful_conversion(converter: SnippetConverter): def test_null_values(converter: SnippetConverter): snippet = snippet_fixture(concluded_license=None, license_comment=None, copyright_text=None, comment=None, - name=None) + name=None, attribution_texts=[], license_info_in_snippet=[]) document = Document(creation_info_fixture(), snippets=[snippet]) converted_dict = converter.convert(snippet, document) @@ -105,6 +105,9 @@ def test_null_values(converter: SnippetConverter): assert converter.json_property_name(SnippetProperty.COPYRIGHT_TEXT) not in converted_dict assert converter.json_property_name(SnippetProperty.COMMENT) not in converted_dict assert converter.json_property_name(SnippetProperty.NAME) not in converted_dict + assert converter.json_property_name(SnippetProperty.ANNOTATIONS) not in converted_dict + assert converter.json_property_name(SnippetProperty.ATTRIBUTION_TEXTS) not in converted_dict + assert converter.json_property_name(SnippetProperty.LICENSE_INFO_IN_SNIPPETS) not in converted_dict def test_spdx_no_assertion(converter: SnippetConverter): From 2052cd24ba3d25d05351dc71280c9aadbfbda793 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Wed, 21 Dec 2022 13:43:05 +0100 Subject: [PATCH 098/362] [issue-359] Add json writer Signed-off-by: Nicolaus Weidner --- src/writer/json/__init__.py | 11 ++ src/writer/json/json_writer.py | 26 +++++ tests/writer/__init__.py | 10 ++ tests/writer/json/__init__.py | 10 ++ .../writer/json/expected_results/__init__.py | 10 ++ .../json/expected_results/expected.json | 101 ++++++++++++++++++ tests/writer/json/test_json_writer.py | 69 ++++++++++++ 7 files changed, 237 insertions(+) create mode 100644 src/writer/json/__init__.py create mode 100644 src/writer/json/json_writer.py create mode 100644 tests/writer/__init__.py create mode 100644 tests/writer/json/__init__.py create mode 100644 tests/writer/json/expected_results/__init__.py create mode 100644 tests/writer/json/expected_results/expected.json create mode 100644 tests/writer/json/test_json_writer.py diff --git a/src/writer/json/__init__.py b/src/writer/json/__init__.py new file mode 100644 index 000000000..7b5337962 --- /dev/null +++ b/src/writer/json/__init__.py @@ -0,0 +1,11 @@ +# 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. + diff --git a/src/writer/json/json_writer.py b/src/writer/json/json_writer.py new file mode 100644 index 000000000..f6c85f5a4 --- /dev/null +++ b/src/writer/json/json_writer.py @@ -0,0 +1,26 @@ +# 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. +import json + +from src.jsonschema.document_converter import DocumentConverter +from src.model.document import Document + + +class JsonWriter: + converter: DocumentConverter + + def __init__(self): + self.converter = DocumentConverter() + + def write_document(self, document: Document, file_name: str) -> None: + document_dict = self.converter.convert(document) + with open(file_name, "w") as out: + json.dump(document_dict, out, indent=4) diff --git a/tests/writer/__init__.py b/tests/writer/__init__.py new file mode 100644 index 000000000..cbc5c4070 --- /dev/null +++ b/tests/writer/__init__.py @@ -0,0 +1,10 @@ +# 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. diff --git a/tests/writer/json/__init__.py b/tests/writer/json/__init__.py new file mode 100644 index 000000000..cbc5c4070 --- /dev/null +++ b/tests/writer/json/__init__.py @@ -0,0 +1,10 @@ +# 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. diff --git a/tests/writer/json/expected_results/__init__.py b/tests/writer/json/expected_results/__init__.py new file mode 100644 index 000000000..cbc5c4070 --- /dev/null +++ b/tests/writer/json/expected_results/__init__.py @@ -0,0 +1,10 @@ +# 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. diff --git a/tests/writer/json/expected_results/expected.json b/tests/writer/json/expected_results/expected.json new file mode 100644 index 000000000..3335e12eb --- /dev/null +++ b/tests/writer/json/expected_results/expected.json @@ -0,0 +1,101 @@ +{ + "SPDXID": "documentId", + "annotations": [ + { + "annotationDate": "2022-12-02T00:00:00Z", + "annotationType": "REVIEW", + "annotator": "Person: reviewerName", + "comment": "reviewComment" + } + ], + "comment": "comment", + "creationInfo": { + "created": "2022-12-01T00:00:00Z", + "creators": [ + "Tool: tools-python (tools-python@github.com)" + ] + }, + "dataLicense": "dataLicense", + "externalDocumentRefs": [ + { + "externalDocumentId": "docRefId", + "spdxDocument": "externalDocumentUri", + "checksum": { + "algorithm": "SHA1", + "checksumValue": "externalRefSha1" + } + } + ], + "hasExtractedLicensingInfo": [ + { + "extractedText": "licenseText", + "licenseId": "licenseId" + } + ], + "name": "documentName", + "spdxVersion": "spdxVersion", + "documentNamespace": "documentNamespace", + "documentDescribes": [ + "packageId", + "fileId" + ], + "packages": [ + { + "SPDXID": "packageId", + "downloadLocation": "NONE", + "filesAnalyzed": true, + "name": "packageName" + } + ], + "files": [ + { + "SPDXID": "fileId", + "annotations": [ + { + "annotationDate": "2022-12-03T00:00:00Z", + "annotationType": "OTHER", + "annotator": "Tool: toolName", + "comment": "otherComment" + } + ], + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "fileSha1" + } + ], + "fileName": "fileName" + } + ], + "snippets": [ + { + "SPDXID": "snippetId", + "ranges": [ + { + "startPointer": { + "reference": "snippetFileId", + "offset": 1 + }, + "endPointer": { + "reference": "snippetFileId", + "offset": 2 + } + } + ], + "snippetFromFile": "snippetFileId" + } + ], + "relationships": [ + { + "spdxElementId": "documentId", + "comment": "relationshipComment", + "relatedSpdxElement": "fileId", + "relationshipType": "DESCRIBES" + }, + { + "spdxElementId": "relationshipOriginId", + "relatedSpdxElement": "relationShipTargetId", + "relationshipType": "AMENDS" + } + ] +} diff --git a/tests/writer/json/test_json_writer.py b/tests/writer/json/test_json_writer.py new file mode 100644 index 000000000..ea699dad4 --- /dev/null +++ b/tests/writer/json/test_json_writer.py @@ -0,0 +1,69 @@ +# 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. +import json +import os +from datetime import datetime + +import pytest + +from src.model.actor import Actor, ActorType +from src.model.annotation import Annotation, AnnotationType +from src.model.checksum import ChecksumAlgorithm, Checksum +from src.model.document import CreationInfo, Document +from src.model.external_document_ref import ExternalDocumentRef +from src.model.extracted_licensing_info import ExtractedLicensingInfo +from src.model.file import File +from src.model.package import Package +from src.model.relationship import RelationshipType, Relationship +from src.model.snippet import Snippet +from src.model.spdx_none import SpdxNone +from src.writer.json.json_writer import JsonWriter + + +@pytest.fixture +def temporary_file_path() -> str: + temporary_file_path = "temp_test_json_writer_output.json" + yield temporary_file_path + os.remove(temporary_file_path) + + +def test_write_json(temporary_file_path: str): + writer = JsonWriter() + creation_info = CreationInfo("spdxVersion", "documentId", "documentName", "documentNamespace", + [Actor(ActorType.TOOL, "tools-python", "tools-python@github.com")], + datetime(2022, 12, 1), document_comment="comment", data_license="dataLicense", + external_document_refs=[ExternalDocumentRef("docRefId", "externalDocumentUri", + Checksum(ChecksumAlgorithm.SHA1, + "externalRefSha1"))]) + package = Package("packageId", "packageName", SpdxNone()) + file = File("fileName", "fileId", [Checksum(ChecksumAlgorithm.SHA1, "fileSha1")]) + snippet = Snippet("snippetId", "snippetFileId", (1, 2)) + relationships = [ + Relationship(creation_info.spdx_id, RelationshipType.DESCRIBES, "packageId"), + Relationship(creation_info.spdx_id, RelationshipType.DESCRIBES, "fileId", "relationshipComment"), + Relationship("relationshipOriginId", RelationshipType.AMENDS, "relationShipTargetId")] + annotations = [ + Annotation("documentId", AnnotationType.REVIEW, Actor(ActorType.PERSON, "reviewerName"), + datetime(2022, 12, 2), "reviewComment"), + Annotation("fileId", AnnotationType.OTHER, Actor(ActorType.TOOL, "toolName"), datetime(2022, 12, 3), + "otherComment")] + extracted_licensing_info = [ExtractedLicensingInfo("licenseId", "licenseText")] + document = Document(creation_info, annotations=annotations, extracted_licensing_info=extracted_licensing_info, + relationships=relationships, packages=[package], files=[file], snippets=[snippet]) + writer.write_document(document, temporary_file_path) + + with open(temporary_file_path) as written_file: + written_json = json.load(written_file) + + with open(os.path.join(os.path.dirname(__file__), 'expected_results', 'expected.json')) as expected_file: + expected_json = json.load(expected_file) + + assert written_json == expected_json From 0d209543a8d83be6232cd45914d052d2f3ee1b8b Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Wed, 21 Dec 2022 23:46:26 +0100 Subject: [PATCH 099/362] [issue-359] Make conversion test assertions more precise by excluding any additional properties Signed-off-by: Nicolaus Weidner --- tests/jsonschema/test_annotation_converter.py | 12 ++-- tests/jsonschema/test_checksum_converter.py | 6 +- tests/jsonschema/test_converter.py | 6 +- .../test_creation_info_converter.py | 11 ++-- tests/jsonschema/test_document_converter.py | 38 ++++++------ .../test_external_document_ref_converter.py | 10 +-- .../test_external_package_ref_converter.py | 11 ++-- ...test_extracted_licensing_info_converter.py | 14 ++--- tests/jsonschema/test_file_converter.py | 33 +++++----- tests/jsonschema/test_package_converter.py | 61 +++++++++---------- ...est_package_verification_code_converter.py | 9 +-- .../jsonschema/test_relationship_converter.py | 10 +-- tests/jsonschema/test_snippet_converter.py | 35 +++++------ 13 files changed, 130 insertions(+), 126 deletions(-) diff --git a/tests/jsonschema/test_annotation_converter.py b/tests/jsonschema/test_annotation_converter.py index 1f64ba336..714b7bb4a 100644 --- a/tests/jsonschema/test_annotation_converter.py +++ b/tests/jsonschema/test_annotation_converter.py @@ -48,9 +48,9 @@ def test_successful_conversion(converter: AnnotationConverter): converted_dict = converter.convert(annotation) - assert converted_dict[converter.json_property_name(AnnotationProperty.ANNOTATION_DATE)] == datetime_to_iso_string( - date) - assert converted_dict[converter.json_property_name(AnnotationProperty.ANNOTATION_TYPE)] == "REVIEW" - assert converted_dict[ - converter.json_property_name(AnnotationProperty.ANNOTATOR)] == annotator.to_serialized_string() - assert converted_dict[converter.json_property_name(AnnotationProperty.COMMENT)] == "comment" + assert converted_dict == { + converter.json_property_name(AnnotationProperty.ANNOTATION_DATE): datetime_to_iso_string(date), + converter.json_property_name(AnnotationProperty.ANNOTATION_TYPE): "REVIEW", + converter.json_property_name(AnnotationProperty.ANNOTATOR): annotator.to_serialized_string(), + converter.json_property_name(AnnotationProperty.COMMENT): "comment" + } diff --git a/tests/jsonschema/test_checksum_converter.py b/tests/jsonschema/test_checksum_converter.py index 088f853f1..9412761de 100644 --- a/tests/jsonschema/test_checksum_converter.py +++ b/tests/jsonschema/test_checksum_converter.py @@ -31,8 +31,10 @@ def test_successful_conversion(converter: ChecksumConverter): converted_dict = converter.convert(checksum) - assert converted_dict[converter.json_property_name(ChecksumProperty.ALGORITHM)] == "SHA1" - assert converted_dict[converter.json_property_name(ChecksumProperty.CHECKSUM_VALUE)] == "123" + assert converted_dict == { + converter.json_property_name(ChecksumProperty.ALGORITHM): "SHA1", + converter.json_property_name(ChecksumProperty.CHECKSUM_VALUE): "123" + } def test_json_type(converter: ChecksumConverter): diff --git a/tests/jsonschema/test_converter.py b/tests/jsonschema/test_converter.py index add64a706..0123c3b19 100644 --- a/tests/jsonschema/test_converter.py +++ b/tests/jsonschema/test_converter.py @@ -63,8 +63,10 @@ def test_conversion(): converted_dict = converter.convert(test_instance) - assert converted_dict.get("jsonFirstName") == "firstPropertyValue" - assert converted_dict.get("jsonSecondName") == 3 + assert converted_dict == { + "jsonFirstName": "firstPropertyValue", + "jsonSecondName": 3 + } def test_wrong_type(): diff --git a/tests/jsonschema/test_creation_info_converter.py b/tests/jsonschema/test_creation_info_converter.py index 6e236d798..87cd0a0a1 100644 --- a/tests/jsonschema/test_creation_info_converter.py +++ b/tests/jsonschema/test_creation_info_converter.py @@ -43,11 +43,12 @@ def test_successful_conversion(converter: CreationInfoConverter): creation_info_fixture(creators=creators, created=created, creator_comment="comment", license_list_version=Version(1, 2))) - assert converted_dict[converter.json_property_name(CreationInfoProperty.CREATED)] == datetime_to_iso_string(created) - assert converted_dict[converter.json_property_name(CreationInfoProperty.CREATORS)] == ["Person: personName", - "Tool: toolName"] - assert converted_dict[converter.json_property_name(CreationInfoProperty.LICENSE_LIST_VERSION)] == "1.2" - assert converted_dict[converter.json_property_name(CreationInfoProperty.COMMENT)] == "comment" + assert converted_dict == { + converter.json_property_name(CreationInfoProperty.CREATED): datetime_to_iso_string(created), + converter.json_property_name(CreationInfoProperty.CREATORS): ["Person: personName", "Tool: toolName"], + converter.json_property_name(CreationInfoProperty.LICENSE_LIST_VERSION): "1.2", + converter.json_property_name(CreationInfoProperty.COMMENT): "comment" + } def test_null_values(converter: CreationInfoConverter): diff --git a/tests/jsonschema/test_document_converter.py b/tests/jsonschema/test_document_converter.py index b16ad4bbc..4a7e29754 100644 --- a/tests/jsonschema/test_document_converter.py +++ b/tests/jsonschema/test_document_converter.py @@ -88,26 +88,24 @@ def test_successful_conversion(converter: DocumentConverter): converted_dict = converter.convert(document) - assert converted_dict[converter.json_property_name(DocumentProperty.SPDX_ID)] == "spdxId" - assert converted_dict[converter.json_property_name(DocumentProperty.ANNOTATIONS)] == ["mock_converted_annotation"] - assert converted_dict[converter.json_property_name(DocumentProperty.COMMENT)] == "comment" - assert converted_dict[ - converter.json_property_name(DocumentProperty.CREATION_INFO)] == "mock_converted_creation_info" - assert converted_dict[converter.json_property_name(DocumentProperty.DATA_LICENSE)] == "dataLicense" - assert converted_dict[ - converter.json_property_name( - DocumentProperty.EXTERNAL_DOCUMENT_REFS)] == ["mock_converted_external_ref"] - assert converted_dict[converter.json_property_name(DocumentProperty.HAS_EXTRACTED_LICENSING_INFO)] == [ - "mock_converted_extracted_licensing_info"] - assert converted_dict[converter.json_property_name(DocumentProperty.NAME)] == "name" - assert converted_dict[converter.json_property_name(DocumentProperty.SPDX_VERSION)] == "spdxVersion" - assert converted_dict[converter.json_property_name(DocumentProperty.DOCUMENT_NAMESPACE)] == "namespace" - assert converted_dict[converter.json_property_name(DocumentProperty.DOCUMENT_DESCRIBES)] == ["describedElementId"] - assert converted_dict[converter.json_property_name(DocumentProperty.PACKAGES)] == ["mock_converted_package"] - assert converted_dict[converter.json_property_name(DocumentProperty.FILES)] == ["mock_converted_file"] - assert converted_dict[converter.json_property_name(DocumentProperty.SNIPPETS)] == ["mock_converted_snippet"] - assert converted_dict[converter.json_property_name(DocumentProperty.RELATIONSHIPS)] == [ - "mock_converted_relationship"] + assert converted_dict == { + converter.json_property_name(DocumentProperty.SPDX_ID): "spdxId", + converter.json_property_name(DocumentProperty.ANNOTATIONS): ["mock_converted_annotation"], + converter.json_property_name(DocumentProperty.COMMENT): "comment", + converter.json_property_name(DocumentProperty.CREATION_INFO): "mock_converted_creation_info", + converter.json_property_name(DocumentProperty.DATA_LICENSE): "dataLicense", + converter.json_property_name(DocumentProperty.EXTERNAL_DOCUMENT_REFS): ["mock_converted_external_ref"], + converter.json_property_name(DocumentProperty.HAS_EXTRACTED_LICENSING_INFO): [ + "mock_converted_extracted_licensing_info"], + converter.json_property_name(DocumentProperty.NAME): "name", + converter.json_property_name(DocumentProperty.SPDX_VERSION): "spdxVersion", + converter.json_property_name(DocumentProperty.DOCUMENT_NAMESPACE): "namespace", + converter.json_property_name(DocumentProperty.DOCUMENT_DESCRIBES): ["describedElementId"], + converter.json_property_name(DocumentProperty.PACKAGES): ["mock_converted_package"], + converter.json_property_name(DocumentProperty.FILES): ["mock_converted_file"], + converter.json_property_name(DocumentProperty.SNIPPETS): ["mock_converted_snippet"], + converter.json_property_name(DocumentProperty.RELATIONSHIPS): ["mock_converted_relationship"] + } def test_json_type(converter: DocumentConverter): diff --git a/tests/jsonschema/test_external_document_ref_converter.py b/tests/jsonschema/test_external_document_ref_converter.py index 0694b95ea..51932c125 100644 --- a/tests/jsonschema/test_external_document_ref_converter.py +++ b/tests/jsonschema/test_external_document_ref_converter.py @@ -44,11 +44,11 @@ def test_successful_conversion(converter: ExternalDocumentRefConverter): converted_dict = converter.convert(external_document_ref) - assert converted_dict[ - converter.json_property_name(ExternalDocumentRefProperty.EXTERNAL_DOCUMENT_ID)] == "document_ref_id" - assert converted_dict[converter.json_property_name(ExternalDocumentRefProperty.SPDX_DOCUMENT)] == "document_uri" - assert converted_dict[ - converter.json_property_name(ExternalDocumentRefProperty.CHECKSUM)] == "mock_converted_checksum" + assert converted_dict == { + converter.json_property_name(ExternalDocumentRefProperty.EXTERNAL_DOCUMENT_ID): "document_ref_id", + converter.json_property_name(ExternalDocumentRefProperty.SPDX_DOCUMENT): "document_uri", + converter.json_property_name(ExternalDocumentRefProperty.CHECKSUM): "mock_converted_checksum" + } def test_json_type(converter: ExternalDocumentRefConverter): diff --git a/tests/jsonschema/test_external_package_ref_converter.py b/tests/jsonschema/test_external_package_ref_converter.py index 44094c00d..7af0752e1 100644 --- a/tests/jsonschema/test_external_package_ref_converter.py +++ b/tests/jsonschema/test_external_package_ref_converter.py @@ -43,8 +43,9 @@ def test_successful_conversion(converter: ExternalPackageRefConverter): converted_dict = converter.convert(external_package_ref) - assert converted_dict[converter.json_property_name(ExternalPackageRefProperty.COMMENT)] == "comment" - assert converted_dict[ - converter.json_property_name(ExternalPackageRefProperty.REFERENCE_CATEGORY)] == "PACKAGE_MANAGER" - assert converted_dict[converter.json_property_name(ExternalPackageRefProperty.REFERENCE_LOCATOR)] == "locator" - assert converted_dict[converter.json_property_name(ExternalPackageRefProperty.REFERENCE_TYPE)] == "type" + assert converted_dict == { + converter.json_property_name(ExternalPackageRefProperty.COMMENT): "comment", + converter.json_property_name(ExternalPackageRefProperty.REFERENCE_CATEGORY): "PACKAGE_MANAGER", + converter.json_property_name(ExternalPackageRefProperty.REFERENCE_LOCATOR): "locator", + converter.json_property_name(ExternalPackageRefProperty.REFERENCE_TYPE): "type" + } diff --git a/tests/jsonschema/test_extracted_licensing_info_converter.py b/tests/jsonschema/test_extracted_licensing_info_converter.py index d5085d889..39c146e84 100644 --- a/tests/jsonschema/test_extracted_licensing_info_converter.py +++ b/tests/jsonschema/test_extracted_licensing_info_converter.py @@ -46,13 +46,13 @@ def test_successful_conversion(converter: ExtractedLicensingInfoConverter): converted_dict = converter.convert(extracted_licensing_info) - assert converted_dict[converter.json_property_name(ExtractedLicensingInfoProperty.LICENSE_ID)] == "licenseId" - assert converted_dict[ - converter.json_property_name(ExtractedLicensingInfoProperty.EXTRACTED_TEXT)] == "Extracted text" - assert converted_dict[converter.json_property_name(ExtractedLicensingInfoProperty.NAME)] == "license name" - assert converted_dict[converter.json_property_name(ExtractedLicensingInfoProperty.SEE_ALSOS)] == ["reference1", - "reference2"] - assert converted_dict[converter.json_property_name(ExtractedLicensingInfoProperty.COMMENT)] == "comment" + assert converted_dict == { + converter.json_property_name(ExtractedLicensingInfoProperty.LICENSE_ID): "licenseId", + converter.json_property_name(ExtractedLicensingInfoProperty.EXTRACTED_TEXT): "Extracted text", + converter.json_property_name(ExtractedLicensingInfoProperty.NAME): "license name", + converter.json_property_name(ExtractedLicensingInfoProperty.SEE_ALSOS): ["reference1", "reference2"], + converter.json_property_name(ExtractedLicensingInfoProperty.COMMENT): "comment" + } def test_null_values(converter: ExtractedLicensingInfoConverter): diff --git a/tests/jsonschema/test_file_converter.py b/tests/jsonschema/test_file_converter.py index d90bf6883..faca0a954 100644 --- a/tests/jsonschema/test_file_converter.py +++ b/tests/jsonschema/test_file_converter.py @@ -82,24 +82,21 @@ def test_successful_conversion(converter: FileConverter): converted_dict = converter.convert(file, document) - assert converted_dict[converter.json_property_name(FileProperty.SPDX_ID)] == "spdxId" - assert converted_dict[converter.json_property_name(FileProperty.ANNOTATIONS)] == ["mock_converted_annotation"] - assert converted_dict[converter.json_property_name(FileProperty.ATTRIBUTION_TEXTS)] == ["attributionText1", - "attributionText2"] - assert converted_dict[converter.json_property_name(FileProperty.CHECKSUMS)] == ["mock_converted_checksum", - "mock_converted_checksum"] - assert converted_dict[converter.json_property_name(FileProperty.COMMENT)] == "comment" - assert converted_dict[ - converter.json_property_name(FileProperty.COPYRIGHT_TEXT)] == "copyrightText" - assert converted_dict[converter.json_property_name(FileProperty.FILE_CONTRIBUTORS)] == ["contributor1", - "contributor2"] - assert converted_dict[converter.json_property_name(FileProperty.FILE_NAME)] == "name" - assert converted_dict[converter.json_property_name(FileProperty.FILE_TYPES)] == ["SPDX", "OTHER"] - assert converted_dict[converter.json_property_name(FileProperty.LICENSE_COMMENTS)] == "licenseComment" - assert converted_dict[converter.json_property_name(FileProperty.LICENSE_CONCLUDED)] == "licenseExpression1" - assert converted_dict[converter.json_property_name(FileProperty.LICENSE_INFO_IN_FILES)] == ["licenseExpression2", - "licenseExpression3"] - assert converted_dict[converter.json_property_name(FileProperty.NOTICE_TEXT)] == "notice" + assert converted_dict == { + converter.json_property_name(FileProperty.SPDX_ID): "spdxId", + converter.json_property_name(FileProperty.ANNOTATIONS): ["mock_converted_annotation"], + converter.json_property_name(FileProperty.ATTRIBUTION_TEXTS): ["attributionText1", "attributionText2"], + converter.json_property_name(FileProperty.CHECKSUMS): ["mock_converted_checksum", "mock_converted_checksum"], + converter.json_property_name(FileProperty.COMMENT): "comment", + converter.json_property_name(FileProperty.COPYRIGHT_TEXT): "copyrightText", + converter.json_property_name(FileProperty.FILE_CONTRIBUTORS): ["contributor1", "contributor2"], + converter.json_property_name(FileProperty.FILE_NAME): "name", + converter.json_property_name(FileProperty.FILE_TYPES): ["SPDX", "OTHER"], + converter.json_property_name(FileProperty.LICENSE_COMMENTS): "licenseComment", + converter.json_property_name(FileProperty.LICENSE_CONCLUDED): "licenseExpression1", + converter.json_property_name(FileProperty.LICENSE_INFO_IN_FILES): ["licenseExpression2", "licenseExpression3"], + converter.json_property_name(FileProperty.NOTICE_TEXT): "notice" + } def test_null_values(converter: FileConverter): diff --git a/tests/jsonschema/test_package_converter.py b/tests/jsonschema/test_package_converter.py index 71e8c1ae7..3592a9e46 100644 --- a/tests/jsonschema/test_package_converter.py +++ b/tests/jsonschema/test_package_converter.py @@ -112,37 +112,36 @@ def test_successful_conversion(converter: PackageConverter): converted_dict = converter.convert(package, document) - assert converted_dict[converter.json_property_name(PackageProperty.SPDX_ID)] == "packageId" - assert converted_dict[converter.json_property_name(PackageProperty.ANNOTATIONS)] == ["mock_converted_annotation"] - assert converted_dict[converter.json_property_name(PackageProperty.ATTRIBUTION_TEXTS)] == ["attributionText1", - "attributionText2"] - assert converted_dict[converter.json_property_name(PackageProperty.NAME)] == "name" - assert converted_dict[converter.json_property_name(PackageProperty.DOWNLOAD_LOCATION)] == "downloadLocation" - assert converted_dict[converter.json_property_name(PackageProperty.VERSION_INFO)] == "version" - assert converted_dict[converter.json_property_name(PackageProperty.PACKAGE_FILE_NAME)] == "fileName" - assert converted_dict[converter.json_property_name(PackageProperty.SUPPLIER)] == "Person: supplierName" - assert converted_dict[converter.json_property_name(PackageProperty.ORIGINATOR)] == "Person: originatorName" - assert converted_dict[converter.json_property_name(PackageProperty.FILES_ANALYZED)] - assert converted_dict[converter.json_property_name( - PackageProperty.PACKAGE_VERIFICATION_CODE)] == "mock_converted_verification_code" - assert converted_dict[converter.json_property_name(PackageProperty.CHECKSUMS)] == ["mock_converted_checksum", - "mock_converted_checksum"] - assert converted_dict[converter.json_property_name(PackageProperty.HOMEPAGE)] == "homepage" - assert converted_dict[converter.json_property_name(PackageProperty.SOURCE_INFO)] == "sourceInfo" - assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_CONCLUDED)] == "licenseExpression1" - assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_INFO_FROM_FILES)] == [ - "licenseExpression2", "licenseExpression3"] - assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_DECLARED)] == "licenseExpression4" - assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_COMMENTS)] == "licenseComment" - assert converted_dict[converter.json_property_name(PackageProperty.COPYRIGHT_TEXT)] == "copyrightText" - assert converted_dict[converter.json_property_name(PackageProperty.SUMMARY)] == "summary" - assert converted_dict[converter.json_property_name(PackageProperty.DESCRIPTION)] == "description" - assert converted_dict[converter.json_property_name(PackageProperty.COMMENT)] == "comment" - assert converted_dict[converter.json_property_name(PackageProperty.EXTERNAL_REFS)] == ["mock_package_ref"] - assert converted_dict[converter.json_property_name(PackageProperty.PRIMARY_PACKAGE_PURPOSE)] == "APPLICATION" - assert converted_dict[converter.json_property_name(PackageProperty.RELEASE_DATE)] == "2022-12-01T00:00:00Z" - assert converted_dict[converter.json_property_name(PackageProperty.BUILT_DATE)] == "2022-12-02T00:00:00Z" - assert converted_dict[converter.json_property_name(PackageProperty.VALID_UNTIL_DATE)] == "2022-12-03T00:00:00Z" + assert converted_dict == { + converter.json_property_name(PackageProperty.SPDX_ID): "packageId", + converter.json_property_name(PackageProperty.ANNOTATIONS): ["mock_converted_annotation"], + converter.json_property_name(PackageProperty.ATTRIBUTION_TEXTS): ["attributionText1", "attributionText2"], + converter.json_property_name(PackageProperty.NAME): "name", + converter.json_property_name(PackageProperty.DOWNLOAD_LOCATION): "downloadLocation", + converter.json_property_name(PackageProperty.VERSION_INFO): "version", + converter.json_property_name(PackageProperty.PACKAGE_FILE_NAME): "fileName", + converter.json_property_name(PackageProperty.SUPPLIER): "Person: supplierName", + converter.json_property_name(PackageProperty.ORIGINATOR): "Person: originatorName", + converter.json_property_name(PackageProperty.FILES_ANALYZED): True, + converter.json_property_name(PackageProperty.PACKAGE_VERIFICATION_CODE): "mock_converted_verification_code", + converter.json_property_name(PackageProperty.CHECKSUMS): ["mock_converted_checksum", "mock_converted_checksum"], + converter.json_property_name(PackageProperty.HOMEPAGE): "homepage", + converter.json_property_name(PackageProperty.SOURCE_INFO): "sourceInfo", + converter.json_property_name(PackageProperty.LICENSE_CONCLUDED): "licenseExpression1", + converter.json_property_name(PackageProperty.LICENSE_INFO_FROM_FILES): ["licenseExpression2", + "licenseExpression3"], + converter.json_property_name(PackageProperty.LICENSE_DECLARED): "licenseExpression4", + converter.json_property_name(PackageProperty.LICENSE_COMMENTS): "licenseComment", + converter.json_property_name(PackageProperty.COPYRIGHT_TEXT): "copyrightText", + converter.json_property_name(PackageProperty.SUMMARY): "summary", + converter.json_property_name(PackageProperty.DESCRIPTION): "description", + converter.json_property_name(PackageProperty.COMMENT): "comment", + converter.json_property_name(PackageProperty.EXTERNAL_REFS): ["mock_package_ref"], + converter.json_property_name(PackageProperty.PRIMARY_PACKAGE_PURPOSE): "APPLICATION", + converter.json_property_name(PackageProperty.RELEASE_DATE): "2022-12-01T00:00:00Z", + converter.json_property_name(PackageProperty.BUILT_DATE): "2022-12-02T00:00:00Z", + converter.json_property_name(PackageProperty.VALID_UNTIL_DATE): "2022-12-03T00:00:00Z" + } def test_null_values(converter: PackageConverter): diff --git a/tests/jsonschema/test_package_verification_code_converter.py b/tests/jsonschema/test_package_verification_code_converter.py index 48d7d649b..a7050c492 100644 --- a/tests/jsonschema/test_package_verification_code_converter.py +++ b/tests/jsonschema/test_package_verification_code_converter.py @@ -43,10 +43,11 @@ def test_successful_conversion(converter: PackageVerificationCodeConverter): converted_dict = converter.convert(package_verification_code) - assert converted_dict[converter.json_property_name( - PackageVerificationCodeProperty.PACKAGE_VERIFICATION_CODE_EXCLUDED_FILES)] == ["file1", "file2"] - assert converted_dict[ - converter.json_property_name(PackageVerificationCodeProperty.PACKAGE_VERIFICATION_CODE_VALUE)] == "value" + assert converted_dict == { + converter.json_property_name(PackageVerificationCodeProperty.PACKAGE_VERIFICATION_CODE_EXCLUDED_FILES): [ + "file1", "file2"], + converter.json_property_name(PackageVerificationCodeProperty.PACKAGE_VERIFICATION_CODE_VALUE): "value" + } def test_null_values(converter: PackageVerificationCodeConverter): diff --git a/tests/jsonschema/test_relationship_converter.py b/tests/jsonschema/test_relationship_converter.py index 6c6a51a57..a4b372935 100644 --- a/tests/jsonschema/test_relationship_converter.py +++ b/tests/jsonschema/test_relationship_converter.py @@ -43,7 +43,9 @@ def test_successful_conversion(converter: RelationshipConverter): converted_dict = converter.convert(relationship) - assert converted_dict[converter.json_property_name(RelationshipProperty.SPDX_ELEMENT_ID)] == "spdxElementId" - assert converted_dict[converter.json_property_name(RelationshipProperty.COMMENT)] == "comment" - assert converted_dict[converter.json_property_name(RelationshipProperty.RELATED_SPDX_ELEMENT)] == "relatedElementId" - assert converted_dict[converter.json_property_name(RelationshipProperty.RELATIONSHIP_TYPE)] == "COPY_OF" + assert converted_dict == { + converter.json_property_name(RelationshipProperty.SPDX_ELEMENT_ID): "spdxElementId", + converter.json_property_name(RelationshipProperty.COMMENT): "comment", + converter.json_property_name(RelationshipProperty.RELATED_SPDX_ELEMENT): "relatedElementId", + converter.json_property_name(RelationshipProperty.RELATIONSHIP_TYPE): "COPY_OF" + } diff --git a/tests/jsonschema/test_snippet_converter.py b/tests/jsonschema/test_snippet_converter.py index 61262ce5d..d75705de4 100644 --- a/tests/jsonschema/test_snippet_converter.py +++ b/tests/jsonschema/test_snippet_converter.py @@ -74,23 +74,24 @@ def test_successful_conversion(converter: SnippetConverter): document = Document(creation_info_fixture(), snippets=[snippet], annotations=[annotation]) converted_dict = converter.convert(snippet, document) - assert converted_dict[converter.json_property_name(SnippetProperty.SPDX_ID)] == "spdxId" - assert converted_dict[converter.json_property_name(SnippetProperty.ANNOTATIONS)] == ["mock_converted_annotation"] - assert converted_dict[converter.json_property_name(SnippetProperty.ATTRIBUTION_TEXTS)] == ["attributionText1", - "attributionText2"] - assert converted_dict[converter.json_property_name(SnippetProperty.COMMENT)] == "comment" - assert converted_dict[converter.json_property_name(SnippetProperty.COPYRIGHT_TEXT)] == "copyrightText" - assert converted_dict[converter.json_property_name(SnippetProperty.LICENSE_COMMENTS)] == "licenseComment" - assert converted_dict[converter.json_property_name(SnippetProperty.LICENSE_CONCLUDED)] == "licenseExpression1" - assert converted_dict[converter.json_property_name(SnippetProperty.LICENSE_INFO_IN_SNIPPETS)] == [ - "licenseExpression2", "licenseExpression3"] - assert converted_dict[converter.json_property_name(SnippetProperty.NAME)] == "name" - assert converted_dict[converter.json_property_name(SnippetProperty.RANGES)] == [ - {"startPointer": {"reference": file_spdx_id, "offset": 1}, - "endPointer": {"reference": file_spdx_id, "offset": 2}}, - {"startPointer": {"reference": file_spdx_id, "lineNumber": 3}, - "endPointer": {"reference": file_spdx_id, "lineNumber": 4}}] - assert converted_dict[converter.json_property_name(SnippetProperty.SNIPPET_FROM_FILE)] == file_spdx_id + assert converted_dict == { + converter.json_property_name(SnippetProperty.SPDX_ID): "spdxId", + converter.json_property_name(SnippetProperty.ANNOTATIONS): ["mock_converted_annotation"], + converter.json_property_name(SnippetProperty.ATTRIBUTION_TEXTS): ["attributionText1", "attributionText2"], + converter.json_property_name(SnippetProperty.COMMENT): "comment", + converter.json_property_name(SnippetProperty.COPYRIGHT_TEXT): "copyrightText", + converter.json_property_name(SnippetProperty.LICENSE_COMMENTS): "licenseComment", + converter.json_property_name(SnippetProperty.LICENSE_CONCLUDED): "licenseExpression1", + converter.json_property_name(SnippetProperty.LICENSE_INFO_IN_SNIPPETS): ["licenseExpression2", + "licenseExpression3"], + converter.json_property_name(SnippetProperty.NAME): "name", + converter.json_property_name(SnippetProperty.RANGES): [ + {"startPointer": {"reference": file_spdx_id, "offset": 1}, + "endPointer": {"reference": file_spdx_id, "offset": 2}}, + {"startPointer": {"reference": file_spdx_id, "lineNumber": 3}, + "endPointer": {"reference": file_spdx_id, "lineNumber": 4}}], + converter.json_property_name(SnippetProperty.SNIPPET_FROM_FILE): file_spdx_id + } def test_null_values(converter: SnippetConverter): From 8e221b4764d664a2c1d52ffffb22153669202e23 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 22 Dec 2022 11:30:25 +0100 Subject: [PATCH 100/362] [issue-359] Add more test fixtures Signed-off-by: Nicolaus Weidner --- tests/fixtures.py | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index ae9d4b3d8..16c757ee0 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -11,13 +11,16 @@ from datetime import datetime from src.model.actor import Actor, ActorType +from src.model.annotation import Annotation, AnnotationType from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.document import CreationInfo +from src.model.document import CreationInfo, Document from src.model.external_document_ref import ExternalDocumentRef +from src.model.extracted_licensing_info import ExtractedLicensingInfo from src.model.file import File, FileType from src.model.license_expression import LicenseExpression from src.model.package import Package, PackageVerificationCode, PackagePurpose, ExternalPackageRef, \ ExternalPackageRefCategory +from src.model.relationship import Relationship, RelationshipType from src.model.snippet import Snippet from src.model.version import Version @@ -105,3 +108,33 @@ def snippet_fixture(spdx_id="snippetId", file_spdx_id="snippetFromFileId", byte_ concluded_license=concluded_license, license_info_in_snippet=license_info_in_snippet, license_comment=license_comment, copyright_text=copyright_text, comment=comment, name=name, attribution_texts=attribution_texts) + + +def annotation_fixture(spdx_id="annotatedElementId", annotation_type=AnnotationType.REVIEW, + annotator=Actor(ActorType.PERSON, "annotatorName"), annotation_date=datetime(2022, 12, 1), + annotation_comment="annotationComment") -> Annotation: + return Annotation(spdx_id=spdx_id, annotation_type=annotation_type, annotator=annotator, + annotation_date=annotation_date, annotation_comment=annotation_comment) + + +def extracted_licensing_info_fixture(license_id="licenseId", extracted_text="extractedText", license_name="licenseName", + cross_references=None, comment="licenseComment") -> ExtractedLicensingInfo: + cross_references = ["crossReference"] if cross_references is None else cross_references + return ExtractedLicensingInfo(license_id=license_id, extracted_text=extracted_text, license_name=license_name, + cross_references=cross_references, comment=comment) + + +def document_fixture(creation_info=None, packages=None, files=None, snippets=None, annotations=None, relationships=None, + extracted_licensing_info=None) -> Document: + creation_info = creation_info_fixture() if creation_info is None else creation_info + packages = [package_fixture()] if packages is None else packages + files = [file_fixture()] if files is None else files + snippets = [snippet_fixture()] if snippets is None else snippets + annotations = [annotation_fixture()] if annotations is None else annotations + relationships = [Relationship("relationshipOriginId", RelationshipType.DESCRIBES, "relationshipTargetId", + "relationshipComment")] if relationships is None else relationships + extracted_licensing_info = [ + extracted_licensing_info_fixture()] if extracted_licensing_info is None else extracted_licensing_info + return Document(creation_info=creation_info, packages=packages, files=files, snippets=snippets, + annotations=annotations, relationships=relationships, + extracted_licensing_info=extracted_licensing_info) From 5a9878e5320df0d27af1ebf76531d0eb490324a5 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 22 Dec 2022 11:30:52 +0100 Subject: [PATCH 101/362] [issue-359] Add utility assertion function for mocks Signed-off-by: Nicolaus Weidner --- tests/mock_utils.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/mock_utils.py diff --git a/tests/mock_utils.py b/tests/mock_utils.py new file mode 100644 index 000000000..9564ae88b --- /dev/null +++ b/tests/mock_utils.py @@ -0,0 +1,19 @@ +# 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 unittest.mock import NonCallableMagicMock + + +def assert_mock_method_called_with_arguments(mock_object: NonCallableMagicMock, method_name: str, *args): + assert len(mock_object.method_calls) == len(args) + for running_index in range(len(args)): + call = mock_object.method_calls[running_index] + assert call[0] == method_name + assert call.args[0] == args[running_index] From 1cff96457bace4de19131d970dcca5b14d4d26e0 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 22 Dec 2022 11:31:12 +0100 Subject: [PATCH 102/362] [issue-359] Add tests for annotation writing logic Signed-off-by: Nicolaus Weidner --- tests/jsonschema/test_document_converter.py | 35 +++++++++++++++++++-- tests/jsonschema/test_file_converter.py | 32 +++++++++++++++++-- tests/jsonschema/test_package_converter.py | 33 +++++++++++++++++-- tests/jsonschema/test_snippet_converter.py | 32 +++++++++++++++++-- 4 files changed, 124 insertions(+), 8 deletions(-) diff --git a/tests/jsonschema/test_document_converter.py b/tests/jsonschema/test_document_converter.py index 4a7e29754..1041ba83d 100644 --- a/tests/jsonschema/test_document_converter.py +++ b/tests/jsonschema/test_document_converter.py @@ -9,11 +9,13 @@ # See the License for the specific language governing permissions and # limitations under the License. from datetime import datetime +from typing import Union from unittest import mock -from unittest.mock import MagicMock +from unittest.mock import MagicMock, NonCallableMagicMock import pytest +from src.jsonschema.annotation_converter import AnnotationConverter from src.jsonschema.document_converter import DocumentConverter from src.jsonschema.document_properties import DocumentProperty from src.model.actor import Actor, ActorType @@ -22,7 +24,8 @@ from src.model.extracted_licensing_info import ExtractedLicensingInfo from src.model.relationship import Relationship, RelationshipType from tests.fixtures import creation_info_fixture, file_fixture, package_fixture, external_document_ref_fixture, \ - snippet_fixture + snippet_fixture, annotation_fixture +from tests.mock_utils import assert_mock_method_called_with_arguments @pytest.fixture @@ -129,3 +132,31 @@ def test_null_values(converter: DocumentConverter): assert converter.json_property_name(DocumentProperty.FILES) not in converted_dict assert converter.json_property_name(DocumentProperty.SNIPPETS) not in converted_dict assert converter.json_property_name(DocumentProperty.RELATIONSHIPS) not in converted_dict + + +def test_document_annotations(converter: DocumentConverter): + file = file_fixture(spdx_id="fileId") + package = package_fixture(spdx_id="packageId") + snippet = snippet_fixture(spdx_id="snippetId") + document_id = "documentId" + + # There are 5 annotations: one each referencing the document, package, file and snippet, and one with an id + # matching none of the Spdx elements. The writer is expected to add the package, file and snippet annotations to + # those elements, so the document should receive the other two. + document_annotation = annotation_fixture(spdx_id=document_id) + other_annotation = annotation_fixture(spdx_id="otherId") + annotations = [annotation_fixture(spdx_id=file.spdx_id), annotation_fixture(spdx_id=package.spdx_id), + annotation_fixture(spdx_id=snippet.spdx_id), document_annotation, + other_annotation] + document = Document(creation_info_fixture(spdx_id=document_id), files=[file], packages=[package], + snippets=[snippet], annotations=annotations) + + # Weird type hint to make warnings about unresolved references from the mock class disappear + annotation_converter: Union[AnnotationConverter, NonCallableMagicMock] = converter.annotation_converter + annotation_converter.convert.return_value = "mock_converted_annotation" + + converted_dict = converter.convert(document) + + assert_mock_method_called_with_arguments(annotation_converter, "convert", document_annotation, other_annotation) + converted_document_annotations = converted_dict.get(converter.json_property_name(DocumentProperty.ANNOTATIONS)) + assert converted_document_annotations == ["mock_converted_annotation", "mock_converted_annotation"] diff --git a/tests/jsonschema/test_file_converter.py b/tests/jsonschema/test_file_converter.py index faca0a954..7dd802544 100644 --- a/tests/jsonschema/test_file_converter.py +++ b/tests/jsonschema/test_file_converter.py @@ -9,11 +9,13 @@ # See the License for the specific language governing permissions and # limitations under the License. from datetime import datetime +from typing import Union from unittest import mock -from unittest.mock import MagicMock +from unittest.mock import MagicMock, NonCallableMagicMock import pytest +from src.jsonschema.annotation_converter import AnnotationConverter from src.jsonschema.file_converter import FileConverter from src.jsonschema.file_properties import FileProperty from src.model.actor import Actor, ActorType @@ -24,7 +26,8 @@ from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING from src.model.spdx_none import SpdxNone, SPDX_NONE_STRING -from tests.fixtures import creation_info_fixture, file_fixture +from tests.fixtures import creation_info_fixture, file_fixture, annotation_fixture, document_fixture +from tests.mock_utils import assert_mock_method_called_with_arguments @pytest.fixture @@ -142,3 +145,28 @@ def test_spdx_none(converter: FileConverter): converter.json_property_name(FileProperty.COPYRIGHT_TEXT)] == SPDX_NONE_STRING assert converted_dict[converter.json_property_name(FileProperty.LICENSE_CONCLUDED)] == SPDX_NONE_STRING assert converted_dict[converter.json_property_name(FileProperty.LICENSE_INFO_IN_FILES)] == SPDX_NONE_STRING + + +def test_file_annotations(converter: FileConverter): + file = file_fixture(spdx_id="fileId") + document = document_fixture(files=[file]) + first_file_annotation = annotation_fixture(spdx_id=file.spdx_id) + second_file_annotation = annotation_fixture(spdx_id=file.spdx_id) + document_annotation = annotation_fixture(spdx_id=document.creation_info.spdx_id) + package_annotation = annotation_fixture(spdx_id=document.packages[0].spdx_id) + snippet_annotation = annotation_fixture(spdx_id=document.snippets[0].spdx_id) + other_annotation = annotation_fixture(spdx_id="otherId") + annotations = [first_file_annotation, second_file_annotation, document_annotation, package_annotation, + snippet_annotation, other_annotation] + document.annotations = annotations + + # Weird type hint to make warnings about unresolved references from the mock class disappear + annotation_converter: Union[AnnotationConverter, NonCallableMagicMock] = converter.annotation_converter + annotation_converter.convert.return_value = "mock_converted_annotation" + + converted_dict = converter.convert(file, document) + + assert_mock_method_called_with_arguments(annotation_converter, "convert", first_file_annotation, + second_file_annotation) + converted_file_annotations = converted_dict.get(converter.json_property_name(FileProperty.ANNOTATIONS)) + assert converted_file_annotations == ["mock_converted_annotation", "mock_converted_annotation"] diff --git a/tests/jsonschema/test_package_converter.py b/tests/jsonschema/test_package_converter.py index 3592a9e46..cd49333cf 100644 --- a/tests/jsonschema/test_package_converter.py +++ b/tests/jsonschema/test_package_converter.py @@ -9,11 +9,13 @@ # See the License for the specific language governing permissions and # limitations under the License. from datetime import datetime +from typing import Union from unittest import mock -from unittest.mock import MagicMock +from unittest.mock import MagicMock, NonCallableMagicMock import pytest +from src.jsonschema.annotation_converter import AnnotationConverter from src.jsonschema.package_converter import PackageConverter from src.jsonschema.package_properties import PackageProperty from src.model.actor import Actor, ActorType @@ -24,7 +26,9 @@ from src.model.package import Package, PackageVerificationCode, PackagePurpose from src.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING from src.model.spdx_none import SpdxNone, SPDX_NONE_STRING -from tests.fixtures import creation_info_fixture, package_fixture, external_package_ref_fixture +from tests.fixtures import creation_info_fixture, package_fixture, external_package_ref_fixture, document_fixture, \ + annotation_fixture +from tests.mock_utils import assert_mock_method_called_with_arguments @pytest.fixture @@ -218,3 +222,28 @@ def test_spdx_none(converter: PackageConverter): assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_INFO_FROM_FILES)] == SPDX_NONE_STRING assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_DECLARED)] == SPDX_NONE_STRING assert converted_dict[converter.json_property_name(PackageProperty.COPYRIGHT_TEXT)] == SPDX_NONE_STRING + + +def test_package_annotations(converter: PackageConverter): + package = package_fixture(spdx_id="packageId") + document = document_fixture(packages=[package]) + first_package_annotation = annotation_fixture(spdx_id=package.spdx_id) + second_package_annotation = annotation_fixture(spdx_id=package.spdx_id) + document_annotation = annotation_fixture(spdx_id=document.creation_info.spdx_id) + file_annotation = annotation_fixture(spdx_id=document.files[0].spdx_id) + snippet_annotation = annotation_fixture(spdx_id=document.snippets[0].spdx_id) + other_annotation = annotation_fixture(spdx_id="otherId") + annotations = [first_package_annotation, second_package_annotation, document_annotation, file_annotation, + snippet_annotation, other_annotation] + document.annotations = annotations + + # Weird type hint to make warnings about unresolved references from the mock class disappear + annotation_converter: Union[AnnotationConverter, NonCallableMagicMock] = converter.annotation_converter + annotation_converter.convert.return_value = "mock_converted_annotation" + + converted_dict = converter.convert(package, document) + + assert_mock_method_called_with_arguments(annotation_converter, "convert", first_package_annotation, + second_package_annotation) + converted_file_annotations = converted_dict.get(converter.json_property_name(PackageProperty.ANNOTATIONS)) + assert converted_file_annotations == ["mock_converted_annotation", "mock_converted_annotation"] diff --git a/tests/jsonschema/test_snippet_converter.py b/tests/jsonschema/test_snippet_converter.py index d75705de4..75432235c 100644 --- a/tests/jsonschema/test_snippet_converter.py +++ b/tests/jsonschema/test_snippet_converter.py @@ -9,11 +9,13 @@ # See the License for the specific language governing permissions and # limitations under the License. from datetime import datetime +from typing import Union from unittest import mock -from unittest.mock import MagicMock +from unittest.mock import MagicMock, NonCallableMagicMock import pytest +from src.jsonschema.annotation_converter import AnnotationConverter from src.jsonschema.snippet_converter import SnippetConverter from src.jsonschema.snippet_properties import SnippetProperty from src.model.actor import Actor, ActorType @@ -23,7 +25,8 @@ from src.model.snippet import Snippet from src.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING from src.model.spdx_none import SpdxNone, SPDX_NONE_STRING -from tests.fixtures import creation_info_fixture, snippet_fixture +from tests.fixtures import creation_info_fixture, snippet_fixture, document_fixture, annotation_fixture +from tests.mock_utils import assert_mock_method_called_with_arguments @pytest.fixture @@ -131,3 +134,28 @@ def test_spdx_none(converter: SnippetConverter): assert converted_dict[converter.json_property_name(SnippetProperty.LICENSE_CONCLUDED)] == SPDX_NONE_STRING assert converted_dict[ converter.json_property_name(SnippetProperty.LICENSE_INFO_IN_SNIPPETS)] == SPDX_NONE_STRING + + +def test_snippet_annotations(converter: SnippetConverter): + snippet = snippet_fixture(spdx_id="snippetId") + document = document_fixture(snippets=[snippet]) + first_snippet_annotation = annotation_fixture(spdx_id=snippet.spdx_id) + second_snippet_annotation = annotation_fixture(spdx_id=snippet.spdx_id) + document_annotation = annotation_fixture(spdx_id=document.creation_info.spdx_id) + package_annotation = annotation_fixture(spdx_id=document.packages[0].spdx_id) + file_annotation = annotation_fixture(spdx_id=document.files[0].spdx_id) + other_annotation = annotation_fixture(spdx_id="otherId") + annotations = [first_snippet_annotation, second_snippet_annotation, document_annotation, package_annotation, + file_annotation, other_annotation] + document.annotations = annotations + + # Weird type hint to make warnings about unresolved references from the mock class disappear + annotation_converter: Union[AnnotationConverter, NonCallableMagicMock] = converter.annotation_converter + annotation_converter.convert.return_value = "mock_converted_annotation" + + converted_dict = converter.convert(snippet, document) + + assert_mock_method_called_with_arguments(annotation_converter, "convert", first_snippet_annotation, + second_snippet_annotation) + converted_file_annotations = converted_dict.get(converter.json_property_name(SnippetProperty.ANNOTATIONS)) + assert converted_file_annotations == ["mock_converted_annotation", "mock_converted_annotation"] From 707443f40e9094e13bb3a51a17f83a17e64bfd51 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 22 Dec 2022 11:38:13 +0100 Subject: [PATCH 103/362] [issue-359] Add relationship fixture to test fixtures Signed-off-by: Nicolaus Weidner --- tests/fixtures.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index 16c757ee0..12da10c32 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -131,10 +131,15 @@ def document_fixture(creation_info=None, packages=None, files=None, snippets=Non files = [file_fixture()] if files is None else files snippets = [snippet_fixture()] if snippets is None else snippets annotations = [annotation_fixture()] if annotations is None else annotations - relationships = [Relationship("relationshipOriginId", RelationshipType.DESCRIBES, "relationshipTargetId", - "relationshipComment")] if relationships is None else relationships + relationships = [relationship_fixture()] if relationships is None else relationships extracted_licensing_info = [ extracted_licensing_info_fixture()] if extracted_licensing_info is None else extracted_licensing_info return Document(creation_info=creation_info, packages=packages, files=files, snippets=snippets, annotations=annotations, relationships=relationships, extracted_licensing_info=extracted_licensing_info) + + +def relationship_fixture(spdx_element_id="relationshipOriginId", relationship_type=RelationshipType.DESCRIBES, + related_spdx_element_id="relationshipTargetId", comment="relationshipComment") -> Relationship: + return Relationship(spdx_element_id=spdx_element_id, relationship_type=relationship_type, + related_spdx_element_id=related_spdx_element_id, comment=comment) From 0ffc35f2ebd38a9e20e523d9b279dfb2af41a5a5 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 22 Dec 2022 12:05:33 +0100 Subject: [PATCH 104/362] [issue-359] Add document_describes and has_files tests Signed-off-by: Nicolaus Weidner --- tests/jsonschema/test_document_converter.py | 23 +++++++++++++- tests/jsonschema/test_package_converter.py | 33 ++++++++++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/tests/jsonschema/test_document_converter.py b/tests/jsonschema/test_document_converter.py index 1041ba83d..923f2fc20 100644 --- a/tests/jsonschema/test_document_converter.py +++ b/tests/jsonschema/test_document_converter.py @@ -24,7 +24,7 @@ from src.model.extracted_licensing_info import ExtractedLicensingInfo from src.model.relationship import Relationship, RelationshipType from tests.fixtures import creation_info_fixture, file_fixture, package_fixture, external_document_ref_fixture, \ - snippet_fixture, annotation_fixture + snippet_fixture, annotation_fixture, document_fixture, relationship_fixture from tests.mock_utils import assert_mock_method_called_with_arguments @@ -160,3 +160,24 @@ def test_document_annotations(converter: DocumentConverter): assert_mock_method_called_with_arguments(annotation_converter, "convert", document_annotation, other_annotation) converted_document_annotations = converted_dict.get(converter.json_property_name(DocumentProperty.ANNOTATIONS)) assert converted_document_annotations == ["mock_converted_annotation", "mock_converted_annotation"] + + +def test_document_describes(converter: DocumentConverter): + document = document_fixture() + document_id = document.creation_info.spdx_id + document_describes_relationship = relationship_fixture(spdx_element_id=document_id, + relationship_type=RelationshipType.DESCRIBES, + related_spdx_element_id="describesId") + described_by_document_relationship = relationship_fixture(related_spdx_element_id=document_id, + relationship_type=RelationshipType.DESCRIBED_BY, + spdx_element_id="describedById") + other_describes_relationship = relationship_fixture(relationship_type=RelationshipType.DESCRIBES) + other_relationship = relationship_fixture(spdx_element_id=document_id, relationship_type=RelationshipType.CONTAINS) + document.relationships = [document_describes_relationship, described_by_document_relationship, + other_describes_relationship, other_relationship] + + converted_dict = converter.convert(document) + + document_describes = converted_dict.get(converter.json_property_name(DocumentProperty.DOCUMENT_DESCRIBES)) + assert document_describes == [document_describes_relationship.related_spdx_element_id, + described_by_document_relationship.spdx_element_id] diff --git a/tests/jsonschema/test_package_converter.py b/tests/jsonschema/test_package_converter.py index cd49333cf..c6e6735be 100644 --- a/tests/jsonschema/test_package_converter.py +++ b/tests/jsonschema/test_package_converter.py @@ -24,10 +24,11 @@ from src.model.document import Document from src.model.license_expression import LicenseExpression from src.model.package import Package, PackageVerificationCode, PackagePurpose +from src.model.relationship import RelationshipType from src.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING from src.model.spdx_none import SpdxNone, SPDX_NONE_STRING from tests.fixtures import creation_info_fixture, package_fixture, external_package_ref_fixture, document_fixture, \ - annotation_fixture + annotation_fixture, file_fixture, relationship_fixture, snippet_fixture from tests.mock_utils import assert_mock_method_called_with_arguments @@ -247,3 +248,33 @@ def test_package_annotations(converter: PackageConverter): second_package_annotation) converted_file_annotations = converted_dict.get(converter.json_property_name(PackageProperty.ANNOTATIONS)) assert converted_file_annotations == ["mock_converted_annotation", "mock_converted_annotation"] + + +def test_has_files(converter: PackageConverter): + package = package_fixture() + first_contained_file = file_fixture(spdx_id="firstFileId") + second_contained_file = file_fixture(spdx_id="secondFileId") + non_contained_file = file_fixture(spdx_id="otherFileId") + snippet = snippet_fixture() + document = document_fixture(packages=[package], + files=[first_contained_file, second_contained_file, non_contained_file], + snippets=[snippet]) + package_contains_file_relationship = relationship_fixture(spdx_element_id=package.spdx_id, + relationship_type=RelationshipType.CONTAINS, + related_spdx_element_id=first_contained_file.spdx_id) + file_contained_in_package_relationship = relationship_fixture(spdx_element_id=second_contained_file.spdx_id, + relationship_type=RelationshipType.CONTAINED_BY, + related_spdx_element_id=package.spdx_id) + package_contains_snippet_relationship = relationship_fixture(spdx_element_id=package.spdx_id, + relationship_type=RelationshipType.CONTAINS, + related_spdx_element_id=snippet.spdx_id) + package_describes_file_relationship = relationship_fixture(spdx_element_id=package.spdx_id, + relationship_type=RelationshipType.DESCRIBES, + related_spdx_element_id=non_contained_file.spdx_id) + document.relationships = [package_contains_file_relationship, file_contained_in_package_relationship, + package_contains_snippet_relationship, package_describes_file_relationship] + + converted_dict = converter.convert(package, document) + + has_files = converted_dict.get(converter.json_property_name(PackageProperty.HAS_FILES)) + assert has_files == [first_contained_file.spdx_id, second_contained_file.spdx_id] From f028180eec662c6db5d7d3f45358da5318901bc9 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 22 Dec 2022 13:45:19 +0100 Subject: [PATCH 105/362] [issue-359] Add tests for relationship conversion logic in DocumentConverter Signed-off-by: Nicolaus Weidner --- tests/jsonschema/test_document_converter.py | 45 ++++++++++++++++++++- tests/mock_utils.py | 4 ++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/tests/jsonschema/test_document_converter.py b/tests/jsonschema/test_document_converter.py index 923f2fc20..934c3c5c6 100644 --- a/tests/jsonschema/test_document_converter.py +++ b/tests/jsonschema/test_document_converter.py @@ -18,6 +18,7 @@ from src.jsonschema.annotation_converter import AnnotationConverter from src.jsonschema.document_converter import DocumentConverter from src.jsonschema.document_properties import DocumentProperty +from src.jsonschema.relationship_converter import RelationshipConverter from src.model.actor import Actor, ActorType from src.model.annotation import Annotation, AnnotationType from src.model.document import Document @@ -25,7 +26,7 @@ from src.model.relationship import Relationship, RelationshipType from tests.fixtures import creation_info_fixture, file_fixture, package_fixture, external_document_ref_fixture, \ snippet_fixture, annotation_fixture, document_fixture, relationship_fixture -from tests.mock_utils import assert_mock_method_called_with_arguments +from tests.mock_utils import assert_mock_method_called_with_arguments, assert_no_mock_methods_called @pytest.fixture @@ -181,3 +182,45 @@ def test_document_describes(converter: DocumentConverter): document_describes = converted_dict.get(converter.json_property_name(DocumentProperty.DOCUMENT_DESCRIBES)) assert document_describes == [document_describes_relationship.related_spdx_element_id, described_by_document_relationship.spdx_element_id] + + +DOCUMENT_ID = "docConverterTestDocumentId" +PACKAGE_ID = "docConverterTestPackageId" +FILE_ID = "docConverterTestFileId" + + +@pytest.mark.parametrize("relationship,should_be_written", + [(relationship_fixture(DOCUMENT_ID, RelationshipType.DESCRIBES), True), + (relationship_fixture(DOCUMENT_ID, RelationshipType.DESCRIBES, comment=None), False), + (relationship_fixture(relationship_type=RelationshipType.DESCRIBED_BY, + related_spdx_element_id=DOCUMENT_ID), True), + (relationship_fixture(relationship_type=RelationshipType.DESCRIBED_BY, + related_spdx_element_id=DOCUMENT_ID, comment=None), False), + (relationship_fixture(DOCUMENT_ID, RelationshipType.AMENDS, comment=None), True), + (relationship_fixture(PACKAGE_ID, RelationshipType.CONTAINS, FILE_ID), True), + (relationship_fixture(PACKAGE_ID, RelationshipType.CONTAINS, FILE_ID, comment=None), False), + (relationship_fixture(FILE_ID, RelationshipType.CONTAINED_BY, PACKAGE_ID), True), + (relationship_fixture(FILE_ID, RelationshipType.CONTAINED_BY, PACKAGE_ID, comment=None), + False), + (relationship_fixture(PACKAGE_ID, RelationshipType.CONTAINS, comment=None), True), + (relationship_fixture(PACKAGE_ID, RelationshipType.COPY_OF, FILE_ID, comment=None), True)]) +def test_document_relationships(converter: DocumentConverter, relationship: Relationship, should_be_written: bool): + package = package_fixture(spdx_id=PACKAGE_ID) + file = file_fixture(spdx_id=FILE_ID) + document = document_fixture(creation_info_fixture(spdx_id=DOCUMENT_ID), packages=[package], files=[file], + relationships=[relationship]) + + # Weird type hint to make warnings about unresolved references from the mock class disappear + relationship_converter: Union[RelationshipConverter, NonCallableMagicMock] = converter.relationship_converter + relationship_converter.convert.return_value = "mock_converted_relationship" + + converted_dict = converter.convert(document) + + relationships = converted_dict.get(converter.json_property_name(DocumentProperty.RELATIONSHIPS)) + + if should_be_written: + assert_mock_method_called_with_arguments(relationship_converter, "convert", relationship) + assert relationships == ["mock_converted_relationship"] + else: + assert_no_mock_methods_called(relationship_converter) + assert relationships is None diff --git a/tests/mock_utils.py b/tests/mock_utils.py index 9564ae88b..06bbc836f 100644 --- a/tests/mock_utils.py +++ b/tests/mock_utils.py @@ -17,3 +17,7 @@ def assert_mock_method_called_with_arguments(mock_object: NonCallableMagicMock, call = mock_object.method_calls[running_index] assert call[0] == method_name assert call.args[0] == args[running_index] + + +def assert_no_mock_methods_called(mock_object: NonCallableMagicMock): + assert len(mock_object.method_calls) == 0 From de3b3449c432de1ac874bd667a1ac9e3fe52d2fb Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 22 Dec 2022 13:49:20 +0100 Subject: [PATCH 106/362] [issue-359] Fix mock ordering in converter tests Signed-off-by: Nicolaus Weidner --- tests/jsonschema/test_document_converter.py | 8 ++++---- tests/jsonschema/test_file_converter.py | 2 +- tests/jsonschema/test_package_converter.py | 5 ++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/jsonschema/test_document_converter.py b/tests/jsonschema/test_document_converter.py index 934c3c5c6..a39b1c64b 100644 --- a/tests/jsonschema/test_document_converter.py +++ b/tests/jsonschema/test_document_converter.py @@ -38,10 +38,10 @@ @mock.patch('src.jsonschema.file_converter.FileConverter', autospec=True) @mock.patch('src.jsonschema.snippet_converter.SnippetConverter', autospec=True) @mock.patch('src.jsonschema.relationship_converter.RelationshipConverter', autospec=True) -def converter(external_ref_converter_mock: MagicMock, creation_info_converter_mock: MagicMock, - package_converter_mock: MagicMock, annotation_converter_mock: MagicMock, - extracted_licensing_info_converter_mock: MagicMock, file_converter_mock: MagicMock, - snippet_converter_mock: MagicMock, relationship_converter_mock: MagicMock) -> DocumentConverter: +def converter(relationship_converter_mock: MagicMock, snippet_converter_mock: MagicMock, file_converter_mock: MagicMock, + extracted_licensing_info_converter_mock: MagicMock, annotation_converter_mock: MagicMock, + package_converter_mock: MagicMock, external_ref_converter_mock: MagicMock, + creation_info_converter_mock: MagicMock) -> DocumentConverter: converter = DocumentConverter() converter.creation_info_converter = creation_info_converter_mock() converter.external_document_ref_converter = external_ref_converter_mock() diff --git a/tests/jsonschema/test_file_converter.py b/tests/jsonschema/test_file_converter.py index 7dd802544..5d291b012 100644 --- a/tests/jsonschema/test_file_converter.py +++ b/tests/jsonschema/test_file_converter.py @@ -33,7 +33,7 @@ @pytest.fixture @mock.patch('src.jsonschema.checksum_converter.ChecksumConverter', autospec=True) @mock.patch('src.jsonschema.annotation_converter.AnnotationConverter', autospec=True) -def converter(checksum_converter_mock: MagicMock, annotation_converter_mock: MagicMock) -> FileConverter: +def converter(annotation_converter_mock: MagicMock, checksum_converter_mock: MagicMock) -> FileConverter: converter = FileConverter() converter.checksum_converter = checksum_converter_mock() converter.annotation_converter = annotation_converter_mock() diff --git a/tests/jsonschema/test_package_converter.py b/tests/jsonschema/test_package_converter.py index c6e6735be..446cac7e5 100644 --- a/tests/jsonschema/test_package_converter.py +++ b/tests/jsonschema/test_package_converter.py @@ -37,9 +37,8 @@ @mock.patch('src.jsonschema.annotation_converter.AnnotationConverter', autospec=True) @mock.patch('src.jsonschema.package_verification_code_converter.PackageVerificationCodeConverter', autospec=True) @mock.patch('src.jsonschema.external_package_ref_converter.ExternalPackageRefConverter', autospec=True) -def converter(checksum_converter_mock: MagicMock, annotation_converter_mock: MagicMock, - verification_code_converter_mock: MagicMock, - package_ref_converter_mock: MagicMock) -> PackageConverter: +def converter(package_ref_converter_mock: MagicMock, verification_code_converter_mock: MagicMock, + annotation_converter_mock: MagicMock, checksum_converter_mock: MagicMock) -> PackageConverter: converter = PackageConverter() converter.checksum_converter = checksum_converter_mock() converter.annotation_converter = annotation_converter_mock() From b6da0e782ac7812492e7787cc81eb0dab77c3553 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Thu, 22 Dec 2022 23:50:55 +0100 Subject: [PATCH 107/362] [issue-359] Make mock utilities compatible with Python 3.7 Signed-off-by: Nicolaus Weidner --- tests/mock_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mock_utils.py b/tests/mock_utils.py index 06bbc836f..09af9f19e 100644 --- a/tests/mock_utils.py +++ b/tests/mock_utils.py @@ -16,7 +16,7 @@ def assert_mock_method_called_with_arguments(mock_object: NonCallableMagicMock, for running_index in range(len(args)): call = mock_object.method_calls[running_index] assert call[0] == method_name - assert call.args[0] == args[running_index] + assert call[1][0] == args[running_index] def assert_no_mock_methods_called(mock_object: NonCallableMagicMock): From e452411ec3c96fdde4f3c088b7df42e6eca75806 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Fri, 30 Dec 2022 17:24:25 +0100 Subject: [PATCH 108/362] [issue-359] Remove license headers from empty init files Signed-off-by: Nicolaus Weidner --- src/writer/json/__init__.py | 11 ----------- tests/writer/__init__.py | 10 ---------- tests/writer/json/__init__.py | 10 ---------- tests/writer/json/expected_results/__init__.py | 10 ---------- 4 files changed, 41 deletions(-) diff --git a/src/writer/json/__init__.py b/src/writer/json/__init__.py index 7b5337962..e69de29bb 100644 --- a/src/writer/json/__init__.py +++ b/src/writer/json/__init__.py @@ -1,11 +0,0 @@ -# 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. - diff --git a/tests/writer/__init__.py b/tests/writer/__init__.py index cbc5c4070..e69de29bb 100644 --- a/tests/writer/__init__.py +++ b/tests/writer/__init__.py @@ -1,10 +0,0 @@ -# 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. diff --git a/tests/writer/json/__init__.py b/tests/writer/json/__init__.py index cbc5c4070..e69de29bb 100644 --- a/tests/writer/json/__init__.py +++ b/tests/writer/json/__init__.py @@ -1,10 +0,0 @@ -# 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. diff --git a/tests/writer/json/expected_results/__init__.py b/tests/writer/json/expected_results/__init__.py index cbc5c4070..e69de29bb 100644 --- a/tests/writer/json/expected_results/__init__.py +++ b/tests/writer/json/expected_results/__init__.py @@ -1,10 +0,0 @@ -# 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 fbd3772448f736ad6cbe4acdb4bce84e79cc0ab3 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Fri, 30 Dec 2022 17:52:06 +0100 Subject: [PATCH 109/362] [issue-359] Move relationship_filters to model package Signed-off-by: Nicolaus Weidner --- src/jsonschema/document_converter.py | 2 +- src/jsonschema/package_converter.py | 2 +- src/{jsonschema => model}/relationship_filters.py | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{jsonschema => model}/relationship_filters.py (100%) diff --git a/src/jsonschema/document_converter.py b/src/jsonschema/document_converter.py index d04e4e8e2..983f36754 100644 --- a/src/jsonschema/document_converter.py +++ b/src/jsonschema/document_converter.py @@ -20,7 +20,7 @@ from src.jsonschema.json_property import JsonProperty from src.jsonschema.package_converter import PackageConverter from src.jsonschema.relationship_converter import RelationshipConverter -from src.jsonschema.relationship_filters import filter_by_type_and_origin, filter_by_type_and_target, \ +from src.model.relationship_filters import filter_by_type_and_origin, filter_by_type_and_target, \ find_package_contains_file_relationships, \ find_file_contained_by_package_relationships from src.jsonschema.snippet_converter import SnippetConverter diff --git a/src/jsonschema/package_converter.py b/src/jsonschema/package_converter.py index a15503e71..45beebd41 100644 --- a/src/jsonschema/package_converter.py +++ b/src/jsonschema/package_converter.py @@ -19,7 +19,7 @@ from src.jsonschema.optional_utils import apply_if_present from src.jsonschema.package_properties import PackageProperty from src.jsonschema.package_verification_code_converter import PackageVerificationCodeConverter -from src.jsonschema.relationship_filters import find_package_contains_file_relationships, \ +from src.model.relationship_filters import find_package_contains_file_relationships, \ find_file_contained_by_package_relationships from src.model.actor import Actor from src.model.document import Document diff --git a/src/jsonschema/relationship_filters.py b/src/model/relationship_filters.py similarity index 100% rename from src/jsonschema/relationship_filters.py rename to src/model/relationship_filters.py From 3ba888d674a6abbeb6f6c2ab561e340e506ae447 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Fri, 30 Dec 2022 18:02:14 +0100 Subject: [PATCH 110/362] [issue-359] Add NOASSERTION case to extracted license name Signed-off-by: Nicolaus Weidner --- src/jsonschema/extracted_licensing_info_converter.py | 3 ++- .../test_extracted_licensing_info_converter.py | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/jsonschema/extracted_licensing_info_converter.py b/src/jsonschema/extracted_licensing_info_converter.py index 87f1312fa..5888247dd 100644 --- a/src/jsonschema/extracted_licensing_info_converter.py +++ b/src/jsonschema/extracted_licensing_info_converter.py @@ -13,6 +13,7 @@ from src.jsonschema.converter import TypedConverter from src.jsonschema.extracted_licensing_info_properties import ExtractedLicensingInfoProperty from src.jsonschema.json_property import JsonProperty +from src.jsonschema.optional_utils import apply_if_present from src.model.document import Document from src.model.extracted_licensing_info import ExtractedLicensingInfo from src.writer.casing_tools import snake_case_to_camel_case @@ -32,7 +33,7 @@ def _get_property_value(self, extracted_licensing_info: ExtractedLicensingInfo, elif extracted_licensing_info_property == ExtractedLicensingInfoProperty.LICENSE_ID: return extracted_licensing_info.license_id elif extracted_licensing_info_property == ExtractedLicensingInfoProperty.NAME: - return extracted_licensing_info.license_name + return apply_if_present(str, extracted_licensing_info.license_name) elif extracted_licensing_info_property == ExtractedLicensingInfoProperty.SEE_ALSOS: return extracted_licensing_info.cross_references or None diff --git a/tests/jsonschema/test_extracted_licensing_info_converter.py b/tests/jsonschema/test_extracted_licensing_info_converter.py index 39c146e84..c90d5c311 100644 --- a/tests/jsonschema/test_extracted_licensing_info_converter.py +++ b/tests/jsonschema/test_extracted_licensing_info_converter.py @@ -13,6 +13,8 @@ from src.jsonschema.extracted_licensing_info_converter import ExtractedLicensingInfoConverter from src.jsonschema.extracted_licensing_info_properties import ExtractedLicensingInfoProperty from src.model.extracted_licensing_info import ExtractedLicensingInfo +from src.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING +from tests.fixtures import extracted_licensing_info_fixture @pytest.fixture @@ -65,3 +67,11 @@ def test_null_values(converter: ExtractedLicensingInfoConverter): assert converter.json_property_name(ExtractedLicensingInfoProperty.NAME) not in converted_dict assert converter.json_property_name(ExtractedLicensingInfoProperty.SEE_ALSOS) not in converted_dict assert converter.json_property_name(ExtractedLicensingInfoProperty.COMMENT) not in converted_dict + + +def test_spdx_no_assertion(converter: ExtractedLicensingInfoConverter): + extracted_licensing_info = extracted_licensing_info_fixture(license_name=SpdxNoAssertion()) + + converted_dict = converter.convert(extracted_licensing_info) + + assert converted_dict[converter.json_property_name(ExtractedLicensingInfoProperty.NAME)] == SPDX_NO_ASSERTION_STRING From fb9d541d23f9bdfca9d2845b741a9caae7cbe846 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Fri, 30 Dec 2022 18:06:55 +0100 Subject: [PATCH 111/362] [issue-359] Add default implementation of json_property_name to converter Signed-off-by: Nicolaus Weidner --- src/jsonschema/annotation_converter.py | 4 ---- src/jsonschema/checksum_converter.py | 4 ---- src/jsonschema/converter.py | 7 ++++--- src/jsonschema/creation_info_converter.py | 4 ---- src/jsonschema/document_converter.py | 9 ++++----- src/jsonschema/external_document_ref_converter.py | 4 ---- src/jsonschema/external_package_ref_converter.py | 4 ---- src/jsonschema/extracted_licensing_info_converter.py | 4 ---- src/jsonschema/file_converter.py | 3 +-- src/jsonschema/package_converter.py | 7 +++---- src/jsonschema/package_verification_code_converter.py | 4 ---- src/jsonschema/relationship_converter.py | 4 ---- src/jsonschema/snippet_converter.py | 3 +-- 13 files changed, 13 insertions(+), 48 deletions(-) diff --git a/src/jsonschema/annotation_converter.py b/src/jsonschema/annotation_converter.py index 503da69e9..9b8905053 100644 --- a/src/jsonschema/annotation_converter.py +++ b/src/jsonschema/annotation_converter.py @@ -16,13 +16,9 @@ from src.jsonschema.json_property import JsonProperty from src.model.annotation import Annotation from src.model.document import Document -from src.writer.casing_tools import snake_case_to_camel_case class AnnotationConverter(TypedConverter[Annotation]): - def json_property_name(self, annotation_property: AnnotationProperty) -> str: - return snake_case_to_camel_case(annotation_property.name) - def _get_property_value(self, annotation: Annotation, annotation_property: AnnotationProperty, document: Document = None) -> Any: if annotation_property == AnnotationProperty.ANNOTATION_DATE: diff --git a/src/jsonschema/checksum_converter.py b/src/jsonschema/checksum_converter.py index d68710234..162a4f174 100644 --- a/src/jsonschema/checksum_converter.py +++ b/src/jsonschema/checksum_converter.py @@ -15,7 +15,6 @@ from src.jsonschema.json_property import JsonProperty from src.model.checksum import Checksum, ChecksumAlgorithm from src.model.document import Document -from src.writer.casing_tools import snake_case_to_camel_case class ChecksumConverter(TypedConverter[Checksum]): @@ -26,9 +25,6 @@ def get_data_model_type(self) -> Type[Checksum]: def get_json_type(self) -> Type[JsonProperty]: return ChecksumProperty - def json_property_name(self, checksum_property: ChecksumProperty) -> str: - return snake_case_to_camel_case(checksum_property.name) - def _get_property_value(self, checksum: Checksum, checksum_property: ChecksumProperty, _document: Document = None) -> str: if checksum_property == ChecksumProperty.ALGORITHM: diff --git a/src/jsonschema/converter.py b/src/jsonschema/converter.py index 1d6d348fa..76972af76 100644 --- a/src/jsonschema/converter.py +++ b/src/jsonschema/converter.py @@ -13,6 +13,7 @@ from src.jsonschema.json_property import JsonProperty from src.model.document import Document +from src.writer.casing_tools import snake_case_to_camel_case MISSING_IMPLEMENTATION_MESSAGE = "Must be implemented" @@ -20,9 +21,6 @@ class TypedConverter(ABC, Generic[T]): - @abstractmethod - def json_property_name(self, json_property: JsonProperty) -> str: - raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE) @abstractmethod def _get_property_value(self, instance: T, json_property: JsonProperty, document: Document = None) -> Any: @@ -36,6 +34,9 @@ def get_json_type(self) -> Type[JsonProperty]: def get_data_model_type(self) -> Type[T]: raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE) + def json_property_name(self, json_property: JsonProperty) -> str: + return snake_case_to_camel_case(json_property.name) + def requires_full_document(self) -> bool: return False diff --git a/src/jsonschema/creation_info_converter.py b/src/jsonschema/creation_info_converter.py index 656f56ee7..0111a8cab 100644 --- a/src/jsonschema/creation_info_converter.py +++ b/src/jsonschema/creation_info_converter.py @@ -16,7 +16,6 @@ from src.jsonschema.json_property import JsonProperty from src.jsonschema.optional_utils import apply_if_present from src.model.document import CreationInfo, Document -from src.writer.casing_tools import snake_case_to_camel_case class CreationInfoConverter(TypedConverter[CreationInfo]): @@ -26,9 +25,6 @@ def get_data_model_type(self) -> Type[CreationInfo]: def get_json_type(self) -> Type[JsonProperty]: return CreationInfoProperty - def json_property_name(self, creation_info_property: CreationInfoProperty) -> str: - return snake_case_to_camel_case(creation_info_property.name) - def _get_property_value(self, creation_info: CreationInfo, creation_info_property: CreationInfoProperty, _document: Document = None) -> Any: if creation_info_property == CreationInfoProperty.CREATED: diff --git a/src/jsonschema/document_converter.py b/src/jsonschema/document_converter.py index 983f36754..d9f6abae1 100644 --- a/src/jsonschema/document_converter.py +++ b/src/jsonschema/document_converter.py @@ -20,13 +20,12 @@ from src.jsonschema.json_property import JsonProperty from src.jsonschema.package_converter import PackageConverter from src.jsonschema.relationship_converter import RelationshipConverter -from src.model.relationship_filters import filter_by_type_and_origin, filter_by_type_and_target, \ - find_package_contains_file_relationships, \ - find_file_contained_by_package_relationships from src.jsonschema.snippet_converter import SnippetConverter from src.model.document import Document from src.model.relationship import RelationshipType -from src.writer.casing_tools import snake_case_to_camel_case +from src.model.relationship_filters import filter_by_type_and_origin, filter_by_type_and_target, \ + find_package_contains_file_relationships, \ + find_file_contained_by_package_relationships class DocumentConverter(TypedConverter[Document]): @@ -58,7 +57,7 @@ def get_data_model_type(self) -> Type[Document]: def json_property_name(self, document_property: DocumentProperty) -> str: if document_property == DocumentProperty.SPDX_ID: return "SPDXID" - return snake_case_to_camel_case(document_property.name) + return super().json_property_name(document_property) def _get_property_value(self, document: Document, document_property: DocumentProperty, _document: Document = None) -> Any: diff --git a/src/jsonschema/external_document_ref_converter.py b/src/jsonschema/external_document_ref_converter.py index 6ac16d56e..893a24014 100644 --- a/src/jsonschema/external_document_ref_converter.py +++ b/src/jsonschema/external_document_ref_converter.py @@ -16,7 +16,6 @@ from src.jsonschema.json_property import JsonProperty from src.model.document import Document from src.model.external_document_ref import ExternalDocumentRef -from src.writer.casing_tools import snake_case_to_camel_case class ExternalDocumentRefConverter(TypedConverter[ExternalDocumentRef]): @@ -25,9 +24,6 @@ class ExternalDocumentRefConverter(TypedConverter[ExternalDocumentRef]): def __init__(self): self.checksum_converter = ChecksumConverter() - def json_property_name(self, external_document_ref_property: ExternalDocumentRefProperty) -> str: - return snake_case_to_camel_case(external_document_ref_property.name) - def _get_property_value(self, external_document_ref: ExternalDocumentRef, external_document_ref_property: ExternalDocumentRefProperty, _document: Document = None) -> Any: diff --git a/src/jsonschema/external_package_ref_converter.py b/src/jsonschema/external_package_ref_converter.py index 55e1ccabf..3993b7c88 100644 --- a/src/jsonschema/external_package_ref_converter.py +++ b/src/jsonschema/external_package_ref_converter.py @@ -15,13 +15,9 @@ from src.jsonschema.json_property import JsonProperty from src.model.document import Document from src.model.package import ExternalPackageRef -from src.writer.casing_tools import snake_case_to_camel_case class ExternalPackageRefConverter(TypedConverter[ExternalPackageRef]): - def json_property_name(self, external_ref_property: ExternalPackageRefProperty) -> str: - return snake_case_to_camel_case(external_ref_property.name) - def _get_property_value(self, external_ref: ExternalPackageRef, external_ref_property: ExternalPackageRefProperty, document: Document = None) -> Any: if external_ref_property == ExternalPackageRefProperty.COMMENT: diff --git a/src/jsonschema/extracted_licensing_info_converter.py b/src/jsonschema/extracted_licensing_info_converter.py index 5888247dd..0aa4630d9 100644 --- a/src/jsonschema/extracted_licensing_info_converter.py +++ b/src/jsonschema/extracted_licensing_info_converter.py @@ -16,13 +16,9 @@ from src.jsonschema.optional_utils import apply_if_present from src.model.document import Document from src.model.extracted_licensing_info import ExtractedLicensingInfo -from src.writer.casing_tools import snake_case_to_camel_case class ExtractedLicensingInfoConverter(TypedConverter[ExtractedLicensingInfo]): - def json_property_name(self, extracted_licensing_info_property: ExtractedLicensingInfoProperty) -> str: - return snake_case_to_camel_case(extracted_licensing_info_property.name) - def _get_property_value(self, extracted_licensing_info: ExtractedLicensingInfo, extracted_licensing_info_property: ExtractedLicensingInfoProperty, document: Document = None) -> Any: diff --git a/src/jsonschema/file_converter.py b/src/jsonschema/file_converter.py index 1b66a72d8..fd76eefa9 100644 --- a/src/jsonschema/file_converter.py +++ b/src/jsonschema/file_converter.py @@ -18,7 +18,6 @@ from src.jsonschema.optional_utils import apply_if_present from src.model.document import Document from src.model.file import File -from src.writer.casing_tools import snake_case_to_camel_case class FileConverter(TypedConverter[File]): @@ -32,7 +31,7 @@ def __init__(self): def json_property_name(self, file_property: FileProperty) -> str: if file_property == FileProperty.SPDX_ID: return "SPDXID" - return snake_case_to_camel_case(file_property.name) + return super().json_property_name(file_property) def _get_property_value(self, file: Any, file_property: FileProperty, document: Document = None) -> Any: if file_property == FileProperty.SPDX_ID: diff --git a/src/jsonschema/package_converter.py b/src/jsonschema/package_converter.py index 45beebd41..fe8a26d71 100644 --- a/src/jsonschema/package_converter.py +++ b/src/jsonschema/package_converter.py @@ -19,12 +19,11 @@ from src.jsonschema.optional_utils import apply_if_present from src.jsonschema.package_properties import PackageProperty from src.jsonschema.package_verification_code_converter import PackageVerificationCodeConverter -from src.model.relationship_filters import find_package_contains_file_relationships, \ - find_file_contained_by_package_relationships from src.model.actor import Actor from src.model.document import Document from src.model.package import Package -from src.writer.casing_tools import snake_case_to_camel_case +from src.model.relationship_filters import find_package_contains_file_relationships, \ + find_file_contained_by_package_relationships class PackageConverter(TypedConverter[Package]): @@ -42,7 +41,7 @@ def __init__(self): def json_property_name(self, package_property: PackageProperty) -> str: if package_property == PackageProperty.SPDX_ID: return "SPDXID" - return snake_case_to_camel_case(package_property.name) + return super().json_property_name(package_property) def _get_property_value(self, package: Package, package_property: PackageProperty, document: Document = None) -> Any: diff --git a/src/jsonschema/package_verification_code_converter.py b/src/jsonschema/package_verification_code_converter.py index c312ef4b5..bb6cd7a6e 100644 --- a/src/jsonschema/package_verification_code_converter.py +++ b/src/jsonschema/package_verification_code_converter.py @@ -15,13 +15,9 @@ from src.jsonschema.package_verification_code_properties import PackageVerificationCodeProperty from src.model.document import Document from src.model.package import PackageVerificationCode -from src.writer.casing_tools import snake_case_to_camel_case class PackageVerificationCodeConverter(TypedConverter[PackageVerificationCode]): - def json_property_name(self, verification_code_property: PackageVerificationCodeProperty) -> str: - return snake_case_to_camel_case(verification_code_property.name) - def _get_property_value(self, verification_code: PackageVerificationCode, verification_code_property: PackageVerificationCodeProperty, document: Document = None) -> Any: diff --git a/src/jsonschema/relationship_converter.py b/src/jsonschema/relationship_converter.py index ea2806765..ea89083b2 100644 --- a/src/jsonschema/relationship_converter.py +++ b/src/jsonschema/relationship_converter.py @@ -15,13 +15,9 @@ from src.jsonschema.relationship_properties import RelationshipProperty from src.model.document import Document from src.model.relationship import Relationship -from src.writer.casing_tools import snake_case_to_camel_case class RelationshipConverter(TypedConverter[Relationship]): - def json_property_name(self, relationship_property: RelationshipProperty) -> str: - return snake_case_to_camel_case(relationship_property.name) - def _get_property_value(self, relationship: Relationship, relationship_property: RelationshipProperty, document: Document = None) -> Any: if relationship_property == RelationshipProperty.SPDX_ELEMENT_ID: diff --git a/src/jsonschema/snippet_converter.py b/src/jsonschema/snippet_converter.py index 6e73f0849..9abb7992e 100644 --- a/src/jsonschema/snippet_converter.py +++ b/src/jsonschema/snippet_converter.py @@ -17,7 +17,6 @@ from src.jsonschema.snippet_properties import SnippetProperty from src.model.document import Document from src.model.snippet import Snippet -from src.writer.casing_tools import snake_case_to_camel_case class SnippetConverter(TypedConverter[Snippet]): @@ -29,7 +28,7 @@ def __init__(self): def json_property_name(self, snippet_property: SnippetProperty) -> str: if snippet_property == SnippetProperty.SPDX_ID: return "SPDXID" - return snake_case_to_camel_case(snippet_property.name) + return super().json_property_name(snippet_property) def _get_property_value(self, snippet: Snippet, snippet_property: SnippetProperty, document: Document = None) -> Any: From d1c47790eb9548a6b9fabe88a866238b084f3fc4 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Fri, 30 Dec 2022 18:11:36 +0100 Subject: [PATCH 112/362] [issue-359] Allow NOASSERTION and NONE for related element id in converter Signed-off-by: Nicolaus Weidner --- src/jsonschema/relationship_converter.py | 2 +- .../jsonschema/test_relationship_converter.py | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/jsonschema/relationship_converter.py b/src/jsonschema/relationship_converter.py index ea89083b2..e5b6a36a1 100644 --- a/src/jsonschema/relationship_converter.py +++ b/src/jsonschema/relationship_converter.py @@ -25,7 +25,7 @@ def _get_property_value(self, relationship: Relationship, relationship_property: elif relationship_property == RelationshipProperty.COMMENT: return relationship.comment elif relationship_property == RelationshipProperty.RELATED_SPDX_ELEMENT: - return relationship.related_spdx_element_id + return str(relationship.related_spdx_element_id) elif relationship_property == RelationshipProperty.RELATIONSHIP_TYPE: return relationship.relationship_type.name diff --git a/tests/jsonschema/test_relationship_converter.py b/tests/jsonschema/test_relationship_converter.py index a4b372935..8646e4dcc 100644 --- a/tests/jsonschema/test_relationship_converter.py +++ b/tests/jsonschema/test_relationship_converter.py @@ -13,6 +13,9 @@ from src.jsonschema.relationship_converter import RelationshipConverter from src.jsonschema.relationship_properties import RelationshipProperty from src.model.relationship import Relationship, RelationshipType +from src.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING +from src.model.spdx_none import SpdxNone, SPDX_NONE_STRING +from tests.fixtures import relationship_fixture @pytest.fixture @@ -49,3 +52,20 @@ def test_successful_conversion(converter: RelationshipConverter): converter.json_property_name(RelationshipProperty.RELATED_SPDX_ELEMENT): "relatedElementId", converter.json_property_name(RelationshipProperty.RELATIONSHIP_TYPE): "COPY_OF" } + + +def test_spdx_no_assertion(converter: RelationshipConverter): + relationship = relationship_fixture(related_spdx_element_id=SpdxNoAssertion()) + + converted_dict = converter.convert(relationship) + + assert converted_dict[ + converter.json_property_name(RelationshipProperty.RELATED_SPDX_ELEMENT)] == SPDX_NO_ASSERTION_STRING + + +def test_spdx_none(converter: RelationshipConverter): + relationship = relationship_fixture(related_spdx_element_id=SpdxNone()) + + converted_dict = converter.convert(relationship) + + assert converted_dict[converter.json_property_name(RelationshipProperty.RELATED_SPDX_ELEMENT)] == SPDX_NONE_STRING From b59d6c2c60b896e0403fb35c29b088d48612549c Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Fri, 30 Dec 2022 18:29:16 +0100 Subject: [PATCH 113/362] [issue-359] Remove JsonWriter class, convert to plain function Signed-off-by: Nicolaus Weidner --- src/writer/json/json_writer.py | 16 ++++++---------- tests/writer/json/test_json_writer.py | 5 ++--- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/writer/json/json_writer.py b/src/writer/json/json_writer.py index f6c85f5a4..1d8eb3ad6 100644 --- a/src/writer/json/json_writer.py +++ b/src/writer/json/json_writer.py @@ -14,13 +14,9 @@ from src.model.document import Document -class JsonWriter: - converter: DocumentConverter - - def __init__(self): - self.converter = DocumentConverter() - - def write_document(self, document: Document, file_name: str) -> None: - document_dict = self.converter.convert(document) - with open(file_name, "w") as out: - json.dump(document_dict, out, indent=4) +def write_document(document: Document, file_name: str, converter: DocumentConverter = None): + if converter is None: + converter = DocumentConverter() + document_dict = converter.convert(document) + with open(file_name, "w") as out: + json.dump(document_dict, out, indent=4) diff --git a/tests/writer/json/test_json_writer.py b/tests/writer/json/test_json_writer.py index ea699dad4..dc3a460af 100644 --- a/tests/writer/json/test_json_writer.py +++ b/tests/writer/json/test_json_writer.py @@ -25,7 +25,7 @@ from src.model.relationship import RelationshipType, Relationship from src.model.snippet import Snippet from src.model.spdx_none import SpdxNone -from src.writer.json.json_writer import JsonWriter +from src.writer.json.json_writer import write_document @pytest.fixture @@ -36,7 +36,6 @@ def temporary_file_path() -> str: def test_write_json(temporary_file_path: str): - writer = JsonWriter() creation_info = CreationInfo("spdxVersion", "documentId", "documentName", "documentNamespace", [Actor(ActorType.TOOL, "tools-python", "tools-python@github.com")], datetime(2022, 12, 1), document_comment="comment", data_license="dataLicense", @@ -58,7 +57,7 @@ def test_write_json(temporary_file_path: str): extracted_licensing_info = [ExtractedLicensingInfo("licenseId", "licenseText")] document = Document(creation_info, annotations=annotations, extracted_licensing_info=extracted_licensing_info, relationships=relationships, packages=[package], files=[file], snippets=[snippet]) - writer.write_document(document, temporary_file_path) + write_document(document, temporary_file_path) with open(temporary_file_path) as written_file: written_json = json.load(written_file) From d9edf4dcf6ddce0342d58407b703e92e11de6715 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Fri, 30 Dec 2022 18:37:20 +0100 Subject: [PATCH 114/362] [issue-359] Add validation to json writer, with the possibility of disabling it Signed-off-by: Nicolaus Weidner --- src/writer/json/json_writer.py | 10 +++++++++- tests/writer/json/test_json_writer.py | 20 +++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/writer/json/json_writer.py b/src/writer/json/json_writer.py index 1d8eb3ad6..cc0c9bf74 100644 --- a/src/writer/json/json_writer.py +++ b/src/writer/json/json_writer.py @@ -9,12 +9,20 @@ # See the License for the specific language governing permissions and # limitations under the License. import json +from typing import List from src.jsonschema.document_converter import DocumentConverter from src.model.document import Document +from src.validation.document_validator import validate_full_spdx_document +from src.validation.validation_message import ValidationMessage -def write_document(document: Document, file_name: str, converter: DocumentConverter = None): +def write_document(document: Document, file_name: str, validate: bool = True, converter: DocumentConverter = None): + if validate: + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, + document.creation_info.spdx_version) + if validation_messages: + raise ValueError(f"Document is not valid. The following errors were detected: {validation_messages}") if converter is None: converter = DocumentConverter() document_dict = converter.convert(document) diff --git a/tests/writer/json/test_json_writer.py b/tests/writer/json/test_json_writer.py index dc3a460af..6e48cf5da 100644 --- a/tests/writer/json/test_json_writer.py +++ b/tests/writer/json/test_json_writer.py @@ -26,6 +26,7 @@ from src.model.snippet import Snippet from src.model.spdx_none import SpdxNone from src.writer.json.json_writer import write_document +from tests.fixtures import document_fixture @pytest.fixture @@ -57,7 +58,8 @@ def test_write_json(temporary_file_path: str): extracted_licensing_info = [ExtractedLicensingInfo("licenseId", "licenseText")] document = Document(creation_info, annotations=annotations, extracted_licensing_info=extracted_licensing_info, relationships=relationships, packages=[package], files=[file], snippets=[snippet]) - write_document(document, temporary_file_path) + # TODO: Enable validation once test data is valid, https://github.com/spdx/tools-python/issues/397 + write_document(document, temporary_file_path, validate=False) with open(temporary_file_path) as written_file: written_json = json.load(written_file) @@ -66,3 +68,19 @@ def test_write_json(temporary_file_path: str): expected_json = json.load(expected_file) assert written_json == expected_json + + +def test_document_is_validated(): + document = document_fixture() + document.creation_info.spdx_id = "InvalidId" + + with pytest.raises(ValueError) as error: + write_document(document, "dummy_path") + assert "Document is not valid" in error.value.args[0] + + +def test_document_validation_can_be_overridden(temporary_file_path: str): + document = document_fixture() + document.creation_info.spdx_id = "InvalidId" + + write_document(document, temporary_file_path, validate=False) From ce293220d47337e10df848cddf40150095c03429 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Mon, 2 Jan 2023 15:33:47 +0100 Subject: [PATCH 115/362] [issue-359] Add docstrings to some core files Signed-off-by: Nicolaus Weidner --- src/jsonschema/converter.py | 16 ++++++++++++++++ src/jsonschema/json_property.py | 1 + src/jsonschema/optional_utils.py | 3 +++ src/writer/json/json_writer.py | 5 +++++ 4 files changed, 25 insertions(+) diff --git a/src/jsonschema/converter.py b/src/jsonschema/converter.py index 76972af76..b5dc2a3c3 100644 --- a/src/jsonschema/converter.py +++ b/src/jsonschema/converter.py @@ -21,6 +21,22 @@ class TypedConverter(ABC, Generic[T]): + """ + Base class for all converters between an instance of the tools-python data model and the corresponding dictionary + representation, following the json schema. The generic type T is the type according to the data model. + Each converter has several methods: + - get_json_type and get_data_model_type: return the data model type and the corresponding JsonProperty subclass. + These methods are abstract in the base class and need to be implemented in subclasses. + - json_property_name: converts an enum value of a JsonProperty subclass to the corresponding property name in the + json schema. The default implementation simply converts from snake case to camel case. Can be overridden in case + of exceptions like "SPDXID". + - convert: converts an instance of type T (one of the data model types) to a dictionary representation. In some + cases, the full document is required (see below). The logic should be generic for all types. + - requires_full_document: indicates whether the full document is required for conversion. Returns False by + default, can be overridden as needed for specific types. + - _get_property_value: Retrieves the value of a specific json property from the data model instance. In some + cases, the full document is required. + """ @abstractmethod def _get_property_value(self, instance: T, json_property: JsonProperty, document: Document = None) -> Any: diff --git a/src/jsonschema/json_property.py b/src/jsonschema/json_property.py index fcdcd7167..0f65a77f2 100644 --- a/src/jsonschema/json_property.py +++ b/src/jsonschema/json_property.py @@ -15,5 +15,6 @@ class JsonProperty(Enum): """ Parent class for all json property classes. Not meant to be instantiated directly, only to have a common parent type that can be used in type hints. + In general, all the child enums list the properties of the corresponding objects from the json schema. """ pass diff --git a/src/jsonschema/optional_utils.py b/src/jsonschema/optional_utils.py index d30fd7607..d5039d3e3 100644 --- a/src/jsonschema/optional_utils.py +++ b/src/jsonschema/optional_utils.py @@ -15,4 +15,7 @@ def apply_if_present(function: Callable[[T], S], optional_value: Optional[T]) -> Optional[S]: + """ + Apply the passed function to the optional value if it is not None. Else returns None. + """ return function(optional_value) if optional_value else None diff --git a/src/writer/json/json_writer.py b/src/writer/json/json_writer.py index cc0c9bf74..5083c8caa 100644 --- a/src/writer/json/json_writer.py +++ b/src/writer/json/json_writer.py @@ -18,6 +18,11 @@ def write_document(document: Document, file_name: str, validate: bool = True, converter: DocumentConverter = None): + """ + Serializes the provided document to json and writes it to a file with the provided name. Unless validate is set + to False, validates the document before serialization. Unless a DocumentConverter instance is provided, + a new one is created. + """ if validate: validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, document.creation_info.spdx_version) From d77ec7a245cdc979acd438564d4cbbbffb1a97dc Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Mon, 2 Jan 2023 15:46:06 +0100 Subject: [PATCH 116/362] [issue-359] Extract common method to get all spdx element ids inside a document Signed-off-by: Nicolaus Weidner --- src/document_utils.py | 20 ++++++++++++++++++++ src/jsonschema/document_converter.py | 5 ++--- src/validation/spdx_id_validators.py | 6 ++---- 3 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 src/document_utils.py diff --git a/src/document_utils.py b/src/document_utils.py new file mode 100644 index 000000000..f0fe18552 --- /dev/null +++ b/src/document_utils.py @@ -0,0 +1,20 @@ +# 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 typing import List + +from src.model.document import Document + + +def get_contained_spdx_element_ids(document: Document) -> List[str]: + element_ids = [file.spdx_id for file in document.files] + element_ids.extend([package.spdx_id for package in document.packages]) + element_ids.extend([snippet.spdx_id for snippet in document.snippets]) + return element_ids diff --git a/src/jsonschema/document_converter.py b/src/jsonschema/document_converter.py index d9f6abae1..4d669962b 100644 --- a/src/jsonschema/document_converter.py +++ b/src/jsonschema/document_converter.py @@ -10,6 +10,7 @@ # limitations under the License. from typing import Type, Any +from src.document_utils import get_contained_spdx_element_ids from src.jsonschema.annotation_converter import AnnotationConverter from src.jsonschema.converter import TypedConverter from src.jsonschema.creation_info_converter import CreationInfoConverter @@ -65,9 +66,7 @@ def _get_property_value(self, document: Document, document_property: DocumentPro return document.creation_info.spdx_id elif document_property == DocumentProperty.ANNOTATIONS: # annotations referencing files, packages or snippets will be added to those elements directly - element_ids = [file.spdx_id for file in document.files] - element_ids.extend([package.spdx_id for package in document.packages]) - element_ids.extend([snippet.spdx_id for snippet in document.snippets]) + element_ids = get_contained_spdx_element_ids(document) document_annotations = filter(lambda annotation: annotation.spdx_id not in element_ids, document.annotations) return [self.annotation_converter.convert(annotation) for annotation in document_annotations] or None diff --git a/src/validation/spdx_id_validators.py b/src/validation/spdx_id_validators.py index 30e90be78..77bc91025 100644 --- a/src/validation/spdx_id_validators.py +++ b/src/validation/spdx_id_validators.py @@ -12,6 +12,7 @@ import re from typing import List +from src.document_utils import get_contained_spdx_element_ids from src.model.document import Document from src.model.file import File @@ -36,10 +37,7 @@ def is_spdx_id_present_in_document(spdx_id: str, document: Document) -> bool: def get_list_of_all_spdx_ids(document: Document) -> List[str]: all_spdx_ids_in_document: List[str] = [document.creation_info.spdx_id] - - all_spdx_ids_in_document.extend([package.spdx_id for package in document.packages]) - all_spdx_ids_in_document.extend([file.spdx_id for file in document.files]) - all_spdx_ids_in_document.extend([snippet.spdx_id for snippet in document.snippets]) + all_spdx_ids_in_document.extend(get_contained_spdx_element_ids(document)) return all_spdx_ids_in_document From 5eb63e4436efdea44fd7f7b74c8dcb2d50e8c101 Mon Sep 17 00:00:00 2001 From: Nicolaus Weidner Date: Mon, 2 Jan 2023 23:54:43 +0100 Subject: [PATCH 117/362] [issue-359] Use extracted relationship filtering methods in document_validator.py Signed-off-by: Nicolaus Weidner --- src/validation/document_validator.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/validation/document_validator.py b/src/validation/document_validator.py index a0a151e21..bf2c11450 100644 --- a/src/validation/document_validator.py +++ b/src/validation/document_validator.py @@ -13,6 +13,7 @@ from src.model.document import Document from src.model.relationship import RelationshipType +from src.model.relationship_filters import filter_by_type_and_origin, filter_by_type_and_target from src.validation.annotation_validator import validate_annotations from src.validation.creation_info_validator import validate_creation_info from src.validation.extracted_licensing_info_validator import validate_extracted_licensing_infos @@ -35,15 +36,16 @@ def validate_full_spdx_document(document: Document, spdx_version: str) -> List[V validation_messages.extend(validate_extracted_licensing_infos(document.extracted_licensing_info)) document_id = document.creation_info.spdx_id - document_describes_relationships = [relationship for relationship in document.relationships if - relationship.relationship_type == RelationshipType.DESCRIBES and relationship.spdx_element_id == document_id] - described_by_document_relationships = [relationship for relationship in document.relationships if - relationship.relationship_type == RelationshipType.DESCRIBED_BY and relationship.related_spdx_element_id == document_id] + document_describes_relationships = filter_by_type_and_origin(document.relationships, RelationshipType.DESCRIBES, + document_id) + described_by_document_relationships = filter_by_type_and_target(document.relationships, + RelationshipType.DESCRIBED_BY, document_id) if not document_describes_relationships + described_by_document_relationships: validation_messages.append( ValidationMessage( - f'there must be at least one relationship "{document_id} DESCRIBES ..." or "... DESCRIBED_BY {document_id}"', + f'there must be at least one relationship "{document_id} DESCRIBES ..." or "... DESCRIBED_BY ' + f'{document_id}"', ValidationContext(spdx_id=document_id, element_type=SpdxElementType.DOCUMENT))) From b964a1734f502ea179df34f6bd22769f8fd1d1ae 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 118/362] [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 c68fbc81ec246ca994e966ff52c5cde96d23926e 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 119/362] [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 8f08e0c6c6afa08a516e406746e455b9e343a3ca 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 120/362] [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 ca9aee5d14abcb22f0a910ce5235ddfd913a4040 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 121/362] [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 17fedb6ccebf6c4e425df6da69b7e59b449ce3f8 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 122/362] [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 aee3e10b9119be4104feaec79a18ab375eec7634 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 123/362] [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 a9b6525f123c0a6d7822c8ab1e94c3e9a2681b13 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 124/362] [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) From f4c48f77201f5f352ff8cafc337b07d77717c861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 4 Jan 2023 09:49:33 +0100 Subject: [PATCH 125/362] [issue-397] change fixtures to use valid defaults MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- tests/fixtures.py | 69 ++++++++++++--------- tests/jsonschema/test_document_converter.py | 3 +- 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index 12da10c32..e8ee68303 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -28,22 +28,35 @@ specified unless relevant for the test.""" -def creation_info_fixture(spdx_version="spdxVersion", spdx_id="documentId", name="documentName", - namespace="documentNamespace", creators=None, created=datetime(2022, 12, 1), +def actor_fixture(actor_type=ActorType.PERSON, name="actorName", email="some@mail.com") -> Actor: + return Actor(actor_type, name, email) + + +def checksum_fixture(algorithm=ChecksumAlgorithm.SHA1, value="71c4025dd9897b364f3ebbb42c484ff43d00791c") -> Checksum: + return Checksum(algorithm, value) + + +def package_verification_code_fixture(value="85ed0817af83a24ad8da68c2b5094de69833983c", excluded_files=None) -> PackageVerificationCode: + excluded_files = ["./exclude.py"] if excluded_files is None else excluded_files + return PackageVerificationCode(value, excluded_files) + + +def creation_info_fixture(spdx_version="SPDX-2.3", spdx_id="SPDXRef-DOCUMENT", name="documentName", + document_namespace="https://some.namespace", creators=None, created=datetime(2022, 12, 1), creator_comment="creatorComment", data_license="CC0-1.0", external_document_refs=None, license_list_version=Version(3, 19), document_comment="documentComment") -> CreationInfo: - creators = [Actor(ActorType.PERSON, "creatorName")] if creators is None else creators + creators = [actor_fixture(name="creatorName")] if creators is None else creators external_document_refs = [ external_document_ref_fixture()] if external_document_refs is None else external_document_refs - return CreationInfo(spdx_version, spdx_id, name, namespace, creators, created, creator_comment, data_license, + return CreationInfo(spdx_version, spdx_id, name, document_namespace, creators, created, creator_comment, data_license, external_document_refs, license_list_version, document_comment) -def file_fixture(name="fileName", spdx_id="fileId", checksums=None, file_type=None, +def file_fixture(name="./fileName.py", spdx_id="SPDXRef-File", checksums=None, file_type=None, concluded_license=LicenseExpression("concludedLicenseExpression"), license_info_in_file=None, license_comment="licenseComment", copyright_text="copyrightText", comment="fileComment", notice="fileNotice", contributors=None, attribution_texts=None) -> File: - checksums = [Checksum(ChecksumAlgorithm.SHA1, "sha1")] if checksums is None else checksums + checksums = [checksum_fixture()] if checksums is None else checksums file_type = [FileType.TEXT] if file_type is None else file_type license_info_in_file = [ LicenseExpression("licenseInfoInFileExpression")] if license_info_in_file is None else license_info_in_file @@ -55,12 +68,11 @@ def file_fixture(name="fileName", spdx_id="fileId", checksums=None, file_type=No contributors=contributors, attribution_texts=attribution_texts) -def package_fixture(spdx_id="packageId", name="packageName", download_location="downloadLocation", - version="packageVersion", file_name="packageFileName", - supplier=Actor(ActorType.PERSON, "supplierName"), - originator=Actor(ActorType.PERSON, "originatorName"), files_analyzed=True, - verification_code=PackageVerificationCode("verificationCode"), checksums=None, - homepage="packageHomepage", source_info="sourceInfo", +def package_fixture(spdx_id="SPDXRef-Package", name="packageName", download_location="https://download.com", + version="12.2", file_name="./packageFileName", + supplier=actor_fixture(name="supplierName"), originator=actor_fixture(name="originatorName"), + files_analyzed=True, verification_code=package_verification_code_fixture(), checksums=None, + homepage="https://homepage.com", source_info="sourceInfo", license_concluded=LicenseExpression("packageLicenseConcluded"), license_info_from_files=None, license_declared=LicenseExpression("packageLicenseDeclared"), license_comment="packageLicenseComment", copyright_text="packageCopyrightText", @@ -68,7 +80,7 @@ def package_fixture(spdx_id="packageId", name="packageName", download_location=" external_references=None, attribution_texts=None, primary_package_purpose=PackagePurpose.SOURCE, release_date=datetime(2022, 12, 1), built_date=datetime(2022, 12, 2), valid_until_date=datetime(2022, 12, 3)) -> Package: - checksums = [Checksum(ChecksumAlgorithm.SHA1, "packageSha1")] if checksums is None else checksums + checksums = [checksum_fixture()] if checksums is None else checksums license_info_from_files = [ LicenseExpression("licenseInfoFromFile")] if license_info_from_files is None else license_info_from_files external_references = [external_package_ref_fixture()] if external_references is None else external_references @@ -83,20 +95,19 @@ def package_fixture(spdx_id="packageId", name="packageName", download_location=" release_date=release_date, built_date=built_date, valid_until_date=valid_until_date) -def external_document_ref_fixture(document_ref_id="externalDocumentRefId", document_uri="externalDocumentUri", - checksum=Checksum(ChecksumAlgorithm.MD5, - "externalDocumentRefMd5")) -> ExternalDocumentRef: +def external_document_ref_fixture(document_ref_id="DocumentRef-external", document_uri="https://namespace.com", + checksum=checksum_fixture()) -> ExternalDocumentRef: return ExternalDocumentRef(document_ref_id=document_ref_id, document_uri=document_uri, checksum=checksum) def external_package_ref_fixture(category=ExternalPackageRefCategory.PACKAGE_MANAGER, - reference_type="externalPackageRefType", - locator="externalPackageRefLocator", + reference_type="maven-central", + locator="org.apache.tomcat:tomcat:9.0.0.M4", comment="externalPackageRefComment") -> ExternalPackageRef: return ExternalPackageRef(category=category, reference_type=reference_type, locator=locator, comment=comment) -def snippet_fixture(spdx_id="snippetId", file_spdx_id="snippetFromFileId", byte_range=(1, 2), +def snippet_fixture(spdx_id="SPDXRef-Snippet", file_spdx_id="SPDXRef-File", byte_range=(1, 2), line_range=(3, 4), concluded_license=LicenseExpression("snippetLicenseConcluded"), license_info_in_snippet=None, license_comment="snippetLicenseComment", copyright_text="licenseCopyrightText", comment="snippetComment", name="snippetName", @@ -110,20 +121,26 @@ def snippet_fixture(spdx_id="snippetId", file_spdx_id="snippetFromFileId", byte_ attribution_texts=attribution_texts) -def annotation_fixture(spdx_id="annotatedElementId", annotation_type=AnnotationType.REVIEW, - annotator=Actor(ActorType.PERSON, "annotatorName"), annotation_date=datetime(2022, 12, 1), +def annotation_fixture(spdx_id="SPDXRef-File", annotation_type=AnnotationType.REVIEW, + annotator=actor_fixture(name="annotatorName"), annotation_date=datetime(2022, 12, 1), annotation_comment="annotationComment") -> Annotation: return Annotation(spdx_id=spdx_id, annotation_type=annotation_type, annotator=annotator, annotation_date=annotation_date, annotation_comment=annotation_comment) -def extracted_licensing_info_fixture(license_id="licenseId", extracted_text="extractedText", license_name="licenseName", +def extracted_licensing_info_fixture(license_id="LicenseRef-1", extracted_text="extractedText", license_name="licenseName", cross_references=None, comment="licenseComment") -> ExtractedLicensingInfo: - cross_references = ["crossReference"] if cross_references is None else cross_references + cross_references = ["https://see.also"] if cross_references is None else cross_references return ExtractedLicensingInfo(license_id=license_id, extracted_text=extracted_text, license_name=license_name, cross_references=cross_references, comment=comment) +def relationship_fixture(spdx_element_id="SPDXRef-DOCUMENT", relationship_type=RelationshipType.DESCRIBES, + related_spdx_element_id="SPDXRef-File", comment="relationshipComment") -> Relationship: + return Relationship(spdx_element_id=spdx_element_id, relationship_type=relationship_type, + related_spdx_element_id=related_spdx_element_id, comment=comment) + + def document_fixture(creation_info=None, packages=None, files=None, snippets=None, annotations=None, relationships=None, extracted_licensing_info=None) -> Document: creation_info = creation_info_fixture() if creation_info is None else creation_info @@ -137,9 +154,3 @@ def document_fixture(creation_info=None, packages=None, files=None, snippets=Non return Document(creation_info=creation_info, packages=packages, files=files, snippets=snippets, annotations=annotations, relationships=relationships, extracted_licensing_info=extracted_licensing_info) - - -def relationship_fixture(spdx_element_id="relationshipOriginId", relationship_type=RelationshipType.DESCRIBES, - related_spdx_element_id="relationshipTargetId", comment="relationshipComment") -> Relationship: - return Relationship(spdx_element_id=spdx_element_id, relationship_type=relationship_type, - related_spdx_element_id=related_spdx_element_id, comment=comment) diff --git a/tests/jsonschema/test_document_converter.py b/tests/jsonschema/test_document_converter.py index a39b1c64b..5facc4f8c 100644 --- a/tests/jsonschema/test_document_converter.py +++ b/tests/jsonschema/test_document_converter.py @@ -172,7 +172,8 @@ def test_document_describes(converter: DocumentConverter): described_by_document_relationship = relationship_fixture(related_spdx_element_id=document_id, relationship_type=RelationshipType.DESCRIBED_BY, spdx_element_id="describedById") - other_describes_relationship = relationship_fixture(relationship_type=RelationshipType.DESCRIBES) + other_describes_relationship = relationship_fixture(spdx_element_id="DocumentRef-external", + relationship_type=RelationshipType.DESCRIBES) other_relationship = relationship_fixture(spdx_element_id=document_id, relationship_type=RelationshipType.CONTAINS) document.relationships = [document_describes_relationship, described_by_document_relationship, other_describes_relationship, other_relationship] From 35ed3d88ac9e97700d02f893929de1c019069d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 4 Jan 2023 10:43:40 +0100 Subject: [PATCH 126/362] [issue-397] replace valid_defaults.py usages with fixtures.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- tests/jsonschema/test_document_converter.py | 2 +- tests/valid_defaults.py | 116 ------------------ tests/validation/test_actor_validator.py | 8 +- tests/validation/test_annotation_validator.py | 14 +-- tests/validation/test_checksum_validator.py | 4 +- .../test_creation_info_validator.py | 18 +-- tests/validation/test_document_validator.py | 9 +- .../test_external_document_ref_validator.py | 6 +- .../test_external_package_ref_validator.py | 12 +- ...test_extracted_licensing_info_validator.py | 10 +- tests/validation/test_file_validator.py | 24 ++-- tests/validation/test_package_validator.py | 33 +++-- .../validation/test_relationship_validator.py | 17 ++- tests/validation/test_snippet_validator.py | 27 ++-- tests/writer/tagvalue/test_package_writer.py | 23 ++-- 15 files changed, 87 insertions(+), 236 deletions(-) delete mode 100644 tests/valid_defaults.py diff --git a/tests/jsonschema/test_document_converter.py b/tests/jsonschema/test_document_converter.py index 5facc4f8c..086712848 100644 --- a/tests/jsonschema/test_document_converter.py +++ b/tests/jsonschema/test_document_converter.py @@ -71,7 +71,7 @@ def test_json_property_names(converter: DocumentConverter, document_property: Do def test_successful_conversion(converter: DocumentConverter): creation_info = creation_info_fixture(spdx_version="spdxVersion", spdx_id="spdxId", name="name", - namespace="namespace", document_comment="comment", data_license="dataLicense", + document_namespace="namespace", document_comment="comment", data_license="dataLicense", external_document_refs=[external_document_ref_fixture()]) document = Document(creation_info, annotations=[ Annotation("annotationId", AnnotationType.REVIEW, Actor(ActorType.PERSON, "reviewerName"), diff --git a/tests/valid_defaults.py b/tests/valid_defaults.py deleted file mode 100644 index a800193f0..000000000 --- a/tests/valid_defaults.py +++ /dev/null @@ -1,116 +0,0 @@ -# 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 datetime import datetime - -from src.model.actor import Actor, ActorType -from src.model.annotation import AnnotationType, Annotation -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.document import CreationInfo, Document -from src.model.external_document_ref import ExternalDocumentRef -from src.model.extracted_licensing_info import ExtractedLicensingInfo -from src.model.file import File -from src.model.package import Package, PackageVerificationCode, ExternalPackageRef, ExternalPackageRefCategory -from src.model.relationship import Relationship, RelationshipType -from src.model.snippet import Snippet -from src.model.spdx_none import SpdxNone - - -def get_actor(actor_type=ActorType.PERSON, name="person name", mail=None) -> Actor: - return Actor(actor_type, name, mail) - - -def get_annotation(spdx_id="SPDXRef-DOCUMENT", annotation_type=AnnotationType.OTHER, annotator=get_actor(), - annotation_date=datetime(2022, 1, 1), annotation_comment="annotation comment") -> Annotation: - return Annotation(spdx_id, annotation_type, annotator, annotation_date, annotation_comment) - - -def get_checksum(algorithm=ChecksumAlgorithm.SHA1, value="85ed0817af83a24ad8da68c2b5094de69833983c") -> Checksum: - return Checksum(algorithm, value) - - -def get_creation_info(spdx_version="SPDX-2.3", spdx_id="SPDXRef-DOCUMENT", name="document_name", - document_namespace="https://some.uri", - creators=None, created=datetime(2022, 1, 1), creator_comment=None, data_license="CC0-1.0", - external_document_refs=None, license_list_version=None, document_comment=None) -> CreationInfo: - if creators is None: - creators = [get_actor()] - - return CreationInfo(spdx_version, spdx_id, name, document_namespace, creators, created, creator_comment, - data_license, external_document_refs, license_list_version, document_comment) - - -def get_document(creation_info=get_creation_info(), packages=None, files=None, snippets=None, annotations=None, - relationships=None, extracted_licensing_info=None) -> Document: - - return Document(creation_info, packages, files, snippets, annotations, relationships, extracted_licensing_info) - - -def get_external_document_ref(document_ref_id="DocumentRef-idstring", document_uri="https://some.uri", - checksum=get_checksum()) -> ExternalDocumentRef: - return ExternalDocumentRef(document_ref_id, document_uri, checksum) - - -def get_extracted_licensing_info(license_id="LicenseRef-1", extracted_text="extracted text", - license_name="license name", cross_references=None, - comment=None, ) -> ExtractedLicensingInfo: - if cross_references is None: - cross_references = ["http://some.url"] - return ExtractedLicensingInfo(license_id, extracted_text, license_name, cross_references, comment) - - -def get_file(name="./file/name.py", spdx_id="SPDXRef-File", checksums=None, file_type=None, concluded_license=None, - license_info_in_file=None, license_comment=None, copyright_text=None, comment=None, notice=None, - contributors=None, attribution_texts=None) -> File: - if checksums is None: - checksums = [get_checksum()] - - return File(name, spdx_id, checksums, file_type, concluded_license, license_info_in_file, license_comment, - copyright_text, comment, notice, contributors, attribution_texts) - - -def get_package_verification_code(value="85ed0817af83a24ad8da68c2b5094de69833983c", - excluded_files=None) -> PackageVerificationCode: - - return PackageVerificationCode(value, excluded_files) - - -def get_external_package_ref(category=ExternalPackageRefCategory.SECURITY, reference_type="cpe22Type", - locator="cpe:/o:canonical:ubuntu_linux:10.04:-:lts", - comment="external package ref comment") -> ExternalPackageRef: - return ExternalPackageRef(category, reference_type, locator, comment) - - -def get_package(spdx_id="SPDXRef-Package", name="package name", download_location=SpdxNone(), version=None, - file_name=None, supplier=None, originator=None, files_analyzed=False, verification_code=None, - checksums=None, homepage=None, source_info=None, license_concluded=None, license_info_from_files=None, - license_declared=None, license_comment=None, copyright_text=None, summary=None, description=None, - comment=None, external_references=None, attribution_texts=None, primary_package_purpose=None, - release_date=None, built_date=None, valid_until_date=None) -> Package: - - return Package(spdx_id, name, download_location, version, file_name, supplier, originator, files_analyzed, - verification_code, checksums, homepage, source_info, license_concluded, license_info_from_files, - license_declared, license_comment, copyright_text, summary, description, comment, - external_references, attribution_texts, primary_package_purpose, release_date, built_date, - valid_until_date) - - -def get_relationship(spdx_element_id="SPDXRef-DOCUMENT", relationship_type=RelationshipType.DESCRIBES, - related_spdx_element_id="SPDXRef-File", comment=None) -> Relationship: - return Relationship(spdx_element_id, relationship_type, related_spdx_element_id, comment) - - -def get_snippet(spdx_id="SPDXRef-Snippet", file_spdx_id="SPDXRef-File", byte_range=(200, 400), line_range=None, - concluded_license=None, license_info_in_snippet=None, license_comment=None, copyright_text=None, - comment=None, name=None, attribution_texts=None) -> Snippet: - - return Snippet(spdx_id, file_spdx_id, byte_range, line_range, concluded_license, license_info_in_snippet, - license_comment, copyright_text, comment, name, attribution_texts) diff --git a/tests/validation/test_actor_validator.py b/tests/validation/test_actor_validator.py index 4215fe7e5..7561f6659 100644 --- a/tests/validation/test_actor_validator.py +++ b/tests/validation/test_actor_validator.py @@ -13,21 +13,21 @@ import pytest -from src.model.actor import ActorType, Actor +from src.model.actor import ActorType from src.validation.actor_validator import validate_actor from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.valid_defaults import get_actor +from tests.fixtures import actor_fixture def test_valid_actor_person(): - actor = Actor(ActorType.PERSON, "person name", "mail@mail.com") + actor = actor_fixture() validation_messages: List[ValidationMessage] = validate_actor(actor, "SPDXRef-DOCUMENT") assert validation_messages == [] @pytest.mark.parametrize("actor, expected_message", - [(get_actor(actor_type=ActorType.TOOL, mail="mail@mail.com"), + [(actor_fixture(actor_type=ActorType.TOOL, email="mail@mail.com"), "email must be None if actor_type is TOOL, but is: mail@mail.com"), ]) def test_invalid_actor(actor, expected_message): diff --git a/tests/validation/test_annotation_validator.py b/tests/validation/test_annotation_validator.py index 885693d8b..5ddf87f41 100644 --- a/tests/validation/test_annotation_validator.py +++ b/tests/validation/test_annotation_validator.py @@ -9,23 +9,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -from datetime import datetime from typing import List import pytest -from src.model.annotation import Annotation, AnnotationType +from src.model.annotation import Annotation from src.model.document import Document from src.validation.annotation_validator import validate_annotation from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.valid_defaults import get_actor, get_annotation, get_document, get_file +from tests.fixtures import document_fixture, annotation_fixture, file_fixture def test_valid_annotation(): - document: Document = get_document(files=[get_file(spdx_id="SPDXRef-File")]) - - annotation = Annotation("SPDXRef-File", AnnotationType.OTHER, get_actor(), datetime(2022, 1, 1), "comment") - validation_messages: List[ValidationMessage] = validate_annotation(annotation, document) + validation_messages: List[ValidationMessage] = validate_annotation(annotation_fixture(), document_fixture()) assert validation_messages == [] @@ -35,8 +31,8 @@ def test_valid_annotation(): "did not find the referenced spdx_id SPDXRef-File in the SPDX document") ]) def test_invalid_annotation(annotation_id, file_id, expected_message): - annotation: Annotation = get_annotation(spdx_id=annotation_id) - document: Document = get_document(files=[get_file(spdx_id=file_id)]) + annotation: Annotation = annotation_fixture(spdx_id=annotation_id) + document: Document = document_fixture(files=[file_fixture(spdx_id=file_id)]) validation_messages: List[ValidationMessage] = validate_annotation(annotation, document) expected = ValidationMessage(expected_message, diff --git a/tests/validation/test_checksum_validator.py b/tests/validation/test_checksum_validator.py index 661fb110a..22a1b6ffb 100644 --- a/tests/validation/test_checksum_validator.py +++ b/tests/validation/test_checksum_validator.py @@ -16,10 +16,12 @@ from src.model.checksum import Checksum, ChecksumAlgorithm from src.validation.checksum_validator import validate_checksum from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from tests.fixtures import checksum_fixture @pytest.mark.parametrize("checksum", - [Checksum(ChecksumAlgorithm.SHA1, "71c4025dd9897b364f3ebbb42c484ff43d00791c"), + [checksum_fixture(), + Checksum(ChecksumAlgorithm.SHA1, "71c4025dd9897b364f3ebbb42c484ff43d00791c"), Checksum(ChecksumAlgorithm.SHA224, "9c9f4e27d957a123cc32d86afe33ae53b1184192cccb23b0f257f588"), Checksum(ChecksumAlgorithm.SHA256, diff --git a/tests/validation/test_creation_info_validator.py b/tests/validation/test_creation_info_validator.py index fb9cc86e9..5de041660 100644 --- a/tests/validation/test_creation_info_validator.py +++ b/tests/validation/test_creation_info_validator.py @@ -9,23 +9,17 @@ # See the License for the specific language governing permissions and # limitations under the License. -from datetime import datetime from typing import List import pytest -from src.model.document import CreationInfo -from src.model.version import Version from src.validation.creation_info_validator import validate_creation_info from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.valid_defaults import get_actor, get_external_document_ref, get_creation_info +from tests.fixtures import creation_info_fixture def test_valid_creation_info(): - creation_info = CreationInfo("SPDX-2.3", "SPDXRef-DOCUMENT", "document name", "https://some.uri", - [get_actor(), get_actor()], datetime(2022, 1, 1), "creator_comment", - "CC0-1.0", [get_external_document_ref(), get_external_document_ref()], Version(6, 3), - "doc_comment") + creation_info = creation_info_fixture() validation_messages: List[ValidationMessage] = validate_creation_info(creation_info) assert validation_messages == [] @@ -33,13 +27,13 @@ def test_valid_creation_info(): @pytest.mark.parametrize \ ("creation_info_input, spdx_id, expected_message", - [(get_creation_info(spdx_version="version-2.3"), "SPDXRef-DOCUMENT", + [(creation_info_fixture(spdx_version="version-2.3"), "SPDXRef-DOCUMENT", 'spdx_version must be of the form "SPDX-[major].[minor]" but is: version-2.3'), - (get_creation_info(spdx_id="SPDXRef-doc"), "SPDXRef-doc", + (creation_info_fixture(spdx_id="SPDXRef-doc"), "SPDXRef-doc", 'spdx_id must be "SPDXRef-DOCUMENT", but is: SPDXRef-doc'), - (get_creation_info(data_license="MIT"), "SPDXRef-DOCUMENT", + (creation_info_fixture(data_license="MIT"), "SPDXRef-DOCUMENT", 'data_license must be "CC0-1.0", but is: MIT'), - (get_creation_info(document_namespace="some_namespace"), "SPDXRef-DOCUMENT", + (creation_info_fixture(document_namespace="some_namespace"), "SPDXRef-DOCUMENT", "document_namespace must be a valid URI specified in RFC-3986, but is: some_namespace"), ]) def test_invalid_creation_info(creation_info_input, expected_message, spdx_id): diff --git a/tests/validation/test_document_validator.py b/tests/validation/test_document_validator.py index be66477c0..637c6ad74 100644 --- a/tests/validation/test_document_validator.py +++ b/tests/validation/test_document_validator.py @@ -11,18 +11,13 @@ from typing import List -from src.model.document import Document from src.validation.document_validator import validate_full_spdx_document from src.validation.validation_message import ValidationMessage -from tests.valid_defaults import get_creation_info, get_package, get_file, get_snippet, get_annotation, \ - get_relationship, get_extracted_licensing_info +from tests.fixtures import document_fixture def test_valid_document(): - document = Document(get_creation_info(), [get_package(), get_package()], [get_file(), get_file()], - [get_snippet(), get_snippet()], [get_annotation(), get_annotation()], - [get_relationship(), get_relationship()], - [get_extracted_licensing_info(), get_extracted_licensing_info()]) + document = document_fixture() validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, "2.3") assert validation_messages == [] diff --git a/tests/validation/test_external_document_ref_validator.py b/tests/validation/test_external_document_ref_validator.py index 84ead91ad..611c1e5cb 100644 --- a/tests/validation/test_external_document_ref_validator.py +++ b/tests/validation/test_external_document_ref_validator.py @@ -11,15 +11,13 @@ from typing import List -from src.model.external_document_ref import ExternalDocumentRef from src.validation.external_document_ref_validator import validate_external_document_ref from src.validation.validation_message import ValidationMessage -from tests.valid_defaults import get_checksum +from tests.fixtures import external_document_ref_fixture def test_valid_external_document_ref(): - - external_document_ref = ExternalDocumentRef("DocumentRef-id", "http://some.uri", get_checksum()) + external_document_ref = external_document_ref_fixture() validation_messages: List[ValidationMessage] = validate_external_document_ref(external_document_ref, "parent_id") assert validation_messages == [] diff --git a/tests/validation/test_external_package_ref_validator.py b/tests/validation/test_external_package_ref_validator.py index 7b94e84e6..456dd7acd 100644 --- a/tests/validation/test_external_package_ref_validator.py +++ b/tests/validation/test_external_package_ref_validator.py @@ -13,26 +13,24 @@ import pytest -from src.model.package import ExternalPackageRef, ExternalPackageRefCategory from src.validation.external_package_ref_validator import validate_external_package_ref from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.valid_defaults import get_external_package_ref +from tests.fixtures import external_package_ref_fixture def test_valid_external_package_ref(): - - external_package_ref = ExternalPackageRef(ExternalPackageRefCategory.OTHER, "swh", - "swh:1:cnt:94a9ed024d3859793618152ea559a168bbcbb5e2", "comment") + external_package_ref = external_package_ref_fixture() validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, "parent_id") assert validation_messages == [] @pytest.mark.parametrize("external_package_ref, expected_message", - [(get_external_package_ref(), + [(external_package_ref_fixture(), "TBD"), ]) -@pytest.mark.skip("add tests once external package ref validation is implemented: https://github.com/spdx/tools-python/issues/373") +@pytest.mark.skip( + "add tests once external package ref validation is implemented: https://github.com/spdx/tools-python/issues/373") def test_invalid_external_package_ref(external_package_ref, expected_message): parent_id = "SPDXRef-Package" validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, parent_id) diff --git a/tests/validation/test_extracted_licensing_info_validator.py b/tests/validation/test_extracted_licensing_info_validator.py index 25047cca5..395c3a000 100644 --- a/tests/validation/test_extracted_licensing_info_validator.py +++ b/tests/validation/test_extracted_licensing_info_validator.py @@ -13,15 +13,13 @@ import pytest -from src.model.extracted_licensing_info import ExtractedLicensingInfo from src.validation.extracted_licensing_info_validator import validate_extracted_licensing_info from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.valid_defaults import get_extracted_licensing_info +from tests.fixtures import extracted_licensing_info_fixture def test_valid_extracted_licensing_info(): - extracted_licensing_info = ExtractedLicensingInfo("LicenseRef-1", "extracted text", "license name", - ["http://some.url"], "comment") + extracted_licensing_info = extracted_licensing_info_fixture() validation_messages: List[ValidationMessage] = validate_extracted_licensing_info(extracted_licensing_info) assert validation_messages == [] @@ -29,9 +27,9 @@ def test_valid_extracted_licensing_info(): # TODO: tests for licenses not on the SPDX License list (i.e. they must provide id, name and cross-references) @pytest.mark.parametrize("extracted_licensing_info, expected_message", - [(get_extracted_licensing_info(extracted_text=None), + [(extracted_licensing_info_fixture(extracted_text=None), 'extracted_text must be provided if there is a license_id assigned'), - (get_extracted_licensing_info(cross_references=["invalid_url"]), + (extracted_licensing_info_fixture(cross_references=["invalid_url"]), 'cross_reference must be a valid URL, but is: invalid_url') ]) def test_invalid_extracted_licensing_info(extracted_licensing_info, expected_message): diff --git a/tests/validation/test_file_validator.py b/tests/validation/test_file_validator.py index fda40ab36..f76822255 100644 --- a/tests/validation/test_file_validator.py +++ b/tests/validation/test_file_validator.py @@ -14,36 +14,32 @@ import pytest from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.file import File, FileType -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone from src.validation.file_validator import validate_file_within_document from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.valid_defaults import get_checksum, get_file, get_document +from tests.fixtures import file_fixture, document_fixture def test_valid_file(): - file = File("./file/name.py", "SPDXRef-File", [get_checksum()], [FileType.OTHER, FileType.SPDX], SpdxNone(), - SpdxNoAssertion(), - "comment on license", "copyright", "comment", "notice", ["contributor"], ["attribution"]) - validation_messages: List[ValidationMessage] = validate_file_within_document(file, get_document()) + file = file_fixture() + validation_messages: List[ValidationMessage] = validate_file_within_document(file, document_fixture()) assert validation_messages == [] @pytest.mark.parametrize("file_input, spdx_id, expected_message", - [(get_file(name="invalid file name"), get_file().spdx_id, + [(file_fixture(name="invalid file name"), file_fixture().spdx_id, 'file name must be a relative path to the file, starting with "./", but is: invalid file name'), - (get_file(checksums=[Checksum(ChecksumAlgorithm.MD2, "d4c41ce30a517d6ce9d79c8c17bb4b66")]), - get_file().spdx_id, - f'checksums must contain a SHA1 algorithm checksum, but only contains: []') + ( + file_fixture(checksums=[Checksum(ChecksumAlgorithm.MD2, "d4c41ce30a517d6ce9d79c8c17bb4b66")]), + file_fixture().spdx_id, + f'checksums must contain a SHA1 algorithm checksum, but only contains: []') ]) def test_invalid_file(file_input, spdx_id, expected_message): - validation_messages: List[ValidationMessage] = validate_file_within_document(file_input, get_document()) + validation_messages: List[ValidationMessage] = validate_file_within_document(file_input, document_fixture()) expected = ValidationMessage(expected_message, ValidationContext(spdx_id=spdx_id, - parent_id=get_document().creation_info.spdx_id, + parent_id=document_fixture().creation_info.spdx_id, element_type=SpdxElementType.FILE, full_element=file_input)) diff --git a/tests/validation/test_package_validator.py b/tests/validation/test_package_validator.py index 8c81c6739..31b86b8fb 100644 --- a/tests/validation/test_package_validator.py +++ b/tests/validation/test_package_validator.py @@ -9,46 +9,43 @@ # See the License for the specific language governing permissions and # limitations under the License. -from datetime import datetime from typing import List import pytest from src.model.license_expression import LicenseExpression -from src.model.package import Package, PackagePurpose from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone from src.validation.package_validator import validate_package_within_document from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.valid_defaults import get_checksum, get_external_package_ref, get_actor, get_package_verification_code, \ - get_package, get_document +from tests.fixtures import package_fixture, package_verification_code_fixture, document_fixture def test_valid_package(): - package = Package("SPDXRef-Package", "package name", "www.download.com", "version", "file_name", SpdxNoAssertion(), - get_actor(), True, - get_package_verification_code(), [get_checksum()], "https://homepage.com", "source_info", None, - [LicenseExpression("expression")], - SpdxNone(), "comment on license", "copyright", "summary", "description", "comment", - [get_external_package_ref()], ["text"], PackagePurpose.OTHER, datetime(2022, 1, 1), None, None) - validation_messages: List[ValidationMessage] = validate_package_within_document(package, get_document()) + package = package_fixture() + validation_messages: List[ValidationMessage] = validate_package_within_document(package, document_fixture()) assert validation_messages == [] @pytest.mark.parametrize("package_input, expected_message", - [(get_package(files_analyzed=False, verification_code=get_package_verification_code()), - f'verification_code must be None if files_analyzed is False, but is: {get_package_verification_code()}'), - (get_package(files_analyzed=False, license_info_from_files=SpdxNone()), + [(package_fixture(files_analyzed=False, verification_code=package_verification_code_fixture(), + license_info_from_files=[]), + f'verification_code must be None if files_analyzed is False, but is: {package_verification_code_fixture()}'), + (package_fixture(files_analyzed=False, license_info_from_files=SpdxNone(), + verification_code=None), 'license_info_from_files must be None if files_analyzed is False, but is: NONE'), - (get_package(files_analyzed=False, license_info_from_files=SpdxNoAssertion()), + (package_fixture(files_analyzed=False, license_info_from_files=SpdxNoAssertion(), + verification_code=None), 'license_info_from_files must be None if files_analyzed is False, but is: NOASSERTION'), - (get_package(files_analyzed=False, - license_info_from_files=[LicenseExpression("some_license")]), + (package_fixture(files_analyzed=False, + license_info_from_files=[LicenseExpression("some_license")], + verification_code=None), 'license_info_from_files must be None if files_analyzed is False, but is: [LicenseExpression(expression_string=\'some_license\')]') ]) def test_invalid_package(package_input, expected_message): - validation_messages: List[ValidationMessage] = validate_package_within_document(package_input, get_document()) + validation_messages: List[ValidationMessage] = validate_package_within_document(package_input, + document_fixture(relationships=[])) expected = ValidationMessage(expected_message, ValidationContext(spdx_id=package_input.spdx_id, parent_id="SPDXRef-DOCUMENT", diff --git a/tests/validation/test_relationship_validator.py b/tests/validation/test_relationship_validator.py index 2e2a08b95..706655551 100644 --- a/tests/validation/test_relationship_validator.py +++ b/tests/validation/test_relationship_validator.py @@ -19,16 +19,14 @@ from src.model.spdx_none import SpdxNone from src.validation.relationship_validator import validate_relationship from src.validation.validation_message import ValidationMessage, SpdxElementType, ValidationContext -from tests.valid_defaults import get_document, get_package, get_relationship, get_file +from tests.fixtures import document_fixture, relationship_fixture @pytest.mark.parametrize("related_spdx_element", ["SPDXRef-Package", SpdxNoAssertion(), SpdxNone()]) def test_valid_relationship(related_spdx_element): - document: Document = get_document(packages=[get_package(spdx_id="SPDXRef-Package")]) - - relationship = Relationship("SPDXRef-DOCUMENT", RelationshipType.AMENDS, related_spdx_element, comment="comment") - validation_messages: List[ValidationMessage] = validate_relationship(relationship, document, "2.3") + relationship = Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, related_spdx_element, comment="comment") + validation_messages: List[ValidationMessage] = validate_relationship(relationship, document_fixture(), "2.3") assert validation_messages == [] @@ -40,10 +38,9 @@ def test_valid_relationship(related_spdx_element): 'did not find the referenced spdx_id SPDXRef-unknownFile in the SPDX document'), ]) def test_unknown_spdx_id(spdx_element_id, related_spdx_element_id, expected_message): - relationship: Relationship = get_relationship(spdx_element_id=spdx_element_id, - related_spdx_element_id=related_spdx_element_id) - document: Document = get_document(files=[get_file(spdx_id="SPDXRef-File")]) - validation_messages: List[ValidationMessage] = validate_relationship(relationship, document, "2.3") + relationship: Relationship = relationship_fixture(spdx_element_id=spdx_element_id, + related_spdx_element_id=related_spdx_element_id) + validation_messages: List[ValidationMessage] = validate_relationship(relationship, document_fixture(), "2.3") expected = ValidationMessage(expected_message, ValidationContext(element_type=SpdxElementType.RELATIONSHIP, @@ -59,7 +56,7 @@ def test_unknown_spdx_id(spdx_element_id, related_spdx_element_id, expected_mess "SPDXRef-Package"), "RelationshipType.REQUIREMENT_DESCRIPTION_FOR is not supported for SPDX versions below 2.3")]) def test_v2_3_only_types(relationship, expected_message): - document: Document = get_document(packages=[get_package(spdx_id="SPDXRef-Package")]) + document: Document = document_fixture() validation_message: List[ValidationMessage] = validate_relationship(relationship, document, "2.2") diff --git a/tests/validation/test_snippet_validator.py b/tests/validation/test_snippet_validator.py index 9ceb675f6..8b072fc94 100644 --- a/tests/validation/test_snippet_validator.py +++ b/tests/validation/test_snippet_validator.py @@ -13,43 +13,34 @@ import pytest -from src.model.document import Document -from src.model.license_expression import LicenseExpression -from src.model.snippet import Snippet -from src.model.spdx_no_assertion import SpdxNoAssertion from src.validation.snippet_validator import validate_snippet_within_document from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.valid_defaults import get_snippet, get_document, get_file +from tests.fixtures import document_fixture, snippet_fixture def test_valid_snippet(): - document: Document = get_document(files=[get_file(spdx_id="SPDXRef-File")]) - - snippet = Snippet("SPDXRef-Snippet", "SPDXRef-File", (200, 400), (20, 40), LicenseExpression("some_license"), - SpdxNoAssertion(), "comment on license", - "copyright", "comment", "name", ["attribution"]) - validation_messages: List[ValidationMessage] = validate_snippet_within_document(snippet, document) + snippet = snippet_fixture() + validation_messages: List[ValidationMessage] = validate_snippet_within_document(snippet, document_fixture()) assert validation_messages == [] @pytest.mark.parametrize("snippet_input, expected_message", - [(get_snippet(byte_range=(-12, 45)), + [(snippet_fixture(byte_range=(-12, 45)), "byte_range values must be greater than or equal to 1, but is: (-12, 45)"), - (get_snippet(byte_range=(45, 23)), + (snippet_fixture(byte_range=(45, 23)), "the first value of byte_range must be less than or equal to the second, but is: (45, 23)"), - (get_snippet(line_range=(-12, 45)), + (snippet_fixture(line_range=(-12, 45)), "line_range values must be greater than or equal to 1, but is: (-12, 45)"), - (get_snippet(line_range=(45, 23)), + (snippet_fixture(line_range=(45, 23)), "the first value of line_range must be less than or equal to the second, but is: (45, 23)") ]) def test_invalid_ranges(snippet_input, expected_message): - validation_messages: List[ValidationMessage] = validate_snippet_within_document(snippet_input, - get_document(files=[get_file()])) + validation_messages: List[ValidationMessage] = validate_snippet_within_document(snippet_input, document_fixture()) expected = ValidationMessage(expected_message, ValidationContext(spdx_id=snippet_input.spdx_id, - parent_id=get_document().creation_info.spdx_id, + parent_id=document_fixture().creation_info.spdx_id, element_type=SpdxElementType.SNIPPET, full_element=snippet_input)) diff --git a/tests/writer/tagvalue/test_package_writer.py b/tests/writer/tagvalue/test_package_writer.py index 052b24dd7..735a983dd 100644 --- a/tests/writer/tagvalue/test_package_writer.py +++ b/tests/writer/tagvalue/test_package_writer.py @@ -11,22 +11,27 @@ from datetime import datetime from unittest.mock import patch, mock_open, call +from src.model.actor import ActorType, Actor +from src.model.checksum import Checksum, ChecksumAlgorithm from src.model.license_expression import LicenseExpression -from src.model.package import PackagePurpose +from src.model.package import PackagePurpose, Package, PackageVerificationCode, ExternalPackageRef, \ + ExternalPackageRefCategory from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone from src.writer.tagvalue.package_writer import write_package -from tests.valid_defaults import get_package, get_package_verification_code, get_actor, get_checksum, \ - get_external_package_ref def test_package_writer(): - package = get_package("SPDXRef-Package", "package name", "www.download.com", "version", "file_name", SpdxNoAssertion(), - get_actor(), True, - get_package_verification_code(), [get_checksum()], "https://homepage.com", "source_info", None, - [LicenseExpression("expression")], + package = Package("SPDXRef-Package", "package name", "www.download.com", "version", "file_name", SpdxNoAssertion(), + Actor(ActorType.PERSON, "person name", "email@mail.com"), True, + PackageVerificationCode("85ed0817af83a24ad8da68c2b5094de69833983c"), + [Checksum(ChecksumAlgorithm.SHA1, "85ed0817af83a24ad8da68c2b5094de69833983c")], + "https://homepage.com", "source_info", None, [LicenseExpression("expression")], SpdxNone(), "comment on license", "copyright", "summary", "description", "comment", - [get_external_package_ref()], ["text"], PackagePurpose.OTHER, datetime(2022, 1, 1), None, None) + [ExternalPackageRef(ExternalPackageRefCategory.SECURITY, "cpe22Type", + "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", + "external package ref comment")], + ["text"], PackagePurpose.OTHER, datetime(2022, 1, 1), None, None) m = mock_open() with patch('{}.open'.format(__name__), m, create=True): @@ -42,7 +47,7 @@ def test_package_writer(): call('PackageVersion: version\n'), call('PackageFileName: file_name\n'), call('PackageSupplier: NOASSERTION\n'), - call('PackageOriginator: Person: person name\n'), + call('PackageOriginator: Person: person name (email@mail.com)\n'), call('PackageDownloadLocation: www.download.com\n'), call('FilesAnalyzed: True\n'), call('PackageVerificationCode: 85ed0817af83a24ad8da68c2b5094de69833983c\n'), From da5705e2707ea18e7a633b48a422170bac41a279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 3 Jan 2023 09:33:48 +0100 Subject: [PATCH 127/362] [issue-403] change spdx version handling during validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/validation/creation_info_validator.py | 7 ++++++- src/validation/document_validator.py | 7 +++++-- src/validation/relationship_validator.py | 4 ++-- src/writer/json/json_writer.py | 3 +-- tests/validation/test_document_validator.py | 2 +- tests/validation/test_relationship_validator.py | 8 ++++---- 6 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/validation/creation_info_validator.py b/src/validation/creation_info_validator.py index 5f7a42783..ca200d9f0 100644 --- a/src/validation/creation_info_validator.py +++ b/src/validation/creation_info_validator.py @@ -19,7 +19,7 @@ from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_creation_info(creation_info: CreationInfo) -> List[ValidationMessage]: +def validate_creation_info(creation_info: CreationInfo, spdx_version: str) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] context = ValidationContext(spdx_id=creation_info.spdx_id, element_type=SpdxElementType.DOCUMENT) @@ -31,6 +31,11 @@ def validate_creation_info(creation_info: CreationInfo) -> List[ValidationMessag context ) ) + elif spdx_version != creation_info.spdx_version: + validation_messages.append( + ValidationMessage(f"provided SPDX version {spdx_version} does not match " + f"the document's SPDX version {creation_info.spdx_version}", context) + ) if creation_info.spdx_id != "SPDXRef-DOCUMENT": validation_messages.append( diff --git a/src/validation/document_validator.py b/src/validation/document_validator.py index bf2c11450..448b326a9 100644 --- a/src/validation/document_validator.py +++ b/src/validation/document_validator.py @@ -24,10 +24,13 @@ from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_full_spdx_document(document: Document, spdx_version: str) -> List[ValidationMessage]: +def validate_full_spdx_document(document: Document, spdx_version: str = None) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] - validation_messages.extend(validate_creation_info(document.creation_info)) + if not spdx_version: + spdx_version = document.creation_info.spdx_version + + validation_messages.extend(validate_creation_info(document.creation_info, spdx_version)) validation_messages.extend(validate_packages(document.packages, document)) validation_messages.extend(validate_files(document.files, document)) validation_messages.extend(validate_snippets(document.snippets, document)) diff --git a/src/validation/relationship_validator.py b/src/validation/relationship_validator.py index 87da7f9ba..bace55213 100644 --- a/src/validation/relationship_validator.py +++ b/src/validation/relationship_validator.py @@ -43,9 +43,9 @@ def validate_relationship(relationship: Relationship, document: Document, spdx_v for message in messages: validation_messages.append(ValidationMessage(message, context)) - if spdx_version != "2.3": + if spdx_version != "SPDX-2.3": if relationship_type == RelationshipType.SPECIFICATION_FOR or relationship_type == RelationshipType.REQUIREMENT_DESCRIPTION_FOR: validation_messages.append( - ValidationMessage(f"{relationship_type} is not supported for SPDX versions below 2.3", context)) + ValidationMessage(f"{relationship_type} is not supported for SPDX versions below SPDX-2.3", context)) return validation_messages diff --git a/src/writer/json/json_writer.py b/src/writer/json/json_writer.py index 5083c8caa..fbde9adc2 100644 --- a/src/writer/json/json_writer.py +++ b/src/writer/json/json_writer.py @@ -24,8 +24,7 @@ def write_document(document: Document, file_name: str, validate: bool = True, co a new one is created. """ if validate: - validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, - document.creation_info.spdx_version) + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document) if validation_messages: raise ValueError(f"Document is not valid. The following errors were detected: {validation_messages}") if converter is None: diff --git a/tests/validation/test_document_validator.py b/tests/validation/test_document_validator.py index 637c6ad74..3036ebfe4 100644 --- a/tests/validation/test_document_validator.py +++ b/tests/validation/test_document_validator.py @@ -18,7 +18,7 @@ def test_valid_document(): document = document_fixture() - validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, "2.3") + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document) assert validation_messages == [] diff --git a/tests/validation/test_relationship_validator.py b/tests/validation/test_relationship_validator.py index 706655551..1d164e29b 100644 --- a/tests/validation/test_relationship_validator.py +++ b/tests/validation/test_relationship_validator.py @@ -40,7 +40,7 @@ def test_valid_relationship(related_spdx_element): def test_unknown_spdx_id(spdx_element_id, related_spdx_element_id, expected_message): relationship: Relationship = relationship_fixture(spdx_element_id=spdx_element_id, related_spdx_element_id=related_spdx_element_id) - validation_messages: List[ValidationMessage] = validate_relationship(relationship, document_fixture(), "2.3") + validation_messages: List[ValidationMessage] = validate_relationship(relationship, document_fixture(), "SPDX-2.3") expected = ValidationMessage(expected_message, ValidationContext(element_type=SpdxElementType.RELATIONSHIP, @@ -51,14 +51,14 @@ def test_unknown_spdx_id(spdx_element_id, related_spdx_element_id, expected_mess @pytest.mark.parametrize("relationship, expected_message", [(Relationship("SPDXRef-DOCUMENT", RelationshipType.SPECIFICATION_FOR, "SPDXRef-Package"), - "RelationshipType.SPECIFICATION_FOR is not supported for SPDX versions below 2.3"), + "RelationshipType.SPECIFICATION_FOR is not supported for SPDX versions below SPDX-2.3"), (Relationship("SPDXRef-DOCUMENT", RelationshipType.REQUIREMENT_DESCRIPTION_FOR, "SPDXRef-Package"), - "RelationshipType.REQUIREMENT_DESCRIPTION_FOR is not supported for SPDX versions below 2.3")]) + "RelationshipType.REQUIREMENT_DESCRIPTION_FOR is not supported for SPDX versions below SPDX-2.3")]) def test_v2_3_only_types(relationship, expected_message): document: Document = document_fixture() - validation_message: List[ValidationMessage] = validate_relationship(relationship, document, "2.2") + validation_message: List[ValidationMessage] = validate_relationship(relationship, document, "SPDX-2.2") expected = [ValidationMessage(expected_message, ValidationContext(element_type=SpdxElementType.RELATIONSHIP, From e455066e5f2326dd8b10e5bce2e9925808ad8d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 5 Jan 2023 11:13:23 +0100 Subject: [PATCH 128/362] [issue-403] restructure spdx version validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/validation/creation_info_validator.py | 15 +------ src/validation/document_validator.py | 28 +++++++++++-- .../test_creation_info_validator.py | 4 +- tests/validation/test_document_validator.py | 41 +++++++++++++++++-- 4 files changed, 64 insertions(+), 24 deletions(-) diff --git a/src/validation/creation_info_validator.py b/src/validation/creation_info_validator.py index ca200d9f0..50446f8a7 100644 --- a/src/validation/creation_info_validator.py +++ b/src/validation/creation_info_validator.py @@ -19,24 +19,11 @@ from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_creation_info(creation_info: CreationInfo, spdx_version: str) -> List[ValidationMessage]: +def validate_creation_info(creation_info: CreationInfo) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] context = ValidationContext(spdx_id=creation_info.spdx_id, element_type=SpdxElementType.DOCUMENT) - if not re.match(r"^SPDX-\d+.\d+$", creation_info.spdx_version): - validation_messages.append( - ValidationMessage( - f'spdx_version must be of the form "SPDX-[major].[minor]" but is: {creation_info.spdx_version}', - context - ) - ) - elif spdx_version != creation_info.spdx_version: - validation_messages.append( - ValidationMessage(f"provided SPDX version {spdx_version} does not match " - f"the document's SPDX version {creation_info.spdx_version}", context) - ) - if creation_info.spdx_id != "SPDXRef-DOCUMENT": validation_messages.append( ValidationMessage( diff --git a/src/validation/document_validator.py b/src/validation/document_validator.py index 448b326a9..cab7b097e 100644 --- a/src/validation/document_validator.py +++ b/src/validation/document_validator.py @@ -8,7 +8,7 @@ # 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 re from typing import List from src.model.document import Document @@ -27,10 +27,32 @@ def validate_full_spdx_document(document: Document, spdx_version: str = None) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] + # SPDX version validation has to happen here because subsequent validators rely on it + document_version: str = document.creation_info.spdx_version + context = ValidationContext(spdx_id=document.creation_info.spdx_id, element_type=SpdxElementType.DOCUMENT) if not spdx_version: - spdx_version = document.creation_info.spdx_version + spdx_version = document_version + + if not re.match(r"^SPDX-\d+.\d+$", document_version): + validation_messages.append( + ValidationMessage( + f'the document\'s spdx_version must be of the form "SPDX-[major].[minor]" but is: {document_version}', + context + ) + ) + elif spdx_version != document_version: + validation_messages.append( + ValidationMessage(f"provided SPDX version {spdx_version} does not match " + f"the document's SPDX version {document_version}", context) + ) + + if validation_messages: + validation_messages.append(ValidationMessage("There are issues concerning the SPDX version of the document. " + "As subsequent validation relies on the correct version, " + "the validation process has been cancelled.", context)) + return validation_messages - validation_messages.extend(validate_creation_info(document.creation_info, spdx_version)) + validation_messages.extend(validate_creation_info(document.creation_info)) validation_messages.extend(validate_packages(document.packages, document)) validation_messages.extend(validate_files(document.files, document)) validation_messages.extend(validate_snippets(document.snippets, document)) diff --git a/tests/validation/test_creation_info_validator.py b/tests/validation/test_creation_info_validator.py index 5de041660..8f0d66121 100644 --- a/tests/validation/test_creation_info_validator.py +++ b/tests/validation/test_creation_info_validator.py @@ -27,9 +27,7 @@ def test_valid_creation_info(): @pytest.mark.parametrize \ ("creation_info_input, spdx_id, expected_message", - [(creation_info_fixture(spdx_version="version-2.3"), "SPDXRef-DOCUMENT", - 'spdx_version must be of the form "SPDX-[major].[minor]" but is: version-2.3'), - (creation_info_fixture(spdx_id="SPDXRef-doc"), "SPDXRef-doc", + [(creation_info_fixture(spdx_id="SPDXRef-doc"), "SPDXRef-doc", 'spdx_id must be "SPDXRef-DOCUMENT", but is: SPDXRef-doc'), (creation_info_fixture(data_license="MIT"), "SPDXRef-DOCUMENT", 'data_license must be "CC0-1.0", but is: MIT'), diff --git a/tests/validation/test_document_validator.py b/tests/validation/test_document_validator.py index 3036ebfe4..a7f2d7a5b 100644 --- a/tests/validation/test_document_validator.py +++ b/tests/validation/test_document_validator.py @@ -9,11 +9,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List +from typing import List, Optional +import pytest + +from src.model.document import Document, CreationInfo from src.validation.document_validator import validate_full_spdx_document -from src.validation.validation_message import ValidationMessage -from tests.fixtures import document_fixture +from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from tests.fixtures import document_fixture, creation_info_fixture def test_valid_document(): @@ -22,4 +25,34 @@ def test_valid_document(): assert validation_messages == [] -# TODO: https://github.com/spdx/tools-python/issues/375 + +@pytest.mark.parametrize("creation_info, version_input, expected_message", + [(creation_info_fixture(spdx_version="SPDX-2.3"), "SPDX-2.3", None), + (creation_info_fixture(spdx_version="SPDX-2.3"), None, None), + (creation_info_fixture(spdx_version="SPDX-2.3"), "SPDX-2.2", + "provided SPDX version SPDX-2.2 does not match the document's SPDX version SPDX-2.3"), + (creation_info_fixture(spdx_version="SPDX-2.3"), "SPDX2.3", + "provided SPDX version SPDX2.3 does not match the document's SPDX version SPDX-2.3"), + (creation_info_fixture(spdx_version="SPDX2.3"), "SPDX-2.3", + 'the document\'s spdx_version must be of the form "SPDX-[major].[minor]" but is: SPDX2.3'), + (creation_info_fixture(spdx_version="SPDX2.3"), None, + 'the document\'s spdx_version must be of the form "SPDX-[major].[minor]" but is: SPDX2.3'), + (creation_info_fixture(spdx_version="SPDX2.3"), "SPDX2.3", + 'the document\'s spdx_version must be of the form "SPDX-[major].[minor]" but is: SPDX2.3'), + ]) +def test_spdx_version_handling(creation_info: CreationInfo, version_input: str, expected_message: Optional[str]): + document: Document = document_fixture(creation_info=creation_info) + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, version_input) + + context = ValidationContext(spdx_id=creation_info.spdx_id, element_type=SpdxElementType.DOCUMENT) + expected: List[ValidationMessage] = [] + + if expected_message: + expected.append(ValidationMessage(expected_message, context)) + expected.append(ValidationMessage("There are issues concerning the SPDX version of the document. " + "As subsequent validation relies on the correct version, " + "the validation process has been cancelled.", context)) + + assert validation_messages == expected + + # TODO: https://github.com/spdx/tools-python/issues/375 From b930facd914e9896f9cb4be5d9624bf30f5e254c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 3 Jan 2023 11:47:18 +0100 Subject: [PATCH 129/362] [issue-406] rename json parser package to jsonlikedict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/parser/{json => jsonlikedict}/__init__.py | 0 .../{json => jsonlikedict}/actor_parser.py | 2 +- .../annotation_parser.py | 4 ++-- .../{json => jsonlikedict}/checksum_parser.py | 2 +- .../creation_info_parser.py | 6 +++--- .../dict_parsing_functions.py | 0 .../extracted_licensing_info_parser.py | 2 +- .../{json => jsonlikedict}/file_parser.py | 6 +++--- .../{json => jsonlikedict}/json_parser.py | 18 +++++++++--------- .../license_expression_parser.py | 2 +- .../{json => jsonlikedict}/package_parser.py | 8 ++++---- .../relationship_parser.py | 2 +- .../{json => jsonlikedict}/snippet_parser.py | 4 ++-- tests/parser/test_actor_parser.py | 2 +- tests/parser/test_annotation_parser.py | 2 +- tests/parser/test_checksum_parser.py | 2 +- tests/parser/test_creation_info_parser.py | 2 +- tests/parser/test_dict_parsing_functions.py | 2 +- .../test_extracted_licensing_info_parser.py | 2 +- tests/parser/test_file_parser.py | 4 ++-- tests/parser/test_json_parser.py | 10 +++++----- tests/parser/test_license_expression_parser.py | 2 +- tests/parser/test_package_parser.py | 4 ++-- tests/parser/test_relationship_parser.py | 2 +- tests/parser/test_snippet_parser.py | 2 +- 25 files changed, 46 insertions(+), 46 deletions(-) rename src/parser/{json => jsonlikedict}/__init__.py (100%) rename src/parser/{json => jsonlikedict}/actor_parser.py (96%) rename src/parser/{json => jsonlikedict}/annotation_parser.py (97%) rename src/parser/{json => jsonlikedict}/checksum_parser.py (92%) rename src/parser/{json => jsonlikedict}/creation_info_parser.py (96%) rename src/parser/{json => jsonlikedict}/dict_parsing_functions.py (100%) rename src/parser/{json => jsonlikedict}/extracted_licensing_info_parser.py (94%) rename src/parser/{json => jsonlikedict}/file_parser.py (94%) rename src/parser/{json => jsonlikedict}/json_parser.py (86%) rename src/parser/{json => jsonlikedict}/license_expression_parser.py (94%) rename src/parser/{json => jsonlikedict}/package_parser.py (97%) rename src/parser/{json => jsonlikedict}/relationship_parser.py (98%) rename src/parser/{json => jsonlikedict}/snippet_parser.py (96%) diff --git a/src/parser/json/__init__.py b/src/parser/jsonlikedict/__init__.py similarity index 100% rename from src/parser/json/__init__.py rename to src/parser/jsonlikedict/__init__.py diff --git a/src/parser/json/actor_parser.py b/src/parser/jsonlikedict/actor_parser.py similarity index 96% rename from src/parser/json/actor_parser.py rename to src/parser/jsonlikedict/actor_parser.py index 31082e0fc..907233aa7 100644 --- a/src/parser/json/actor_parser.py +++ b/src/parser/jsonlikedict/actor_parser.py @@ -13,7 +13,7 @@ from src.model.actor import Actor, ActorType from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error +from src.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error class ActorParser: diff --git a/src/parser/json/annotation_parser.py b/src/parser/jsonlikedict/annotation_parser.py similarity index 97% rename from src/parser/json/annotation_parser.py rename to src/parser/jsonlikedict/annotation_parser.py index 85bf4115d..8fa928228 100644 --- a/src/parser/json/annotation_parser.py +++ b/src/parser/jsonlikedict/annotation_parser.py @@ -14,8 +14,8 @@ from src.model.actor import Actor from src.model.annotation import Annotation, AnnotationType from src.parser.error import SPDXParsingError -from src.parser.json.actor_parser import ActorParser -from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error, \ +from src.parser.jsonlikedict.actor_parser import ActorParser +from src.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error, \ parse_field_or_log_error, append_parsed_field_or_log_error, raise_parsing_error_if_logger_has_messages from src.datetime_conversions import datetime_from_str from src.parser.logger import Logger diff --git a/src/parser/json/checksum_parser.py b/src/parser/jsonlikedict/checksum_parser.py similarity index 92% rename from src/parser/json/checksum_parser.py rename to src/parser/jsonlikedict/checksum_parser.py index d6f82c412..a73a9d443 100644 --- a/src/parser/json/checksum_parser.py +++ b/src/parser/jsonlikedict/checksum_parser.py @@ -11,7 +11,7 @@ from typing import Dict, Optional from src.model.checksum import Checksum, ChecksumAlgorithm -from src.parser.json.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, \ +from src.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, \ construct_or_raise_parsing_error from src.parser.logger import Logger diff --git a/src/parser/json/creation_info_parser.py b/src/parser/jsonlikedict/creation_info_parser.py similarity index 96% rename from src/parser/json/creation_info_parser.py rename to src/parser/jsonlikedict/creation_info_parser.py index 51c115d10..4b51056ad 100644 --- a/src/parser/json/creation_info_parser.py +++ b/src/parser/jsonlikedict/creation_info_parser.py @@ -17,9 +17,9 @@ from src.model.external_document_ref import ExternalDocumentRef from src.model.version import Version from src.parser.error import SPDXParsingError -from src.parser.json.actor_parser import ActorParser -from src.parser.json.checksum_parser import ChecksumParser -from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, \ +from src.parser.jsonlikedict.actor_parser import ActorParser +from src.parser.jsonlikedict.checksum_parser import ChecksumParser +from src.parser.jsonlikedict.dict_parsing_functions import append_parsed_field_or_log_error, \ raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error, parse_field_or_log_error, \ parse_field_or_no_assertion from src.datetime_conversions import datetime_from_str diff --git a/src/parser/json/dict_parsing_functions.py b/src/parser/jsonlikedict/dict_parsing_functions.py similarity index 100% rename from src/parser/json/dict_parsing_functions.py rename to src/parser/jsonlikedict/dict_parsing_functions.py diff --git a/src/parser/json/extracted_licensing_info_parser.py b/src/parser/jsonlikedict/extracted_licensing_info_parser.py similarity index 94% rename from src/parser/json/extracted_licensing_info_parser.py rename to src/parser/jsonlikedict/extracted_licensing_info_parser.py index dcb4360bb..1c6b51e85 100644 --- a/src/parser/json/extracted_licensing_info_parser.py +++ b/src/parser/jsonlikedict/extracted_licensing_info_parser.py @@ -12,7 +12,7 @@ from src.model.extracted_licensing_info import ExtractedLicensingInfo from src.model.spdx_no_assertion import SpdxNoAssertion -from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error, parse_field_or_no_assertion +from src.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error, parse_field_or_no_assertion from src.parser.logger import Logger diff --git a/src/parser/json/file_parser.py b/src/parser/jsonlikedict/file_parser.py similarity index 94% rename from src/parser/json/file_parser.py rename to src/parser/jsonlikedict/file_parser.py index 8b92b0290..0c63c7f24 100644 --- a/src/parser/json/file_parser.py +++ b/src/parser/jsonlikedict/file_parser.py @@ -15,11 +15,11 @@ from src.model.license_expression import LicenseExpression from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone -from src.parser.json.checksum_parser import ChecksumParser -from src.parser.json.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ +from src.parser.jsonlikedict.checksum_parser import ChecksumParser +from src.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ construct_or_raise_parsing_error, parse_field_or_log_error, \ parse_field_or_no_assertion_or_none -from src.parser.json.license_expression_parser import LicenseExpressionParser +from src.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger diff --git a/src/parser/json/json_parser.py b/src/parser/jsonlikedict/json_parser.py similarity index 86% rename from src/parser/json/json_parser.py rename to src/parser/jsonlikedict/json_parser.py index e81e71636..8440433ff 100644 --- a/src/parser/json/json_parser.py +++ b/src/parser/jsonlikedict/json_parser.py @@ -12,19 +12,19 @@ from src.model.document import Document from src.parser.error import SPDXParsingError -from src.parser.json.annotation_parser import AnnotationParser -from src.parser.json.creation_info_parser import CreationInfoParser -from src.parser.json.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ +from src.parser.jsonlikedict.annotation_parser import AnnotationParser +from src.parser.jsonlikedict.creation_info_parser import CreationInfoParser +from src.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ construct_or_raise_parsing_error, parse_list_of_elements -from src.parser.json.extracted_licensing_info_parser import ExtractedLicensingInfoParser -from src.parser.json.file_parser import FileParser +from src.parser.jsonlikedict.extracted_licensing_info_parser import ExtractedLicensingInfoParser +from src.parser.jsonlikedict.file_parser import FileParser from src.parser.logger import Logger -from src.parser.json.package_parser import PackageParser -from src.parser.json.relationship_parser import RelationshipParser -from src.parser.json.snippet_parser import SnippetParser +from src.parser.jsonlikedict.package_parser import PackageParser +from src.parser.jsonlikedict.relationship_parser import RelationshipParser +from src.parser.jsonlikedict.snippet_parser import SnippetParser -class JsonParser: +class JsonLikeDictParser: logger: Logger creation_info_parser: CreationInfoParser package_parser: PackageParser diff --git a/src/parser/json/license_expression_parser.py b/src/parser/jsonlikedict/license_expression_parser.py similarity index 94% rename from src/parser/json/license_expression_parser.py rename to src/parser/jsonlikedict/license_expression_parser.py index 5fc0df4e1..b1ce2468a 100644 --- a/src/parser/json/license_expression_parser.py +++ b/src/parser/jsonlikedict/license_expression_parser.py @@ -12,7 +12,7 @@ from src.model.license_expression import LicenseExpression from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error, append_parsed_field_or_log_error, \ +from src.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error, append_parsed_field_or_log_error, \ raise_parsing_error_if_logger_has_messages from src.parser.logger import Logger diff --git a/src/parser/json/package_parser.py b/src/parser/jsonlikedict/package_parser.py similarity index 97% rename from src/parser/json/package_parser.py rename to src/parser/jsonlikedict/package_parser.py index 82c1e4d95..ed58d8096 100644 --- a/src/parser/json/package_parser.py +++ b/src/parser/jsonlikedict/package_parser.py @@ -18,13 +18,13 @@ from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone from src.parser.error import SPDXParsingError -from src.parser.json.actor_parser import ActorParser -from src.parser.json.checksum_parser import ChecksumParser -from src.parser.json.dict_parsing_functions import append_parsed_field_or_log_error, \ +from src.parser.jsonlikedict.actor_parser import ActorParser +from src.parser.jsonlikedict.checksum_parser import ChecksumParser +from src.parser.jsonlikedict.dict_parsing_functions import append_parsed_field_or_log_error, \ raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, construct_or_raise_parsing_error, \ parse_field_or_log_error, parse_field_or_no_assertion_or_none, parse_field_or_no_assertion from src.datetime_conversions import datetime_from_str -from src.parser.json.license_expression_parser import LicenseExpressionParser +from src.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger diff --git a/src/parser/json/relationship_parser.py b/src/parser/jsonlikedict/relationship_parser.py similarity index 98% rename from src/parser/json/relationship_parser.py rename to src/parser/jsonlikedict/relationship_parser.py index e2c910e51..5221f1c15 100644 --- a/src/parser/json/relationship_parser.py +++ b/src/parser/jsonlikedict/relationship_parser.py @@ -13,7 +13,7 @@ from src.model.relationship import Relationship, RelationshipType from src.model.typing.constructor_type_errors import ConstructorTypeErrors from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, \ +from src.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, \ construct_or_raise_parsing_error, \ parse_field_or_log_error, parse_field_or_no_assertion_or_none from src.parser.logger import Logger diff --git a/src/parser/json/snippet_parser.py b/src/parser/jsonlikedict/snippet_parser.py similarity index 96% rename from src/parser/json/snippet_parser.py rename to src/parser/jsonlikedict/snippet_parser.py index 97ab6632c..c91f90826 100644 --- a/src/parser/json/snippet_parser.py +++ b/src/parser/jsonlikedict/snippet_parser.py @@ -16,10 +16,10 @@ from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import construct_or_raise_parsing_error, parse_field_or_log_error, \ +from src.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error, parse_field_or_log_error, \ parse_field_or_no_assertion_or_none -from src.parser.json.license_expression_parser import LicenseExpressionParser +from src.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser from src.parser.logger import Logger diff --git a/tests/parser/test_actor_parser.py b/tests/parser/test_actor_parser.py index d73376c9d..93429e42c 100644 --- a/tests/parser/test_actor_parser.py +++ b/tests/parser/test_actor_parser.py @@ -13,7 +13,7 @@ from src.model.actor import ActorType from src.parser.error import SPDXParsingError -from src.parser.json.actor_parser import ActorParser +from src.parser.jsonlikedict.actor_parser import ActorParser @pytest.mark.parametrize("actor_string,expected_type,expected_name,expected_mail", [ diff --git a/tests/parser/test_annotation_parser.py b/tests/parser/test_annotation_parser.py index 0330b5b0b..1ffd819f8 100644 --- a/tests/parser/test_annotation_parser.py +++ b/tests/parser/test_annotation_parser.py @@ -16,7 +16,7 @@ from src.model.actor import Actor, ActorType from src.model.annotation import AnnotationType, Annotation from src.parser.error import SPDXParsingError -from src.parser.json.annotation_parser import AnnotationParser +from src.parser.jsonlikedict.annotation_parser import AnnotationParser def test_parse_annotation(): diff --git a/tests/parser/test_checksum_parser.py b/tests/parser/test_checksum_parser.py index 11ad1610c..86d4b6d5d 100644 --- a/tests/parser/test_checksum_parser.py +++ b/tests/parser/test_checksum_parser.py @@ -14,7 +14,7 @@ from src.model.checksum import ChecksumAlgorithm from src.parser.error import SPDXParsingError -from src.parser.json.checksum_parser import ChecksumParser +from src.parser.jsonlikedict.checksum_parser import ChecksumParser def test_parse_checksum(): diff --git a/tests/parser/test_creation_info_parser.py b/tests/parser/test_creation_info_parser.py index 8126b7fae..0193c1938 100644 --- a/tests/parser/test_creation_info_parser.py +++ b/tests/parser/test_creation_info_parser.py @@ -18,7 +18,7 @@ from src.model.external_document_ref import ExternalDocumentRef from src.model.version import Version from src.parser.error import SPDXParsingError -from src.parser.json.creation_info_parser import CreationInfoParser +from src.parser.jsonlikedict.creation_info_parser import CreationInfoParser def test_parse_creation_info(): diff --git a/tests/parser/test_dict_parsing_functions.py b/tests/parser/test_dict_parsing_functions.py index c77458f94..8d9f80d32 100644 --- a/tests/parser/test_dict_parsing_functions.py +++ b/tests/parser/test_dict_parsing_functions.py @@ -16,7 +16,7 @@ from src.model.spdx_no_assertion import SpdxNoAssertion from src.model.spdx_none import SpdxNone from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import json_str_to_enum_name, \ +from src.parser.jsonlikedict.dict_parsing_functions import json_str_to_enum_name, \ parse_field_or_no_assertion, parse_field_or_no_assertion_or_none from src.datetime_conversions import datetime_from_str diff --git a/tests/parser/test_extracted_licensing_info_parser.py b/tests/parser/test_extracted_licensing_info_parser.py index 8b529aace..848576d37 100644 --- a/tests/parser/test_extracted_licensing_info_parser.py +++ b/tests/parser/test_extracted_licensing_info_parser.py @@ -13,7 +13,7 @@ import pytest from src.parser.error import SPDXParsingError -from src.parser.json.extracted_licensing_info_parser import ExtractedLicensingInfoParser +from src.parser.jsonlikedict.extracted_licensing_info_parser import ExtractedLicensingInfoParser def test_parse_extracted_licensing_info(): diff --git a/tests/parser/test_file_parser.py b/tests/parser/test_file_parser.py index e3ae52ea6..bf26e29e8 100644 --- a/tests/parser/test_file_parser.py +++ b/tests/parser/test_file_parser.py @@ -16,8 +16,8 @@ from src.model.file import FileType from src.model.license_expression import LicenseExpression from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import parse_list_of_elements -from src.parser.json.file_parser import FileParser +from src.parser.jsonlikedict.dict_parsing_functions import parse_list_of_elements +from src.parser.jsonlikedict.file_parser import FileParser def test_parse_file(): diff --git a/tests/parser/test_json_parser.py b/tests/parser/test_json_parser.py index 65a24cc47..78c2733f7 100644 --- a/tests/parser/test_json_parser.py +++ b/tests/parser/test_json_parser.py @@ -13,18 +13,18 @@ import pytest from src.model.document import Document -from src.parser.json.json_parser import JsonParser +from src.parser.jsonlikedict.json_parser import JsonLikeDictParser def test_parse_json_file_not_found(): with pytest.raises(FileNotFoundError) as err: wrong_file_path = os.path.join(os.path.dirname(__file__), 'hnjfkjsedhnflsiafg.json') - JsonParser().parse(wrong_file_path) + JsonLikeDictParser().parse(wrong_file_path) assert err.value.args[1] == "No such file or directory" def test_parse_json_with_2_3_example(): - doc = JsonParser().parse(os.path.join(os.path.dirname(__file__),"../data/formats/SPDXJSONExample-v2.3.spdx.json")) + doc = JsonLikeDictParser().parse(os.path.join(os.path.dirname(__file__), "../data/formats/SPDXJSONExample-v2.3.spdx.json")) assert type(doc) == Document assert len(doc.annotations) == 5 assert len(doc.files) == 5 @@ -34,7 +34,7 @@ def test_parse_json_with_2_3_example(): assert len(doc.extracted_licensing_info) == 5 def test_parse_json_with_2_2_example(): - doc = JsonParser().parse(os.path.join(os.path.dirname(__file__),"../data/formats/SPDXJSONExample-v2.2.spdx.json")) + doc = JsonLikeDictParser().parse(os.path.join(os.path.dirname(__file__), "../data/formats/SPDXJSONExample-v2.2.spdx.json")) assert type(doc) == Document assert len(doc.annotations) == 5 assert len(doc.files) == 4 @@ -44,7 +44,7 @@ def test_parse_json_with_2_2_example(): assert len(doc.extracted_licensing_info) == 5 def test_parse_json_with_2_1_example(): - doc = JsonParser().parse(os.path.join(os.path.dirname(__file__),"../data/formats/SPDXJsonExample.json")) + doc = JsonLikeDictParser().parse(os.path.join(os.path.dirname(__file__), "../data/formats/SPDXJsonExample.json")) assert type(doc) == Document assert len(doc.annotations) == 1 assert len(doc.files) == 2 diff --git a/tests/parser/test_license_expression_parser.py b/tests/parser/test_license_expression_parser.py index 89e23a59a..7e30e2ab2 100644 --- a/tests/parser/test_license_expression_parser.py +++ b/tests/parser/test_license_expression_parser.py @@ -14,7 +14,7 @@ from src.model.license_expression import LicenseExpression from src.parser.error import SPDXParsingError -from src.parser.json.license_expression_parser import LicenseExpressionParser +from src.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser @pytest.mark.parametrize("invalid_license_expression,expected_message", diff --git a/tests/parser/test_package_parser.py b/tests/parser/test_package_parser.py index d5b9d9e7e..5d04331e3 100644 --- a/tests/parser/test_package_parser.py +++ b/tests/parser/test_package_parser.py @@ -18,8 +18,8 @@ from src.model.license_expression import LicenseExpression from src.model.package import PackageVerificationCode, ExternalPackageRef, ExternalPackageRefCategory, PackagePurpose from src.parser.error import SPDXParsingError -from src.parser.json.dict_parsing_functions import parse_list_of_elements -from src.parser.json.package_parser import PackageParser +from src.parser.jsonlikedict.dict_parsing_functions import parse_list_of_elements +from src.parser.jsonlikedict.package_parser import PackageParser def test_parse_package(): diff --git a/tests/parser/test_relationship_parser.py b/tests/parser/test_relationship_parser.py index 27f2c4f09..83bdd212e 100644 --- a/tests/parser/test_relationship_parser.py +++ b/tests/parser/test_relationship_parser.py @@ -15,7 +15,7 @@ from src.model.relationship import RelationshipType, Relationship from src.model.spdx_no_assertion import SpdxNoAssertion from src.parser.error import SPDXParsingError -from src.parser.json.relationship_parser import RelationshipParser +from src.parser.jsonlikedict.relationship_parser import RelationshipParser def test_parse_relationship(): diff --git a/tests/parser/test_snippet_parser.py b/tests/parser/test_snippet_parser.py index 96e1baee9..c068ffd97 100644 --- a/tests/parser/test_snippet_parser.py +++ b/tests/parser/test_snippet_parser.py @@ -14,7 +14,7 @@ from src.model.license_expression import LicenseExpression from src.parser.error import SPDXParsingError -from src.parser.json.snippet_parser import SnippetParser +from src.parser.jsonlikedict.snippet_parser import SnippetParser def test_parse_snippet(): From 669659770da8e5c92750d4f1ec1f406664ba7432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 3 Jan 2023 12:02:34 +0100 Subject: [PATCH 130/362] [issue-406] extract json parser and restructure test order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/parser/json/__init__.py | 0 src/parser/json/json_parser.py | 22 +++++++ ...son_parser.py => json_like_dict_parser.py} | 32 +++++----- tests/parser/json/__init__.py | 0 tests/parser/json/test_json_parser.py | 58 +++++++++++++++++++ tests/parser/jsonlikedict/__init__.py | 0 .../{ => jsonlikedict}/test_actor_parser.py | 0 .../test_annotation_parser.py | 0 .../test_checksum_parser.py | 0 .../test_creation_info_parser.py | 0 .../test_dict_parsing_functions.py | 0 .../test_extracted_licensing_info_parser.py | 0 .../{ => jsonlikedict}/test_file_parser.py | 0 .../test_license_expression_parser.py | 0 .../{ => jsonlikedict}/test_package_parser.py | 0 .../test_relationship_parser.py | 0 .../{ => jsonlikedict}/test_snippet_parser.py | 0 tests/parser/test_json_parser.py | 54 ----------------- 18 files changed, 95 insertions(+), 71 deletions(-) create mode 100644 src/parser/json/__init__.py create mode 100644 src/parser/json/json_parser.py rename src/parser/jsonlikedict/{json_parser.py => json_like_dict_parser.py} (70%) create mode 100644 tests/parser/json/__init__.py create mode 100644 tests/parser/json/test_json_parser.py create mode 100644 tests/parser/jsonlikedict/__init__.py rename tests/parser/{ => jsonlikedict}/test_actor_parser.py (100%) rename tests/parser/{ => jsonlikedict}/test_annotation_parser.py (100%) rename tests/parser/{ => jsonlikedict}/test_checksum_parser.py (100%) rename tests/parser/{ => jsonlikedict}/test_creation_info_parser.py (100%) rename tests/parser/{ => jsonlikedict}/test_dict_parsing_functions.py (100%) rename tests/parser/{ => jsonlikedict}/test_extracted_licensing_info_parser.py (100%) rename tests/parser/{ => jsonlikedict}/test_file_parser.py (100%) rename tests/parser/{ => jsonlikedict}/test_license_expression_parser.py (100%) rename tests/parser/{ => jsonlikedict}/test_package_parser.py (100%) rename tests/parser/{ => jsonlikedict}/test_relationship_parser.py (100%) rename tests/parser/{ => jsonlikedict}/test_snippet_parser.py (100%) delete mode 100644 tests/parser/test_json_parser.py diff --git a/src/parser/json/__init__.py b/src/parser/json/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/parser/json/json_parser.py b/src/parser/json/json_parser.py new file mode 100644 index 000000000..e02da72c7 --- /dev/null +++ b/src/parser/json/json_parser.py @@ -0,0 +1,22 @@ +# 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. +import json +from typing import Dict + +from src.model.document import Document +from src.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser + + +def parse_from_file(file_name: str) -> Document: + with open(file_name) as file: + input_doc_as_dict: Dict = json.load(file) + + return JsonLikeDictParser().parse(input_doc_as_dict) diff --git a/src/parser/jsonlikedict/json_parser.py b/src/parser/jsonlikedict/json_like_dict_parser.py similarity index 70% rename from src/parser/jsonlikedict/json_parser.py rename to src/parser/jsonlikedict/json_like_dict_parser.py index 8440433ff..db78629bb 100644 --- a/src/parser/jsonlikedict/json_parser.py +++ b/src/parser/jsonlikedict/json_like_dict_parser.py @@ -9,6 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import json +from typing import Dict from src.model.document import Document from src.parser.error import SPDXParsingError @@ -44,24 +45,21 @@ def __init__(self): self.relationship_parser = RelationshipParser() self.annotation_parser = AnnotationParser() - def parse(self, filename: str) -> Document: + def parse(self, json_like_dict: Dict) -> Document: - with open(filename) as file: - input_doc_as_dict = json.load(file) - - fields_to_parse = [("creation_info", input_doc_as_dict, self.creation_info_parser.parse_creation_info, False), - ("packages", input_doc_as_dict.get("packages"), lambda x: parse_list_of_elements(x, - self.package_parser.parse_package, - self.package_parser.logger), True), - ("files", input_doc_as_dict.get("files"), lambda x: parse_list_of_elements(x, - self.file_parser.parse_file, - self.file_parser.logger), True), - ("annotations", input_doc_as_dict, self.annotation_parser.parse_all_annotations, True), - ("snippets", input_doc_as_dict.get("snippets"), lambda x: parse_list_of_elements(x, - self.snippet_parser.parse_snippet, - self.snippet_parser.logger), True), - ("relationships", input_doc_as_dict, self.relationship_parser.parse_all_relationships, True), - ("extracted_licensing_info", input_doc_as_dict.get("hasExtractedLicensingInfos"), + fields_to_parse = [("creation_info", json_like_dict, self.creation_info_parser.parse_creation_info, False), + ("packages", json_like_dict.get("packages"), lambda x: parse_list_of_elements(x, + self.package_parser.parse_package, + self.package_parser.logger), True), + ("files", json_like_dict.get("files"), lambda x: parse_list_of_elements(x, + self.file_parser.parse_file, + self.file_parser.logger), True), + ("annotations", json_like_dict, self.annotation_parser.parse_all_annotations, True), + ("snippets", json_like_dict.get("snippets"), lambda x: parse_list_of_elements(x, + self.snippet_parser.parse_snippet, + self.snippet_parser.logger), True), + ("relationships", json_like_dict, self.relationship_parser.parse_all_relationships, True), + ("extracted_licensing_info", json_like_dict.get("hasExtractedLicensingInfos"), lambda x: parse_list_of_elements(x, self.extracted_licensing_info_parser.parse_extracted_licensing_info, self.extracted_licensing_info_parser.logger), True)] diff --git a/tests/parser/json/__init__.py b/tests/parser/json/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/parser/json/test_json_parser.py b/tests/parser/json/test_json_parser.py new file mode 100644 index 000000000..b21d4b4cb --- /dev/null +++ b/tests/parser/json/test_json_parser.py @@ -0,0 +1,58 @@ +# 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. + +import os +import pytest + +from src.model.document import Document +from src.parser.json import json_parser +from src.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser + +def test_parse_json_file_not_found(): + with pytest.raises(FileNotFoundError) as err: + wrong_file_path = os.path.join(os.path.dirname(__file__), 'hnjfkjsedhnflsiafg.json') + json_parser.parse_from_file(wrong_file_path) + + assert err.value.args[1] == "No such file or directory" + + +def test_parse_json_with_2_3_example(): + doc = json_parser.parse_from_file(os.path.join(os.path.dirname(__file__), + "../../data/formats/SPDXJSONExample-v2.3.spdx.json")) + assert type(doc) == Document + assert len(doc.annotations) == 5 + assert len(doc.files) == 5 + assert len(doc.packages) == 4 + assert len(doc.snippets) == 1 + assert len(doc.relationships) == 23 + assert len(doc.extracted_licensing_info) == 5 + +def test_parse_json_with_2_2_example(): + doc = json_parser.parse_from_file(os.path.join(os.path.dirname(__file__), + "../../data/formats/SPDXJSONExample-v2.2.spdx.json")) + assert type(doc) == Document + assert len(doc.annotations) == 5 + assert len(doc.files) == 4 + assert len(doc.packages) == 4 + assert len(doc.snippets) == 1 + assert len(doc.relationships) == 11 + assert len(doc.extracted_licensing_info) == 5 + +def test_parse_json_with_2_1_example(): + doc = json_parser.parse_from_file(os.path.join(os.path.dirname(__file__), + "../../data/formats/SPDXJsonExample.json")) + assert type(doc) == Document + assert len(doc.annotations) == 1 + assert len(doc.files) == 2 + assert len(doc.packages) == 1 + assert len(doc.snippets) == 1 + assert len(doc.relationships) == 3 + assert len(doc.extracted_licensing_info) == 4 diff --git a/tests/parser/jsonlikedict/__init__.py b/tests/parser/jsonlikedict/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/parser/test_actor_parser.py b/tests/parser/jsonlikedict/test_actor_parser.py similarity index 100% rename from tests/parser/test_actor_parser.py rename to tests/parser/jsonlikedict/test_actor_parser.py diff --git a/tests/parser/test_annotation_parser.py b/tests/parser/jsonlikedict/test_annotation_parser.py similarity index 100% rename from tests/parser/test_annotation_parser.py rename to tests/parser/jsonlikedict/test_annotation_parser.py diff --git a/tests/parser/test_checksum_parser.py b/tests/parser/jsonlikedict/test_checksum_parser.py similarity index 100% rename from tests/parser/test_checksum_parser.py rename to tests/parser/jsonlikedict/test_checksum_parser.py diff --git a/tests/parser/test_creation_info_parser.py b/tests/parser/jsonlikedict/test_creation_info_parser.py similarity index 100% rename from tests/parser/test_creation_info_parser.py rename to tests/parser/jsonlikedict/test_creation_info_parser.py diff --git a/tests/parser/test_dict_parsing_functions.py b/tests/parser/jsonlikedict/test_dict_parsing_functions.py similarity index 100% rename from tests/parser/test_dict_parsing_functions.py rename to tests/parser/jsonlikedict/test_dict_parsing_functions.py diff --git a/tests/parser/test_extracted_licensing_info_parser.py b/tests/parser/jsonlikedict/test_extracted_licensing_info_parser.py similarity index 100% rename from tests/parser/test_extracted_licensing_info_parser.py rename to tests/parser/jsonlikedict/test_extracted_licensing_info_parser.py diff --git a/tests/parser/test_file_parser.py b/tests/parser/jsonlikedict/test_file_parser.py similarity index 100% rename from tests/parser/test_file_parser.py rename to tests/parser/jsonlikedict/test_file_parser.py diff --git a/tests/parser/test_license_expression_parser.py b/tests/parser/jsonlikedict/test_license_expression_parser.py similarity index 100% rename from tests/parser/test_license_expression_parser.py rename to tests/parser/jsonlikedict/test_license_expression_parser.py diff --git a/tests/parser/test_package_parser.py b/tests/parser/jsonlikedict/test_package_parser.py similarity index 100% rename from tests/parser/test_package_parser.py rename to tests/parser/jsonlikedict/test_package_parser.py diff --git a/tests/parser/test_relationship_parser.py b/tests/parser/jsonlikedict/test_relationship_parser.py similarity index 100% rename from tests/parser/test_relationship_parser.py rename to tests/parser/jsonlikedict/test_relationship_parser.py diff --git a/tests/parser/test_snippet_parser.py b/tests/parser/jsonlikedict/test_snippet_parser.py similarity index 100% rename from tests/parser/test_snippet_parser.py rename to tests/parser/jsonlikedict/test_snippet_parser.py diff --git a/tests/parser/test_json_parser.py b/tests/parser/test_json_parser.py deleted file mode 100644 index 78c2733f7..000000000 --- a/tests/parser/test_json_parser.py +++ /dev/null @@ -1,54 +0,0 @@ -# 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. - -import os -import pytest - -from src.model.document import Document -from src.parser.jsonlikedict.json_parser import JsonLikeDictParser - -def test_parse_json_file_not_found(): - with pytest.raises(FileNotFoundError) as err: - wrong_file_path = os.path.join(os.path.dirname(__file__), 'hnjfkjsedhnflsiafg.json') - JsonLikeDictParser().parse(wrong_file_path) - - assert err.value.args[1] == "No such file or directory" - - -def test_parse_json_with_2_3_example(): - doc = JsonLikeDictParser().parse(os.path.join(os.path.dirname(__file__), "../data/formats/SPDXJSONExample-v2.3.spdx.json")) - assert type(doc) == Document - assert len(doc.annotations) == 5 - assert len(doc.files) == 5 - assert len(doc.packages) == 4 - assert len(doc.snippets) == 1 - assert len(doc.relationships) == 23 - assert len(doc.extracted_licensing_info) == 5 - -def test_parse_json_with_2_2_example(): - doc = JsonLikeDictParser().parse(os.path.join(os.path.dirname(__file__), "../data/formats/SPDXJSONExample-v2.2.spdx.json")) - assert type(doc) == Document - assert len(doc.annotations) == 5 - assert len(doc.files) == 4 - assert len(doc.packages) == 4 - assert len(doc.snippets) == 1 - assert len(doc.relationships) == 11 - assert len(doc.extracted_licensing_info) == 5 - -def test_parse_json_with_2_1_example(): - doc = JsonLikeDictParser().parse(os.path.join(os.path.dirname(__file__), "../data/formats/SPDXJsonExample.json")) - assert type(doc) == Document - assert len(doc.annotations) == 1 - assert len(doc.files) == 2 - assert len(doc.packages) == 1 - assert len(doc.snippets) == 1 - assert len(doc.relationships) == 3 - assert len(doc.extracted_licensing_info) == 4 From b59da62da8c3d2f6d46a755ca8404adca460ecea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 3 Jan 2023 11:34:45 +0100 Subject: [PATCH 131/362] [issue-406] add xml writer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/writer/xml/__init__.py | 10 ++++++++++ src/writer/xml/xml_writer.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 src/writer/xml/__init__.py create mode 100644 src/writer/xml/xml_writer.py diff --git a/src/writer/xml/__init__.py b/src/writer/xml/__init__.py new file mode 100644 index 000000000..cbc5c4070 --- /dev/null +++ b/src/writer/xml/__init__.py @@ -0,0 +1,10 @@ +# 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. diff --git a/src/writer/xml/xml_writer.py b/src/writer/xml/xml_writer.py new file mode 100644 index 000000000..9afa2cef2 --- /dev/null +++ b/src/writer/xml/xml_writer.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 typing import List + +import xmltodict + +from src.jsonschema.document_converter import DocumentConverter +from src.model.document import Document +from src.validation.document_validator import validate_full_spdx_document +from src.validation.validation_message import ValidationMessage + + +def write_document(document: Document, file_name: str, validate: bool = True, converter: DocumentConverter = None): + """ + Serializes the provided document to XML and writes it to a file with the provided name. Unless validate is set + to False, validates the document before serialization. Unless a DocumentConverter instance is provided, + a new one is created. + """ + if validate: + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, + document.creation_info.spdx_version) + if validation_messages: + raise ValueError(f"Document is not valid. The following errors were detected: {validation_messages}") + if converter is None: + converter = DocumentConverter() + document_dict = converter.convert(document) + with open(file_name, "w") as out: + xmltodict.unparse(document_dict, out, encoding="utf-8", pretty=True) From 5e6534924299bb5ebbe59a76e4c2f58c7bf67459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 3 Jan 2023 12:57:02 +0100 Subject: [PATCH 132/362] [issue-406] add xml parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/parser/xml/__init__.py | 0 src/parser/xml/xml_parser.py | 75 ++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 src/parser/xml/__init__.py create mode 100644 src/parser/xml/xml_parser.py diff --git a/src/parser/xml/__init__.py b/src/parser/xml/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/parser/xml/xml_parser.py b/src/parser/xml/xml_parser.py new file mode 100644 index 000000000..3f1237e28 --- /dev/null +++ b/src/parser/xml/xml_parser.py @@ -0,0 +1,75 @@ +# 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 typing import Dict, Any + +import xmltodict + +from src.model.document import Document +from src.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser + + +LIST_LIKE_FIELDS = [ + "creators", + "externalDocumentRefs", + "extractedLicenseInfos", + "seeAlsos", + "annotations", + "relationships", + "snippets", + "reviewers", + "fileTypes", + "licenseInfoFromFiles", + "licenseInfoInFiles", + "artifactOf", + "fileContributors", + "fileDependencies", + "files", + "documentDescribes", + "packages", + "checksums", + "hasFiles", + "externalRefs", + "ranges", + "licenseInfoInSnippets", + "packageVerificationCodeExcludedFiles", + ] + + +def parse_from_file(file_name: str) -> Document: + with open(file_name) as file: + parsed_xml: Dict = xmltodict.parse(file.read(), encoding="utf-8") + + input_doc_as_dict: Dict = _fix_list_like_fields(parsed_xml) + + return JsonLikeDictParser().parse(input_doc_as_dict) + + +def _fix_list_like_fields(data: Any) -> Any: + """ + XML files do not contain lists. Thus, single fields that should be a list in SPDX have to be manually cast. + This method takes a parsed dictionary and converts all values with key from LIST_LIKE_FIELDS to lists. + """ + if isinstance(data, dict): + new_data = {} + for key, value in data.items(): + if key in LIST_LIKE_FIELDS and not isinstance(value, list): + new_data[key] = [_fix_list_like_fields(value)] + else: + new_data[key] = _fix_list_like_fields(value) + return new_data + + if isinstance(data, list): + new_data = [] + for element in data: + new_data.append(_fix_list_like_fields(element)) + return new_data + + return data From 483f45990060704c2c7c75bfeca3eeb45fe55fe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 3 Jan 2023 11:17:48 +0100 Subject: [PATCH 133/362] [issue-406] add yaml writer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/writer/yaml/__init__.py | 10 ++++++++++ src/writer/yaml/yaml_writer.py | 36 ++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 src/writer/yaml/__init__.py create mode 100644 src/writer/yaml/yaml_writer.py diff --git a/src/writer/yaml/__init__.py b/src/writer/yaml/__init__.py new file mode 100644 index 000000000..cbc5c4070 --- /dev/null +++ b/src/writer/yaml/__init__.py @@ -0,0 +1,10 @@ +# 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. diff --git a/src/writer/yaml/yaml_writer.py b/src/writer/yaml/yaml_writer.py new file mode 100644 index 000000000..304c09631 --- /dev/null +++ b/src/writer/yaml/yaml_writer.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 typing import List + +import yaml + +from src.jsonschema.document_converter import DocumentConverter +from src.model.document import Document +from src.validation.document_validator import validate_full_spdx_document +from src.validation.validation_message import ValidationMessage + + +def write_document_to_file(document: Document, file_name: str, validate: bool = True, converter: DocumentConverter = None): + """ + Serializes the provided document to yaml and writes it to a file with the provided name. Unless validate is set + to False, validates the document before serialization. Unless a DocumentConverter instance is provided, + a new one is created. + """ + if validate: + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, + document.creation_info.spdx_version) + if validation_messages: + raise ValueError(f"Document is not valid. The following errors were detected: {validation_messages}") + if converter is None: + converter = DocumentConverter() + document_dict = converter.convert(document) + with open(file_name, "w") as out: + yaml.safe_dump(document_dict, out, indent=2) From ea747c8957c6947a192471ace78d8ae8210652f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 3 Jan 2023 13:00:20 +0100 Subject: [PATCH 134/362] [issue-406] add yaml parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/parser/yaml/__init__.py | 0 src/parser/yaml/yaml_parser.py | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 src/parser/yaml/__init__.py create mode 100644 src/parser/yaml/yaml_parser.py diff --git a/src/parser/yaml/__init__.py b/src/parser/yaml/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/parser/yaml/yaml_parser.py b/src/parser/yaml/yaml_parser.py new file mode 100644 index 000000000..90c6abf0c --- /dev/null +++ b/src/parser/yaml/yaml_parser.py @@ -0,0 +1,23 @@ +# 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 typing import Dict + +import yaml + +from src.model.document import Document +from src.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser + + +def parse_from_file(file_name: str) -> Document: + with open(file_name) as file: + input_doc_as_dict: Dict = yaml.safe_load(file) + + return JsonLikeDictParser().parse(input_doc_as_dict) From 3765409b5531e9a638ac474814c895ef6c7a8076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 3 Jan 2023 17:24:21 +0100 Subject: [PATCH 135/362] [issue-406] fix xml specific problems, update parse/write_anything MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/parser/jsonlikedict/package_parser.py | 9 ++++++++- src/parser/jsonlikedict/snippet_parser.py | 20 +++++++++++++++++-- src/parser/parse_anything.py | 10 ++++++---- src/parser/xml/xml_parser.py | 7 ++++++- src/writer/write_anything.py | 8 +++++--- src/writer/xml/xml_writer.py | 4 ++-- .../jsonlikedict/test_snippet_parser.py | 4 ++-- 7 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/parser/jsonlikedict/package_parser.py b/src/parser/jsonlikedict/package_parser.py index ed58d8096..a5a2f46e6 100644 --- a/src/parser/jsonlikedict/package_parser.py +++ b/src/parser/jsonlikedict/package_parser.py @@ -60,8 +60,15 @@ def parse_package(self, package_dict: Dict) -> Package: external_refs: List[ExternalPackageRef] = parse_field_or_log_error(logger, package_dict.get("externalRefs"), self.parse_external_refs) - files_analyzed: Optional[bool] = parse_field_or_log_error(logger, package_dict.get("filesAnalyzed"), + files_analyzed: Optional[Union[bool, str]] = parse_field_or_log_error(logger, package_dict.get("filesAnalyzed"), lambda x: x, True) + + if isinstance(files_analyzed, str): # XML does not support boolean typed values + if files_analyzed.lower() == "true": + files_analyzed = True + elif files_analyzed.lower() == "false": + files_analyzed = False + homepage: Optional[str] = package_dict.get("homepage") license_comments: Optional[str] = package_dict.get("licenseComments") license_concluded = parse_field_or_log_error( diff --git a/src/parser/jsonlikedict/snippet_parser.py b/src/parser/jsonlikedict/snippet_parser.py index c91f90826..778defc9f 100644 --- a/src/parser/jsonlikedict/snippet_parser.py +++ b/src/parser/jsonlikedict/snippet_parser.py @@ -41,9 +41,13 @@ def parse_snippet(self, snippet_dict: Dict) -> Snippet: spdx_id: Optional[str] = snippet_dict.get("SPDXID") file_spdx_id: Optional[str] = snippet_dict.get("snippetFromFile") name: Optional[str] = snippet_dict.get("name") + ranges: Dict = parse_field_or_log_error(logger, snippet_dict.get("ranges", []), self.parse_ranges, default={}) - byte_range: Tuple[int, int] = ranges.get(RangeType.BYTE) - line_range: Optional[Tuple[int, int]] = ranges.get(RangeType.LINE) + byte_range: Optional[Tuple[Union[int, str], Union[int, str]]] = ranges.get(RangeType.BYTE) + line_range: Optional[Tuple[Union[int, str], Union[int, str]]] = ranges.get(RangeType.LINE) + byte_range = self.convert_range_from_str(byte_range) + line_range = self.convert_range_from_str(line_range) + attribution_texts: List[str] = snippet_dict.get("attributionTexts", []) comment: Optional[str] = snippet_dict.get("comment") copyright_text: Optional[str] = snippet_dict.get("copyrightText") @@ -114,3 +118,15 @@ def validate_pointer_and_get_type(pointer: Dict) -> RangeType: if "offset" not in pointer and "lineNumber" not in pointer: raise ValueError('Couldn\'t determine type of pointer: neither "offset" nor "lineNumber" provided as key.') return RangeType.BYTE if "offset" in pointer else RangeType.LINE + + @staticmethod + def convert_range_from_str(_range: Tuple[Union[int, str], Union[int, str]]) -> Tuple[Union[int, str], Union[int, str]]: + # XML does not support integers, so we have to convert from string (if possible) + if not _range: + return _range + + if isinstance(_range[0], str) and _range[0].isdigit(): + _range = int(_range[0]), _range[1] + if isinstance(_range[1], str) and _range[1].isdigit(): + _range = _range[0], int(_range[1]) + return _range diff --git a/src/parser/parse_anything.py b/src/parser/parse_anything.py index 541188a59..eae23db85 100644 --- a/src/parser/parse_anything.py +++ b/src/parser/parse_anything.py @@ -9,7 +9,9 @@ # 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.parser.json.json_parser import JsonParser +from src.parser.json import json_parser +from src.parser.xml import xml_parser +from src.parser.yaml import yaml_parser def parse_file(file_name: str): @@ -19,8 +21,8 @@ def parse_file(file_name: str): elif input_format == FileFormat.TAG_VALUE: raise NotImplementedError("Currently, the tag-value parser is not implemented") elif input_format == FileFormat.JSON: - return JsonParser().parse(file_name) + return json_parser.parse_from_file(file_name) elif input_format == FileFormat.XML: - raise NotImplementedError("Currently, the xml parser is not implemented") + return xml_parser.parse_from_file(file_name) elif input_format == FileFormat.YAML: - raise NotImplementedError("Currently, the yaml parser is not implemented") + return yaml_parser.parse_from_file(file_name) diff --git a/src/parser/xml/xml_parser.py b/src/parser/xml/xml_parser.py index 3f1237e28..1c7d36f2f 100644 --- a/src/parser/xml/xml_parser.py +++ b/src/parser/xml/xml_parser.py @@ -13,6 +13,7 @@ import xmltodict from src.model.document import Document +from src.parser.error import SPDXParsingError from src.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser @@ -40,6 +41,7 @@ "ranges", "licenseInfoInSnippets", "packageVerificationCodeExcludedFiles", + "attributionTexts" ] @@ -47,7 +49,10 @@ def parse_from_file(file_name: str) -> Document: with open(file_name) as file: parsed_xml: Dict = xmltodict.parse(file.read(), encoding="utf-8") - input_doc_as_dict: Dict = _fix_list_like_fields(parsed_xml) + input_doc_as_dict: Dict = _fix_list_like_fields(parsed_xml).get("Document") + + if not input_doc_as_dict: + raise SPDXParsingError(['Did not find the XML top level tag "Document".']) return JsonLikeDictParser().parse(input_doc_as_dict) diff --git a/src/writer/write_anything.py b/src/writer/write_anything.py index ca01ba86e..f0a2d7176 100644 --- a/src/writer/write_anything.py +++ b/src/writer/write_anything.py @@ -12,16 +12,18 @@ from src.model.document import Document from src.writer.json import json_writer from src.writer.tagvalue import tagvalue_writer +from src.writer.xml import xml_writer +from src.writer.yaml import yaml_writer 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, validate) + json_writer.write_document(document, file_name, validate=False) elif output_format == FileFormat.YAML: - raise NotImplementedError("Currently, the yaml writer is not implemented") + yaml_writer.write_document_to_file(document, file_name, validate=False) elif output_format == FileFormat.XML: - raise NotImplementedError("Currently, the xml writer is not implemented") + xml_writer.write_document_to_file(document, file_name, validate=False) elif output_format == FileFormat.TAG_VALUE: tagvalue_writer.write_document_to_file(document, file_name) elif output_format == FileFormat.RDF_XML: diff --git a/src/writer/xml/xml_writer.py b/src/writer/xml/xml_writer.py index 9afa2cef2..23d8448d6 100644 --- a/src/writer/xml/xml_writer.py +++ b/src/writer/xml/xml_writer.py @@ -18,7 +18,7 @@ from src.validation.validation_message import ValidationMessage -def write_document(document: Document, file_name: str, validate: bool = True, converter: DocumentConverter = None): +def write_document_to_file(document: Document, file_name: str, validate: bool = True, converter: DocumentConverter = None): """ Serializes the provided document to XML and writes it to a file with the provided name. Unless validate is set to False, validates the document before serialization. Unless a DocumentConverter instance is provided, @@ -31,6 +31,6 @@ def write_document(document: Document, file_name: str, validate: bool = True, co raise ValueError(f"Document is not valid. The following errors were detected: {validation_messages}") if converter is None: converter = DocumentConverter() - document_dict = converter.convert(document) + document_dict = {"Document": converter.convert(document)} with open(file_name, "w") as out: xmltodict.unparse(document_dict, out, encoding="utf-8", pretty=True) diff --git a/tests/parser/jsonlikedict/test_snippet_parser.py b/tests/parser/jsonlikedict/test_snippet_parser.py index c068ffd97..91d1d446c 100644 --- a/tests/parser/jsonlikedict/test_snippet_parser.py +++ b/tests/parser/jsonlikedict/test_snippet_parser.py @@ -92,7 +92,7 @@ def test_parse_snippet_with_invalid_snippet_range(): "reference": "SPDXRef-DoapSource" }, "startPointer": { - "offset": "310", + "offset": "310s", "reference": "SPDXRef-DoapSource" } }] @@ -105,7 +105,7 @@ def test_parse_snippet_with_invalid_snippet_range(): ["Error while constructing Snippet: ['SetterError Snippet: type of argument " '"file_spdx_id" must be str; got NoneType instead: None\', \'SetterError ' 'Snippet: type of argument "byte_range"[0] must be int; got str instead: ' - "(\\'310\\', 23)']"]) + "(\\'310s\\', 23)']"]) def test_parse_invalid_snippet_range(): From 664a646b628c5a0a61603f03b7f26268ae91e676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 5 Jan 2023 12:11:13 +0100 Subject: [PATCH 136/362] [issue-406] fix copyright texts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/document_utils.py | 20 +++++++++--------- src/formats.py | 20 +++++++++--------- src/jsonschema/annotation_converter.py | 20 +++++++++--------- src/jsonschema/annotation_properties.py | 20 +++++++++--------- src/jsonschema/checksum_converter.py | 20 +++++++++--------- src/jsonschema/checksum_properties.py | 20 +++++++++--------- src/jsonschema/converter.py | 20 +++++++++--------- src/jsonschema/creation_info_converter.py | 20 +++++++++--------- src/jsonschema/document_converter.py | 20 +++++++++--------- src/jsonschema/document_properties.py | 20 +++++++++--------- .../external_document_ref_converter.py | 20 +++++++++--------- .../external_document_ref_properties.py | 20 +++++++++--------- .../external_package_ref_converter.py | 20 +++++++++--------- .../external_package_ref_properties.py | 20 +++++++++--------- .../extracted_licensing_info_converter.py | 20 +++++++++--------- .../extracted_licensing_info_properties.py | 20 +++++++++--------- src/jsonschema/file_converter.py | 20 +++++++++--------- src/jsonschema/file_properties.py | 20 +++++++++--------- src/jsonschema/optional_utils.py | 20 +++++++++--------- src/jsonschema/package_converter.py | 20 +++++++++--------- src/jsonschema/package_properties.py | 20 +++++++++--------- .../package_verification_code_converter.py | 20 +++++++++--------- .../package_verification_code_properties.py | 20 +++++++++--------- src/jsonschema/relationship_converter.py | 20 +++++++++--------- src/jsonschema/relationship_properties.py | 20 +++++++++--------- src/jsonschema/snippet_converter.py | 20 +++++++++--------- src/jsonschema/snippet_properties.py | 20 +++++++++--------- src/model/relationship_filters.py | 20 +++++++++--------- src/parser/json/json_parser.py | 20 +++++++++--------- src/parser/xml/xml_parser.py | 20 +++++++++--------- src/parser/yaml/yaml_parser.py | 20 +++++++++--------- src/validation/actor_validator.py | 20 +++++++++--------- src/validation/annotation_validator.py | 20 +++++++++--------- src/validation/checksum_validator.py | 20 +++++++++--------- src/validation/creation_info_validator.py | 20 +++++++++--------- src/validation/document_validator.py | 20 +++++++++--------- .../external_document_ref_validator.py | 20 +++++++++--------- .../external_package_ref_validator.py | 20 +++++++++--------- .../extracted_licensing_info_validator.py | 20 +++++++++--------- src/validation/file_validator.py | 20 +++++++++--------- .../license_expression_validator.py | 20 +++++++++--------- src/validation/package_validator.py | 20 +++++++++--------- .../package_verification_code_validator.py | 20 +++++++++--------- src/validation/relationship_validator.py | 20 +++++++++--------- src/validation/snippet_validator.py | 20 +++++++++--------- src/validation/spdx_id_validators.py | 20 +++++++++--------- src/validation/uri_validators.py | 20 +++++++++--------- src/validation/validation_message.py | 20 +++++++++--------- src/writer/json/json_writer.py | 20 +++++++++--------- src/writer/write_anything.py | 20 +++++++++--------- src/writer/xml/__init__.py | 10 --------- src/writer/xml/xml_writer.py | 20 +++++++++--------- src/writer/yaml/__init__.py | 10 --------- src/writer/yaml/yaml_writer.py | 20 +++++++++--------- tests/fixtures.py | 20 +++++++++--------- tests/jsonschema/test_annotation_converter.py | 20 +++++++++--------- tests/jsonschema/test_checksum_converter.py | 20 +++++++++--------- tests/jsonschema/test_converter.py | 20 +++++++++--------- .../test_creation_info_converter.py | 20 +++++++++--------- tests/jsonschema/test_document_converter.py | 20 +++++++++--------- .../test_external_document_ref_converter.py | 20 +++++++++--------- .../test_external_package_ref_converter.py | 20 +++++++++--------- ...test_extracted_licensing_info_converter.py | 20 +++++++++--------- tests/jsonschema/test_file_converter.py | 20 +++++++++--------- tests/jsonschema/test_package_converter.py | 20 +++++++++--------- ...est_package_verification_code_converter.py | 20 +++++++++--------- .../jsonschema/test_relationship_converter.py | 20 +++++++++--------- tests/jsonschema/test_snippet_converter.py | 20 +++++++++--------- tests/mock_utils.py | 20 +++++++++--------- tests/parser/json/test_json_parser.py | 21 +++++++++---------- tests/test_datetime_conversions.py | 20 +++++++++--------- tests/validation/test_actor_validator.py | 20 +++++++++--------- tests/validation/test_annotation_validator.py | 20 +++++++++--------- tests/validation/test_checksum_validator.py | 20 +++++++++--------- .../test_creation_info_validator.py | 20 +++++++++--------- tests/validation/test_document_validator.py | 20 +++++++++--------- .../test_external_document_ref_validator.py | 20 +++++++++--------- .../test_external_package_ref_validator.py | 20 +++++++++--------- ...test_extracted_licensing_info_validator.py | 20 +++++++++--------- tests/validation/test_file_validator.py | 20 +++++++++--------- .../test_license_expression_validator.py | 20 +++++++++--------- tests/validation/test_package_validator.py | 20 +++++++++--------- .../validation/test_relationship_validator.py | 20 +++++++++--------- tests/validation/test_snippet_validator.py | 20 +++++++++--------- tests/validation/test_spdx_id_validators.py | 20 +++++++++--------- tests/validation/test_uri_validators.py | 20 +++++++++--------- tests/writer/json/test_json_writer.py | 20 +++++++++--------- 87 files changed, 850 insertions(+), 871 deletions(-) diff --git a/src/document_utils.py b/src/document_utils.py index f0fe18552..2f727bbe4 100644 --- a/src/document_utils.py +++ b/src/document_utils.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List from src.model.document import Document diff --git a/src/formats.py b/src/formats.py index fecaf2c24..2c1aa72f9 100644 --- a/src/formats.py +++ b/src/formats.py @@ -1,13 +1,13 @@ -# 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. +# 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 diff --git a/src/jsonschema/annotation_converter.py b/src/jsonschema/annotation_converter.py index 9b8905053..65bd3a889 100644 --- a/src/jsonschema/annotation_converter.py +++ b/src/jsonschema/annotation_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import Type, Any from src.datetime_conversions import datetime_to_iso_string diff --git a/src/jsonschema/annotation_properties.py b/src/jsonschema/annotation_properties.py index 5215a2819..24b6121bf 100644 --- a/src/jsonschema/annotation_properties.py +++ b/src/jsonschema/annotation_properties.py @@ -1,13 +1,13 @@ -# 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. +# 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 auto from src.jsonschema.json_property import JsonProperty diff --git a/src/jsonschema/checksum_converter.py b/src/jsonschema/checksum_converter.py index 162a4f174..8eb5f3172 100644 --- a/src/jsonschema/checksum_converter.py +++ b/src/jsonschema/checksum_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import Type from src.jsonschema.checksum_properties import ChecksumProperty diff --git a/src/jsonschema/checksum_properties.py b/src/jsonschema/checksum_properties.py index 6b974d5bb..9f975f64c 100644 --- a/src/jsonschema/checksum_properties.py +++ b/src/jsonschema/checksum_properties.py @@ -1,13 +1,13 @@ -# 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. +# 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 auto from src.jsonschema.json_property import JsonProperty diff --git a/src/jsonschema/converter.py b/src/jsonschema/converter.py index b5dc2a3c3..2016d943d 100644 --- a/src/jsonschema/converter.py +++ b/src/jsonschema/converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 abc import ABC, abstractmethod from typing import Any, Type, Dict, TypeVar, Generic diff --git a/src/jsonschema/creation_info_converter.py b/src/jsonschema/creation_info_converter.py index 0111a8cab..fc51ba83e 100644 --- a/src/jsonschema/creation_info_converter.py +++ b/src/jsonschema/creation_info_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import Type, Any from src.datetime_conversions import datetime_to_iso_string diff --git a/src/jsonschema/document_converter.py b/src/jsonschema/document_converter.py index 4d669962b..8c90d60d8 100644 --- a/src/jsonschema/document_converter.py +++ b/src/jsonschema/document_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import Type, Any from src.document_utils import get_contained_spdx_element_ids diff --git a/src/jsonschema/document_properties.py b/src/jsonschema/document_properties.py index e684fe80f..0fe1e3b07 100644 --- a/src/jsonschema/document_properties.py +++ b/src/jsonschema/document_properties.py @@ -1,13 +1,13 @@ -# 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. +# 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 auto from src.jsonschema.json_property import JsonProperty diff --git a/src/jsonschema/external_document_ref_converter.py b/src/jsonschema/external_document_ref_converter.py index 893a24014..9e022a4ab 100644 --- a/src/jsonschema/external_document_ref_converter.py +++ b/src/jsonschema/external_document_ref_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import Type, Any from src.jsonschema.checksum_converter import ChecksumConverter diff --git a/src/jsonschema/external_document_ref_properties.py b/src/jsonschema/external_document_ref_properties.py index fd2c1eb3a..ac76b9e03 100644 --- a/src/jsonschema/external_document_ref_properties.py +++ b/src/jsonschema/external_document_ref_properties.py @@ -1,13 +1,13 @@ -# 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. +# 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 auto from src.jsonschema.json_property import JsonProperty diff --git a/src/jsonschema/external_package_ref_converter.py b/src/jsonschema/external_package_ref_converter.py index 3993b7c88..38047b6d9 100644 --- a/src/jsonschema/external_package_ref_converter.py +++ b/src/jsonschema/external_package_ref_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import Type, Any from src.jsonschema.converter import TypedConverter diff --git a/src/jsonschema/external_package_ref_properties.py b/src/jsonschema/external_package_ref_properties.py index d59348821..fceee06ba 100644 --- a/src/jsonschema/external_package_ref_properties.py +++ b/src/jsonschema/external_package_ref_properties.py @@ -1,13 +1,13 @@ -# 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. +# 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 auto from src.jsonschema.json_property import JsonProperty diff --git a/src/jsonschema/extracted_licensing_info_converter.py b/src/jsonschema/extracted_licensing_info_converter.py index 0aa4630d9..32a523bcd 100644 --- a/src/jsonschema/extracted_licensing_info_converter.py +++ b/src/jsonschema/extracted_licensing_info_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import Type, Any from src.jsonschema.converter import TypedConverter diff --git a/src/jsonschema/extracted_licensing_info_properties.py b/src/jsonschema/extracted_licensing_info_properties.py index 0bfcda02a..97e97ed5e 100644 --- a/src/jsonschema/extracted_licensing_info_properties.py +++ b/src/jsonschema/extracted_licensing_info_properties.py @@ -1,13 +1,13 @@ -# 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. +# 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 auto from src.jsonschema.json_property import JsonProperty diff --git a/src/jsonschema/file_converter.py b/src/jsonschema/file_converter.py index fd76eefa9..7aecbea02 100644 --- a/src/jsonschema/file_converter.py +++ b/src/jsonschema/file_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import Type, Any from src.jsonschema.annotation_converter import AnnotationConverter diff --git a/src/jsonschema/file_properties.py b/src/jsonschema/file_properties.py index 02cc8a25b..c4101aeef 100644 --- a/src/jsonschema/file_properties.py +++ b/src/jsonschema/file_properties.py @@ -1,13 +1,13 @@ -# 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. +# 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 auto from src.jsonschema.json_property import JsonProperty diff --git a/src/jsonschema/optional_utils.py b/src/jsonschema/optional_utils.py index d5039d3e3..81c0ba041 100644 --- a/src/jsonschema/optional_utils.py +++ b/src/jsonschema/optional_utils.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import Callable, TypeVar, Optional T = TypeVar("T") diff --git a/src/jsonschema/package_converter.py b/src/jsonschema/package_converter.py index fe8a26d71..3f569c8a8 100644 --- a/src/jsonschema/package_converter.py +++ b/src/jsonschema/package_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import Type, Any from src.datetime_conversions import datetime_to_iso_string diff --git a/src/jsonschema/package_properties.py b/src/jsonschema/package_properties.py index 467ef5fc1..4d5319718 100644 --- a/src/jsonschema/package_properties.py +++ b/src/jsonschema/package_properties.py @@ -1,13 +1,13 @@ -# 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. +# 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 auto from src.jsonschema.json_property import JsonProperty diff --git a/src/jsonschema/package_verification_code_converter.py b/src/jsonschema/package_verification_code_converter.py index bb6cd7a6e..e9e24dcde 100644 --- a/src/jsonschema/package_verification_code_converter.py +++ b/src/jsonschema/package_verification_code_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import Type, Any from src.jsonschema.converter import TypedConverter diff --git a/src/jsonschema/package_verification_code_properties.py b/src/jsonschema/package_verification_code_properties.py index ee202f51a..88e4aa50d 100644 --- a/src/jsonschema/package_verification_code_properties.py +++ b/src/jsonschema/package_verification_code_properties.py @@ -1,13 +1,13 @@ -# 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. +# 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 auto from src.jsonschema.json_property import JsonProperty diff --git a/src/jsonschema/relationship_converter.py b/src/jsonschema/relationship_converter.py index e5b6a36a1..264d83c57 100644 --- a/src/jsonschema/relationship_converter.py +++ b/src/jsonschema/relationship_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import Type, Any from src.jsonschema.converter import TypedConverter diff --git a/src/jsonschema/relationship_properties.py b/src/jsonschema/relationship_properties.py index bd6b787d6..2bac5ac5b 100644 --- a/src/jsonschema/relationship_properties.py +++ b/src/jsonschema/relationship_properties.py @@ -1,13 +1,13 @@ -# 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. +# 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 auto from src.jsonschema.json_property import JsonProperty diff --git a/src/jsonschema/snippet_converter.py b/src/jsonschema/snippet_converter.py index 9abb7992e..68289b618 100644 --- a/src/jsonschema/snippet_converter.py +++ b/src/jsonschema/snippet_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import Type, Any, Tuple, Dict from src.jsonschema.annotation_converter import AnnotationConverter diff --git a/src/jsonschema/snippet_properties.py b/src/jsonschema/snippet_properties.py index 679326b26..f9cb12918 100644 --- a/src/jsonschema/snippet_properties.py +++ b/src/jsonschema/snippet_properties.py @@ -1,13 +1,13 @@ -# 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. +# 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 auto from src.jsonschema.json_property import JsonProperty diff --git a/src/model/relationship_filters.py b/src/model/relationship_filters.py index 4d388b5f5..7b075dd6a 100644 --- a/src/model/relationship_filters.py +++ b/src/model/relationship_filters.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List from src.model.document import Document diff --git a/src/parser/json/json_parser.py b/src/parser/json/json_parser.py index e02da72c7..290c896d7 100644 --- a/src/parser/json/json_parser.py +++ b/src/parser/json/json_parser.py @@ -1,13 +1,13 @@ -# 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. +# 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. import json from typing import Dict diff --git a/src/parser/xml/xml_parser.py b/src/parser/xml/xml_parser.py index 1c7d36f2f..551222bcd 100644 --- a/src/parser/xml/xml_parser.py +++ b/src/parser/xml/xml_parser.py @@ -1,13 +1,13 @@ -# 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. +# Copyright (c) 2023 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 typing import Dict, Any import xmltodict diff --git a/src/parser/yaml/yaml_parser.py b/src/parser/yaml/yaml_parser.py index 90c6abf0c..e0595f065 100644 --- a/src/parser/yaml/yaml_parser.py +++ b/src/parser/yaml/yaml_parser.py @@ -1,13 +1,13 @@ -# 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. +# Copyright (c) 2023 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 typing import Dict import yaml diff --git a/src/validation/actor_validator.py b/src/validation/actor_validator.py index 99f82223b..4c596a440 100644 --- a/src/validation/actor_validator.py +++ b/src/validation/actor_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List diff --git a/src/validation/annotation_validator.py b/src/validation/annotation_validator.py index 89eea269f..2b11fb498 100644 --- a/src/validation/annotation_validator.py +++ b/src/validation/annotation_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List diff --git a/src/validation/checksum_validator.py b/src/validation/checksum_validator.py index 12119f787..72962d7e4 100644 --- a/src/validation/checksum_validator.py +++ b/src/validation/checksum_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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. import re from typing import List, Dict diff --git a/src/validation/creation_info_validator.py b/src/validation/creation_info_validator.py index 50446f8a7..3fce083f9 100644 --- a/src/validation/creation_info_validator.py +++ b/src/validation/creation_info_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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. import re from typing import List diff --git a/src/validation/document_validator.py b/src/validation/document_validator.py index cab7b097e..014f5d6ba 100644 --- a/src/validation/document_validator.py +++ b/src/validation/document_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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. import re from typing import List diff --git a/src/validation/external_document_ref_validator.py b/src/validation/external_document_ref_validator.py index 5a10db208..71fd2d44f 100644 --- a/src/validation/external_document_ref_validator.py +++ b/src/validation/external_document_ref_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List diff --git a/src/validation/external_package_ref_validator.py b/src/validation/external_package_ref_validator.py index 29aaf51b9..5e245a539 100644 --- a/src/validation/external_package_ref_validator.py +++ b/src/validation/external_package_ref_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List diff --git a/src/validation/extracted_licensing_info_validator.py b/src/validation/extracted_licensing_info_validator.py index 7a94b3aec..7797f03b8 100644 --- a/src/validation/extracted_licensing_info_validator.py +++ b/src/validation/extracted_licensing_info_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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. import re from typing import List, Optional diff --git a/src/validation/file_validator.py b/src/validation/file_validator.py index 54560de07..0b1104c61 100644 --- a/src/validation/file_validator.py +++ b/src/validation/file_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List, Optional diff --git a/src/validation/license_expression_validator.py b/src/validation/license_expression_validator.py index a478fe68b..503044eb3 100644 --- a/src/validation/license_expression_validator.py +++ b/src/validation/license_expression_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List, Optional, Union diff --git a/src/validation/package_validator.py b/src/validation/package_validator.py index a6686a5b1..68c6a1d59 100644 --- a/src/validation/package_validator.py +++ b/src/validation/package_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List, Optional diff --git a/src/validation/package_verification_code_validator.py b/src/validation/package_verification_code_validator.py index ccf216fb3..aaf858da7 100644 --- a/src/validation/package_verification_code_validator.py +++ b/src/validation/package_verification_code_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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. import re from typing import List diff --git a/src/validation/relationship_validator.py b/src/validation/relationship_validator.py index bace55213..e1b48358f 100644 --- a/src/validation/relationship_validator.py +++ b/src/validation/relationship_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List diff --git a/src/validation/snippet_validator.py b/src/validation/snippet_validator.py index 1b127addb..c17928108 100644 --- a/src/validation/snippet_validator.py +++ b/src/validation/snippet_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List, Optional diff --git a/src/validation/spdx_id_validators.py b/src/validation/spdx_id_validators.py index 77bc91025..d161e4e82 100644 --- a/src/validation/spdx_id_validators.py +++ b/src/validation/spdx_id_validators.py @@ -1,13 +1,13 @@ -# 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. +# 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. import re from typing import List diff --git a/src/validation/uri_validators.py b/src/validation/uri_validators.py index a43ef2e9a..701ec3cee 100644 --- a/src/validation/uri_validators.py +++ b/src/validation/uri_validators.py @@ -1,13 +1,13 @@ -# 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. +# 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. import re from typing import List diff --git a/src/validation/validation_message.py b/src/validation/validation_message.py index 1407ffcc7..bf1490126 100644 --- a/src/validation/validation_message.py +++ b/src/validation/validation_message.py @@ -1,13 +1,13 @@ -# 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. +# 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 dataclasses import dataclass from enum import Enum, auto diff --git a/src/writer/json/json_writer.py b/src/writer/json/json_writer.py index fbde9adc2..347f2d622 100644 --- a/src/writer/json/json_writer.py +++ b/src/writer/json/json_writer.py @@ -1,13 +1,13 @@ -# 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. +# 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. import json from typing import List diff --git a/src/writer/write_anything.py b/src/writer/write_anything.py index f0a2d7176..29d07cbfa 100644 --- a/src/writer/write_anything.py +++ b/src/writer/write_anything.py @@ -1,13 +1,13 @@ -# 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. +# 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.json import json_writer diff --git a/src/writer/xml/__init__.py b/src/writer/xml/__init__.py index cbc5c4070..e69de29bb 100644 --- a/src/writer/xml/__init__.py +++ b/src/writer/xml/__init__.py @@ -1,10 +0,0 @@ -# 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. diff --git a/src/writer/xml/xml_writer.py b/src/writer/xml/xml_writer.py index 23d8448d6..b0a374162 100644 --- a/src/writer/xml/xml_writer.py +++ b/src/writer/xml/xml_writer.py @@ -1,13 +1,13 @@ -# 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. +# Copyright (c) 2023 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 typing import List import xmltodict diff --git a/src/writer/yaml/__init__.py b/src/writer/yaml/__init__.py index cbc5c4070..e69de29bb 100644 --- a/src/writer/yaml/__init__.py +++ b/src/writer/yaml/__init__.py @@ -1,10 +0,0 @@ -# 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. diff --git a/src/writer/yaml/yaml_writer.py b/src/writer/yaml/yaml_writer.py index 304c09631..07f36917d 100644 --- a/src/writer/yaml/yaml_writer.py +++ b/src/writer/yaml/yaml_writer.py @@ -1,13 +1,13 @@ -# 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. +# Copyright (c) 2023 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 typing import List import yaml diff --git a/tests/fixtures.py b/tests/fixtures.py index e8ee68303..284d84149 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -1,13 +1,13 @@ -# 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. +# 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 datetime import datetime from src.model.actor import Actor, ActorType diff --git a/tests/jsonschema/test_annotation_converter.py b/tests/jsonschema/test_annotation_converter.py index 714b7bb4a..a6b593190 100644 --- a/tests/jsonschema/test_annotation_converter.py +++ b/tests/jsonschema/test_annotation_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 datetime import datetime import pytest diff --git a/tests/jsonschema/test_checksum_converter.py b/tests/jsonschema/test_checksum_converter.py index 9412761de..4f2eef537 100644 --- a/tests/jsonschema/test_checksum_converter.py +++ b/tests/jsonschema/test_checksum_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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. import pytest from src.jsonschema.checksum_converter import ChecksumConverter diff --git a/tests/jsonschema/test_converter.py b/tests/jsonschema/test_converter.py index 0123c3b19..ca5091144 100644 --- a/tests/jsonschema/test_converter.py +++ b/tests/jsonschema/test_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 auto from typing import Type, Any diff --git a/tests/jsonschema/test_creation_info_converter.py b/tests/jsonschema/test_creation_info_converter.py index 87cd0a0a1..012659083 100644 --- a/tests/jsonschema/test_creation_info_converter.py +++ b/tests/jsonschema/test_creation_info_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 datetime import datetime import pytest diff --git a/tests/jsonschema/test_document_converter.py b/tests/jsonschema/test_document_converter.py index 086712848..66af878a3 100644 --- a/tests/jsonschema/test_document_converter.py +++ b/tests/jsonschema/test_document_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 datetime import datetime from typing import Union from unittest import mock diff --git a/tests/jsonschema/test_external_document_ref_converter.py b/tests/jsonschema/test_external_document_ref_converter.py index 51932c125..ebd9bc97b 100644 --- a/tests/jsonschema/test_external_document_ref_converter.py +++ b/tests/jsonschema/test_external_document_ref_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 unittest import mock from unittest.mock import MagicMock diff --git a/tests/jsonschema/test_external_package_ref_converter.py b/tests/jsonschema/test_external_package_ref_converter.py index 7af0752e1..fb4aad6b1 100644 --- a/tests/jsonschema/test_external_package_ref_converter.py +++ b/tests/jsonschema/test_external_package_ref_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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. import pytest from src.jsonschema.external_package_ref_converter import ExternalPackageRefConverter diff --git a/tests/jsonschema/test_extracted_licensing_info_converter.py b/tests/jsonschema/test_extracted_licensing_info_converter.py index c90d5c311..5b892389a 100644 --- a/tests/jsonschema/test_extracted_licensing_info_converter.py +++ b/tests/jsonschema/test_extracted_licensing_info_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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. import pytest from src.jsonschema.extracted_licensing_info_converter import ExtractedLicensingInfoConverter diff --git a/tests/jsonschema/test_file_converter.py b/tests/jsonschema/test_file_converter.py index 5d291b012..46f39e215 100644 --- a/tests/jsonschema/test_file_converter.py +++ b/tests/jsonschema/test_file_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 datetime import datetime from typing import Union from unittest import mock diff --git a/tests/jsonschema/test_package_converter.py b/tests/jsonschema/test_package_converter.py index 446cac7e5..1f9c22ff0 100644 --- a/tests/jsonschema/test_package_converter.py +++ b/tests/jsonschema/test_package_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 datetime import datetime from typing import Union from unittest import mock diff --git a/tests/jsonschema/test_package_verification_code_converter.py b/tests/jsonschema/test_package_verification_code_converter.py index a7050c492..6d8ac4476 100644 --- a/tests/jsonschema/test_package_verification_code_converter.py +++ b/tests/jsonschema/test_package_verification_code_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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. import pytest from src.jsonschema.package_verification_code_converter import PackageVerificationCodeConverter diff --git a/tests/jsonschema/test_relationship_converter.py b/tests/jsonschema/test_relationship_converter.py index 8646e4dcc..d2bcffb5b 100644 --- a/tests/jsonschema/test_relationship_converter.py +++ b/tests/jsonschema/test_relationship_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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. import pytest from src.jsonschema.relationship_converter import RelationshipConverter diff --git a/tests/jsonschema/test_snippet_converter.py b/tests/jsonschema/test_snippet_converter.py index 75432235c..5549c9a5a 100644 --- a/tests/jsonschema/test_snippet_converter.py +++ b/tests/jsonschema/test_snippet_converter.py @@ -1,13 +1,13 @@ -# 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. +# 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 datetime import datetime from typing import Union from unittest import mock diff --git a/tests/mock_utils.py b/tests/mock_utils.py index 09af9f19e..c99715903 100644 --- a/tests/mock_utils.py +++ b/tests/mock_utils.py @@ -1,13 +1,13 @@ -# 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. +# 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 unittest.mock import NonCallableMagicMock diff --git a/tests/parser/json/test_json_parser.py b/tests/parser/json/test_json_parser.py index b21d4b4cb..80a7de0ab 100644 --- a/tests/parser/json/test_json_parser.py +++ b/tests/parser/json/test_json_parser.py @@ -1,20 +1,19 @@ -# 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. +# 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. import os import pytest from src.model.document import Document from src.parser.json import json_parser -from src.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser def test_parse_json_file_not_found(): with pytest.raises(FileNotFoundError) as err: diff --git a/tests/test_datetime_conversions.py b/tests/test_datetime_conversions.py index e70e1dc86..f015a4cd6 100644 --- a/tests/test_datetime_conversions.py +++ b/tests/test_datetime_conversions.py @@ -1,13 +1,13 @@ -# 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. +# 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 datetime import datetime import pytest diff --git a/tests/validation/test_actor_validator.py b/tests/validation/test_actor_validator.py index 7561f6659..417755236 100644 --- a/tests/validation/test_actor_validator.py +++ b/tests/validation/test_actor_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List diff --git a/tests/validation/test_annotation_validator.py b/tests/validation/test_annotation_validator.py index 5ddf87f41..9081fe260 100644 --- a/tests/validation/test_annotation_validator.py +++ b/tests/validation/test_annotation_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List diff --git a/tests/validation/test_checksum_validator.py b/tests/validation/test_checksum_validator.py index 22a1b6ffb..72ecb0cd9 100644 --- a/tests/validation/test_checksum_validator.py +++ b/tests/validation/test_checksum_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List diff --git a/tests/validation/test_creation_info_validator.py b/tests/validation/test_creation_info_validator.py index 8f0d66121..93b93c512 100644 --- a/tests/validation/test_creation_info_validator.py +++ b/tests/validation/test_creation_info_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List diff --git a/tests/validation/test_document_validator.py b/tests/validation/test_document_validator.py index a7f2d7a5b..0c615255b 100644 --- a/tests/validation/test_document_validator.py +++ b/tests/validation/test_document_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List, Optional diff --git a/tests/validation/test_external_document_ref_validator.py b/tests/validation/test_external_document_ref_validator.py index 611c1e5cb..a94bd1d17 100644 --- a/tests/validation/test_external_document_ref_validator.py +++ b/tests/validation/test_external_document_ref_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List diff --git a/tests/validation/test_external_package_ref_validator.py b/tests/validation/test_external_package_ref_validator.py index 456dd7acd..4430e96f5 100644 --- a/tests/validation/test_external_package_ref_validator.py +++ b/tests/validation/test_external_package_ref_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List diff --git a/tests/validation/test_extracted_licensing_info_validator.py b/tests/validation/test_extracted_licensing_info_validator.py index 395c3a000..c2e557495 100644 --- a/tests/validation/test_extracted_licensing_info_validator.py +++ b/tests/validation/test_extracted_licensing_info_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List diff --git a/tests/validation/test_file_validator.py b/tests/validation/test_file_validator.py index f76822255..5aaa95350 100644 --- a/tests/validation/test_file_validator.py +++ b/tests/validation/test_file_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List diff --git a/tests/validation/test_license_expression_validator.py b/tests/validation/test_license_expression_validator.py index a8f334a3f..6203f5688 100644 --- a/tests/validation/test_license_expression_validator.py +++ b/tests/validation/test_license_expression_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List diff --git a/tests/validation/test_package_validator.py b/tests/validation/test_package_validator.py index 31b86b8fb..b54941c50 100644 --- a/tests/validation/test_package_validator.py +++ b/tests/validation/test_package_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List diff --git a/tests/validation/test_relationship_validator.py b/tests/validation/test_relationship_validator.py index 1d164e29b..c9f4e84d3 100644 --- a/tests/validation/test_relationship_validator.py +++ b/tests/validation/test_relationship_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List diff --git a/tests/validation/test_snippet_validator.py b/tests/validation/test_snippet_validator.py index 8b072fc94..3add700a0 100644 --- a/tests/validation/test_snippet_validator.py +++ b/tests/validation/test_snippet_validator.py @@ -1,13 +1,13 @@ -# 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. +# 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 typing import List diff --git a/tests/validation/test_spdx_id_validators.py b/tests/validation/test_spdx_id_validators.py index 5358305de..4c001697f 100644 --- a/tests/validation/test_spdx_id_validators.py +++ b/tests/validation/test_spdx_id_validators.py @@ -1,12 +1,12 @@ -# 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. +# 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. # TODO: https://github.com/spdx/tools-python/issues/376 diff --git a/tests/validation/test_uri_validators.py b/tests/validation/test_uri_validators.py index 704dbbea0..552fbf6c0 100644 --- a/tests/validation/test_uri_validators.py +++ b/tests/validation/test_uri_validators.py @@ -1,13 +1,13 @@ -# 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. +# 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. import pytest diff --git a/tests/writer/json/test_json_writer.py b/tests/writer/json/test_json_writer.py index 6e48cf5da..9b2a11747 100644 --- a/tests/writer/json/test_json_writer.py +++ b/tests/writer/json/test_json_writer.py @@ -1,13 +1,13 @@ -# 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. +# 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. import json import os from datetime import datetime From 7dc8024fbce2f5fbddf2b21133fbf7b1f06e3b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 4 Jan 2023 14:47:29 +0100 Subject: [PATCH 137/362] [issue-267] add GitHub Actions workflow and remove CircleCI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- .circleci/config.yml | 129 ------------------------- .github/workflows/install_and_test.yml | 28 ++++++ README.md | 15 +-- 3 files changed, 31 insertions(+), 141 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .github/workflows/install_and_test.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 1cbf9811e..000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,129 +0,0 @@ -version: 2.1 - -commands: - mac_install_python: - parameters: - python_version: - description: "version of python to install" - type: string - default: 3.7.10 - steps: - - run: | - brew update - python --version - sudo -H pip install --upgrade virtualenv - brew install pyenv - echo 'eval "$(pyenv init --path)"' >> ~/.bash_profile - echo 'eval "$(pyenv init -)"' >> ~/.bash_profile - source ~/.bash_profile - pyenv install --list - pyenv install << parameters.python_version >> - pyenv versions - pyenv global << parameters.python_version >> - python --version - - update_packaging_tools: - steps: - - run: | - python -m pip install --upgrade pip - - run: | - python -m pip install --upgrade setuptools wheel setuptools_scm build - - install_run_tests: - steps: - - run: | - python -m build -nwx . - python -m pip install --upgrade ./dist/*.whl - python -m pip install pytest - - run: pytest - -jobs: - mac_python_3_7: - shell: /bin/bash --login - macos: - xcode: '13.0.0' - steps: - - checkout - - mac_install_python: - python_version: "3.7.10" - - update_packaging_tools - - install_run_tests - - - mac_python_3_8: - shell: /bin/bash --login - macos: - xcode: '13.0.0' - steps: - - checkout - - mac_install_python: - python_version: "3.8.10" - - update_packaging_tools - - install_run_tests - - mac_python_3_9: - shell: /bin/bash --login - macos: - xcode: '13.0.0' - steps: - - checkout - - mac_install_python: - python_version: "3.9.5" - - update_packaging_tools - - install_run_tests - - mac_python_3_10: - shell: /bin/bash --login - macos: - xcode: '13.0.0' - steps: - - checkout - - mac_install_python: - python_version: "3.10.6" - - update_packaging_tools - - install_run_tests - - linux_python_3_7: - docker: - - image: python:3.7 - steps: - - checkout - - update_packaging_tools - - install_run_tests - - linux_python_3_8: - docker: - - image: python:3.8 - steps: - - checkout - - update_packaging_tools - - install_run_tests - - linux_python_3_9: - docker: - - image: python:3.9 - steps: - - checkout - - update_packaging_tools - - install_run_tests - - linux_python_3_10: - docker: - - image: python:3.10 - steps: - - checkout - - update_packaging_tools - - install_run_tests - -workflows: - version: 2 - python_matrix_build: - jobs: - - mac_python_3_7 - - mac_python_3_8 - - mac_python_3_9 - - mac_python_3_10 - - linux_python_3_7 - - linux_python_3_8 - - linux_python_3_9 - - linux_python_3_10 diff --git a/.github/workflows/install_and_test.yml b/.github/workflows/install_and_test.yml new file mode 100644 index 000000000..d310be830 --- /dev/null +++ b/.github/workflows/install_and_test.yml @@ -0,0 +1,28 @@ +name: Install and Test + +on: [ push, workflow_dispatch ] + +jobs: + install_and_test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-latest, macos-latest, windows-latest ] + python-version: [ "3.7", "3.8", "3.9", "3.10" ] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Installation + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade setuptools wheel setuptools_scm build + python -m build -nwx . + python -m pip install --upgrade ./dist/*.whl + python -m pip install pytest + shell: bash + - name: Run tests + run: pytest diff --git a/README.md b/README.md index e4a12ad49..e281354ac 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,11 @@ # Python library to parse, validate and create SPDX documents -| Linux | macOS | Windows | -|:-------------------------------|:------------------------------|:--------------------------------| -| [ ![Linux build status][1]][2] | [![macOS build status][3]][4] | [![Windows build status][5]][6] | +CI status (Linux, macOS and Windows): [![Install and Test][1]][2] -[1]: https://travis-ci.org/spdx/tools-python.svg?branch=master +[1]: https://github.com/spdx/tools-python/actions/workflows/install_and_test.yml/badge.svg -[2]: https://travis-ci.org/spdx/tools-python +[2]: https://github.com/spdx/tools-python/actions/workflows/install_and_test.yml -[3]: https://circleci.com/gh/spdx/tools-python/tree/master.svg?style=shield&circle-token=36cca2dfa3639886fc34e22d92495a6773bdae6d - -[4]: https://circleci.com/gh/spdx/tools-python/tree/master - -[5]: https://ci.appveyor.com/api/projects/status/0bf9glha2yg9x8ef/branch/master?svg=true - -[6]: https://ci.appveyor.com/project/spdx/tools-python/branch/master # CURRENT STATE From 1245e22933648fa3ddbca1dea43a1530844f9c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 5 Jan 2023 11:16:41 +0100 Subject: [PATCH 138/362] [issue-267] add pull_request to CI triggers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- .github/workflows/install_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/install_and_test.yml b/.github/workflows/install_and_test.yml index d310be830..e5a6cc6e3 100644 --- a/.github/workflows/install_and_test.yml +++ b/.github/workflows/install_and_test.yml @@ -1,6 +1,6 @@ name: Install and Test -on: [ push, workflow_dispatch ] +on: [ push, workflow_dispatch, pull_request ] jobs: install_and_test: From a25a3848e034396592d77f69a87c64d33c25f8d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 5 Jan 2023 16:19:38 +0100 Subject: [PATCH 139/362] [issue-267] remove on push trigger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- .github/workflows/install_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/install_and_test.yml b/.github/workflows/install_and_test.yml index e5a6cc6e3..14842b008 100644 --- a/.github/workflows/install_and_test.yml +++ b/.github/workflows/install_and_test.yml @@ -1,6 +1,6 @@ name: Install and Test -on: [ push, workflow_dispatch, pull_request ] +on: [ workflow_dispatch, pull_request ] jobs: install_and_test: From 9b4dd28eb7de26a1b462020cdf9905d2acc94fa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 5 Jan 2023 14:23:33 +0100 Subject: [PATCH 140/362] fix hasExtractedLicensingInfos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/jsonschema/document_converter.py | 2 +- src/jsonschema/document_properties.py | 2 +- src/parser/xml/xml_parser.py | 2 +- tests/jsonschema/test_document_converter.py | 6 +++--- tests/writer/json/expected_results/expected.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jsonschema/document_converter.py b/src/jsonschema/document_converter.py index 8c90d60d8..7730a3842 100644 --- a/src/jsonschema/document_converter.py +++ b/src/jsonschema/document_converter.py @@ -79,7 +79,7 @@ def _get_property_value(self, document: Document, document_property: DocumentPro elif document_property == DocumentProperty.EXTERNAL_DOCUMENT_REFS: return [self.external_document_ref_converter.convert(external_document_ref) for external_document_ref in document.creation_info.external_document_refs] or None - elif document_property == DocumentProperty.HAS_EXTRACTED_LICENSING_INFO: + elif document_property == DocumentProperty.HAS_EXTRACTED_LICENSING_INFOS: return [self.extracted_licensing_info_converter.convert(licensing_info) for licensing_info in document.extracted_licensing_info] or None elif document_property == DocumentProperty.NAME: diff --git a/src/jsonschema/document_properties.py b/src/jsonschema/document_properties.py index 0fe1e3b07..52eefa836 100644 --- a/src/jsonschema/document_properties.py +++ b/src/jsonschema/document_properties.py @@ -20,7 +20,7 @@ class DocumentProperty(JsonProperty): CREATION_INFO = auto() DATA_LICENSE = auto() EXTERNAL_DOCUMENT_REFS = auto() - HAS_EXTRACTED_LICENSING_INFO = auto() + HAS_EXTRACTED_LICENSING_INFOS = auto() NAME = auto() SPDX_VERSION = auto() DOCUMENT_NAMESPACE = auto() diff --git a/src/parser/xml/xml_parser.py b/src/parser/xml/xml_parser.py index 551222bcd..f1e6b8d20 100644 --- a/src/parser/xml/xml_parser.py +++ b/src/parser/xml/xml_parser.py @@ -20,7 +20,7 @@ LIST_LIKE_FIELDS = [ "creators", "externalDocumentRefs", - "extractedLicenseInfos", + "hasExtractedLicensingInfos", "seeAlsos", "annotations", "relationships", diff --git a/tests/jsonschema/test_document_converter.py b/tests/jsonschema/test_document_converter.py index 66af878a3..82e5ba34a 100644 --- a/tests/jsonschema/test_document_converter.py +++ b/tests/jsonschema/test_document_converter.py @@ -63,7 +63,7 @@ def converter(relationship_converter_mock: MagicMock, snippet_converter_mock: Ma (DocumentProperty.PACKAGES, "packages"), (DocumentProperty.FILES, "files"), (DocumentProperty.SNIPPETS, "snippets"), (DocumentProperty.ANNOTATIONS, "annotations"), (DocumentProperty.RELATIONSHIPS, "relationships"), - (DocumentProperty.HAS_EXTRACTED_LICENSING_INFO, "hasExtractedLicensingInfo")]) + (DocumentProperty.HAS_EXTRACTED_LICENSING_INFOS, "hasExtractedLicensingInfos")]) def test_json_property_names(converter: DocumentConverter, document_property: DocumentProperty, expected: str): assert converter.json_property_name(document_property) == expected @@ -99,7 +99,7 @@ def test_successful_conversion(converter: DocumentConverter): converter.json_property_name(DocumentProperty.CREATION_INFO): "mock_converted_creation_info", converter.json_property_name(DocumentProperty.DATA_LICENSE): "dataLicense", converter.json_property_name(DocumentProperty.EXTERNAL_DOCUMENT_REFS): ["mock_converted_external_ref"], - converter.json_property_name(DocumentProperty.HAS_EXTRACTED_LICENSING_INFO): [ + converter.json_property_name(DocumentProperty.HAS_EXTRACTED_LICENSING_INFOS): [ "mock_converted_extracted_licensing_info"], converter.json_property_name(DocumentProperty.NAME): "name", converter.json_property_name(DocumentProperty.SPDX_VERSION): "spdxVersion", @@ -127,7 +127,7 @@ def test_null_values(converter: DocumentConverter): assert converter.json_property_name(DocumentProperty.ANNOTATIONS) not in converted_dict assert converter.json_property_name(DocumentProperty.EXTERNAL_DOCUMENT_REFS) not in converted_dict - assert converter.json_property_name(DocumentProperty.HAS_EXTRACTED_LICENSING_INFO) not in converted_dict + assert converter.json_property_name(DocumentProperty.HAS_EXTRACTED_LICENSING_INFOS) not in converted_dict assert converter.json_property_name(DocumentProperty.DOCUMENT_DESCRIBES) not in converted_dict assert converter.json_property_name(DocumentProperty.PACKAGES) not in converted_dict assert converter.json_property_name(DocumentProperty.FILES) not in converted_dict diff --git a/tests/writer/json/expected_results/expected.json b/tests/writer/json/expected_results/expected.json index 3335e12eb..df1402ec1 100644 --- a/tests/writer/json/expected_results/expected.json +++ b/tests/writer/json/expected_results/expected.json @@ -26,7 +26,7 @@ } } ], - "hasExtractedLicensingInfo": [ + "hasExtractedLicensingInfos": [ { "extractedText": "licenseText", "licenseId": "licenseId" From 9034f3da5f964163602bfe2e52df5f9aed4da0ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 5 Jan 2023 15:12:25 +0100 Subject: [PATCH 141/362] fix filesAnalyzed parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/parser/jsonlikedict/package_parser.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/parser/jsonlikedict/package_parser.py b/src/parser/jsonlikedict/package_parser.py index a5a2f46e6..45ab46b3f 100644 --- a/src/parser/jsonlikedict/package_parser.py +++ b/src/parser/jsonlikedict/package_parser.py @@ -60,10 +60,11 @@ def parse_package(self, package_dict: Dict) -> Package: external_refs: List[ExternalPackageRef] = parse_field_or_log_error(logger, package_dict.get("externalRefs"), self.parse_external_refs) - files_analyzed: Optional[Union[bool, str]] = parse_field_or_log_error(logger, package_dict.get("filesAnalyzed"), - lambda x: x, True) + files_analyzed: Optional[Union[bool, str]] = package_dict.get("filesAnalyzed") - if isinstance(files_analyzed, str): # XML does not support boolean typed values + if files_analyzed is None: # default value is True + files_analyzed = True + elif isinstance(files_analyzed, str): # XML does not support boolean typed values if files_analyzed.lower() == "true": files_analyzed = True elif files_analyzed.lower() == "false": From 533328b9692194698985ba85afd74fdd84579d55 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 9 Jan 2023 14:18:49 +0100 Subject: [PATCH 142/362] [issue-415] introduce src-layout to fix cli Signed-off-by: Meret Behrens --- pyproject.toml | 2 +- src/{ => spdx}/__init__.py | 0 src/{ => spdx}/clitools/__init__.py | 0 src/{ => spdx}/clitools/pyspdxtools.py | 12 +++---- src/{ => spdx}/config.py | 2 +- src/{ => spdx}/datetime_conversions.py | 0 src/{ => spdx}/document_utils.py | 2 +- src/{ => spdx}/exceptions.json | 0 src/{ => spdx}/formats.py | 2 +- src/{ => spdx}/jsonschema/__init__.py | 0 .../jsonschema/annotation_converter.py | 12 +++---- .../jsonschema/annotation_properties.py | 2 +- .../jsonschema/checksum_converter.py | 10 +++--- .../jsonschema/checksum_properties.py | 2 +- src/{ => spdx}/jsonschema/converter.py | 6 ++-- .../jsonschema/creation_info_converter.py | 12 +++---- .../jsonschema/creation_info_properties.py | 2 +- .../jsonschema/document_converter.py | 30 ++++++++-------- .../jsonschema/document_properties.py | 2 +- .../external_document_ref_converter.py | 12 +++---- .../external_document_ref_properties.py | 2 +- .../external_package_ref_converter.py | 10 +++--- .../external_package_ref_properties.py | 2 +- .../extracted_licensing_info_converter.py | 12 +++---- .../extracted_licensing_info_properties.py | 2 +- src/{ => spdx}/jsonschema/file_converter.py | 16 ++++----- src/{ => spdx}/jsonschema/file_properties.py | 2 +- src/{ => spdx}/jsonschema/json_property.py | 0 src/{ => spdx}/jsonschema/optional_utils.py | 0 .../jsonschema/package_converter.py | 26 +++++++------- .../jsonschema/package_properties.py | 2 +- .../package_verification_code_converter.py | 10 +++--- .../package_verification_code_properties.py | 2 +- .../jsonschema/relationship_converter.py | 10 +++--- .../jsonschema/relationship_properties.py | 2 +- .../jsonschema/snippet_converter.py | 14 ++++---- .../jsonschema/snippet_properties.py | 2 +- src/{ => spdx}/licenses.json | 0 src/{ => spdx}/model/__init__.py | 0 src/{ => spdx}/model/actor.py | 4 +-- src/{ => spdx}/model/annotation.py | 6 ++-- src/{ => spdx}/model/checksum.py | 4 +-- src/{ => spdx}/model/document.py | 22 ++++++------ src/{ => spdx}/model/external_document_ref.py | 6 ++-- .../model/extracted_licensing_info.py | 6 ++-- src/{ => spdx}/model/file.py | 12 +++---- src/{ => spdx}/model/license.py | 2 +- src/{ => spdx}/model/license_expression.py | 4 +-- src/{ => spdx}/model/package.py | 14 ++++---- src/{ => spdx}/model/relationship.py | 8 ++--- src/{ => spdx}/model/relationship_filters.py | 6 ++-- src/{ => spdx}/model/snippet.py | 10 +++--- src/{ => spdx}/model/spdx_no_assertion.py | 0 src/{ => spdx}/model/spdx_none.py | 0 src/{ => spdx}/model/typing/__init__.py | 0 .../model/typing/constructor_type_errors.py | 0 .../model/typing/dataclass_with_properties.py | 0 src/{ => spdx}/model/typing/type_checks.py | 2 +- src/{ => spdx}/model/version.py | 0 src/{ => spdx}/parser/__init__.py | 0 src/{ => spdx}/parser/error.py | 0 src/{ => spdx}/parser/json/__init__.py | 0 src/{ => spdx}/parser/json/json_parser.py | 4 +-- .../parser/jsonlikedict/__init__.py | 0 .../parser/jsonlikedict/actor_parser.py | 6 ++-- .../parser/jsonlikedict/annotation_parser.py | 14 ++++---- .../parser/jsonlikedict/checksum_parser.py | 6 ++-- .../jsonlikedict/creation_info_parser.py | 22 ++++++------ .../jsonlikedict/dict_parsing_functions.py | 10 +++--- .../extracted_licensing_info_parser.py | 8 ++--- .../parser/jsonlikedict/file_parser.py | 18 +++++----- .../jsonlikedict/json_like_dict_parser.py | 22 ++++++------ .../jsonlikedict/license_expression_parser.py | 8 ++--- .../parser/jsonlikedict/package_parser.py | 24 ++++++------- .../jsonlikedict/relationship_parser.py | 10 +++--- .../parser/jsonlikedict/snippet_parser.py | 16 ++++----- src/{ => spdx}/parser/logger.py | 0 src/{ => spdx}/parser/parse_anything.py | 8 ++--- src/{ => spdx}/parser/xml/__init__.py | 0 src/{ => spdx}/parser/xml/xml_parser.py | 6 ++-- src/{ => spdx}/parser/yaml/__init__.py | 0 src/{ => spdx}/parser/yaml/yaml_parser.py | 4 +-- src/{ => spdx}/validation/__init__.py | 0 src/{ => spdx}/validation/actor_validator.py | 4 +-- .../validation/annotation_validator.py | 10 +++--- .../validation/checksum_validator.py | 4 +-- .../validation/creation_info_validator.py | 10 +++--- .../validation/document_validator.py | 22 ++++++------ .../external_document_ref_validator.py | 10 +++--- .../external_package_ref_validator.py | 4 +-- .../extracted_licensing_info_validator.py | 6 ++-- src/{ => spdx}/validation/file_validator.py | 14 ++++---- .../license_expression_validator.py | 8 ++--- .../validation/package_validator.py | 20 +++++------ .../package_verification_code_validator.py | 4 +-- .../validation/relationship_validator.py | 12 +++---- .../validation/snippet_validator.py | 10 +++--- .../validation/spdx_id_validators.py | 6 ++-- src/{ => spdx}/validation/uri_validators.py | 0 .../validation/validation_message.py | 0 src/{ => spdx}/writer/__init__.py | 0 src/{ => spdx}/writer/casing_tools.py | 0 src/{ => spdx}/writer/json/__init__.py | 0 src/{ => spdx}/writer/json/json_writer.py | 8 ++--- src/{ => spdx}/writer/tagvalue/__init__.py | 0 .../writer/tagvalue/annotation_writer.py | 6 ++-- .../writer/tagvalue/checksum_writer.py | 2 +- .../writer/tagvalue/creation_info_writer.py | 6 ++-- .../extracted_licensing_info_writer.py | 4 +-- src/{ => spdx}/writer/tagvalue/file_writer.py | 6 ++-- .../writer/tagvalue/package_writer.py | 8 ++--- .../writer/tagvalue/relationship_writer.py | 4 +-- .../writer/tagvalue/snippet_writer.py | 4 +-- .../writer/tagvalue/tagvalue_writer.py | 18 +++++----- .../tagvalue_writer_helper_functions.py | 16 ++++----- src/{ => spdx}/writer/write_anything.py | 12 +++---- src/{ => spdx}/writer/xml/__init__.py | 0 src/{ => spdx}/writer/xml/xml_writer.py | 8 ++--- src/{ => spdx}/writer/yaml/__init__.py | 0 src/{ => spdx}/writer/yaml/yaml_writer.py | 8 ++--- tests/fixtures.py | 24 ++++++------- tests/jsonschema/test_annotation_converter.py | 10 +++--- tests/jsonschema/test_checksum_converter.py | 6 ++-- tests/jsonschema/test_converter.py | 12 +++---- .../test_creation_info_converter.py | 12 +++---- tests/jsonschema/test_document_converter.py | 34 +++++++++---------- .../test_external_document_ref_converter.py | 10 +++--- .../test_external_package_ref_converter.py | 6 ++-- ...test_extracted_licensing_info_converter.py | 8 ++--- tests/jsonschema/test_file_converter.py | 26 +++++++------- tests/jsonschema/test_package_converter.py | 32 ++++++++--------- ...est_package_verification_code_converter.py | 6 ++-- .../jsonschema/test_relationship_converter.py | 10 +++--- tests/jsonschema/test_snippet_converter.py | 22 ++++++------ tests/model/test_actor.py | 2 +- tests/model/test_annotation.py | 14 ++++---- tests/model/test_checksum.py | 2 +- tests/model/test_creation_info.py | 28 +++++++-------- tests/model/test_document.py | 30 ++++++++-------- tests/model/test_external_document_ref.py | 8 ++--- .../model/test_external_package_reference.py | 2 +- tests/model/test_extracted_licensing_info.py | 2 +- tests/model/test_file.py | 34 +++++++++---------- tests/model/test_license.py | 2 +- tests/model/test_package.py | 18 +++++----- tests/model/test_package_verification_code.py | 2 +- tests/model/test_relationship.py | 4 +-- tests/model/test_snippet.py | 6 ++-- tests/model/test_version.py | 2 +- tests/parser/json/test_json_parser.py | 4 +-- .../parser/jsonlikedict/test_actor_parser.py | 6 ++-- .../jsonlikedict/test_annotation_parser.py | 12 +++---- .../jsonlikedict/test_checksum_parser.py | 6 ++-- .../jsonlikedict/test_creation_info_parser.py | 12 +++---- .../test_dict_parsing_functions.py | 10 +++--- .../test_extracted_licensing_info_parser.py | 4 +-- tests/parser/jsonlikedict/test_file_parser.py | 12 +++---- .../test_license_expression_parser.py | 6 ++-- .../jsonlikedict/test_package_parser.py | 18 +++++----- .../jsonlikedict/test_relationship_parser.py | 10 +++--- .../jsonlikedict/test_snippet_parser.py | 6 ++-- tests/test_datetime_conversions.py | 2 +- tests/validation/test_actor_validator.py | 6 ++-- tests/validation/test_annotation_validator.py | 8 ++--- tests/validation/test_checksum_validator.py | 6 ++-- .../test_creation_info_validator.py | 4 +-- tests/validation/test_document_validator.py | 6 ++-- .../test_external_document_ref_validator.py | 4 +-- .../test_external_package_ref_validator.py | 4 +-- ...test_extracted_licensing_info_validator.py | 4 +-- tests/validation/test_file_validator.py | 6 ++-- .../test_license_expression_validator.py | 6 ++-- tests/validation/test_package_validator.py | 10 +++--- .../validation/test_relationship_validator.py | 12 +++---- tests/validation/test_snippet_validator.py | 4 +-- tests/validation/test_uri_validators.py | 2 +- tests/writer/json/test_json_writer.py | 24 ++++++------- tests/writer/tagvalue/test_package_writer.py | 14 ++++---- tests/writer/tagvalue/test_tagvalue_writer.py | 24 ++++++------- 179 files changed, 698 insertions(+), 698 deletions(-) rename src/{ => spdx}/__init__.py (100%) rename src/{ => spdx}/clitools/__init__.py (100%) rename src/{ => spdx}/clitools/pyspdxtools.py (89%) rename src/{ => spdx}/config.py (98%) rename src/{ => spdx}/datetime_conversions.py (100%) rename src/{ => spdx}/document_utils.py (95%) rename src/{ => spdx}/exceptions.json (100%) rename src/{ => spdx}/formats.py (96%) rename src/{ => spdx}/jsonschema/__init__.py (100%) rename src/{ => spdx}/jsonschema/annotation_converter.py (82%) rename src/{ => spdx}/jsonschema/annotation_properties.py (93%) rename src/{ => spdx}/jsonschema/checksum_converter.py (84%) rename src/{ => spdx}/jsonschema/checksum_properties.py (92%) rename src/{ => spdx}/jsonschema/converter.py (95%) rename src/{ => spdx}/jsonschema/creation_info_converter.py (81%) rename src/{ => spdx}/jsonschema/creation_info_properties.py (93%) rename src/{ => spdx}/jsonschema/document_converter.py (86%) rename src/{ => spdx}/jsonschema/document_properties.py (94%) rename src/{ => spdx}/jsonschema/external_document_ref_converter.py (82%) rename src/{ => spdx}/jsonschema/external_document_ref_properties.py (93%) rename src/{ => spdx}/jsonschema/external_package_ref_converter.py (84%) rename src/{ => spdx}/jsonschema/external_package_ref_properties.py (93%) rename src/{ => spdx}/jsonschema/extracted_licensing_info_converter.py (83%) rename src/{ => spdx}/jsonschema/extracted_licensing_info_properties.py (93%) rename src/{ => spdx}/jsonschema/file_converter.py (88%) rename src/{ => spdx}/jsonschema/file_properties.py (94%) rename src/{ => spdx}/jsonschema/json_property.py (100%) rename src/{ => spdx}/jsonschema/optional_utils.py (100%) rename src/{ => spdx}/jsonschema/package_converter.py (88%) rename src/{ => spdx}/jsonschema/package_properties.py (96%) rename src/{ => spdx}/jsonschema/package_verification_code_converter.py (82%) rename src/{ => spdx}/jsonschema/package_verification_code_properties.py (93%) rename src/{ => spdx}/jsonschema/relationship_converter.py (84%) rename src/{ => spdx}/jsonschema/relationship_properties.py (93%) rename src/{ => spdx}/jsonschema/snippet_converter.py (91%) rename src/{ => spdx}/jsonschema/snippet_properties.py (94%) rename src/{ => spdx}/licenses.json (100%) rename src/{ => spdx}/model/__init__.py (100%) rename src/{ => spdx}/model/actor.py (89%) rename src/{ => spdx}/model/annotation.py (85%) rename src/{ => spdx}/model/checksum.py (88%) rename src/{ => spdx}/model/document.py (83%) rename src/{ => spdx}/model/external_document_ref.py (82%) rename src/{ => spdx}/model/extracted_licensing_info.py (86%) rename src/{ => spdx}/model/file.py (89%) rename src/{ => spdx}/model/license.py (98%) rename src/{ => spdx}/model/license_expression.py (86%) rename src/{ => spdx}/model/package.py (93%) rename src/{ => spdx}/model/relationship.py (90%) rename src/{ => spdx}/model/relationship_filters.py (94%) rename src/{ => spdx}/model/snippet.py (86%) rename src/{ => spdx}/model/spdx_no_assertion.py (100%) rename src/{ => spdx}/model/spdx_none.py (100%) rename src/{ => spdx}/model/typing/__init__.py (100%) rename src/{ => spdx}/model/typing/constructor_type_errors.py (100%) rename src/{ => spdx}/model/typing/dataclass_with_properties.py (100%) rename src/{ => spdx}/model/typing/type_checks.py (93%) rename src/{ => spdx}/model/version.py (100%) rename src/{ => spdx}/parser/__init__.py (100%) rename src/{ => spdx}/parser/error.py (100%) rename src/{ => spdx}/parser/json/__init__.py (100%) rename src/{ => spdx}/parser/json/json_parser.py (87%) rename src/{ => spdx}/parser/jsonlikedict/__init__.py (100%) rename src/{ => spdx}/parser/jsonlikedict/actor_parser.py (92%) rename src/{ => spdx}/parser/jsonlikedict/annotation_parser.py (93%) rename src/{ => spdx}/parser/jsonlikedict/checksum_parser.py (86%) rename src/{ => spdx}/parser/jsonlikedict/creation_info_parser.py (91%) rename src/{ => spdx}/parser/jsonlikedict/dict_parsing_functions.py (93%) rename src/{ => spdx}/parser/jsonlikedict/extracted_licensing_info_parser.py (87%) rename src/{ => spdx}/parser/jsonlikedict/file_parser.py (88%) rename src/{ => spdx}/parser/jsonlikedict/json_like_dict_parser.py (84%) rename src/{ => spdx}/parser/jsonlikedict/license_expression_parser.py (87%) rename src/{ => spdx}/parser/jsonlikedict/package_parser.py (93%) rename src/{ => spdx}/parser/jsonlikedict/relationship_parser.py (96%) rename src/{ => spdx}/parser/jsonlikedict/snippet_parser.py (92%) rename src/{ => spdx}/parser/logger.py (100%) rename src/{ => spdx}/parser/parse_anything.py (87%) rename src/{ => spdx}/parser/xml/__init__.py (100%) rename src/{ => spdx}/parser/xml/xml_parser.py (93%) rename src/{ => spdx}/parser/yaml/__init__.py (100%) rename src/{ => spdx}/parser/yaml/yaml_parser.py (87%) rename src/{ => spdx}/validation/__init__.py (100%) rename src/{ => spdx}/validation/actor_validator.py (90%) rename src/{ => spdx}/validation/annotation_validator.py (82%) rename src/{ => spdx}/validation/checksum_validator.py (94%) rename src/{ => spdx}/validation/creation_info_validator.py (83%) rename src/{ => spdx}/validation/document_validator.py (81%) rename src/{ => spdx}/validation/external_document_ref_validator.py (84%) rename src/{ => spdx}/validation/external_package_ref_validator.py (90%) rename src/{ => spdx}/validation/extracted_licensing_info_validator.py (90%) rename src/{ => spdx}/validation/file_validator.py (85%) rename src/{ => spdx}/validation/license_expression_validator.py (85%) rename src/{ => spdx}/validation/package_validator.py (87%) rename src/{ => spdx}/validation/package_verification_code_validator.py (91%) rename src/{ => spdx}/validation/relationship_validator.py (85%) rename src/{ => spdx}/validation/snippet_validator.py (91%) rename src/{ => spdx}/validation/spdx_id_validators.py (96%) rename src/{ => spdx}/validation/uri_validators.py (100%) rename src/{ => spdx}/validation/validation_message.py (100%) rename src/{ => spdx}/writer/__init__.py (100%) rename src/{ => spdx}/writer/casing_tools.py (100%) rename src/{ => spdx}/writer/json/__init__.py (100%) rename src/{ => spdx}/writer/json/json_writer.py (85%) rename src/{ => spdx}/writer/tagvalue/__init__.py (100%) rename src/{ => spdx}/writer/tagvalue/annotation_writer.py (84%) rename src/{ => spdx}/writer/tagvalue/checksum_writer.py (95%) rename src/{ => spdx}/writer/tagvalue/creation_info_writer.py (90%) rename src/{ => spdx}/writer/tagvalue/extracted_licensing_info_writer.py (87%) rename src/{ => spdx}/writer/tagvalue/file_writer.py (89%) rename src/{ => spdx}/writer/tagvalue/package_writer.py (93%) rename src/{ => spdx}/writer/tagvalue/relationship_writer.py (86%) rename src/{ => spdx}/writer/tagvalue/snippet_writer.py (91%) rename src/{ => spdx}/writer/tagvalue/tagvalue_writer.py (84%) rename src/{ => spdx}/writer/tagvalue/tagvalue_writer_helper_functions.py (92%) rename src/{ => spdx}/writer/write_anything.py (82%) rename src/{ => spdx}/writer/xml/__init__.py (100%) rename src/{ => spdx}/writer/xml/xml_writer.py (86%) rename src/{ => spdx}/writer/yaml/__init__.py (100%) rename src/{ => spdx}/writer/yaml/yaml_writer.py (86%) diff --git a/pyproject.toml b/pyproject.toml index 4d03a65d2..352933462 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,7 @@ dynamic = ["version"] test = ["pytest"] [project.scripts] -pyspdxtools = "src.clitools.pyspdxtools:main" +pyspdxtools = "spdx.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/__init__.py b/src/spdx/__init__.py similarity index 100% rename from src/__init__.py rename to src/spdx/__init__.py diff --git a/src/clitools/__init__.py b/src/spdx/clitools/__init__.py similarity index 100% rename from src/clitools/__init__.py rename to src/spdx/clitools/__init__.py diff --git a/src/clitools/pyspdxtools.py b/src/spdx/clitools/pyspdxtools.py similarity index 89% rename from src/clitools/pyspdxtools.py rename to src/spdx/clitools/pyspdxtools.py index d9dbed945..1f98b624a 100644 --- a/src/clitools/pyspdxtools.py +++ b/src/spdx/clitools/pyspdxtools.py @@ -15,12 +15,12 @@ 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 +from spdx.model.document import Document +from spdx.parser.parse_anything import parse_file +from spdx.validation.document_validator import validate_full_spdx_document +from spdx.validation.validation_message import ValidationMessage +from spdx.writer.tagvalue import tagvalue_writer +from spdx.writer.write_anything import write_file @click.command() diff --git a/src/config.py b/src/spdx/config.py similarity index 98% rename from src/config.py rename to src/spdx/config.py index e48aacdfa..3830fbfe3 100644 --- a/src/config.py +++ b/src/spdx/config.py @@ -13,7 +13,7 @@ import json import os -from src.model.version import Version +from spdx.model.version import Version _base_dir = os.path.dirname(__file__) _licenses = os.path.join(_base_dir, "licenses.json") diff --git a/src/datetime_conversions.py b/src/spdx/datetime_conversions.py similarity index 100% rename from src/datetime_conversions.py rename to src/spdx/datetime_conversions.py diff --git a/src/document_utils.py b/src/spdx/document_utils.py similarity index 95% rename from src/document_utils.py rename to src/spdx/document_utils.py index 2f727bbe4..10947a719 100644 --- a/src/document_utils.py +++ b/src/spdx/document_utils.py @@ -10,7 +10,7 @@ # limitations under the License. from typing import List -from src.model.document import Document +from spdx.model.document import Document def get_contained_spdx_element_ids(document: Document) -> List[str]: diff --git a/src/exceptions.json b/src/spdx/exceptions.json similarity index 100% rename from src/exceptions.json rename to src/spdx/exceptions.json diff --git a/src/formats.py b/src/spdx/formats.py similarity index 96% rename from src/formats.py rename to src/spdx/formats.py index 2c1aa72f9..2b6b2d64c 100644 --- a/src/formats.py +++ b/src/spdx/formats.py @@ -10,7 +10,7 @@ # limitations under the License. from enum import Enum, auto -from src.parser.error import SPDXParsingError +from spdx.parser.error import SPDXParsingError class FileFormat(Enum): diff --git a/src/jsonschema/__init__.py b/src/spdx/jsonschema/__init__.py similarity index 100% rename from src/jsonschema/__init__.py rename to src/spdx/jsonschema/__init__.py diff --git a/src/jsonschema/annotation_converter.py b/src/spdx/jsonschema/annotation_converter.py similarity index 82% rename from src/jsonschema/annotation_converter.py rename to src/spdx/jsonschema/annotation_converter.py index 65bd3a889..c4bf4277e 100644 --- a/src/jsonschema/annotation_converter.py +++ b/src/spdx/jsonschema/annotation_converter.py @@ -10,12 +10,12 @@ # limitations under the License. from typing import Type, Any -from src.datetime_conversions import datetime_to_iso_string -from src.jsonschema.annotation_properties import AnnotationProperty -from src.jsonschema.converter import TypedConverter -from src.jsonschema.json_property import JsonProperty -from src.model.annotation import Annotation -from src.model.document import Document +from spdx.datetime_conversions import datetime_to_iso_string +from spdx.jsonschema.annotation_properties import AnnotationProperty +from spdx.jsonschema.converter import TypedConverter +from spdx.jsonschema.json_property import JsonProperty +from spdx.model.annotation import Annotation +from spdx.model.document import Document class AnnotationConverter(TypedConverter[Annotation]): diff --git a/src/jsonschema/annotation_properties.py b/src/spdx/jsonschema/annotation_properties.py similarity index 93% rename from src/jsonschema/annotation_properties.py rename to src/spdx/jsonschema/annotation_properties.py index 24b6121bf..ffed44494 100644 --- a/src/jsonschema/annotation_properties.py +++ b/src/spdx/jsonschema/annotation_properties.py @@ -10,7 +10,7 @@ # limitations under the License. from enum import auto -from src.jsonschema.json_property import JsonProperty +from spdx.jsonschema.json_property import JsonProperty class AnnotationProperty(JsonProperty): diff --git a/src/jsonschema/checksum_converter.py b/src/spdx/jsonschema/checksum_converter.py similarity index 84% rename from src/jsonschema/checksum_converter.py rename to src/spdx/jsonschema/checksum_converter.py index 8eb5f3172..6782adba2 100644 --- a/src/jsonschema/checksum_converter.py +++ b/src/spdx/jsonschema/checksum_converter.py @@ -10,11 +10,11 @@ # limitations under the License. from typing import Type -from src.jsonschema.checksum_properties import ChecksumProperty -from src.jsonschema.converter import TypedConverter -from src.jsonschema.json_property import JsonProperty -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.document import Document +from spdx.jsonschema.checksum_properties import ChecksumProperty +from spdx.jsonschema.converter import TypedConverter +from spdx.jsonschema.json_property import JsonProperty +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.model.document import Document class ChecksumConverter(TypedConverter[Checksum]): diff --git a/src/jsonschema/checksum_properties.py b/src/spdx/jsonschema/checksum_properties.py similarity index 92% rename from src/jsonschema/checksum_properties.py rename to src/spdx/jsonschema/checksum_properties.py index 9f975f64c..d0733fa65 100644 --- a/src/jsonschema/checksum_properties.py +++ b/src/spdx/jsonschema/checksum_properties.py @@ -10,7 +10,7 @@ # limitations under the License. from enum import auto -from src.jsonschema.json_property import JsonProperty +from spdx.jsonschema.json_property import JsonProperty class ChecksumProperty(JsonProperty): diff --git a/src/jsonschema/converter.py b/src/spdx/jsonschema/converter.py similarity index 95% rename from src/jsonschema/converter.py rename to src/spdx/jsonschema/converter.py index 2016d943d..e6f362568 100644 --- a/src/jsonschema/converter.py +++ b/src/spdx/jsonschema/converter.py @@ -11,9 +11,9 @@ from abc import ABC, abstractmethod from typing import Any, Type, Dict, TypeVar, Generic -from src.jsonschema.json_property import JsonProperty -from src.model.document import Document -from src.writer.casing_tools import snake_case_to_camel_case +from spdx.jsonschema.json_property import JsonProperty +from spdx.model.document import Document +from spdx.writer.casing_tools import snake_case_to_camel_case MISSING_IMPLEMENTATION_MESSAGE = "Must be implemented" diff --git a/src/jsonschema/creation_info_converter.py b/src/spdx/jsonschema/creation_info_converter.py similarity index 81% rename from src/jsonschema/creation_info_converter.py rename to src/spdx/jsonschema/creation_info_converter.py index fc51ba83e..c4896659a 100644 --- a/src/jsonschema/creation_info_converter.py +++ b/src/spdx/jsonschema/creation_info_converter.py @@ -10,12 +10,12 @@ # limitations under the License. from typing import Type, Any -from src.datetime_conversions import datetime_to_iso_string -from src.jsonschema.converter import TypedConverter -from src.jsonschema.creation_info_properties import CreationInfoProperty -from src.jsonschema.json_property import JsonProperty -from src.jsonschema.optional_utils import apply_if_present -from src.model.document import CreationInfo, Document +from spdx.datetime_conversions import datetime_to_iso_string +from spdx.jsonschema.converter import TypedConverter +from spdx.jsonschema.creation_info_properties import CreationInfoProperty +from spdx.jsonschema.json_property import JsonProperty +from spdx.jsonschema.optional_utils import apply_if_present +from spdx.model.document import CreationInfo, Document class CreationInfoConverter(TypedConverter[CreationInfo]): diff --git a/src/jsonschema/creation_info_properties.py b/src/spdx/jsonschema/creation_info_properties.py similarity index 93% rename from src/jsonschema/creation_info_properties.py rename to src/spdx/jsonschema/creation_info_properties.py index ccd52f58c..35acc20ba 100644 --- a/src/jsonschema/creation_info_properties.py +++ b/src/spdx/jsonschema/creation_info_properties.py @@ -10,7 +10,7 @@ # limitations under the License. from enum import auto -from src.jsonschema.json_property import JsonProperty +from spdx.jsonschema.json_property import JsonProperty class CreationInfoProperty(JsonProperty): diff --git a/src/jsonschema/document_converter.py b/src/spdx/jsonschema/document_converter.py similarity index 86% rename from src/jsonschema/document_converter.py rename to src/spdx/jsonschema/document_converter.py index 7730a3842..71a3c76c5 100644 --- a/src/jsonschema/document_converter.py +++ b/src/spdx/jsonschema/document_converter.py @@ -10,21 +10,21 @@ # limitations under the License. from typing import Type, Any -from src.document_utils import get_contained_spdx_element_ids -from src.jsonschema.annotation_converter import AnnotationConverter -from src.jsonschema.converter import TypedConverter -from src.jsonschema.creation_info_converter import CreationInfoConverter -from src.jsonschema.document_properties import DocumentProperty -from src.jsonschema.external_document_ref_converter import ExternalDocumentRefConverter -from src.jsonschema.extracted_licensing_info_converter import ExtractedLicensingInfoConverter -from src.jsonschema.file_converter import FileConverter -from src.jsonschema.json_property import JsonProperty -from src.jsonschema.package_converter import PackageConverter -from src.jsonschema.relationship_converter import RelationshipConverter -from src.jsonschema.snippet_converter import SnippetConverter -from src.model.document import Document -from src.model.relationship import RelationshipType -from src.model.relationship_filters import filter_by_type_and_origin, filter_by_type_and_target, \ +from spdx.document_utils import get_contained_spdx_element_ids +from spdx.jsonschema.annotation_converter import AnnotationConverter +from spdx.jsonschema.converter import TypedConverter +from spdx.jsonschema.creation_info_converter import CreationInfoConverter +from spdx.jsonschema.document_properties import DocumentProperty +from spdx.jsonschema.external_document_ref_converter import ExternalDocumentRefConverter +from spdx.jsonschema.extracted_licensing_info_converter import ExtractedLicensingInfoConverter +from spdx.jsonschema.file_converter import FileConverter +from spdx.jsonschema.json_property import JsonProperty +from spdx.jsonschema.package_converter import PackageConverter +from spdx.jsonschema.relationship_converter import RelationshipConverter +from spdx.jsonschema.snippet_converter import SnippetConverter +from spdx.model.document import Document +from spdx.model.relationship import RelationshipType +from spdx.model.relationship_filters import filter_by_type_and_origin, filter_by_type_and_target, \ find_package_contains_file_relationships, \ find_file_contained_by_package_relationships diff --git a/src/jsonschema/document_properties.py b/src/spdx/jsonschema/document_properties.py similarity index 94% rename from src/jsonschema/document_properties.py rename to src/spdx/jsonschema/document_properties.py index 52eefa836..7acd02520 100644 --- a/src/jsonschema/document_properties.py +++ b/src/spdx/jsonschema/document_properties.py @@ -10,7 +10,7 @@ # limitations under the License. from enum import auto -from src.jsonschema.json_property import JsonProperty +from spdx.jsonschema.json_property import JsonProperty class DocumentProperty(JsonProperty): diff --git a/src/jsonschema/external_document_ref_converter.py b/src/spdx/jsonschema/external_document_ref_converter.py similarity index 82% rename from src/jsonschema/external_document_ref_converter.py rename to src/spdx/jsonschema/external_document_ref_converter.py index 9e022a4ab..023b38136 100644 --- a/src/jsonschema/external_document_ref_converter.py +++ b/src/spdx/jsonschema/external_document_ref_converter.py @@ -10,12 +10,12 @@ # limitations under the License. from typing import Type, Any -from src.jsonschema.checksum_converter import ChecksumConverter -from src.jsonschema.converter import TypedConverter -from src.jsonschema.external_document_ref_properties import ExternalDocumentRefProperty -from src.jsonschema.json_property import JsonProperty -from src.model.document import Document -from src.model.external_document_ref import ExternalDocumentRef +from spdx.jsonschema.checksum_converter import ChecksumConverter +from spdx.jsonschema.converter import TypedConverter +from spdx.jsonschema.external_document_ref_properties import ExternalDocumentRefProperty +from spdx.jsonschema.json_property import JsonProperty +from spdx.model.document import Document +from spdx.model.external_document_ref import ExternalDocumentRef class ExternalDocumentRefConverter(TypedConverter[ExternalDocumentRef]): diff --git a/src/jsonschema/external_document_ref_properties.py b/src/spdx/jsonschema/external_document_ref_properties.py similarity index 93% rename from src/jsonschema/external_document_ref_properties.py rename to src/spdx/jsonschema/external_document_ref_properties.py index ac76b9e03..d014e4e77 100644 --- a/src/jsonschema/external_document_ref_properties.py +++ b/src/spdx/jsonschema/external_document_ref_properties.py @@ -10,7 +10,7 @@ # limitations under the License. from enum import auto -from src.jsonschema.json_property import JsonProperty +from spdx.jsonschema.json_property import JsonProperty class ExternalDocumentRefProperty(JsonProperty): diff --git a/src/jsonschema/external_package_ref_converter.py b/src/spdx/jsonschema/external_package_ref_converter.py similarity index 84% rename from src/jsonschema/external_package_ref_converter.py rename to src/spdx/jsonschema/external_package_ref_converter.py index 38047b6d9..f1374c7c2 100644 --- a/src/jsonschema/external_package_ref_converter.py +++ b/src/spdx/jsonschema/external_package_ref_converter.py @@ -10,11 +10,11 @@ # limitations under the License. from typing import Type, Any -from src.jsonschema.converter import TypedConverter -from src.jsonschema.external_package_ref_properties import ExternalPackageRefProperty -from src.jsonschema.json_property import JsonProperty -from src.model.document import Document -from src.model.package import ExternalPackageRef +from spdx.jsonschema.converter import TypedConverter +from spdx.jsonschema.external_package_ref_properties import ExternalPackageRefProperty +from spdx.jsonschema.json_property import JsonProperty +from spdx.model.document import Document +from spdx.model.package import ExternalPackageRef class ExternalPackageRefConverter(TypedConverter[ExternalPackageRef]): diff --git a/src/jsonschema/external_package_ref_properties.py b/src/spdx/jsonschema/external_package_ref_properties.py similarity index 93% rename from src/jsonschema/external_package_ref_properties.py rename to src/spdx/jsonschema/external_package_ref_properties.py index fceee06ba..9c90f3317 100644 --- a/src/jsonschema/external_package_ref_properties.py +++ b/src/spdx/jsonschema/external_package_ref_properties.py @@ -10,7 +10,7 @@ # limitations under the License. from enum import auto -from src.jsonschema.json_property import JsonProperty +from spdx.jsonschema.json_property import JsonProperty class ExternalPackageRefProperty(JsonProperty): diff --git a/src/jsonschema/extracted_licensing_info_converter.py b/src/spdx/jsonschema/extracted_licensing_info_converter.py similarity index 83% rename from src/jsonschema/extracted_licensing_info_converter.py rename to src/spdx/jsonschema/extracted_licensing_info_converter.py index 32a523bcd..3d198e3d6 100644 --- a/src/jsonschema/extracted_licensing_info_converter.py +++ b/src/spdx/jsonschema/extracted_licensing_info_converter.py @@ -10,12 +10,12 @@ # limitations under the License. from typing import Type, Any -from src.jsonschema.converter import TypedConverter -from src.jsonschema.extracted_licensing_info_properties import ExtractedLicensingInfoProperty -from src.jsonschema.json_property import JsonProperty -from src.jsonschema.optional_utils import apply_if_present -from src.model.document import Document -from src.model.extracted_licensing_info import ExtractedLicensingInfo +from spdx.jsonschema.converter import TypedConverter +from spdx.jsonschema.extracted_licensing_info_properties import ExtractedLicensingInfoProperty +from spdx.jsonschema.json_property import JsonProperty +from spdx.jsonschema.optional_utils import apply_if_present +from spdx.model.document import Document +from spdx.model.extracted_licensing_info import ExtractedLicensingInfo class ExtractedLicensingInfoConverter(TypedConverter[ExtractedLicensingInfo]): diff --git a/src/jsonschema/extracted_licensing_info_properties.py b/src/spdx/jsonschema/extracted_licensing_info_properties.py similarity index 93% rename from src/jsonschema/extracted_licensing_info_properties.py rename to src/spdx/jsonschema/extracted_licensing_info_properties.py index 97e97ed5e..f1b793fbe 100644 --- a/src/jsonschema/extracted_licensing_info_properties.py +++ b/src/spdx/jsonschema/extracted_licensing_info_properties.py @@ -10,7 +10,7 @@ # limitations under the License. from enum import auto -from src.jsonschema.json_property import JsonProperty +from spdx.jsonschema.json_property import JsonProperty class ExtractedLicensingInfoProperty(JsonProperty): diff --git a/src/jsonschema/file_converter.py b/src/spdx/jsonschema/file_converter.py similarity index 88% rename from src/jsonschema/file_converter.py rename to src/spdx/jsonschema/file_converter.py index 7aecbea02..78af0b90f 100644 --- a/src/jsonschema/file_converter.py +++ b/src/spdx/jsonschema/file_converter.py @@ -10,14 +10,14 @@ # limitations under the License. from typing import Type, Any -from src.jsonschema.annotation_converter import AnnotationConverter -from src.jsonschema.checksum_converter import ChecksumConverter -from src.jsonschema.converter import TypedConverter -from src.jsonschema.file_properties import FileProperty -from src.jsonschema.json_property import JsonProperty -from src.jsonschema.optional_utils import apply_if_present -from src.model.document import Document -from src.model.file import File +from spdx.jsonschema.annotation_converter import AnnotationConverter +from spdx.jsonschema.checksum_converter import ChecksumConverter +from spdx.jsonschema.converter import TypedConverter +from spdx.jsonschema.file_properties import FileProperty +from spdx.jsonschema.json_property import JsonProperty +from spdx.jsonschema.optional_utils import apply_if_present +from spdx.model.document import Document +from spdx.model.file import File class FileConverter(TypedConverter[File]): diff --git a/src/jsonschema/file_properties.py b/src/spdx/jsonschema/file_properties.py similarity index 94% rename from src/jsonschema/file_properties.py rename to src/spdx/jsonschema/file_properties.py index c4101aeef..8706a092f 100644 --- a/src/jsonschema/file_properties.py +++ b/src/spdx/jsonschema/file_properties.py @@ -10,7 +10,7 @@ # limitations under the License. from enum import auto -from src.jsonschema.json_property import JsonProperty +from spdx.jsonschema.json_property import JsonProperty class FileProperty(JsonProperty): diff --git a/src/jsonschema/json_property.py b/src/spdx/jsonschema/json_property.py similarity index 100% rename from src/jsonschema/json_property.py rename to src/spdx/jsonschema/json_property.py diff --git a/src/jsonschema/optional_utils.py b/src/spdx/jsonschema/optional_utils.py similarity index 100% rename from src/jsonschema/optional_utils.py rename to src/spdx/jsonschema/optional_utils.py diff --git a/src/jsonschema/package_converter.py b/src/spdx/jsonschema/package_converter.py similarity index 88% rename from src/jsonschema/package_converter.py rename to src/spdx/jsonschema/package_converter.py index 3f569c8a8..5929f7794 100644 --- a/src/jsonschema/package_converter.py +++ b/src/spdx/jsonschema/package_converter.py @@ -10,19 +10,19 @@ # limitations under the License. from typing import Type, Any -from src.datetime_conversions import datetime_to_iso_string -from src.jsonschema.annotation_converter import AnnotationConverter -from src.jsonschema.checksum_converter import ChecksumConverter -from src.jsonschema.converter import TypedConverter -from src.jsonschema.external_package_ref_converter import ExternalPackageRefConverter -from src.jsonschema.json_property import JsonProperty -from src.jsonschema.optional_utils import apply_if_present -from src.jsonschema.package_properties import PackageProperty -from src.jsonschema.package_verification_code_converter import PackageVerificationCodeConverter -from src.model.actor import Actor -from src.model.document import Document -from src.model.package import Package -from src.model.relationship_filters import find_package_contains_file_relationships, \ +from spdx.datetime_conversions import datetime_to_iso_string +from spdx.jsonschema.annotation_converter import AnnotationConverter +from spdx.jsonschema.checksum_converter import ChecksumConverter +from spdx.jsonschema.converter import TypedConverter +from spdx.jsonschema.external_package_ref_converter import ExternalPackageRefConverter +from spdx.jsonschema.json_property import JsonProperty +from spdx.jsonschema.optional_utils import apply_if_present +from spdx.jsonschema.package_properties import PackageProperty +from spdx.jsonschema.package_verification_code_converter import PackageVerificationCodeConverter +from spdx.model.actor import Actor +from spdx.model.document import Document +from spdx.model.package import Package +from spdx.model.relationship_filters import find_package_contains_file_relationships, \ find_file_contained_by_package_relationships diff --git a/src/jsonschema/package_properties.py b/src/spdx/jsonschema/package_properties.py similarity index 96% rename from src/jsonschema/package_properties.py rename to src/spdx/jsonschema/package_properties.py index 4d5319718..b59d94723 100644 --- a/src/jsonschema/package_properties.py +++ b/src/spdx/jsonschema/package_properties.py @@ -10,7 +10,7 @@ # limitations under the License. from enum import auto -from src.jsonschema.json_property import JsonProperty +from spdx.jsonschema.json_property import JsonProperty class PackageProperty(JsonProperty): diff --git a/src/jsonschema/package_verification_code_converter.py b/src/spdx/jsonschema/package_verification_code_converter.py similarity index 82% rename from src/jsonschema/package_verification_code_converter.py rename to src/spdx/jsonschema/package_verification_code_converter.py index e9e24dcde..a45bf1727 100644 --- a/src/jsonschema/package_verification_code_converter.py +++ b/src/spdx/jsonschema/package_verification_code_converter.py @@ -10,11 +10,11 @@ # limitations under the License. from typing import Type, Any -from src.jsonschema.converter import TypedConverter -from src.jsonschema.json_property import JsonProperty -from src.jsonschema.package_verification_code_properties import PackageVerificationCodeProperty -from src.model.document import Document -from src.model.package import PackageVerificationCode +from spdx.jsonschema.converter import TypedConverter +from spdx.jsonschema.json_property import JsonProperty +from spdx.jsonschema.package_verification_code_properties import PackageVerificationCodeProperty +from spdx.model.document import Document +from spdx.model.package import PackageVerificationCode class PackageVerificationCodeConverter(TypedConverter[PackageVerificationCode]): diff --git a/src/jsonschema/package_verification_code_properties.py b/src/spdx/jsonschema/package_verification_code_properties.py similarity index 93% rename from src/jsonschema/package_verification_code_properties.py rename to src/spdx/jsonschema/package_verification_code_properties.py index 88e4aa50d..845058469 100644 --- a/src/jsonschema/package_verification_code_properties.py +++ b/src/spdx/jsonschema/package_verification_code_properties.py @@ -10,7 +10,7 @@ # limitations under the License. from enum import auto -from src.jsonschema.json_property import JsonProperty +from spdx.jsonschema.json_property import JsonProperty class PackageVerificationCodeProperty(JsonProperty): diff --git a/src/jsonschema/relationship_converter.py b/src/spdx/jsonschema/relationship_converter.py similarity index 84% rename from src/jsonschema/relationship_converter.py rename to src/spdx/jsonschema/relationship_converter.py index 264d83c57..f6d8d8d97 100644 --- a/src/jsonschema/relationship_converter.py +++ b/src/spdx/jsonschema/relationship_converter.py @@ -10,11 +10,11 @@ # limitations under the License. from typing import Type, Any -from src.jsonschema.converter import TypedConverter -from src.jsonschema.json_property import JsonProperty -from src.jsonschema.relationship_properties import RelationshipProperty -from src.model.document import Document -from src.model.relationship import Relationship +from spdx.jsonschema.converter import TypedConverter +from spdx.jsonschema.json_property import JsonProperty +from spdx.jsonschema.relationship_properties import RelationshipProperty +from spdx.model.document import Document +from spdx.model.relationship import Relationship class RelationshipConverter(TypedConverter[Relationship]): diff --git a/src/jsonschema/relationship_properties.py b/src/spdx/jsonschema/relationship_properties.py similarity index 93% rename from src/jsonschema/relationship_properties.py rename to src/spdx/jsonschema/relationship_properties.py index 2bac5ac5b..f5461f1ce 100644 --- a/src/jsonschema/relationship_properties.py +++ b/src/spdx/jsonschema/relationship_properties.py @@ -10,7 +10,7 @@ # limitations under the License. from enum import auto -from src.jsonschema.json_property import JsonProperty +from spdx.jsonschema.json_property import JsonProperty class RelationshipProperty(JsonProperty): diff --git a/src/jsonschema/snippet_converter.py b/src/spdx/jsonschema/snippet_converter.py similarity index 91% rename from src/jsonschema/snippet_converter.py rename to src/spdx/jsonschema/snippet_converter.py index 68289b618..0d3013e53 100644 --- a/src/jsonschema/snippet_converter.py +++ b/src/spdx/jsonschema/snippet_converter.py @@ -10,13 +10,13 @@ # limitations under the License. from typing import Type, Any, Tuple, Dict -from src.jsonschema.annotation_converter import AnnotationConverter -from src.jsonschema.converter import TypedConverter -from src.jsonschema.json_property import JsonProperty -from src.jsonschema.optional_utils import apply_if_present -from src.jsonschema.snippet_properties import SnippetProperty -from src.model.document import Document -from src.model.snippet import Snippet +from spdx.jsonschema.annotation_converter import AnnotationConverter +from spdx.jsonschema.converter import TypedConverter +from spdx.jsonschema.json_property import JsonProperty +from spdx.jsonschema.optional_utils import apply_if_present +from spdx.jsonschema.snippet_properties import SnippetProperty +from spdx.model.document import Document +from spdx.model.snippet import Snippet class SnippetConverter(TypedConverter[Snippet]): diff --git a/src/jsonschema/snippet_properties.py b/src/spdx/jsonschema/snippet_properties.py similarity index 94% rename from src/jsonschema/snippet_properties.py rename to src/spdx/jsonschema/snippet_properties.py index f9cb12918..2fd5eadd8 100644 --- a/src/jsonschema/snippet_properties.py +++ b/src/spdx/jsonschema/snippet_properties.py @@ -10,7 +10,7 @@ # limitations under the License. from enum import auto -from src.jsonschema.json_property import JsonProperty +from spdx.jsonschema.json_property import JsonProperty class SnippetProperty(JsonProperty): diff --git a/src/licenses.json b/src/spdx/licenses.json similarity index 100% rename from src/licenses.json rename to src/spdx/licenses.json diff --git a/src/model/__init__.py b/src/spdx/model/__init__.py similarity index 100% rename from src/model/__init__.py rename to src/spdx/model/__init__.py diff --git a/src/model/actor.py b/src/spdx/model/actor.py similarity index 89% rename from src/model/actor.py rename to src/spdx/model/actor.py index a44da8939..f058b0b03 100644 --- a/src/model/actor.py +++ b/src/spdx/model/actor.py @@ -11,8 +11,8 @@ from enum import Enum, auto from typing import Optional -from src.model.typing.dataclass_with_properties import dataclass_with_properties -from src.model.typing.type_checks import check_types_and_set_values +from spdx.model.typing.dataclass_with_properties import dataclass_with_properties +from spdx.model.typing.type_checks import check_types_and_set_values class ActorType(Enum): diff --git a/src/model/annotation.py b/src/spdx/model/annotation.py similarity index 85% rename from src/model/annotation.py rename to src/spdx/model/annotation.py index 02e92aae5..0e9d2e66b 100644 --- a/src/model/annotation.py +++ b/src/spdx/model/annotation.py @@ -11,9 +11,9 @@ from datetime import datetime from enum import Enum, auto -from src.model.actor import Actor -from src.model.typing.dataclass_with_properties import dataclass_with_properties -from src.model.typing.type_checks import check_types_and_set_values +from spdx.model.actor import Actor +from spdx.model.typing.dataclass_with_properties import dataclass_with_properties +from spdx.model.typing.type_checks import check_types_and_set_values class AnnotationType(Enum): diff --git a/src/model/checksum.py b/src/spdx/model/checksum.py similarity index 88% rename from src/model/checksum.py rename to src/spdx/model/checksum.py index 3555fa45b..775892b08 100644 --- a/src/model/checksum.py +++ b/src/spdx/model/checksum.py @@ -10,8 +10,8 @@ # limitations under the License. from enum import auto, Enum -from src.model.typing.dataclass_with_properties import dataclass_with_properties -from src.model.typing.type_checks import check_types_and_set_values +from spdx.model.typing.dataclass_with_properties import dataclass_with_properties +from spdx.model.typing.type_checks import check_types_and_set_values class ChecksumAlgorithm(Enum): diff --git a/src/model/document.py b/src/spdx/model/document.py similarity index 83% rename from src/model/document.py rename to src/spdx/model/document.py index a11f1c615..1dfac798e 100644 --- a/src/model/document.py +++ b/src/spdx/model/document.py @@ -12,17 +12,17 @@ from datetime import datetime from typing import List, Optional -from src.model.actor import Actor -from src.model.annotation import Annotation -from src.model.typing.dataclass_with_properties import dataclass_with_properties -from src.model.external_document_ref import ExternalDocumentRef -from src.model.extracted_licensing_info import ExtractedLicensingInfo -from src.model.file import File -from src.model.package import Package -from src.model.relationship import Relationship -from src.model.snippet import Snippet -from src.model.typing.type_checks import check_types_and_set_values -from src.model.version import Version +from spdx.model.actor import Actor +from spdx.model.annotation import Annotation +from spdx.model.typing.dataclass_with_properties import dataclass_with_properties +from spdx.model.external_document_ref import ExternalDocumentRef +from spdx.model.extracted_licensing_info import ExtractedLicensingInfo +from spdx.model.file import File +from spdx.model.package import Package +from spdx.model.relationship import Relationship +from spdx.model.snippet import Snippet +from spdx.model.typing.type_checks import check_types_and_set_values +from spdx.model.version import Version @dataclass_with_properties diff --git a/src/model/external_document_ref.py b/src/spdx/model/external_document_ref.py similarity index 82% rename from src/model/external_document_ref.py rename to src/spdx/model/external_document_ref.py index 52010c938..6e3b807bb 100644 --- a/src/model/external_document_ref.py +++ b/src/spdx/model/external_document_ref.py @@ -9,9 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from src.model.checksum import Checksum -from src.model.typing.dataclass_with_properties import dataclass_with_properties -from src.model.typing.type_checks import check_types_and_set_values +from spdx.model.checksum import Checksum +from spdx.model.typing.dataclass_with_properties import dataclass_with_properties +from spdx.model.typing.type_checks import check_types_and_set_values @dataclass_with_properties diff --git a/src/model/extracted_licensing_info.py b/src/spdx/model/extracted_licensing_info.py similarity index 86% rename from src/model/extracted_licensing_info.py rename to src/spdx/model/extracted_licensing_info.py index 659f996c8..97574b4a0 100644 --- a/src/model/extracted_licensing_info.py +++ b/src/spdx/model/extracted_licensing_info.py @@ -11,9 +11,9 @@ from dataclasses import field from typing import Optional, List, Union -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.typing.dataclass_with_properties import dataclass_with_properties -from src.model.typing.type_checks import check_types_and_set_values +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.typing.dataclass_with_properties import dataclass_with_properties +from spdx.model.typing.type_checks import check_types_and_set_values @dataclass_with_properties diff --git a/src/model/file.py b/src/spdx/model/file.py similarity index 89% rename from src/model/file.py rename to src/spdx/model/file.py index 6fd15ed4a..fa23f54e5 100644 --- a/src/model/file.py +++ b/src/spdx/model/file.py @@ -12,12 +12,12 @@ from enum import Enum, auto from typing import Optional, List, Union -from src.model.checksum import Checksum -from src.model.typing.dataclass_with_properties import dataclass_with_properties -from src.model.license_expression import LicenseExpression -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone -from src.model.typing.type_checks import check_types_and_set_values +from spdx.model.checksum import Checksum +from spdx.model.typing.dataclass_with_properties import dataclass_with_properties +from spdx.model.license_expression import LicenseExpression +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone +from spdx.model.typing.type_checks import check_types_and_set_values class FileType(Enum): diff --git a/src/model/license.py b/src/spdx/model/license.py similarity index 98% rename from src/model/license.py rename to src/spdx/model/license.py index dce4cbee5..f0ea53ac1 100644 --- a/src/model/license.py +++ b/src/spdx/model/license.py @@ -10,7 +10,7 @@ # limitations under the License. -from src import config +from spdx import config def determine_full_name(identifier: str, full_name: str): diff --git a/src/model/license_expression.py b/src/spdx/model/license_expression.py similarity index 86% rename from src/model/license_expression.py rename to src/spdx/model/license_expression.py index 226b5e438..291242b63 100644 --- a/src/model/license_expression.py +++ b/src/spdx/model/license_expression.py @@ -9,8 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from src.model.typing.dataclass_with_properties import dataclass_with_properties -from src.model.typing.type_checks import check_types_and_set_values +from spdx.model.typing.dataclass_with_properties import dataclass_with_properties +from spdx.model.typing.type_checks import check_types_and_set_values @dataclass_with_properties diff --git a/src/model/package.py b/src/spdx/model/package.py similarity index 93% rename from src/model/package.py rename to src/spdx/model/package.py index b9ab7e0a0..6a1836fdd 100644 --- a/src/model/package.py +++ b/src/spdx/model/package.py @@ -13,13 +13,13 @@ from enum import Enum, auto from typing import Optional, Union, List -from src.model.actor import Actor -from src.model.checksum import Checksum -from src.model.typing.dataclass_with_properties import dataclass_with_properties -from src.model.license_expression import LicenseExpression -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone -from src.model.typing.type_checks import check_types_and_set_values +from spdx.model.actor import Actor +from spdx.model.checksum import Checksum +from spdx.model.typing.dataclass_with_properties import dataclass_with_properties +from spdx.model.license_expression import LicenseExpression +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone +from spdx.model.typing.type_checks import check_types_and_set_values class PackagePurpose(Enum): diff --git a/src/model/relationship.py b/src/spdx/model/relationship.py similarity index 90% rename from src/model/relationship.py rename to src/spdx/model/relationship.py index 88026c9fd..6e639af70 100644 --- a/src/model/relationship.py +++ b/src/spdx/model/relationship.py @@ -11,10 +11,10 @@ from enum import auto, Enum from typing import Optional, Union -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone -from src.model.typing.dataclass_with_properties import dataclass_with_properties -from src.model.typing.type_checks import check_types_and_set_values +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone +from spdx.model.typing.dataclass_with_properties import dataclass_with_properties +from spdx.model.typing.type_checks import check_types_and_set_values class RelationshipType(Enum): diff --git a/src/model/relationship_filters.py b/src/spdx/model/relationship_filters.py similarity index 94% rename from src/model/relationship_filters.py rename to src/spdx/model/relationship_filters.py index 7b075dd6a..f981578e3 100644 --- a/src/model/relationship_filters.py +++ b/src/spdx/model/relationship_filters.py @@ -10,9 +10,9 @@ # limitations under the License. from typing import List -from src.model.document import Document -from src.model.package import Package -from src.model.relationship import Relationship, RelationshipType +from spdx.model.document import Document +from spdx.model.package import Package +from spdx.model.relationship import Relationship, RelationshipType def find_package_contains_file_relationships(document: Document, package: Package) -> List[Relationship]: diff --git a/src/model/snippet.py b/src/spdx/model/snippet.py similarity index 86% rename from src/model/snippet.py rename to src/spdx/model/snippet.py index 690647fad..5fba0c69d 100644 --- a/src/model/snippet.py +++ b/src/spdx/model/snippet.py @@ -11,11 +11,11 @@ from dataclasses import field from typing import Tuple, Optional, List, Union -from src.model.typing.dataclass_with_properties import dataclass_with_properties -from src.model.license_expression import LicenseExpression -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone -from src.model.typing.type_checks import check_types_and_set_values +from spdx.model.typing.dataclass_with_properties import dataclass_with_properties +from spdx.model.license_expression import LicenseExpression +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone +from spdx.model.typing.type_checks import check_types_and_set_values @dataclass_with_properties diff --git a/src/model/spdx_no_assertion.py b/src/spdx/model/spdx_no_assertion.py similarity index 100% rename from src/model/spdx_no_assertion.py rename to src/spdx/model/spdx_no_assertion.py diff --git a/src/model/spdx_none.py b/src/spdx/model/spdx_none.py similarity index 100% rename from src/model/spdx_none.py rename to src/spdx/model/spdx_none.py diff --git a/src/model/typing/__init__.py b/src/spdx/model/typing/__init__.py similarity index 100% rename from src/model/typing/__init__.py rename to src/spdx/model/typing/__init__.py diff --git a/src/model/typing/constructor_type_errors.py b/src/spdx/model/typing/constructor_type_errors.py similarity index 100% rename from src/model/typing/constructor_type_errors.py rename to src/spdx/model/typing/constructor_type_errors.py diff --git a/src/model/typing/dataclass_with_properties.py b/src/spdx/model/typing/dataclass_with_properties.py similarity index 100% rename from src/model/typing/dataclass_with_properties.py rename to src/spdx/model/typing/dataclass_with_properties.py diff --git a/src/model/typing/type_checks.py b/src/spdx/model/typing/type_checks.py similarity index 93% rename from src/model/typing/type_checks.py rename to src/spdx/model/typing/type_checks.py index ab3b39fe4..b06ab5d34 100644 --- a/src/model/typing/type_checks.py +++ b/src/spdx/model/typing/type_checks.py @@ -1,6 +1,6 @@ from typing import Any, Dict -from src.model.typing.constructor_type_errors import ConstructorTypeErrors +from spdx.model.typing.constructor_type_errors import ConstructorTypeErrors def check_types_and_set_values(instance_under_construction: Any, local_variables: Dict) -> None: diff --git a/src/model/version.py b/src/spdx/model/version.py similarity index 100% rename from src/model/version.py rename to src/spdx/model/version.py diff --git a/src/parser/__init__.py b/src/spdx/parser/__init__.py similarity index 100% rename from src/parser/__init__.py rename to src/spdx/parser/__init__.py diff --git a/src/parser/error.py b/src/spdx/parser/error.py similarity index 100% rename from src/parser/error.py rename to src/spdx/parser/error.py diff --git a/src/parser/json/__init__.py b/src/spdx/parser/json/__init__.py similarity index 100% rename from src/parser/json/__init__.py rename to src/spdx/parser/json/__init__.py diff --git a/src/parser/json/json_parser.py b/src/spdx/parser/json/json_parser.py similarity index 87% rename from src/parser/json/json_parser.py rename to src/spdx/parser/json/json_parser.py index 290c896d7..9ee169b3c 100644 --- a/src/parser/json/json_parser.py +++ b/src/spdx/parser/json/json_parser.py @@ -11,8 +11,8 @@ import json from typing import Dict -from src.model.document import Document -from src.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser +from spdx.model.document import Document +from spdx.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser def parse_from_file(file_name: str) -> Document: diff --git a/src/parser/jsonlikedict/__init__.py b/src/spdx/parser/jsonlikedict/__init__.py similarity index 100% rename from src/parser/jsonlikedict/__init__.py rename to src/spdx/parser/jsonlikedict/__init__.py diff --git a/src/parser/jsonlikedict/actor_parser.py b/src/spdx/parser/jsonlikedict/actor_parser.py similarity index 92% rename from src/parser/jsonlikedict/actor_parser.py rename to src/spdx/parser/jsonlikedict/actor_parser.py index 907233aa7..9126782a4 100644 --- a/src/parser/jsonlikedict/actor_parser.py +++ b/src/spdx/parser/jsonlikedict/actor_parser.py @@ -11,9 +11,9 @@ import re from typing import Pattern, Match, Optional -from src.model.actor import Actor, ActorType -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error +from spdx.model.actor import Actor, ActorType +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error class ActorParser: diff --git a/src/parser/jsonlikedict/annotation_parser.py b/src/spdx/parser/jsonlikedict/annotation_parser.py similarity index 93% rename from src/parser/jsonlikedict/annotation_parser.py rename to src/spdx/parser/jsonlikedict/annotation_parser.py index 8fa928228..bdfd5e3df 100644 --- a/src/parser/jsonlikedict/annotation_parser.py +++ b/src/spdx/parser/jsonlikedict/annotation_parser.py @@ -11,14 +11,14 @@ from datetime import datetime from typing import Dict, Optional, List -from src.model.actor import Actor -from src.model.annotation import Annotation, AnnotationType -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.actor_parser import ActorParser -from src.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error, \ +from spdx.model.actor import Actor +from spdx.model.annotation import Annotation, AnnotationType +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.actor_parser import ActorParser +from spdx.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error, \ parse_field_or_log_error, append_parsed_field_or_log_error, raise_parsing_error_if_logger_has_messages -from src.datetime_conversions import datetime_from_str -from src.parser.logger import Logger +from spdx.datetime_conversions import datetime_from_str +from spdx.parser.logger import Logger class AnnotationParser: diff --git a/src/parser/jsonlikedict/checksum_parser.py b/src/spdx/parser/jsonlikedict/checksum_parser.py similarity index 86% rename from src/parser/jsonlikedict/checksum_parser.py rename to src/spdx/parser/jsonlikedict/checksum_parser.py index a73a9d443..bd03b57a6 100644 --- a/src/parser/jsonlikedict/checksum_parser.py +++ b/src/spdx/parser/jsonlikedict/checksum_parser.py @@ -10,10 +10,10 @@ # limitations under the License. from typing import Dict, Optional -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, \ +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, \ construct_or_raise_parsing_error -from src.parser.logger import Logger +from spdx.parser.logger import Logger class ChecksumParser: diff --git a/src/parser/jsonlikedict/creation_info_parser.py b/src/spdx/parser/jsonlikedict/creation_info_parser.py similarity index 91% rename from src/parser/jsonlikedict/creation_info_parser.py rename to src/spdx/parser/jsonlikedict/creation_info_parser.py index 4b51056ad..c628bdc89 100644 --- a/src/parser/jsonlikedict/creation_info_parser.py +++ b/src/spdx/parser/jsonlikedict/creation_info_parser.py @@ -11,19 +11,19 @@ from datetime import datetime from typing import Dict, Optional, List -from src.model.actor import Actor -from src.model.checksum import Checksum -from src.model.document import CreationInfo -from src.model.external_document_ref import ExternalDocumentRef -from src.model.version import Version -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.actor_parser import ActorParser -from src.parser.jsonlikedict.checksum_parser import ChecksumParser -from src.parser.jsonlikedict.dict_parsing_functions import append_parsed_field_or_log_error, \ +from spdx.model.actor import Actor +from spdx.model.checksum import Checksum +from spdx.model.document import CreationInfo +from spdx.model.external_document_ref import ExternalDocumentRef +from spdx.model.version import Version +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.actor_parser import ActorParser +from spdx.parser.jsonlikedict.checksum_parser import ChecksumParser +from spdx.parser.jsonlikedict.dict_parsing_functions import append_parsed_field_or_log_error, \ raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error, parse_field_or_log_error, \ parse_field_or_no_assertion -from src.datetime_conversions import datetime_from_str -from src.parser.logger import Logger +from spdx.datetime_conversions import datetime_from_str +from spdx.parser.logger import Logger class CreationInfoParser: diff --git a/src/parser/jsonlikedict/dict_parsing_functions.py b/src/spdx/parser/jsonlikedict/dict_parsing_functions.py similarity index 93% rename from src/parser/jsonlikedict/dict_parsing_functions.py rename to src/spdx/parser/jsonlikedict/dict_parsing_functions.py index 2f7359924..f5d361067 100644 --- a/src/parser/jsonlikedict/dict_parsing_functions.py +++ b/src/spdx/parser/jsonlikedict/dict_parsing_functions.py @@ -10,11 +10,11 @@ # limitations under the License. from typing import Any, Callable, Dict, List, Optional -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone -from src.model.typing.constructor_type_errors import ConstructorTypeErrors -from src.parser.error import SPDXParsingError -from src.parser.logger import Logger +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone +from spdx.model.typing.constructor_type_errors import ConstructorTypeErrors +from spdx.parser.error import SPDXParsingError +from spdx.parser.logger import Logger def json_str_to_enum_name(json_str: str) -> str: diff --git a/src/parser/jsonlikedict/extracted_licensing_info_parser.py b/src/spdx/parser/jsonlikedict/extracted_licensing_info_parser.py similarity index 87% rename from src/parser/jsonlikedict/extracted_licensing_info_parser.py rename to src/spdx/parser/jsonlikedict/extracted_licensing_info_parser.py index 1c6b51e85..0d70ccaba 100644 --- a/src/parser/jsonlikedict/extracted_licensing_info_parser.py +++ b/src/spdx/parser/jsonlikedict/extracted_licensing_info_parser.py @@ -10,10 +10,10 @@ # limitations under the License. from typing import Dict, List, Optional, Union -from src.model.extracted_licensing_info import ExtractedLicensingInfo -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error, parse_field_or_no_assertion -from src.parser.logger import Logger +from spdx.model.extracted_licensing_info import ExtractedLicensingInfo +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error, parse_field_or_no_assertion +from spdx.parser.logger import Logger class ExtractedLicensingInfoParser: diff --git a/src/parser/jsonlikedict/file_parser.py b/src/spdx/parser/jsonlikedict/file_parser.py similarity index 88% rename from src/parser/jsonlikedict/file_parser.py rename to src/spdx/parser/jsonlikedict/file_parser.py index 0c63c7f24..44a62b5ab 100644 --- a/src/parser/jsonlikedict/file_parser.py +++ b/src/spdx/parser/jsonlikedict/file_parser.py @@ -10,17 +10,17 @@ # limitations under the License. from typing import Dict, List, Optional, Union -from src.model.checksum import Checksum -from src.model.file import File, FileType -from src.model.license_expression import LicenseExpression -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone -from src.parser.jsonlikedict.checksum_parser import ChecksumParser -from src.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ +from spdx.model.checksum import Checksum +from spdx.model.file import File, FileType +from spdx.model.license_expression import LicenseExpression +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone +from spdx.parser.jsonlikedict.checksum_parser import ChecksumParser +from spdx.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ construct_or_raise_parsing_error, parse_field_or_log_error, \ parse_field_or_no_assertion_or_none -from src.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser -from src.parser.logger import Logger +from spdx.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser +from spdx.parser.logger import Logger class FileParser: diff --git a/src/parser/jsonlikedict/json_like_dict_parser.py b/src/spdx/parser/jsonlikedict/json_like_dict_parser.py similarity index 84% rename from src/parser/jsonlikedict/json_like_dict_parser.py rename to src/spdx/parser/jsonlikedict/json_like_dict_parser.py index db78629bb..213f9da50 100644 --- a/src/parser/jsonlikedict/json_like_dict_parser.py +++ b/src/spdx/parser/jsonlikedict/json_like_dict_parser.py @@ -11,18 +11,18 @@ import json from typing import Dict -from src.model.document import Document -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.annotation_parser import AnnotationParser -from src.parser.jsonlikedict.creation_info_parser import CreationInfoParser -from src.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ +from spdx.model.document import Document +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.annotation_parser import AnnotationParser +from spdx.parser.jsonlikedict.creation_info_parser import CreationInfoParser +from spdx.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ construct_or_raise_parsing_error, parse_list_of_elements -from src.parser.jsonlikedict.extracted_licensing_info_parser import ExtractedLicensingInfoParser -from src.parser.jsonlikedict.file_parser import FileParser -from src.parser.logger import Logger -from src.parser.jsonlikedict.package_parser import PackageParser -from src.parser.jsonlikedict.relationship_parser import RelationshipParser -from src.parser.jsonlikedict.snippet_parser import SnippetParser +from spdx.parser.jsonlikedict.extracted_licensing_info_parser import ExtractedLicensingInfoParser +from spdx.parser.jsonlikedict.file_parser import FileParser +from spdx.parser.logger import Logger +from spdx.parser.jsonlikedict.package_parser import PackageParser +from spdx.parser.jsonlikedict.relationship_parser import RelationshipParser +from spdx.parser.jsonlikedict.snippet_parser import SnippetParser class JsonLikeDictParser: diff --git a/src/parser/jsonlikedict/license_expression_parser.py b/src/spdx/parser/jsonlikedict/license_expression_parser.py similarity index 87% rename from src/parser/jsonlikedict/license_expression_parser.py rename to src/spdx/parser/jsonlikedict/license_expression_parser.py index b1ce2468a..663d53af8 100644 --- a/src/parser/jsonlikedict/license_expression_parser.py +++ b/src/spdx/parser/jsonlikedict/license_expression_parser.py @@ -10,11 +10,11 @@ # limitations under the License. from typing import Union, List -from src.model.license_expression import LicenseExpression -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error, append_parsed_field_or_log_error, \ +from spdx.model.license_expression import LicenseExpression +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error, append_parsed_field_or_log_error, \ raise_parsing_error_if_logger_has_messages -from src.parser.logger import Logger +from spdx.parser.logger import Logger class LicenseExpressionParser: diff --git a/src/parser/jsonlikedict/package_parser.py b/src/spdx/parser/jsonlikedict/package_parser.py similarity index 93% rename from src/parser/jsonlikedict/package_parser.py rename to src/spdx/parser/jsonlikedict/package_parser.py index 45ab46b3f..d2677f5c3 100644 --- a/src/parser/jsonlikedict/package_parser.py +++ b/src/spdx/parser/jsonlikedict/package_parser.py @@ -11,21 +11,21 @@ from datetime import datetime from typing import Dict, List, Optional, Union -from src.model.actor import Actor -from src.model.license_expression import LicenseExpression -from src.model.package import Package, ExternalPackageRef, PackageVerificationCode, PackagePurpose, \ +from spdx.model.actor import Actor +from spdx.model.license_expression import LicenseExpression +from spdx.model.package import Package, ExternalPackageRef, PackageVerificationCode, PackagePurpose, \ ExternalPackageRefCategory -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.actor_parser import ActorParser -from src.parser.jsonlikedict.checksum_parser import ChecksumParser -from src.parser.jsonlikedict.dict_parsing_functions import append_parsed_field_or_log_error, \ +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.actor_parser import ActorParser +from spdx.parser.jsonlikedict.checksum_parser import ChecksumParser +from spdx.parser.jsonlikedict.dict_parsing_functions import append_parsed_field_or_log_error, \ raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, construct_or_raise_parsing_error, \ parse_field_or_log_error, parse_field_or_no_assertion_or_none, parse_field_or_no_assertion -from src.datetime_conversions import datetime_from_str -from src.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser -from src.parser.logger import Logger +from spdx.datetime_conversions import datetime_from_str +from spdx.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser +from spdx.parser.logger import Logger class PackageParser: diff --git a/src/parser/jsonlikedict/relationship_parser.py b/src/spdx/parser/jsonlikedict/relationship_parser.py similarity index 96% rename from src/parser/jsonlikedict/relationship_parser.py rename to src/spdx/parser/jsonlikedict/relationship_parser.py index 5221f1c15..f17a63738 100644 --- a/src/parser/jsonlikedict/relationship_parser.py +++ b/src/spdx/parser/jsonlikedict/relationship_parser.py @@ -10,13 +10,13 @@ # limitations under the License. from typing import Dict, List, Optional -from src.model.relationship import Relationship, RelationshipType -from src.model.typing.constructor_type_errors import ConstructorTypeErrors -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, \ +from spdx.model.relationship import Relationship, RelationshipType +from spdx.model.typing.constructor_type_errors import ConstructorTypeErrors +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, \ construct_or_raise_parsing_error, \ parse_field_or_log_error, parse_field_or_no_assertion_or_none -from src.parser.logger import Logger +from spdx.parser.logger import Logger class RelationshipParser: diff --git a/src/parser/jsonlikedict/snippet_parser.py b/src/spdx/parser/jsonlikedict/snippet_parser.py similarity index 92% rename from src/parser/jsonlikedict/snippet_parser.py rename to src/spdx/parser/jsonlikedict/snippet_parser.py index 778defc9f..f0a8fef9c 100644 --- a/src/parser/jsonlikedict/snippet_parser.py +++ b/src/spdx/parser/jsonlikedict/snippet_parser.py @@ -11,16 +11,16 @@ from enum import auto, Enum from typing import Dict, Tuple, List, Optional, Union -from src.model.license_expression import LicenseExpression -from src.model.snippet import Snippet -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error, parse_field_or_log_error, \ +from spdx.model.license_expression import LicenseExpression +from spdx.model.snippet import Snippet +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error, parse_field_or_log_error, \ parse_field_or_no_assertion_or_none -from src.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser -from src.parser.logger import Logger +from spdx.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser +from spdx.parser.logger import Logger class RangeType(Enum): diff --git a/src/parser/logger.py b/src/spdx/parser/logger.py similarity index 100% rename from src/parser/logger.py rename to src/spdx/parser/logger.py diff --git a/src/parser/parse_anything.py b/src/spdx/parser/parse_anything.py similarity index 87% rename from src/parser/parse_anything.py rename to src/spdx/parser/parse_anything.py index eae23db85..0180abe92 100644 --- a/src/parser/parse_anything.py +++ b/src/spdx/parser/parse_anything.py @@ -8,10 +8,10 @@ # 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.parser.json import json_parser -from src.parser.xml import xml_parser -from src.parser.yaml import yaml_parser +from spdx.formats import file_name_to_format, FileFormat +from spdx.parser.json import json_parser +from spdx.parser.xml import xml_parser +from spdx.parser.yaml import yaml_parser def parse_file(file_name: str): diff --git a/src/parser/xml/__init__.py b/src/spdx/parser/xml/__init__.py similarity index 100% rename from src/parser/xml/__init__.py rename to src/spdx/parser/xml/__init__.py diff --git a/src/parser/xml/xml_parser.py b/src/spdx/parser/xml/xml_parser.py similarity index 93% rename from src/parser/xml/xml_parser.py rename to src/spdx/parser/xml/xml_parser.py index f1e6b8d20..ccccd15d3 100644 --- a/src/parser/xml/xml_parser.py +++ b/src/spdx/parser/xml/xml_parser.py @@ -12,9 +12,9 @@ import xmltodict -from src.model.document import Document -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser +from spdx.model.document import Document +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser LIST_LIKE_FIELDS = [ diff --git a/src/parser/yaml/__init__.py b/src/spdx/parser/yaml/__init__.py similarity index 100% rename from src/parser/yaml/__init__.py rename to src/spdx/parser/yaml/__init__.py diff --git a/src/parser/yaml/yaml_parser.py b/src/spdx/parser/yaml/yaml_parser.py similarity index 87% rename from src/parser/yaml/yaml_parser.py rename to src/spdx/parser/yaml/yaml_parser.py index e0595f065..b3750938d 100644 --- a/src/parser/yaml/yaml_parser.py +++ b/src/spdx/parser/yaml/yaml_parser.py @@ -12,8 +12,8 @@ import yaml -from src.model.document import Document -from src.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser +from spdx.model.document import Document +from spdx.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser def parse_from_file(file_name: str) -> Document: diff --git a/src/validation/__init__.py b/src/spdx/validation/__init__.py similarity index 100% rename from src/validation/__init__.py rename to src/spdx/validation/__init__.py diff --git a/src/validation/actor_validator.py b/src/spdx/validation/actor_validator.py similarity index 90% rename from src/validation/actor_validator.py rename to src/spdx/validation/actor_validator.py index 4c596a440..033d2e94c 100644 --- a/src/validation/actor_validator.py +++ b/src/spdx/validation/actor_validator.py @@ -11,8 +11,8 @@ from typing import List -from src.model.actor import Actor, ActorType -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.model.actor import Actor, ActorType +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType def validate_actors(actors: List[Actor], parent_id: str) -> List[ValidationMessage]: diff --git a/src/validation/annotation_validator.py b/src/spdx/validation/annotation_validator.py similarity index 82% rename from src/validation/annotation_validator.py rename to src/spdx/validation/annotation_validator.py index 2b11fb498..407b2f7b5 100644 --- a/src/validation/annotation_validator.py +++ b/src/spdx/validation/annotation_validator.py @@ -11,11 +11,11 @@ from typing import List -from src.model.annotation import Annotation -from src.model.document import Document -from src.validation.actor_validator import validate_actor -from src.validation.spdx_id_validators import validate_spdx_id -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.model.annotation import Annotation +from spdx.model.document import Document +from spdx.validation.actor_validator import validate_actor +from spdx.validation.spdx_id_validators import validate_spdx_id +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType def validate_annotations(annotations: List[Annotation], document: Document) -> List[ValidationMessage]: diff --git a/src/validation/checksum_validator.py b/src/spdx/validation/checksum_validator.py similarity index 94% rename from src/validation/checksum_validator.py rename to src/spdx/validation/checksum_validator.py index 72962d7e4..b5f3f8078 100644 --- a/src/validation/checksum_validator.py +++ b/src/spdx/validation/checksum_validator.py @@ -12,8 +12,8 @@ import re from typing import List, Dict -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType # in hexadecimal digits algorithm_length: Dict = { diff --git a/src/validation/creation_info_validator.py b/src/spdx/validation/creation_info_validator.py similarity index 83% rename from src/validation/creation_info_validator.py rename to src/spdx/validation/creation_info_validator.py index 3fce083f9..f4bfc81ed 100644 --- a/src/validation/creation_info_validator.py +++ b/src/spdx/validation/creation_info_validator.py @@ -12,11 +12,11 @@ import re from typing import List -from src.model.document import CreationInfo -from src.validation.actor_validator import validate_actors -from src.validation.external_document_ref_validator import validate_external_document_refs -from src.validation.uri_validators import validate_uri -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.model.document import CreationInfo +from spdx.validation.actor_validator import validate_actors +from spdx.validation.external_document_ref_validator import validate_external_document_refs +from spdx.validation.uri_validators import validate_uri +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType def validate_creation_info(creation_info: CreationInfo) -> List[ValidationMessage]: diff --git a/src/validation/document_validator.py b/src/spdx/validation/document_validator.py similarity index 81% rename from src/validation/document_validator.py rename to src/spdx/validation/document_validator.py index 014f5d6ba..a3fa00758 100644 --- a/src/validation/document_validator.py +++ b/src/spdx/validation/document_validator.py @@ -11,17 +11,17 @@ import re from typing import List -from src.model.document import Document -from src.model.relationship import RelationshipType -from src.model.relationship_filters import filter_by_type_and_origin, filter_by_type_and_target -from src.validation.annotation_validator import validate_annotations -from src.validation.creation_info_validator import validate_creation_info -from src.validation.extracted_licensing_info_validator import validate_extracted_licensing_infos -from src.validation.file_validator import validate_files -from src.validation.package_validator import validate_packages -from src.validation.relationship_validator import validate_relationships -from src.validation.snippet_validator import validate_snippets -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.model.document import Document +from spdx.model.relationship import RelationshipType +from spdx.model.relationship_filters import filter_by_type_and_origin, filter_by_type_and_target +from spdx.validation.annotation_validator import validate_annotations +from spdx.validation.creation_info_validator import validate_creation_info +from spdx.validation.extracted_licensing_info_validator import validate_extracted_licensing_infos +from spdx.validation.file_validator import validate_files +from spdx.validation.package_validator import validate_packages +from spdx.validation.relationship_validator import validate_relationships +from spdx.validation.snippet_validator import validate_snippets +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType def validate_full_spdx_document(document: Document, spdx_version: str = None) -> List[ValidationMessage]: diff --git a/src/validation/external_document_ref_validator.py b/src/spdx/validation/external_document_ref_validator.py similarity index 84% rename from src/validation/external_document_ref_validator.py rename to src/spdx/validation/external_document_ref_validator.py index 71fd2d44f..3d35434a7 100644 --- a/src/validation/external_document_ref_validator.py +++ b/src/spdx/validation/external_document_ref_validator.py @@ -11,11 +11,11 @@ from typing import List -from src.model.external_document_ref import ExternalDocumentRef -from src.validation.checksum_validator import validate_checksum -from src.validation.spdx_id_validators import is_valid_external_doc_ref_id -from src.validation.uri_validators import validate_uri -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.model.external_document_ref import ExternalDocumentRef +from spdx.validation.checksum_validator import validate_checksum +from spdx.validation.spdx_id_validators import is_valid_external_doc_ref_id +from spdx.validation.uri_validators import validate_uri +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType def validate_external_document_refs(external_document_refs: List[ExternalDocumentRef], parent_id: str) -> List[ diff --git a/src/validation/external_package_ref_validator.py b/src/spdx/validation/external_package_ref_validator.py similarity index 90% rename from src/validation/external_package_ref_validator.py rename to src/spdx/validation/external_package_ref_validator.py index 5e245a539..10ff0ee15 100644 --- a/src/validation/external_package_ref_validator.py +++ b/src/spdx/validation/external_package_ref_validator.py @@ -11,8 +11,8 @@ from typing import List -from src.model.package import ExternalPackageRef -from src.validation.validation_message import ValidationMessage +from spdx.model.package import ExternalPackageRef +from spdx.validation.validation_message import ValidationMessage def validate_external_package_refs(external_package_refs: List[ExternalPackageRef], parent_id: str) -> List[ diff --git a/src/validation/extracted_licensing_info_validator.py b/src/spdx/validation/extracted_licensing_info_validator.py similarity index 90% rename from src/validation/extracted_licensing_info_validator.py rename to src/spdx/validation/extracted_licensing_info_validator.py index 7797f03b8..92cd7c395 100644 --- a/src/validation/extracted_licensing_info_validator.py +++ b/src/spdx/validation/extracted_licensing_info_validator.py @@ -12,9 +12,9 @@ import re from typing import List, Optional -from src.model.extracted_licensing_info import ExtractedLicensingInfo -from src.validation.uri_validators import validate_url -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.model.extracted_licensing_info import ExtractedLicensingInfo +from spdx.validation.uri_validators import validate_url +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType def validate_extracted_licensing_infos(extracted_licensing_infos: Optional[List[ExtractedLicensingInfo]]) -> List[ValidationMessage]: diff --git a/src/validation/file_validator.py b/src/spdx/validation/file_validator.py similarity index 85% rename from src/validation/file_validator.py rename to src/spdx/validation/file_validator.py index 0b1104c61..33cc73242 100644 --- a/src/validation/file_validator.py +++ b/src/spdx/validation/file_validator.py @@ -11,13 +11,13 @@ from typing import List, Optional -from src.model.checksum import ChecksumAlgorithm -from src.model.document import Document -from src.model.file import File -from src.validation.checksum_validator import validate_checksums -from src.validation.license_expression_validator import validate_license_expressions, validate_license_expression -from src.validation.spdx_id_validators import validate_spdx_id -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.model.checksum import ChecksumAlgorithm +from spdx.model.document import Document +from spdx.model.file import File +from spdx.validation.checksum_validator import validate_checksums +from spdx.validation.license_expression_validator import validate_license_expressions, validate_license_expression +from spdx.validation.spdx_id_validators import validate_spdx_id +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType def validate_files(files: List[File], document: Optional[Document] = None) -> List[ValidationMessage]: diff --git a/src/validation/license_expression_validator.py b/src/spdx/validation/license_expression_validator.py similarity index 85% rename from src/validation/license_expression_validator.py rename to src/spdx/validation/license_expression_validator.py index 503044eb3..3146e6f00 100644 --- a/src/validation/license_expression_validator.py +++ b/src/spdx/validation/license_expression_validator.py @@ -11,10 +11,10 @@ from typing import List, Optional, Union -from src.model.license_expression import LicenseExpression -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone -from src.validation.validation_message import ValidationMessage +from spdx.model.license_expression import LicenseExpression +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone +from spdx.validation.validation_message import ValidationMessage def validate_license_expressions(license_expressions: Optional[ diff --git a/src/validation/package_validator.py b/src/spdx/validation/package_validator.py similarity index 87% rename from src/validation/package_validator.py rename to src/spdx/validation/package_validator.py index 68c6a1d59..5131d39e4 100644 --- a/src/validation/package_validator.py +++ b/src/spdx/validation/package_validator.py @@ -11,16 +11,16 @@ from typing import List, Optional -from src.model.document import Document -from src.model.package import Package -from src.model.relationship import RelationshipType -from src.validation.checksum_validator import validate_checksums -from src.validation.external_package_ref_validator import validate_external_package_refs -from src.validation.license_expression_validator import validate_license_expression, validate_license_expressions -from src.validation.package_verification_code_validator import validate_verification_code -from src.validation.spdx_id_validators import validate_spdx_id -from src.validation.uri_validators import validate_url, validate_download_location -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.model.document import Document +from spdx.model.package import Package +from spdx.model.relationship import RelationshipType +from spdx.validation.checksum_validator import validate_checksums +from spdx.validation.external_package_ref_validator import validate_external_package_refs +from spdx.validation.license_expression_validator import validate_license_expression, validate_license_expressions +from spdx.validation.package_verification_code_validator import validate_verification_code +from spdx.validation.spdx_id_validators import validate_spdx_id +from spdx.validation.uri_validators import validate_url, validate_download_location +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType def validate_packages(packages: List[Package], document: Optional[Document] = None) -> List[ValidationMessage]: diff --git a/src/validation/package_verification_code_validator.py b/src/spdx/validation/package_verification_code_validator.py similarity index 91% rename from src/validation/package_verification_code_validator.py rename to src/spdx/validation/package_verification_code_validator.py index aaf858da7..2b86520cc 100644 --- a/src/validation/package_verification_code_validator.py +++ b/src/spdx/validation/package_verification_code_validator.py @@ -12,8 +12,8 @@ import re from typing import List -from src.model.package import PackageVerificationCode -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.model.package import PackageVerificationCode +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType # TODO: make test for this (https://github.com/spdx/tools-python/issues/386) diff --git a/src/validation/relationship_validator.py b/src/spdx/validation/relationship_validator.py similarity index 85% rename from src/validation/relationship_validator.py rename to src/spdx/validation/relationship_validator.py index e1b48358f..f919ebaae 100644 --- a/src/validation/relationship_validator.py +++ b/src/spdx/validation/relationship_validator.py @@ -11,12 +11,12 @@ from typing import List -from src.model.document import Document -from src.model.relationship import Relationship, RelationshipType -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone -from src.validation.spdx_id_validators import validate_spdx_id -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.model.document import Document +from spdx.model.relationship import Relationship, RelationshipType +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone +from spdx.validation.spdx_id_validators import validate_spdx_id +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType def validate_relationships(relationships: List[Relationship], document: Document, spdx_version: str) -> List[ValidationMessage]: diff --git a/src/validation/snippet_validator.py b/src/spdx/validation/snippet_validator.py similarity index 91% rename from src/validation/snippet_validator.py rename to src/spdx/validation/snippet_validator.py index c17928108..3140a85a9 100644 --- a/src/validation/snippet_validator.py +++ b/src/spdx/validation/snippet_validator.py @@ -11,12 +11,12 @@ from typing import List, Optional -from src.model.document import Document -from src.model.snippet import Snippet -from src.validation.license_expression_validator import validate_license_expression, \ +from spdx.model.document import Document +from spdx.model.snippet import Snippet +from spdx.validation.license_expression_validator import validate_license_expression, \ validate_license_expressions -from src.validation.spdx_id_validators import validate_spdx_id -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.validation.spdx_id_validators import validate_spdx_id +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType def validate_snippets(snippets: List[Snippet], document: Optional[Document] = None) -> List[ValidationMessage]: diff --git a/src/validation/spdx_id_validators.py b/src/spdx/validation/spdx_id_validators.py similarity index 96% rename from src/validation/spdx_id_validators.py rename to src/spdx/validation/spdx_id_validators.py index d161e4e82..dfe25f416 100644 --- a/src/validation/spdx_id_validators.py +++ b/src/spdx/validation/spdx_id_validators.py @@ -12,9 +12,9 @@ import re from typing import List -from src.document_utils import get_contained_spdx_element_ids -from src.model.document import Document -from src.model.file import File +from spdx.document_utils import get_contained_spdx_element_ids +from spdx.model.document import Document +from spdx.model.file import File def is_valid_internal_spdx_id(spdx_id: str) -> bool: diff --git a/src/validation/uri_validators.py b/src/spdx/validation/uri_validators.py similarity index 100% rename from src/validation/uri_validators.py rename to src/spdx/validation/uri_validators.py diff --git a/src/validation/validation_message.py b/src/spdx/validation/validation_message.py similarity index 100% rename from src/validation/validation_message.py rename to src/spdx/validation/validation_message.py diff --git a/src/writer/__init__.py b/src/spdx/writer/__init__.py similarity index 100% rename from src/writer/__init__.py rename to src/spdx/writer/__init__.py diff --git a/src/writer/casing_tools.py b/src/spdx/writer/casing_tools.py similarity index 100% rename from src/writer/casing_tools.py rename to src/spdx/writer/casing_tools.py diff --git a/src/writer/json/__init__.py b/src/spdx/writer/json/__init__.py similarity index 100% rename from src/writer/json/__init__.py rename to src/spdx/writer/json/__init__.py diff --git a/src/writer/json/json_writer.py b/src/spdx/writer/json/json_writer.py similarity index 85% rename from src/writer/json/json_writer.py rename to src/spdx/writer/json/json_writer.py index 347f2d622..4cfef383a 100644 --- a/src/writer/json/json_writer.py +++ b/src/spdx/writer/json/json_writer.py @@ -11,10 +11,10 @@ import json from typing import List -from src.jsonschema.document_converter import DocumentConverter -from src.model.document import Document -from src.validation.document_validator import validate_full_spdx_document -from src.validation.validation_message import ValidationMessage +from spdx.jsonschema.document_converter import DocumentConverter +from spdx.model.document import Document +from spdx.validation.document_validator import validate_full_spdx_document +from spdx.validation.validation_message import ValidationMessage def write_document(document: Document, file_name: str, validate: bool = True, converter: DocumentConverter = None): diff --git a/src/writer/tagvalue/__init__.py b/src/spdx/writer/tagvalue/__init__.py similarity index 100% rename from src/writer/tagvalue/__init__.py rename to src/spdx/writer/tagvalue/__init__.py diff --git a/src/writer/tagvalue/annotation_writer.py b/src/spdx/writer/tagvalue/annotation_writer.py similarity index 84% rename from src/writer/tagvalue/annotation_writer.py rename to src/spdx/writer/tagvalue/annotation_writer.py index 5cb7b8b46..231a7f5ea 100644 --- a/src/writer/tagvalue/annotation_writer.py +++ b/src/spdx/writer/tagvalue/annotation_writer.py @@ -10,9 +10,9 @@ # limitations under the License. from typing import TextIO -from src.datetime_conversions import datetime_to_iso_string -from src.model.annotation import Annotation -from src.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value +from spdx.datetime_conversions import datetime_to_iso_string +from spdx.model.annotation import Annotation +from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value def write_annotation(annotation: Annotation, text_output: TextIO): diff --git a/src/writer/tagvalue/checksum_writer.py b/src/spdx/writer/tagvalue/checksum_writer.py similarity index 95% rename from src/writer/tagvalue/checksum_writer.py rename to src/spdx/writer/tagvalue/checksum_writer.py index 7a31cad6a..80df31e35 100644 --- a/src/writer/tagvalue/checksum_writer.py +++ b/src/spdx/writer/tagvalue/checksum_writer.py @@ -8,7 +8,7 @@ # 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.model.checksum import Checksum, ChecksumAlgorithm +from spdx.model.checksum import Checksum, ChecksumAlgorithm def write_checksum_to_tag_value(checksum: Checksum) -> str: diff --git a/src/writer/tagvalue/creation_info_writer.py b/src/spdx/writer/tagvalue/creation_info_writer.py similarity index 90% rename from src/writer/tagvalue/creation_info_writer.py rename to src/spdx/writer/tagvalue/creation_info_writer.py index d2c515332..45788410b 100644 --- a/src/writer/tagvalue/creation_info_writer.py +++ b/src/spdx/writer/tagvalue/creation_info_writer.py @@ -10,9 +10,9 @@ # limitations under the License. from typing import TextIO -from src.datetime_conversions import datetime_to_iso_string -from src.model.document import CreationInfo -from src.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, write_optional_heading, \ +from spdx.datetime_conversions import datetime_to_iso_string +from spdx.model.document import CreationInfo +from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, write_optional_heading, \ write_separator diff --git a/src/writer/tagvalue/extracted_licensing_info_writer.py b/src/spdx/writer/tagvalue/extracted_licensing_info_writer.py similarity index 87% rename from src/writer/tagvalue/extracted_licensing_info_writer.py rename to src/spdx/writer/tagvalue/extracted_licensing_info_writer.py index eed0e07f2..bddbe48c2 100644 --- a/src/writer/tagvalue/extracted_licensing_info_writer.py +++ b/src/spdx/writer/tagvalue/extracted_licensing_info_writer.py @@ -10,8 +10,8 @@ # limitations under the License. from typing import TextIO -from src.model.extracted_licensing_info import ExtractedLicensingInfo -from src.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value +from spdx.model.extracted_licensing_info import ExtractedLicensingInfo +from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value def write_extracted_licensing_info(extracted_licensing_info: ExtractedLicensingInfo, text_output: TextIO): diff --git a/src/writer/tagvalue/file_writer.py b/src/spdx/writer/tagvalue/file_writer.py similarity index 89% rename from src/writer/tagvalue/file_writer.py rename to src/spdx/writer/tagvalue/file_writer.py index 67780d146..e281d4fd7 100644 --- a/src/writer/tagvalue/file_writer.py +++ b/src/spdx/writer/tagvalue/file_writer.py @@ -10,10 +10,10 @@ # limitations under the License. from typing import TextIO -from src.model.file import File -from src.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, \ +from spdx.model.file import File +from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, \ write_license_expression -from src.writer.tagvalue.checksum_writer import write_checksum_to_tag_value +from spdx.writer.tagvalue.checksum_writer import write_checksum_to_tag_value def write_file(file: File, text_output: TextIO): diff --git a/src/writer/tagvalue/package_writer.py b/src/spdx/writer/tagvalue/package_writer.py similarity index 93% rename from src/writer/tagvalue/package_writer.py rename to src/spdx/writer/tagvalue/package_writer.py index 78bb90dc7..045e509e4 100644 --- a/src/writer/tagvalue/package_writer.py +++ b/src/spdx/writer/tagvalue/package_writer.py @@ -10,11 +10,11 @@ # limitations under the License. from typing import TextIO -from src.datetime_conversions import datetime_to_iso_string -from src.model.package import Package, PackageVerificationCode -from src.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, \ +from spdx.datetime_conversions import datetime_to_iso_string +from spdx.model.package import Package, PackageVerificationCode +from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, \ write_license_expression, transform_enum_name_to_tv, write_actor -from src.writer.tagvalue.checksum_writer import write_checksum_to_tag_value +from spdx.writer.tagvalue.checksum_writer import write_checksum_to_tag_value def write_package(package: Package, text_output: TextIO): diff --git a/src/writer/tagvalue/relationship_writer.py b/src/spdx/writer/tagvalue/relationship_writer.py similarity index 86% rename from src/writer/tagvalue/relationship_writer.py rename to src/spdx/writer/tagvalue/relationship_writer.py index 15aeb3cd4..8972996df 100644 --- a/src/writer/tagvalue/relationship_writer.py +++ b/src/spdx/writer/tagvalue/relationship_writer.py @@ -10,8 +10,8 @@ # limitations under the License. from typing import TextIO -from src.model.relationship import Relationship -from src.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value +from spdx.model.relationship import Relationship +from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value def write_relationship(relationship: Relationship, text_output: TextIO): diff --git a/src/writer/tagvalue/snippet_writer.py b/src/spdx/writer/tagvalue/snippet_writer.py similarity index 91% rename from src/writer/tagvalue/snippet_writer.py rename to src/spdx/writer/tagvalue/snippet_writer.py index b4a4740e6..7f5d8d600 100644 --- a/src/writer/tagvalue/snippet_writer.py +++ b/src/spdx/writer/tagvalue/snippet_writer.py @@ -10,8 +10,8 @@ # limitations under the License. from typing import TextIO -from src.model.snippet import Snippet -from src.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, write_range, \ +from spdx.model.snippet import Snippet +from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, write_range, \ write_license_expression diff --git a/src/writer/tagvalue/tagvalue_writer.py b/src/spdx/writer/tagvalue/tagvalue_writer.py similarity index 84% rename from src/writer/tagvalue/tagvalue_writer.py rename to src/spdx/writer/tagvalue/tagvalue_writer.py index 0f609602f..bce0db50d 100644 --- a/src/writer/tagvalue/tagvalue_writer.py +++ b/src/spdx/writer/tagvalue/tagvalue_writer.py @@ -11,15 +11,15 @@ from typing import TextIO -from src.model.document import Document -from src.writer.tagvalue.annotation_writer import write_annotation -from src.writer.tagvalue.creation_info_writer import write_creation_info -from src.writer.tagvalue.extracted_licensing_info_writer import write_extracted_licensing_info -from src.writer.tagvalue.file_writer import write_file -from src.writer.tagvalue.package_writer import write_package -from src.writer.tagvalue.relationship_writer import write_relationship -from src.writer.tagvalue.snippet_writer import write_snippet -from src.writer.tagvalue.tagvalue_writer_helper_functions import write_separator, scan_relationships, \ +from spdx.model.document import Document +from spdx.writer.tagvalue.annotation_writer import write_annotation +from spdx.writer.tagvalue.creation_info_writer import write_creation_info +from spdx.writer.tagvalue.extracted_licensing_info_writer import write_extracted_licensing_info +from spdx.writer.tagvalue.file_writer import write_file +from spdx.writer.tagvalue.package_writer import write_package +from spdx.writer.tagvalue.relationship_writer import write_relationship +from spdx.writer.tagvalue.snippet_writer import write_snippet +from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_separator, scan_relationships, \ get_file_ids_with_contained_snippets, write_optional_heading, write_list_of_elements diff --git a/src/writer/tagvalue/tagvalue_writer_helper_functions.py b/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py similarity index 92% rename from src/writer/tagvalue/tagvalue_writer_helper_functions.py rename to src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py index 967281551..445b6c207 100644 --- a/src/writer/tagvalue/tagvalue_writer_helper_functions.py +++ b/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py @@ -10,14 +10,14 @@ # limitations under the License. from typing import TextIO, Tuple, List, Dict, Any, Union, Callable, Optional -from src.model.actor import Actor -from src.model.file import File -from src.model.license_expression import LicenseExpression -from src.model.package import Package -from src.model.relationship import Relationship -from src.model.snippet import Snippet -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone +from spdx.model.actor import Actor +from spdx.model.file import File +from spdx.model.license_expression import LicenseExpression +from spdx.model.package import Package +from spdx.model.relationship import Relationship +from spdx.model.snippet import Snippet +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone def write_separator(out: TextIO): diff --git a/src/writer/write_anything.py b/src/spdx/writer/write_anything.py similarity index 82% rename from src/writer/write_anything.py rename to src/spdx/writer/write_anything.py index 29d07cbfa..43f786639 100644 --- a/src/writer/write_anything.py +++ b/src/spdx/writer/write_anything.py @@ -8,12 +8,12 @@ # 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.json import json_writer -from src.writer.tagvalue import tagvalue_writer -from src.writer.xml import xml_writer -from src.writer.yaml import yaml_writer +from spdx.formats import file_name_to_format, FileFormat +from spdx.model.document import Document +from spdx.writer.json import json_writer +from spdx.writer.tagvalue import tagvalue_writer +from spdx.writer.xml import xml_writer +from spdx.writer.yaml import yaml_writer def write_file(document: Document, file_name: str, validate: bool = True): diff --git a/src/writer/xml/__init__.py b/src/spdx/writer/xml/__init__.py similarity index 100% rename from src/writer/xml/__init__.py rename to src/spdx/writer/xml/__init__.py diff --git a/src/writer/xml/xml_writer.py b/src/spdx/writer/xml/xml_writer.py similarity index 86% rename from src/writer/xml/xml_writer.py rename to src/spdx/writer/xml/xml_writer.py index b0a374162..f6bcf738e 100644 --- a/src/writer/xml/xml_writer.py +++ b/src/spdx/writer/xml/xml_writer.py @@ -12,10 +12,10 @@ import xmltodict -from src.jsonschema.document_converter import DocumentConverter -from src.model.document import Document -from src.validation.document_validator import validate_full_spdx_document -from src.validation.validation_message import ValidationMessage +from spdx.jsonschema.document_converter import DocumentConverter +from spdx.model.document import Document +from spdx.validation.document_validator import validate_full_spdx_document +from spdx.validation.validation_message import ValidationMessage def write_document_to_file(document: Document, file_name: str, validate: bool = True, converter: DocumentConverter = None): diff --git a/src/writer/yaml/__init__.py b/src/spdx/writer/yaml/__init__.py similarity index 100% rename from src/writer/yaml/__init__.py rename to src/spdx/writer/yaml/__init__.py diff --git a/src/writer/yaml/yaml_writer.py b/src/spdx/writer/yaml/yaml_writer.py similarity index 86% rename from src/writer/yaml/yaml_writer.py rename to src/spdx/writer/yaml/yaml_writer.py index 07f36917d..38dc40ac4 100644 --- a/src/writer/yaml/yaml_writer.py +++ b/src/spdx/writer/yaml/yaml_writer.py @@ -12,10 +12,10 @@ import yaml -from src.jsonschema.document_converter import DocumentConverter -from src.model.document import Document -from src.validation.document_validator import validate_full_spdx_document -from src.validation.validation_message import ValidationMessage +from spdx.jsonschema.document_converter import DocumentConverter +from spdx.model.document import Document +from spdx.validation.document_validator import validate_full_spdx_document +from spdx.validation.validation_message import ValidationMessage def write_document_to_file(document: Document, file_name: str, validate: bool = True, converter: DocumentConverter = None): diff --git a/tests/fixtures.py b/tests/fixtures.py index 284d84149..8bcb0f3d0 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -10,19 +10,19 @@ # limitations under the License. from datetime import datetime -from src.model.actor import Actor, ActorType -from src.model.annotation import Annotation, AnnotationType -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.document import CreationInfo, Document -from src.model.external_document_ref import ExternalDocumentRef -from src.model.extracted_licensing_info import ExtractedLicensingInfo -from src.model.file import File, FileType -from src.model.license_expression import LicenseExpression -from src.model.package import Package, PackageVerificationCode, PackagePurpose, ExternalPackageRef, \ +from spdx.model.actor import Actor, ActorType +from spdx.model.annotation import Annotation, AnnotationType +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.model.document import CreationInfo, Document +from spdx.model.external_document_ref import ExternalDocumentRef +from spdx.model.extracted_licensing_info import ExtractedLicensingInfo +from spdx.model.file import File, FileType +from spdx.model.license_expression import LicenseExpression +from spdx.model.package import Package, PackageVerificationCode, PackagePurpose, ExternalPackageRef, \ ExternalPackageRefCategory -from src.model.relationship import Relationship, RelationshipType -from src.model.snippet import Snippet -from src.model.version import Version +from spdx.model.relationship import Relationship, RelationshipType +from spdx.model.snippet import Snippet +from spdx.model.version import Version """Utility methods to create data model instances. All properties have valid defaults, so they don't need to be specified unless relevant for the test.""" diff --git a/tests/jsonschema/test_annotation_converter.py b/tests/jsonschema/test_annotation_converter.py index a6b593190..b843ff275 100644 --- a/tests/jsonschema/test_annotation_converter.py +++ b/tests/jsonschema/test_annotation_converter.py @@ -12,11 +12,11 @@ import pytest -from src.datetime_conversions import datetime_to_iso_string -from src.jsonschema.annotation_converter import AnnotationConverter -from src.jsonschema.annotation_properties import AnnotationProperty -from src.model.actor import Actor, ActorType -from src.model.annotation import Annotation, AnnotationType +from spdx.datetime_conversions import datetime_to_iso_string +from spdx.jsonschema.annotation_converter import AnnotationConverter +from spdx.jsonschema.annotation_properties import AnnotationProperty +from spdx.model.actor import Actor, ActorType +from spdx.model.annotation import Annotation, AnnotationType @pytest.fixture diff --git a/tests/jsonschema/test_checksum_converter.py b/tests/jsonschema/test_checksum_converter.py index 4f2eef537..857745b7c 100644 --- a/tests/jsonschema/test_checksum_converter.py +++ b/tests/jsonschema/test_checksum_converter.py @@ -10,9 +10,9 @@ # limitations under the License. import pytest -from src.jsonschema.checksum_converter import ChecksumConverter -from src.jsonschema.checksum_properties import ChecksumProperty -from src.model.checksum import Checksum, ChecksumAlgorithm +from spdx.jsonschema.checksum_converter import ChecksumConverter +from spdx.jsonschema.checksum_properties import ChecksumProperty +from spdx.model.checksum import Checksum, ChecksumAlgorithm @pytest.fixture diff --git a/tests/jsonschema/test_converter.py b/tests/jsonschema/test_converter.py index ca5091144..7a5b4783c 100644 --- a/tests/jsonschema/test_converter.py +++ b/tests/jsonschema/test_converter.py @@ -13,12 +13,12 @@ import pytest -from src.jsonschema.converter import TypedConverter -from src.jsonschema.json_property import JsonProperty -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.document import Document -from src.model.typing.dataclass_with_properties import dataclass_with_properties -from src.model.typing.type_checks import check_types_and_set_values +from spdx.jsonschema.converter import TypedConverter +from spdx.jsonschema.json_property import JsonProperty +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.model.document import Document +from spdx.model.typing.dataclass_with_properties import dataclass_with_properties +from spdx.model.typing.type_checks import check_types_and_set_values class TestPropertyType(JsonProperty): diff --git a/tests/jsonschema/test_creation_info_converter.py b/tests/jsonschema/test_creation_info_converter.py index 012659083..47b825802 100644 --- a/tests/jsonschema/test_creation_info_converter.py +++ b/tests/jsonschema/test_creation_info_converter.py @@ -12,12 +12,12 @@ import pytest -from src.datetime_conversions import datetime_to_iso_string -from src.jsonschema.creation_info_converter import CreationInfoConverter -from src.jsonschema.creation_info_properties import CreationInfoProperty -from src.model.actor import Actor, ActorType -from src.model.document import CreationInfo -from src.model.version import Version +from spdx.datetime_conversions import datetime_to_iso_string +from spdx.jsonschema.creation_info_converter import CreationInfoConverter +from spdx.jsonschema.creation_info_properties import CreationInfoProperty +from spdx.model.actor import Actor, ActorType +from spdx.model.document import CreationInfo +from spdx.model.version import Version from tests.fixtures import creation_info_fixture diff --git a/tests/jsonschema/test_document_converter.py b/tests/jsonschema/test_document_converter.py index 82e5ba34a..95bf0dd15 100644 --- a/tests/jsonschema/test_document_converter.py +++ b/tests/jsonschema/test_document_converter.py @@ -15,29 +15,29 @@ import pytest -from src.jsonschema.annotation_converter import AnnotationConverter -from src.jsonschema.document_converter import DocumentConverter -from src.jsonschema.document_properties import DocumentProperty -from src.jsonschema.relationship_converter import RelationshipConverter -from src.model.actor import Actor, ActorType -from src.model.annotation import Annotation, AnnotationType -from src.model.document import Document -from src.model.extracted_licensing_info import ExtractedLicensingInfo -from src.model.relationship import Relationship, RelationshipType +from spdx.jsonschema.annotation_converter import AnnotationConverter +from spdx.jsonschema.document_converter import DocumentConverter +from spdx.jsonschema.document_properties import DocumentProperty +from spdx.jsonschema.relationship_converter import RelationshipConverter +from spdx.model.actor import Actor, ActorType +from spdx.model.annotation import Annotation, AnnotationType +from spdx.model.document import Document +from spdx.model.extracted_licensing_info import ExtractedLicensingInfo +from spdx.model.relationship import Relationship, RelationshipType from tests.fixtures import creation_info_fixture, file_fixture, package_fixture, external_document_ref_fixture, \ snippet_fixture, annotation_fixture, document_fixture, relationship_fixture from tests.mock_utils import assert_mock_method_called_with_arguments, assert_no_mock_methods_called @pytest.fixture -@mock.patch('src.jsonschema.creation_info_converter.CreationInfoConverter', autospec=True) -@mock.patch('src.jsonschema.external_document_ref_converter.ExternalDocumentRefConverter', autospec=True) -@mock.patch('src.jsonschema.package_converter.PackageConverter', autospec=True) -@mock.patch('src.jsonschema.annotation_converter.AnnotationConverter', autospec=True) -@mock.patch('src.jsonschema.extracted_licensing_info_converter.ExtractedLicensingInfoConverter', autospec=True) -@mock.patch('src.jsonschema.file_converter.FileConverter', autospec=True) -@mock.patch('src.jsonschema.snippet_converter.SnippetConverter', autospec=True) -@mock.patch('src.jsonschema.relationship_converter.RelationshipConverter', autospec=True) +@mock.patch('spdx.jsonschema.creation_info_converter.CreationInfoConverter', autospec=True) +@mock.patch('spdx.jsonschema.external_document_ref_converter.ExternalDocumentRefConverter', autospec=True) +@mock.patch('spdx.jsonschema.package_converter.PackageConverter', autospec=True) +@mock.patch('spdx.jsonschema.annotation_converter.AnnotationConverter', autospec=True) +@mock.patch('spdx.jsonschema.extracted_licensing_info_converter.ExtractedLicensingInfoConverter', autospec=True) +@mock.patch('spdx.jsonschema.file_converter.FileConverter', autospec=True) +@mock.patch('spdx.jsonschema.snippet_converter.SnippetConverter', autospec=True) +@mock.patch('spdx.jsonschema.relationship_converter.RelationshipConverter', autospec=True) def converter(relationship_converter_mock: MagicMock, snippet_converter_mock: MagicMock, file_converter_mock: MagicMock, extracted_licensing_info_converter_mock: MagicMock, annotation_converter_mock: MagicMock, package_converter_mock: MagicMock, external_ref_converter_mock: MagicMock, diff --git a/tests/jsonschema/test_external_document_ref_converter.py b/tests/jsonschema/test_external_document_ref_converter.py index ebd9bc97b..dc85c42b9 100644 --- a/tests/jsonschema/test_external_document_ref_converter.py +++ b/tests/jsonschema/test_external_document_ref_converter.py @@ -13,14 +13,14 @@ import pytest -from src.jsonschema.external_document_ref_converter import ExternalDocumentRefConverter -from src.jsonschema.external_document_ref_properties import ExternalDocumentRefProperty -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.external_document_ref import ExternalDocumentRef +from spdx.jsonschema.external_document_ref_converter import ExternalDocumentRefConverter +from spdx.jsonschema.external_document_ref_properties import ExternalDocumentRefProperty +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.model.external_document_ref import ExternalDocumentRef @pytest.fixture -@mock.patch('src.jsonschema.checksum_converter.ChecksumConverter', autospec=True) +@mock.patch('spdx.jsonschema.checksum_converter.ChecksumConverter', autospec=True) def converter(checksum_converter_magic_mock: MagicMock) -> ExternalDocumentRefConverter: mocked_checksum_converter = checksum_converter_magic_mock() converter = ExternalDocumentRefConverter() diff --git a/tests/jsonschema/test_external_package_ref_converter.py b/tests/jsonschema/test_external_package_ref_converter.py index fb4aad6b1..64c61228e 100644 --- a/tests/jsonschema/test_external_package_ref_converter.py +++ b/tests/jsonschema/test_external_package_ref_converter.py @@ -10,9 +10,9 @@ # limitations under the License. import pytest -from src.jsonschema.external_package_ref_converter import ExternalPackageRefConverter -from src.jsonschema.external_package_ref_properties import ExternalPackageRefProperty -from src.model.package import ExternalPackageRef, ExternalPackageRefCategory +from spdx.jsonschema.external_package_ref_converter import ExternalPackageRefConverter +from spdx.jsonschema.external_package_ref_properties import ExternalPackageRefProperty +from spdx.model.package import ExternalPackageRef, ExternalPackageRefCategory @pytest.fixture diff --git a/tests/jsonschema/test_extracted_licensing_info_converter.py b/tests/jsonschema/test_extracted_licensing_info_converter.py index 5b892389a..52fef8f80 100644 --- a/tests/jsonschema/test_extracted_licensing_info_converter.py +++ b/tests/jsonschema/test_extracted_licensing_info_converter.py @@ -10,10 +10,10 @@ # limitations under the License. import pytest -from src.jsonschema.extracted_licensing_info_converter import ExtractedLicensingInfoConverter -from src.jsonschema.extracted_licensing_info_properties import ExtractedLicensingInfoProperty -from src.model.extracted_licensing_info import ExtractedLicensingInfo -from src.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING +from spdx.jsonschema.extracted_licensing_info_converter import ExtractedLicensingInfoConverter +from spdx.jsonschema.extracted_licensing_info_properties import ExtractedLicensingInfoProperty +from spdx.model.extracted_licensing_info import ExtractedLicensingInfo +from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING from tests.fixtures import extracted_licensing_info_fixture diff --git a/tests/jsonschema/test_file_converter.py b/tests/jsonschema/test_file_converter.py index 46f39e215..452d08c51 100644 --- a/tests/jsonschema/test_file_converter.py +++ b/tests/jsonschema/test_file_converter.py @@ -15,24 +15,24 @@ import pytest -from src.jsonschema.annotation_converter import AnnotationConverter -from src.jsonschema.file_converter import FileConverter -from src.jsonschema.file_properties import FileProperty -from src.model.actor import Actor, ActorType -from src.model.annotation import Annotation, AnnotationType -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.document import Document -from src.model.file import File, FileType -from src.model.license_expression import LicenseExpression -from src.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING -from src.model.spdx_none import SpdxNone, SPDX_NONE_STRING +from spdx.jsonschema.annotation_converter import AnnotationConverter +from spdx.jsonschema.file_converter import FileConverter +from spdx.jsonschema.file_properties import FileProperty +from spdx.model.actor import Actor, ActorType +from spdx.model.annotation import Annotation, AnnotationType +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.model.document import Document +from spdx.model.file import File, FileType +from spdx.model.license_expression import LicenseExpression +from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING +from spdx.model.spdx_none import SpdxNone, SPDX_NONE_STRING from tests.fixtures import creation_info_fixture, file_fixture, annotation_fixture, document_fixture from tests.mock_utils import assert_mock_method_called_with_arguments @pytest.fixture -@mock.patch('src.jsonschema.checksum_converter.ChecksumConverter', autospec=True) -@mock.patch('src.jsonschema.annotation_converter.AnnotationConverter', autospec=True) +@mock.patch('spdx.jsonschema.checksum_converter.ChecksumConverter', autospec=True) +@mock.patch('spdx.jsonschema.annotation_converter.AnnotationConverter', autospec=True) def converter(annotation_converter_mock: MagicMock, checksum_converter_mock: MagicMock) -> FileConverter: converter = FileConverter() converter.checksum_converter = checksum_converter_mock() diff --git a/tests/jsonschema/test_package_converter.py b/tests/jsonschema/test_package_converter.py index 1f9c22ff0..f6c757a17 100644 --- a/tests/jsonschema/test_package_converter.py +++ b/tests/jsonschema/test_package_converter.py @@ -15,28 +15,28 @@ import pytest -from src.jsonschema.annotation_converter import AnnotationConverter -from src.jsonschema.package_converter import PackageConverter -from src.jsonschema.package_properties import PackageProperty -from src.model.actor import Actor, ActorType -from src.model.annotation import Annotation, AnnotationType -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.document import Document -from src.model.license_expression import LicenseExpression -from src.model.package import Package, PackageVerificationCode, PackagePurpose -from src.model.relationship import RelationshipType -from src.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING -from src.model.spdx_none import SpdxNone, SPDX_NONE_STRING +from spdx.jsonschema.annotation_converter import AnnotationConverter +from spdx.jsonschema.package_converter import PackageConverter +from spdx.jsonschema.package_properties import PackageProperty +from spdx.model.actor import Actor, ActorType +from spdx.model.annotation import Annotation, AnnotationType +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.model.document import Document +from spdx.model.license_expression import LicenseExpression +from spdx.model.package import Package, PackageVerificationCode, PackagePurpose +from spdx.model.relationship import RelationshipType +from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING +from spdx.model.spdx_none import SpdxNone, SPDX_NONE_STRING from tests.fixtures import creation_info_fixture, package_fixture, external_package_ref_fixture, document_fixture, \ annotation_fixture, file_fixture, relationship_fixture, snippet_fixture from tests.mock_utils import assert_mock_method_called_with_arguments @pytest.fixture -@mock.patch('src.jsonschema.checksum_converter.ChecksumConverter', autospec=True) -@mock.patch('src.jsonschema.annotation_converter.AnnotationConverter', autospec=True) -@mock.patch('src.jsonschema.package_verification_code_converter.PackageVerificationCodeConverter', autospec=True) -@mock.patch('src.jsonschema.external_package_ref_converter.ExternalPackageRefConverter', autospec=True) +@mock.patch('spdx.jsonschema.checksum_converter.ChecksumConverter', autospec=True) +@mock.patch('spdx.jsonschema.annotation_converter.AnnotationConverter', autospec=True) +@mock.patch('spdx.jsonschema.package_verification_code_converter.PackageVerificationCodeConverter', autospec=True) +@mock.patch('spdx.jsonschema.external_package_ref_converter.ExternalPackageRefConverter', autospec=True) def converter(package_ref_converter_mock: MagicMock, verification_code_converter_mock: MagicMock, annotation_converter_mock: MagicMock, checksum_converter_mock: MagicMock) -> PackageConverter: converter = PackageConverter() diff --git a/tests/jsonschema/test_package_verification_code_converter.py b/tests/jsonschema/test_package_verification_code_converter.py index 6d8ac4476..2ee715484 100644 --- a/tests/jsonschema/test_package_verification_code_converter.py +++ b/tests/jsonschema/test_package_verification_code_converter.py @@ -10,9 +10,9 @@ # limitations under the License. import pytest -from src.jsonschema.package_verification_code_converter import PackageVerificationCodeConverter -from src.jsonschema.package_verification_code_properties import PackageVerificationCodeProperty -from src.model.package import PackageVerificationCode +from spdx.jsonschema.package_verification_code_converter import PackageVerificationCodeConverter +from spdx.jsonschema.package_verification_code_properties import PackageVerificationCodeProperty +from spdx.model.package import PackageVerificationCode @pytest.fixture diff --git a/tests/jsonschema/test_relationship_converter.py b/tests/jsonschema/test_relationship_converter.py index d2bcffb5b..98ff88e46 100644 --- a/tests/jsonschema/test_relationship_converter.py +++ b/tests/jsonschema/test_relationship_converter.py @@ -10,11 +10,11 @@ # limitations under the License. import pytest -from src.jsonschema.relationship_converter import RelationshipConverter -from src.jsonschema.relationship_properties import RelationshipProperty -from src.model.relationship import Relationship, RelationshipType -from src.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING -from src.model.spdx_none import SpdxNone, SPDX_NONE_STRING +from spdx.jsonschema.relationship_converter import RelationshipConverter +from spdx.jsonschema.relationship_properties import RelationshipProperty +from spdx.model.relationship import Relationship, RelationshipType +from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING +from spdx.model.spdx_none import SpdxNone, SPDX_NONE_STRING from tests.fixtures import relationship_fixture diff --git a/tests/jsonschema/test_snippet_converter.py b/tests/jsonschema/test_snippet_converter.py index 5549c9a5a..c29ad0806 100644 --- a/tests/jsonschema/test_snippet_converter.py +++ b/tests/jsonschema/test_snippet_converter.py @@ -15,22 +15,22 @@ import pytest -from src.jsonschema.annotation_converter import AnnotationConverter -from src.jsonschema.snippet_converter import SnippetConverter -from src.jsonschema.snippet_properties import SnippetProperty -from src.model.actor import Actor, ActorType -from src.model.annotation import Annotation, AnnotationType -from src.model.document import Document -from src.model.license_expression import LicenseExpression -from src.model.snippet import Snippet -from src.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING -from src.model.spdx_none import SpdxNone, SPDX_NONE_STRING +from spdx.jsonschema.annotation_converter import AnnotationConverter +from spdx.jsonschema.snippet_converter import SnippetConverter +from spdx.jsonschema.snippet_properties import SnippetProperty +from spdx.model.actor import Actor, ActorType +from spdx.model.annotation import Annotation, AnnotationType +from spdx.model.document import Document +from spdx.model.license_expression import LicenseExpression +from spdx.model.snippet import Snippet +from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING +from spdx.model.spdx_none import SpdxNone, SPDX_NONE_STRING from tests.fixtures import creation_info_fixture, snippet_fixture, document_fixture, annotation_fixture from tests.mock_utils import assert_mock_method_called_with_arguments @pytest.fixture -@mock.patch('src.jsonschema.annotation_converter.AnnotationConverter', autospec=True) +@mock.patch('spdx.jsonschema.annotation_converter.AnnotationConverter', autospec=True) def converter(annotation_converter_mock: MagicMock) -> SnippetConverter: converter = SnippetConverter() converter.annotation_converter = annotation_converter_mock() diff --git a/tests/model/test_actor.py b/tests/model/test_actor.py index c39ac744a..9a0ba5a14 100644 --- a/tests/model/test_actor.py +++ b/tests/model/test_actor.py @@ -1,6 +1,6 @@ import pytest -from src.model.actor import Actor, ActorType +from spdx.model.actor import Actor, ActorType def test_correct_initialization(): diff --git a/tests/model/test_annotation.py b/tests/model/test_annotation.py index 5455569bc..c2d357c43 100644 --- a/tests/model/test_annotation.py +++ b/tests/model/test_annotation.py @@ -3,10 +3,10 @@ import pytest -from src.model.annotation import Annotation, AnnotationType +from spdx.model.annotation import Annotation, AnnotationType -@mock.patch('src.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_correct_initialization(actor): annotation = Annotation("id", AnnotationType.OTHER, actor, datetime(2022, 1, 1), "comment") assert annotation.spdx_id == "id" @@ -16,31 +16,31 @@ def test_correct_initialization(actor): assert annotation.annotation_comment == "comment" -@mock.patch('src.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_wrong_type_in_spdx_id(actor): with pytest.raises(TypeError): Annotation(42, AnnotationType.OTHER, actor, datetime(2022, 1, 1), "comment") -@mock.patch('src.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_wrong_type_in_annotation_type(actor): with pytest.raises(TypeError): Annotation("id", 42, actor, datetime(2022, 1, 1), "comment") -@mock.patch('src.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_wrong_type_in_annotator(actor): with pytest.raises(TypeError): Annotation("id", AnnotationType.OTHER, 42, datetime(2022, 1, 1), "comment") -@mock.patch('src.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_wrong_type_in_annotation_date(actor): with pytest.raises(TypeError): Annotation("id", AnnotationType.OTHER, actor, 42, "comment") -@mock.patch('src.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_wrong_type_in_annotation_comment(actor): with pytest.raises(TypeError): Annotation("id", AnnotationType.OTHER, actor, datetime(2022, 1, 1), 42) diff --git a/tests/model/test_checksum.py b/tests/model/test_checksum.py index 41aeaf7b6..8b1c3e43b 100644 --- a/tests/model/test_checksum.py +++ b/tests/model/test_checksum.py @@ -1,6 +1,6 @@ import pytest -from src.model.checksum import Checksum, ChecksumAlgorithm +from spdx.model.checksum import Checksum, ChecksumAlgorithm def test_correct_initialization(): diff --git a/tests/model/test_creation_info.py b/tests/model/test_creation_info.py index a0ba11b9e..68f5f323e 100644 --- a/tests/model/test_creation_info.py +++ b/tests/model/test_creation_info.py @@ -3,12 +3,12 @@ import pytest -from src.model.document import CreationInfo -from src.model.version import Version +from spdx.model.document import CreationInfo +from spdx.model.version import Version -@mock.patch('src.model.actor.Actor', autospec=True) -@mock.patch('src.model.external_document_ref.ExternalDocumentRef', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.external_document_ref.ExternalDocumentRef', autospec=True) def test_correct_initialization(actor, ext_ref): creation_info = CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), "creator_comment", "CC0-1.1", [ext_ref, ext_ref], Version(6, 3), "doc_comment") @@ -25,25 +25,25 @@ def test_correct_initialization(actor, ext_ref): assert creation_info.document_comment == "doc_comment" -@mock.patch('src.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_wrong_type_in_spdx_version(actor): with pytest.raises(TypeError): CreationInfo(42, "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1)) -@mock.patch('src.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_wrong_type_in_spdx_id(actor): with pytest.raises(TypeError): CreationInfo("version", 42, "name", "namespace", [actor, actor], datetime(2022, 1, 1)) -@mock.patch('src.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_wrong_type_in_name(actor): with pytest.raises(TypeError): CreationInfo("version", "id", 42, "namespace", [actor, actor], datetime(2022, 1, 1)) -@mock.patch('src.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_wrong_type_in_document_namespace(actor): with pytest.raises(TypeError): CreationInfo("version", "id", "name", 42, [actor, actor], datetime(2022, 1, 1)) @@ -54,40 +54,40 @@ def test_wrong_type_in_creators(): CreationInfo("version", "id", "name", "namespace", ["person"], datetime(2022, 1, 1)) -@mock.patch('src.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_wrong_type_in_created(actor): with pytest.raises(TypeError): CreationInfo("version", "id", "name", "namespace", [actor, actor], "2022-01-01") -@mock.patch('src.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_wrong_type_in_creator_comment(actor): with pytest.raises(TypeError): CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), creator_comment=["string"]) -@mock.patch('src.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_wrong_type_in_data_license(actor): with pytest.raises(TypeError): CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), data_license=42) -@mock.patch('src.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_wrong_type_in_external_document_refs(actor): with pytest.raises(TypeError): CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), external_document_refs=()) -@mock.patch('src.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_wrong_type_in_license_list_version(actor): with pytest.raises(TypeError): CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), license_list_version="6.4") -@mock.patch('src.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_wrong_type_in_document_comment(actor): with pytest.raises(TypeError): CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), document_comment=["1"]) diff --git a/tests/model/test_document.py b/tests/model/test_document.py index 1a0a92087..a27370db5 100644 --- a/tests/model/test_document.py +++ b/tests/model/test_document.py @@ -2,16 +2,16 @@ import pytest -from src.model.document import Document +from spdx.model.document import Document -@mock.patch('src.model.document.CreationInfo', autospec=True) -@mock.patch('src.model.package.Package', autospec=True) -@mock.patch('src.model.file.File', autospec=True) -@mock.patch('src.model.snippet.Snippet', autospec=True) -@mock.patch('src.model.annotation.Annotation', autospec=True) -@mock.patch('src.model.relationship.Relationship', autospec=True) -@mock.patch('src.model.extracted_licensing_info.ExtractedLicensingInfo', autospec=True) +@mock.patch('spdx.model.document.CreationInfo', autospec=True) +@mock.patch('spdx.model.package.Package', autospec=True) +@mock.patch('spdx.model.file.File', autospec=True) +@mock.patch('spdx.model.snippet.Snippet', autospec=True) +@mock.patch('spdx.model.annotation.Annotation', autospec=True) +@mock.patch('spdx.model.relationship.Relationship', autospec=True) +@mock.patch('spdx.model.extracted_licensing_info.ExtractedLicensingInfo', autospec=True) def test_correct_initialization(creation_info, package, file, snippet, annotation, relationship, extracted_lic): document = Document(creation_info, [package, package], [file, file], [snippet, snippet], [annotation, annotation], @@ -25,7 +25,7 @@ def test_correct_initialization(creation_info, package, file, snippet, annotatio assert document.extracted_licensing_info == [extracted_lic, extracted_lic] -@mock.patch('src.model.document.CreationInfo', autospec=True) +@mock.patch('spdx.model.document.CreationInfo', autospec=True) def test_correct_initialization_with_default_values(creation_info): document = Document(creation_info) assert document.creation_info == creation_info @@ -42,37 +42,37 @@ def test_wrong_type_in_creation_info(): Document("string") -@mock.patch('src.model.document.CreationInfo', autospec=True) +@mock.patch('spdx.model.document.CreationInfo', autospec=True) def test_wrong_type_in_packages(creation_info): with pytest.raises(TypeError): Document(creation_info, packages=["string"]) -@mock.patch('src.model.document.CreationInfo', autospec=True) +@mock.patch('spdx.model.document.CreationInfo', autospec=True) def test_wrong_type_in_files(creation_info): with pytest.raises(TypeError): Document(creation_info, files={}) -@mock.patch('src.model.document.CreationInfo', autospec=True) +@mock.patch('spdx.model.document.CreationInfo', autospec=True) def test_wrong_type_in_snippets(creation_info): with pytest.raises(TypeError): Document(creation_info, snippets=()) -@mock.patch('src.model.document.CreationInfo', autospec=True) +@mock.patch('spdx.model.document.CreationInfo', autospec=True) def test_wrong_type_in_annotations(creation_info): with pytest.raises(TypeError): Document(creation_info, annotations=["string"]) -@mock.patch('src.model.document.CreationInfo', autospec=True) +@mock.patch('spdx.model.document.CreationInfo', autospec=True) def test_wrong_type_in_relationships(creation_info): with pytest.raises(TypeError): Document(creation_info, relationships="string") -@mock.patch('src.model.document.CreationInfo', autospec=True) +@mock.patch('spdx.model.document.CreationInfo', autospec=True) def test_wrong_type_in_extracted_licensing_info(creation_info): with pytest.raises(TypeError): Document(creation_info, extracted_licensing_info=42) diff --git a/tests/model/test_external_document_ref.py b/tests/model/test_external_document_ref.py index fecf4bb93..c210910af 100644 --- a/tests/model/test_external_document_ref.py +++ b/tests/model/test_external_document_ref.py @@ -2,10 +2,10 @@ import pytest -from src.model.external_document_ref import ExternalDocumentRef +from spdx.model.external_document_ref import ExternalDocumentRef -@mock.patch('src.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_correct_initialization(checksum): external_document_ref = ExternalDocumentRef("id", "uri", checksum) assert external_document_ref.document_ref_id == "id" @@ -13,13 +13,13 @@ def test_correct_initialization(checksum): assert external_document_ref.checksum == checksum -@mock.patch('src.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_wrong_type_in_spdx_id(checksum): with pytest.raises(TypeError): ExternalDocumentRef(42, "uri", checksum) -@mock.patch('src.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_wrong_type_in_document_uri(checksum): with pytest.raises(TypeError): ExternalDocumentRef("id", 42, checksum) diff --git a/tests/model/test_external_package_reference.py b/tests/model/test_external_package_reference.py index 52a2c2519..768e56329 100644 --- a/tests/model/test_external_package_reference.py +++ b/tests/model/test_external_package_reference.py @@ -1,6 +1,6 @@ import pytest -from src.model.package import ExternalPackageRef, ExternalPackageRefCategory +from spdx.model.package import ExternalPackageRef, ExternalPackageRefCategory def test_correct_initialization(): diff --git a/tests/model/test_extracted_licensing_info.py b/tests/model/test_extracted_licensing_info.py index 1d83077a3..cefba997a 100644 --- a/tests/model/test_extracted_licensing_info.py +++ b/tests/model/test_extracted_licensing_info.py @@ -1,6 +1,6 @@ import pytest -from src.model.extracted_licensing_info import ExtractedLicensingInfo +from spdx.model.extracted_licensing_info import ExtractedLicensingInfo def test_correct_initialization(): diff --git a/tests/model/test_file.py b/tests/model/test_file.py index 0c8cd3513..40a77d74b 100644 --- a/tests/model/test_file.py +++ b/tests/model/test_file.py @@ -2,13 +2,13 @@ import pytest -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.file import File, FileType -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.model.file import File, FileType +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone -@mock.patch('src.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_correct_initialization(checksum): file = File("name", "id", [checksum, checksum], [FileType.OTHER, FileType.SPDX], SpdxNone(), SpdxNoAssertion(), "comment on license", "copyright", "comment", "notice", ["contributor"], ["attribution"]) @@ -26,7 +26,7 @@ def test_correct_initialization(checksum): assert file.attribution_texts == ["attribution"] -@mock.patch('src.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_correct_initialization_with_default_values(checksum): file = File("name", "id", [checksum, checksum]) assert file.name == "name" @@ -43,13 +43,13 @@ def test_correct_initialization_with_default_values(checksum): assert file.attribution_texts == [] -@mock.patch('src.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_wrong_type_in_name(checksum): with pytest.raises(TypeError): File(42, "id", [checksum]) -@mock.patch('src.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_wrong_type_in_spdx_id(checksum): with pytest.raises(TypeError): File("name", 42, [checksum]) @@ -61,55 +61,55 @@ def test_wrong_type_in_checksum(): File("name", "id", checksum) -@mock.patch('src.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_wrong_type_in_file_type(checksum): with pytest.raises(TypeError): File("name", "id", [checksum], file_type=FileType.OTHER) -@mock.patch('src.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_wrong_type_in_concluded_license(checksum): with pytest.raises(TypeError): File("name", "id", [checksum], concluded_license="NONE") -@mock.patch('src.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_wrong_type_in_license_info_in_file(checksum): with pytest.raises(TypeError): File("name", "id", [checksum], license_info_in_file=[SpdxNone]) -@mock.patch('src.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_wrong_type_in_license_comment(checksum): with pytest.raises(TypeError): File("name", "id", [checksum], license_comment=42) -@mock.patch('src.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_wrong_type_in_copyright_text(checksum): with pytest.raises(TypeError): File("name", "id", [checksum], copyright_text=[SpdxNone()]) -@mock.patch('src.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_wrong_type_in_comment(checksum): with pytest.raises(TypeError): File("name", "id", [checksum], comment=42) -@mock.patch('src.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_wrong_type_in_notice(checksum): with pytest.raises(TypeError): File("name", "id", [checksum], notice=["notice"]) -@mock.patch('src.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_wrong_type_in_contributors(checksum): with pytest.raises(TypeError): File("name", "id", [checksum], contributors="contributor") -@mock.patch('src.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_wrong_type_in_attribution_texts(checksum): with pytest.raises(TypeError): File("name", "id", [checksum], attribution_texts=["attribution", 42]) diff --git a/tests/model/test_license.py b/tests/model/test_license.py index 210ad53a5..69bf7fa88 100644 --- a/tests/model/test_license.py +++ b/tests/model/test_license.py @@ -12,7 +12,7 @@ import pytest -from src.model.license import determine_full_name, determine_identifier +from spdx.model.license import determine_full_name, determine_identifier @pytest.mark.parametrize("identifier,full_name,expected", diff --git a/tests/model/test_package.py b/tests/model/test_package.py index 85f1d7920..bb0e92816 100644 --- a/tests/model/test_package.py +++ b/tests/model/test_package.py @@ -3,17 +3,17 @@ import pytest -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.license_expression import LicenseExpression -from src.model.package import Package, PackagePurpose -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.model.license_expression import LicenseExpression +from spdx.model.package import Package, PackagePurpose +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone -@mock.patch('src.model.actor.Actor', autospec=True) -@mock.patch('src.model.package.PackageVerificationCode', autospec=True) -@mock.patch('src.model.checksum.Checksum', autospec=True) -@mock.patch('src.model.package.ExternalPackageRef', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) +@mock.patch('spdx.model.package.PackageVerificationCode', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.package.ExternalPackageRef', autospec=True) def test_correct_initialization(actor, verif_code, checksum, ext_ref): package = Package("id", "name", SpdxNoAssertion(), "version", "file_name", SpdxNoAssertion(), actor, True, verif_code, [checksum], "homepage", "source_info", None, [LicenseExpression("expression")], diff --git a/tests/model/test_package_verification_code.py b/tests/model/test_package_verification_code.py index 266b7f157..2afe5c434 100644 --- a/tests/model/test_package_verification_code.py +++ b/tests/model/test_package_verification_code.py @@ -1,6 +1,6 @@ import pytest -from src.model.package import PackageVerificationCode +from spdx.model.package import PackageVerificationCode def test_correct_initialization(): diff --git a/tests/model/test_relationship.py b/tests/model/test_relationship.py index c3b99a689..0413729ed 100644 --- a/tests/model/test_relationship.py +++ b/tests/model/test_relationship.py @@ -1,7 +1,7 @@ import pytest -from src.model.relationship import Relationship, RelationshipType -from src.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.relationship import Relationship, RelationshipType +from spdx.model.spdx_no_assertion import SpdxNoAssertion def test_correct_initialization(): diff --git a/tests/model/test_snippet.py b/tests/model/test_snippet.py index 0e7030507..779e0c6dc 100644 --- a/tests/model/test_snippet.py +++ b/tests/model/test_snippet.py @@ -1,8 +1,8 @@ import pytest -from src.model.snippet import Snippet -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone +from spdx.model.snippet import Snippet +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone def test_correct_initialization(): diff --git a/tests/model/test_version.py b/tests/model/test_version.py index 997de23e2..574a5faa2 100644 --- a/tests/model/test_version.py +++ b/tests/model/test_version.py @@ -12,7 +12,7 @@ import pytest -from src.model.version import Version +from spdx.model.version import Version @pytest.mark.parametrize("input_string,expected", [("1.2", Version(1, 2)), ("12.345", Version(12, 345))]) diff --git a/tests/parser/json/test_json_parser.py b/tests/parser/json/test_json_parser.py index 80a7de0ab..3acbfa14e 100644 --- a/tests/parser/json/test_json_parser.py +++ b/tests/parser/json/test_json_parser.py @@ -12,8 +12,8 @@ import os import pytest -from src.model.document import Document -from src.parser.json import json_parser +from spdx.model.document import Document +from spdx.parser.json import json_parser def test_parse_json_file_not_found(): with pytest.raises(FileNotFoundError) as err: diff --git a/tests/parser/jsonlikedict/test_actor_parser.py b/tests/parser/jsonlikedict/test_actor_parser.py index 93429e42c..3938d9536 100644 --- a/tests/parser/jsonlikedict/test_actor_parser.py +++ b/tests/parser/jsonlikedict/test_actor_parser.py @@ -11,9 +11,9 @@ import pytest from unittest import TestCase -from src.model.actor import ActorType -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.actor_parser import ActorParser +from spdx.model.actor import ActorType +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.actor_parser import ActorParser @pytest.mark.parametrize("actor_string,expected_type,expected_name,expected_mail", [ diff --git a/tests/parser/jsonlikedict/test_annotation_parser.py b/tests/parser/jsonlikedict/test_annotation_parser.py index 1ffd819f8..7bf6b66f8 100644 --- a/tests/parser/jsonlikedict/test_annotation_parser.py +++ b/tests/parser/jsonlikedict/test_annotation_parser.py @@ -13,10 +13,10 @@ import pytest -from src.model.actor import Actor, ActorType -from src.model.annotation import AnnotationType, Annotation -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.annotation_parser import AnnotationParser +from spdx.model.actor import Actor, ActorType +from spdx.model.annotation import AnnotationType, Annotation +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.annotation_parser import AnnotationParser def test_parse_annotation(): @@ -105,8 +105,8 @@ def test_parse_all_annotations(): @pytest.mark.parametrize("incomplete_annotation_dict,expected_message", [({"annotator": "Person: Jane Doe ()"}, [ - "Error while constructing Annotation: ['SetterError Annotation: type of " 'argument "spdx_id" must be str; got NoneType instead: None\', \'SetterError Annotation: type of argument "annotation_type" must be ' "src.model.annotation.AnnotationType; got NoneType instead: None', " '\'SetterError Annotation: type of argument "annotation_date" must be ' "datetime.datetime; got NoneType instead: None', 'SetterError Annotation: " 'type of argument "annotation_comment" must be str; got NoneType instead: ' "None']"]), - ({"annotationDate": "2010-01-29T18:30:22Z"}, ["Error while constructing Annotation: ['SetterError Annotation: type of " 'argument "spdx_id" must be str; got NoneType instead: None\', \'SetterError Annotation: type of argument "annotation_type" must be ' "src.model.annotation.AnnotationType; got NoneType instead: None', " '\'SetterError Annotation: type of argument "annotator" must be ' "src.model.actor.Actor; got NoneType instead: None', 'SetterError Annotation: " 'type of argument "annotation_comment" must be str; got NoneType instead: ' "None']"])]) + "Error while constructing Annotation: ['SetterError Annotation: type of " 'argument "spdx_id" must be str; got NoneType instead: None\', \'SetterError Annotation: type of argument "annotation_type" must be ' "spdx.model.annotation.AnnotationType; got NoneType instead: None', " '\'SetterError Annotation: type of argument "annotation_date" must be ' "datetime.datetime; got NoneType instead: None', 'SetterError Annotation: " 'type of argument "annotation_comment" must be str; got NoneType instead: ' "None']"]), + ({"annotationDate": "2010-01-29T18:30:22Z"}, ["Error while constructing Annotation: ['SetterError Annotation: type of " 'argument "spdx_id" must be str; got NoneType instead: None\', \'SetterError Annotation: type of argument "annotation_type" must be ' "spdx.model.annotation.AnnotationType; got NoneType instead: None', " '\'SetterError Annotation: type of argument "annotator" must be ' "spdx.model.actor.Actor; got NoneType instead: None', 'SetterError Annotation: " 'type of argument "annotation_comment" must be str; got NoneType instead: ' "None']"])]) def test_parse_incomplete_annotation(incomplete_annotation_dict, expected_message): annotation_parser = AnnotationParser() diff --git a/tests/parser/jsonlikedict/test_checksum_parser.py b/tests/parser/jsonlikedict/test_checksum_parser.py index 86d4b6d5d..a60249787 100644 --- a/tests/parser/jsonlikedict/test_checksum_parser.py +++ b/tests/parser/jsonlikedict/test_checksum_parser.py @@ -12,9 +12,9 @@ import pytest -from src.model.checksum import ChecksumAlgorithm -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.checksum_parser import ChecksumParser +from spdx.model.checksum import ChecksumAlgorithm +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.checksum_parser import ChecksumParser def test_parse_checksum(): diff --git a/tests/parser/jsonlikedict/test_creation_info_parser.py b/tests/parser/jsonlikedict/test_creation_info_parser.py index 0193c1938..a45f204b6 100644 --- a/tests/parser/jsonlikedict/test_creation_info_parser.py +++ b/tests/parser/jsonlikedict/test_creation_info_parser.py @@ -13,12 +13,12 @@ import pytest -from src.model.actor import Actor, ActorType -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.external_document_ref import ExternalDocumentRef -from src.model.version import Version -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.creation_info_parser import CreationInfoParser +from spdx.model.actor import Actor, ActorType +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.model.external_document_ref import ExternalDocumentRef +from spdx.model.version import Version +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.creation_info_parser import CreationInfoParser def test_parse_creation_info(): diff --git a/tests/parser/jsonlikedict/test_dict_parsing_functions.py b/tests/parser/jsonlikedict/test_dict_parsing_functions.py index 8d9f80d32..30232e6ae 100644 --- a/tests/parser/jsonlikedict/test_dict_parsing_functions.py +++ b/tests/parser/jsonlikedict/test_dict_parsing_functions.py @@ -13,12 +13,12 @@ import pytest -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.dict_parsing_functions import json_str_to_enum_name, \ +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.dict_parsing_functions import json_str_to_enum_name, \ parse_field_or_no_assertion, parse_field_or_no_assertion_or_none -from src.datetime_conversions import datetime_from_str +from spdx.datetime_conversions import datetime_from_str def test_json_str_to_enum(): diff --git a/tests/parser/jsonlikedict/test_extracted_licensing_info_parser.py b/tests/parser/jsonlikedict/test_extracted_licensing_info_parser.py index 848576d37..d73ed5c0e 100644 --- a/tests/parser/jsonlikedict/test_extracted_licensing_info_parser.py +++ b/tests/parser/jsonlikedict/test_extracted_licensing_info_parser.py @@ -12,8 +12,8 @@ import pytest -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.extracted_licensing_info_parser import ExtractedLicensingInfoParser +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.extracted_licensing_info_parser import ExtractedLicensingInfoParser def test_parse_extracted_licensing_info(): diff --git a/tests/parser/jsonlikedict/test_file_parser.py b/tests/parser/jsonlikedict/test_file_parser.py index bf26e29e8..ea8eabed9 100644 --- a/tests/parser/jsonlikedict/test_file_parser.py +++ b/tests/parser/jsonlikedict/test_file_parser.py @@ -12,12 +12,12 @@ import pytest -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.file import FileType -from src.model.license_expression import LicenseExpression -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.dict_parsing_functions import parse_list_of_elements -from src.parser.jsonlikedict.file_parser import FileParser +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.model.file import FileType +from spdx.model.license_expression import LicenseExpression +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.dict_parsing_functions import parse_list_of_elements +from spdx.parser.jsonlikedict.file_parser import FileParser def test_parse_file(): diff --git a/tests/parser/jsonlikedict/test_license_expression_parser.py b/tests/parser/jsonlikedict/test_license_expression_parser.py index 7e30e2ab2..b21583426 100644 --- a/tests/parser/jsonlikedict/test_license_expression_parser.py +++ b/tests/parser/jsonlikedict/test_license_expression_parser.py @@ -12,9 +12,9 @@ import pytest -from src.model.license_expression import LicenseExpression -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser +from spdx.model.license_expression import LicenseExpression +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser @pytest.mark.parametrize("invalid_license_expression,expected_message", diff --git a/tests/parser/jsonlikedict/test_package_parser.py b/tests/parser/jsonlikedict/test_package_parser.py index 5d04331e3..6839af812 100644 --- a/tests/parser/jsonlikedict/test_package_parser.py +++ b/tests/parser/jsonlikedict/test_package_parser.py @@ -13,13 +13,13 @@ import pytest -from src.model.actor import Actor, ActorType -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.license_expression import LicenseExpression -from src.model.package import PackageVerificationCode, ExternalPackageRef, ExternalPackageRefCategory, PackagePurpose -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.dict_parsing_functions import parse_list_of_elements -from src.parser.jsonlikedict.package_parser import PackageParser +from spdx.model.actor import Actor, ActorType +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.model.license_expression import LicenseExpression +from spdx.model.package import PackageVerificationCode, ExternalPackageRef, ExternalPackageRefCategory, PackagePurpose +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.dict_parsing_functions import parse_list_of_elements +from spdx.parser.jsonlikedict.package_parser import PackageParser def test_parse_package(): @@ -128,7 +128,7 @@ def test_parse_package(): @pytest.mark.parametrize("incomplete_package_dict,expected_message", [({"SPDXID": "SPDXRef-Package"}, [ - "Error while constructing Package: ['SetterError Package: type of " 'argument "name" must be str; got NoneType instead: None\', \'SetterError Package: type of argument "download_location" must be one of (str, src.model.spdx_no_assertion.SpdxNoAssertion, src.model.spdx_none.SpdxNone); ' "got NoneType instead: None']"]), + "Error while constructing Package: ['SetterError Package: type of " 'argument "name" must be str; got NoneType instead: None\', \'SetterError Package: type of argument "download_location" must be one of (str, spdx.model.spdx_no_assertion.SpdxNoAssertion, spdx.model.spdx_none.SpdxNone); ' "got NoneType instead: None']"]), ({"SPDXID": "SPDXRef-Package", "name": 5, "downloadLocation": "NONE"}, [ "Error while constructing Package: ['SetterError Package: type of argument " '"name" must be str; got int instead: 5\']'])]) @@ -199,7 +199,7 @@ def test_parse_external_ref(): package_parser.parse_external_ref(external_ref) TestCase().assertCountEqual(err.value.get_messages(), [ - "Error while constructing ExternalPackageRef: ['SetterError " 'ExternalPackageRef: type of argument "category" must be ' "src.model.package.ExternalPackageRefCategory; got NoneType instead: None', " '\'SetterError ExternalPackageRef: type of argument "locator" must be str; ' "got NoneType instead: None']"]) + "Error while constructing ExternalPackageRef: ['SetterError " 'ExternalPackageRef: type of argument "category" must be ' "spdx.model.package.ExternalPackageRefCategory; got NoneType instead: None', " '\'SetterError ExternalPackageRef: type of argument "locator" must be str; ' "got NoneType instead: None']"]) def test_parse_invalid_external_package_ref_category(): package_parser = PackageParser() diff --git a/tests/parser/jsonlikedict/test_relationship_parser.py b/tests/parser/jsonlikedict/test_relationship_parser.py index 83bdd212e..20dc39a42 100644 --- a/tests/parser/jsonlikedict/test_relationship_parser.py +++ b/tests/parser/jsonlikedict/test_relationship_parser.py @@ -12,10 +12,10 @@ import pytest -from src.model.relationship import RelationshipType, Relationship -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.relationship_parser import RelationshipParser +from spdx.model.relationship import RelationshipType, Relationship +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.relationship_parser import RelationshipParser def test_parse_relationship(): @@ -48,7 +48,7 @@ def test_parse_incomplete_relationship(): relationship_parser.parse_relationship(relationship_dict) TestCase().assertCountEqual(err.value.get_messages(), [ - "Error while constructing Relationship: ['SetterError Relationship: type of " 'argument "relationship_type" must be ' "src.model.relationship.RelationshipType; got NoneType instead: None']"]) + "Error while constructing Relationship: ['SetterError Relationship: type of " 'argument "relationship_type" must be ' "spdx.model.relationship.RelationshipType; got NoneType instead: None']"]) def test_parse_relationship_type(): diff --git a/tests/parser/jsonlikedict/test_snippet_parser.py b/tests/parser/jsonlikedict/test_snippet_parser.py index 91d1d446c..e9108eed7 100644 --- a/tests/parser/jsonlikedict/test_snippet_parser.py +++ b/tests/parser/jsonlikedict/test_snippet_parser.py @@ -12,9 +12,9 @@ import pytest -from src.model.license_expression import LicenseExpression -from src.parser.error import SPDXParsingError -from src.parser.jsonlikedict.snippet_parser import SnippetParser +from spdx.model.license_expression import LicenseExpression +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.snippet_parser import SnippetParser def test_parse_snippet(): diff --git a/tests/test_datetime_conversions.py b/tests/test_datetime_conversions.py index f015a4cd6..265054c0b 100644 --- a/tests/test_datetime_conversions.py +++ b/tests/test_datetime_conversions.py @@ -12,7 +12,7 @@ import pytest -from src.datetime_conversions import datetime_from_str, datetime_to_iso_string +from spdx.datetime_conversions import datetime_from_str, datetime_to_iso_string def test_datetime_to_iso_string(): diff --git a/tests/validation/test_actor_validator.py b/tests/validation/test_actor_validator.py index 417755236..f7f7e22da 100644 --- a/tests/validation/test_actor_validator.py +++ b/tests/validation/test_actor_validator.py @@ -13,9 +13,9 @@ import pytest -from src.model.actor import ActorType -from src.validation.actor_validator import validate_actor -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.model.actor import ActorType +from spdx.validation.actor_validator import validate_actor +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.fixtures import actor_fixture diff --git a/tests/validation/test_annotation_validator.py b/tests/validation/test_annotation_validator.py index 9081fe260..b6a624855 100644 --- a/tests/validation/test_annotation_validator.py +++ b/tests/validation/test_annotation_validator.py @@ -13,10 +13,10 @@ import pytest -from src.model.annotation import Annotation -from src.model.document import Document -from src.validation.annotation_validator import validate_annotation -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.model.annotation import Annotation +from spdx.model.document import Document +from spdx.validation.annotation_validator import validate_annotation +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.fixtures import document_fixture, annotation_fixture, file_fixture diff --git a/tests/validation/test_checksum_validator.py b/tests/validation/test_checksum_validator.py index 72ecb0cd9..c57e7b022 100644 --- a/tests/validation/test_checksum_validator.py +++ b/tests/validation/test_checksum_validator.py @@ -13,9 +13,9 @@ import pytest -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.validation.checksum_validator import validate_checksum -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.validation.checksum_validator import validate_checksum +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.fixtures import checksum_fixture diff --git a/tests/validation/test_creation_info_validator.py b/tests/validation/test_creation_info_validator.py index 93b93c512..323252f97 100644 --- a/tests/validation/test_creation_info_validator.py +++ b/tests/validation/test_creation_info_validator.py @@ -13,8 +13,8 @@ import pytest -from src.validation.creation_info_validator import validate_creation_info -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.validation.creation_info_validator import validate_creation_info +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.fixtures import creation_info_fixture diff --git a/tests/validation/test_document_validator.py b/tests/validation/test_document_validator.py index 0c615255b..6156f4ca4 100644 --- a/tests/validation/test_document_validator.py +++ b/tests/validation/test_document_validator.py @@ -13,9 +13,9 @@ import pytest -from src.model.document import Document, CreationInfo -from src.validation.document_validator import validate_full_spdx_document -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.model.document import Document, CreationInfo +from spdx.validation.document_validator import validate_full_spdx_document +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.fixtures import document_fixture, creation_info_fixture diff --git a/tests/validation/test_external_document_ref_validator.py b/tests/validation/test_external_document_ref_validator.py index a94bd1d17..870e80f06 100644 --- a/tests/validation/test_external_document_ref_validator.py +++ b/tests/validation/test_external_document_ref_validator.py @@ -11,8 +11,8 @@ from typing import List -from src.validation.external_document_ref_validator import validate_external_document_ref -from src.validation.validation_message import ValidationMessage +from spdx.validation.external_document_ref_validator import validate_external_document_ref +from spdx.validation.validation_message import ValidationMessage from tests.fixtures import external_document_ref_fixture diff --git a/tests/validation/test_external_package_ref_validator.py b/tests/validation/test_external_package_ref_validator.py index 4430e96f5..f347f9a7c 100644 --- a/tests/validation/test_external_package_ref_validator.py +++ b/tests/validation/test_external_package_ref_validator.py @@ -13,8 +13,8 @@ import pytest -from src.validation.external_package_ref_validator import validate_external_package_ref -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.validation.external_package_ref_validator import validate_external_package_ref +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.fixtures import external_package_ref_fixture diff --git a/tests/validation/test_extracted_licensing_info_validator.py b/tests/validation/test_extracted_licensing_info_validator.py index c2e557495..2017c151c 100644 --- a/tests/validation/test_extracted_licensing_info_validator.py +++ b/tests/validation/test_extracted_licensing_info_validator.py @@ -13,8 +13,8 @@ import pytest -from src.validation.extracted_licensing_info_validator import validate_extracted_licensing_info -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.validation.extracted_licensing_info_validator import validate_extracted_licensing_info +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.fixtures import extracted_licensing_info_fixture diff --git a/tests/validation/test_file_validator.py b/tests/validation/test_file_validator.py index 5aaa95350..433fd1145 100644 --- a/tests/validation/test_file_validator.py +++ b/tests/validation/test_file_validator.py @@ -13,9 +13,9 @@ import pytest -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.validation.file_validator import validate_file_within_document -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.validation.file_validator import validate_file_within_document +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.fixtures import file_fixture, document_fixture diff --git a/tests/validation/test_license_expression_validator.py b/tests/validation/test_license_expression_validator.py index 6203f5688..ac13a537a 100644 --- a/tests/validation/test_license_expression_validator.py +++ b/tests/validation/test_license_expression_validator.py @@ -11,9 +11,9 @@ from typing import List -from src.model.license_expression import LicenseExpression -from src.validation.license_expression_validator import validate_license_expression -from src.validation.validation_message import ValidationMessage +from spdx.model.license_expression import LicenseExpression +from spdx.validation.license_expression_validator import validate_license_expression +from spdx.validation.validation_message import ValidationMessage def test_valid_license_expression(): diff --git a/tests/validation/test_package_validator.py b/tests/validation/test_package_validator.py index b54941c50..929f17bcc 100644 --- a/tests/validation/test_package_validator.py +++ b/tests/validation/test_package_validator.py @@ -13,11 +13,11 @@ import pytest -from src.model.license_expression import LicenseExpression -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone -from src.validation.package_validator import validate_package_within_document -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.model.license_expression import LicenseExpression +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone +from spdx.validation.package_validator import validate_package_within_document +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.fixtures import package_fixture, package_verification_code_fixture, document_fixture diff --git a/tests/validation/test_relationship_validator.py b/tests/validation/test_relationship_validator.py index c9f4e84d3..684334b7f 100644 --- a/tests/validation/test_relationship_validator.py +++ b/tests/validation/test_relationship_validator.py @@ -13,12 +13,12 @@ import pytest -from src.model.document import Document -from src.model.relationship import Relationship, RelationshipType -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone -from src.validation.relationship_validator import validate_relationship -from src.validation.validation_message import ValidationMessage, SpdxElementType, ValidationContext +from spdx.model.document import Document +from spdx.model.relationship import Relationship, RelationshipType +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone +from spdx.validation.relationship_validator import validate_relationship +from spdx.validation.validation_message import ValidationMessage, SpdxElementType, ValidationContext from tests.fixtures import document_fixture, relationship_fixture diff --git a/tests/validation/test_snippet_validator.py b/tests/validation/test_snippet_validator.py index 3add700a0..46c72ae0d 100644 --- a/tests/validation/test_snippet_validator.py +++ b/tests/validation/test_snippet_validator.py @@ -13,8 +13,8 @@ import pytest -from src.validation.snippet_validator import validate_snippet_within_document -from src.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from spdx.validation.snippet_validator import validate_snippet_within_document +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.fixtures import document_fixture, snippet_fixture diff --git a/tests/validation/test_uri_validators.py b/tests/validation/test_uri_validators.py index 552fbf6c0..5eac56145 100644 --- a/tests/validation/test_uri_validators.py +++ b/tests/validation/test_uri_validators.py @@ -11,7 +11,7 @@ import pytest -from src.validation.uri_validators import validate_url, validate_download_location, validate_uri +from spdx.validation.uri_validators import validate_url, validate_download_location, validate_uri @pytest.mark.parametrize("input_value", ["https://some.url", diff --git a/tests/writer/json/test_json_writer.py b/tests/writer/json/test_json_writer.py index 9b2a11747..8d8da5875 100644 --- a/tests/writer/json/test_json_writer.py +++ b/tests/writer/json/test_json_writer.py @@ -14,18 +14,18 @@ import pytest -from src.model.actor import Actor, ActorType -from src.model.annotation import Annotation, AnnotationType -from src.model.checksum import ChecksumAlgorithm, Checksum -from src.model.document import CreationInfo, Document -from src.model.external_document_ref import ExternalDocumentRef -from src.model.extracted_licensing_info import ExtractedLicensingInfo -from src.model.file import File -from src.model.package import Package -from src.model.relationship import RelationshipType, Relationship -from src.model.snippet import Snippet -from src.model.spdx_none import SpdxNone -from src.writer.json.json_writer import write_document +from spdx.model.actor import Actor, ActorType +from spdx.model.annotation import Annotation, AnnotationType +from spdx.model.checksum import ChecksumAlgorithm, Checksum +from spdx.model.document import CreationInfo, Document +from spdx.model.external_document_ref import ExternalDocumentRef +from spdx.model.extracted_licensing_info import ExtractedLicensingInfo +from spdx.model.file import File +from spdx.model.package import Package +from spdx.model.relationship import RelationshipType, Relationship +from spdx.model.snippet import Snippet +from spdx.model.spdx_none import SpdxNone +from spdx.writer.json.json_writer import write_document from tests.fixtures import document_fixture diff --git a/tests/writer/tagvalue/test_package_writer.py b/tests/writer/tagvalue/test_package_writer.py index 735a983dd..b00f5fc78 100644 --- a/tests/writer/tagvalue/test_package_writer.py +++ b/tests/writer/tagvalue/test_package_writer.py @@ -11,14 +11,14 @@ from datetime import datetime from unittest.mock import patch, mock_open, call -from src.model.actor import ActorType, Actor -from src.model.checksum import Checksum, ChecksumAlgorithm -from src.model.license_expression import LicenseExpression -from src.model.package import PackagePurpose, Package, PackageVerificationCode, ExternalPackageRef, \ +from spdx.model.actor import ActorType, Actor +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.model.license_expression import LicenseExpression +from spdx.model.package import PackagePurpose, Package, PackageVerificationCode, ExternalPackageRef, \ ExternalPackageRefCategory -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone -from src.writer.tagvalue.package_writer import write_package +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone +from spdx.writer.tagvalue.package_writer import write_package def test_package_writer(): diff --git a/tests/writer/tagvalue/test_tagvalue_writer.py b/tests/writer/tagvalue/test_tagvalue_writer.py index d7a1a9968..1d4277f11 100644 --- a/tests/writer/tagvalue/test_tagvalue_writer.py +++ b/tests/writer/tagvalue/test_tagvalue_writer.py @@ -14,18 +14,18 @@ import pytest -from src.model.actor import Actor, ActorType -from src.model.annotation import Annotation, AnnotationType -from src.model.checksum import ChecksumAlgorithm, Checksum -from src.model.document import CreationInfo, Document -from src.model.external_document_ref import ExternalDocumentRef -from src.model.extracted_licensing_info import ExtractedLicensingInfo -from src.model.file import File -from src.model.package import Package -from src.model.relationship import RelationshipType, Relationship -from src.model.snippet import Snippet -from src.model.spdx_none import SpdxNone -from src.writer.tagvalue.tagvalue_writer import write_document_to_file +from spdx.model.actor import Actor, ActorType +from spdx.model.annotation import Annotation, AnnotationType +from spdx.model.checksum import ChecksumAlgorithm, Checksum +from spdx.model.document import CreationInfo, Document +from spdx.model.external_document_ref import ExternalDocumentRef +from spdx.model.extracted_licensing_info import ExtractedLicensingInfo +from spdx.model.file import File +from spdx.model.package import Package +from spdx.model.relationship import RelationshipType, Relationship +from spdx.model.snippet import Snippet +from spdx.model.spdx_none import SpdxNone +from spdx.writer.tagvalue.tagvalue_writer import write_document_to_file @pytest.fixture From 7f3cd5e2cd5f1c83241d94515f1e0214c2d2ac1d Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 9 Jan 2023 14:48:19 +0100 Subject: [PATCH 143/362] [issue-415] run cli tool in GitHub action Signed-off-by: Meret Behrens --- .github/workflows/install_and_test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/install_and_test.yml b/.github/workflows/install_and_test.yml index 14842b008..eb3539380 100644 --- a/.github/workflows/install_and_test.yml +++ b/.github/workflows/install_and_test.yml @@ -26,3 +26,5 @@ jobs: shell: bash - name: Run tests run: pytest + - name: Run CLI + run: pyspdxtools -i ./tests/data/formats/SPDXJSONExample-v2.3.spdx.json From 67774675e6adcfe73996a1ebd4290ecd3e83cf6d Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 10 Jan 2023 10:38:06 +0100 Subject: [PATCH 144/362] add dummy config.yml to pass pipeline Signed-off-by: Meret Behrens --- .circleci/config.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000..7bca2704d --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,15 @@ +# Empty Circle CI configuration file to make pipeline pass + +version: 2.1 + +jobs: + empty-job: + docker: + - image: python:3.11 + steps: + - run: echo "Empty Job to make CircleCI green, we switched to https://github.com/spdx/tools-python/actions" + +workflows: + simple-workflow: + jobs: + - empty-job From 904782f66acc0efbf8876eabaac414bbc61df05f Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 9 Jan 2023 16:34:59 +0100 Subject: [PATCH 145/362] [issue-397] use fixtures in writer tests and enable validation in test_write_json Signed-off-by: Meret Behrens --- .../json/expected_results/expected.json | 200 ++++++++++++------ tests/writer/json/test_json_writer.py | 37 +--- tests/writer/tagvalue/test_package_writer.py | 65 +++--- tests/writer/tagvalue/test_tagvalue_writer.py | 34 +-- 4 files changed, 167 insertions(+), 169 deletions(-) diff --git a/tests/writer/json/expected_results/expected.json b/tests/writer/json/expected_results/expected.json index df1402ec1..ee4b8e91a 100644 --- a/tests/writer/json/expected_results/expected.json +++ b/tests/writer/json/expected_results/expected.json @@ -1,101 +1,175 @@ { - "SPDXID": "documentId", - "annotations": [ - { - "annotationDate": "2022-12-02T00:00:00Z", - "annotationType": "REVIEW", - "annotator": "Person: reviewerName", - "comment": "reviewComment" - } - ], - "comment": "comment", + "SPDXID": "SPDXRef-DOCUMENT", + "comment": "documentComment", "creationInfo": { + "comment": "creatorComment", "created": "2022-12-01T00:00:00Z", "creators": [ - "Tool: tools-python (tools-python@github.com)" - ] + "Person: creatorName (some@mail.com)" + ], + "licenseListVersion": "3.19" }, - "dataLicense": "dataLicense", + "dataLicense": "CC0-1.0", + "documentDescribes": [ + "SPDXRef-File" + ], + "documentNamespace": "https://some.namespace", "externalDocumentRefs": [ { - "externalDocumentId": "docRefId", - "spdxDocument": "externalDocumentUri", "checksum": { "algorithm": "SHA1", - "checksumValue": "externalRefSha1" - } + "checksumValue": "71c4025dd9897b364f3ebbb42c484ff43d00791c" + }, + "externalDocumentId": "DocumentRef-external", + "spdxDocument": "https://namespace.com" } ], - "hasExtractedLicensingInfos": [ + "files": [ { - "extractedText": "licenseText", - "licenseId": "licenseId" + "SPDXID": "SPDXRef-File", + "annotations": [ + { + "annotationDate": "2022-12-01T00:00:00Z", + "annotationType": "REVIEW", + "annotator": "Person: annotatorName (some@mail.com)", + "comment": "annotationComment" + } + ], + "attributionTexts": [ + "fileAttributionText" + ], + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "71c4025dd9897b364f3ebbb42c484ff43d00791c" + } + ], + "comment": "fileComment", + "copyrightText": "copyrightText", + "fileContributors": [ + "fileContributor" + ], + "fileName": "./fileName.py", + "fileTypes": [ + "TEXT" + ], + "licenseComments": "licenseComment", + "licenseConcluded": "concludedLicenseExpression", + "licenseInfoInFiles": [ + "licenseInfoInFileExpression" + ], + "noticeText": "fileNotice" } ], - "name": "documentName", - "spdxVersion": "spdxVersion", - "documentNamespace": "documentNamespace", - "documentDescribes": [ - "packageId", - "fileId" - ], - "packages": [ + "hasExtractedLicensingInfos": [ { - "SPDXID": "packageId", - "downloadLocation": "NONE", - "filesAnalyzed": true, - "name": "packageName" + "comment": "licenseComment", + "extractedText": "extractedText", + "licenseId": "LicenseRef-1", + "name": "licenseName", + "seeAlsos": [ + "https://see.also" + ] } ], - "files": [ + "name": "documentName", + "packages": [ { - "SPDXID": "fileId", - "annotations": [ - { - "annotationDate": "2022-12-03T00:00:00Z", - "annotationType": "OTHER", - "annotator": "Tool: toolName", - "comment": "otherComment" - } + "SPDXID": "SPDXRef-Package", + "attributionTexts": [ + "packageAttributionText" ], + "builtDate": "2022-12-02T00:00:00Z", "checksums": [ { "algorithm": "SHA1", - "checksumValue": "fileSha1" + "checksumValue": "71c4025dd9897b364f3ebbb42c484ff43d00791c" + } + ], + "comment": "packageComment", + "copyrightText": "packageCopyrightText", + "description": "packageDescription", + "downloadLocation": "https://download.com", + "externalRefs": [ + { + "comment": "externalPackageRefComment", + "referenceCategory": "PACKAGE_MANAGER", + "referenceLocator": "org.apache.tomcat:tomcat:9.0.0.M4", + "referenceType": "maven-central" } ], - "fileName": "fileName" + "filesAnalyzed": true, + "homepage": "https://homepage.com", + "licenseComments": "packageLicenseComment", + "licenseConcluded": "packageLicenseConcluded", + "licenseDeclared": "packageLicenseDeclared", + "licenseInfoFromFiles": [ + "licenseInfoFromFile" + ], + "name": "packageName", + "originator": "Person: originatorName (some@mail.com)", + "packageFileName": "./packageFileName", + "packageVerificationCode": { + "packageVerificationCodeExcludedFiles": [ + "./exclude.py" + ], + "packageVerificationCodeValue": "85ed0817af83a24ad8da68c2b5094de69833983c" + }, + "primaryPackagePurpose": "SOURCE", + "releaseDate": "2022-12-01T00:00:00Z", + "sourceInfo": "sourceInfo", + "summary": "packageSummary", + "supplier": "Person: supplierName (some@mail.com)", + "validUntilDate": "2022-12-03T00:00:00Z", + "versionInfo": "12.2" + } + ], + "relationships": [ + { + "comment": "relationshipComment", + "relatedSpdxElement": "SPDXRef-File", + "relationshipType": "DESCRIBES", + "spdxElementId": "SPDXRef-DOCUMENT" } ], "snippets": [ { - "SPDXID": "snippetId", + "SPDXID": "SPDXRef-Snippet", + "attributionTexts": [ + "snippetAttributionText" + ], + "comment": "snippetComment", + "copyrightText": "licenseCopyrightText", + "licenseComments": "snippetLicenseComment", + "licenseConcluded": "snippetLicenseConcluded", + "licenseInfoInSnippets": [ + "licenseInfoInSnippet" + ], + "name": "snippetName", "ranges": [ { - "startPointer": { - "reference": "snippetFileId", - "offset": 1 + "endPointer": { + "offset": 2, + "reference": "SPDXRef-File" }, + "startPointer": { + "offset": 1, + "reference": "SPDXRef-File" + } + }, + { "endPointer": { - "reference": "snippetFileId", - "offset": 2 + "lineNumber": 4, + "reference": "SPDXRef-File" + }, + "startPointer": { + "lineNumber": 3, + "reference": "SPDXRef-File" } } ], - "snippetFromFile": "snippetFileId" + "snippetFromFile": "SPDXRef-File" } ], - "relationships": [ - { - "spdxElementId": "documentId", - "comment": "relationshipComment", - "relatedSpdxElement": "fileId", - "relationshipType": "DESCRIBES" - }, - { - "spdxElementId": "relationshipOriginId", - "relatedSpdxElement": "relationShipTargetId", - "relationshipType": "AMENDS" - } - ] + "spdxVersion": "SPDX-2.3" } diff --git a/tests/writer/json/test_json_writer.py b/tests/writer/json/test_json_writer.py index 8d8da5875..280e0fa4e 100644 --- a/tests/writer/json/test_json_writer.py +++ b/tests/writer/json/test_json_writer.py @@ -10,21 +10,9 @@ # limitations under the License. import json import os -from datetime import datetime import pytest -from spdx.model.actor import Actor, ActorType -from spdx.model.annotation import Annotation, AnnotationType -from spdx.model.checksum import ChecksumAlgorithm, Checksum -from spdx.model.document import CreationInfo, Document -from spdx.model.external_document_ref import ExternalDocumentRef -from spdx.model.extracted_licensing_info import ExtractedLicensingInfo -from spdx.model.file import File -from spdx.model.package import Package -from spdx.model.relationship import RelationshipType, Relationship -from spdx.model.snippet import Snippet -from spdx.model.spdx_none import SpdxNone from spdx.writer.json.json_writer import write_document from tests.fixtures import document_fixture @@ -37,29 +25,8 @@ def temporary_file_path() -> str: def test_write_json(temporary_file_path: str): - creation_info = CreationInfo("spdxVersion", "documentId", "documentName", "documentNamespace", - [Actor(ActorType.TOOL, "tools-python", "tools-python@github.com")], - datetime(2022, 12, 1), document_comment="comment", data_license="dataLicense", - external_document_refs=[ExternalDocumentRef("docRefId", "externalDocumentUri", - Checksum(ChecksumAlgorithm.SHA1, - "externalRefSha1"))]) - package = Package("packageId", "packageName", SpdxNone()) - file = File("fileName", "fileId", [Checksum(ChecksumAlgorithm.SHA1, "fileSha1")]) - snippet = Snippet("snippetId", "snippetFileId", (1, 2)) - relationships = [ - Relationship(creation_info.spdx_id, RelationshipType.DESCRIBES, "packageId"), - Relationship(creation_info.spdx_id, RelationshipType.DESCRIBES, "fileId", "relationshipComment"), - Relationship("relationshipOriginId", RelationshipType.AMENDS, "relationShipTargetId")] - annotations = [ - Annotation("documentId", AnnotationType.REVIEW, Actor(ActorType.PERSON, "reviewerName"), - datetime(2022, 12, 2), "reviewComment"), - Annotation("fileId", AnnotationType.OTHER, Actor(ActorType.TOOL, "toolName"), datetime(2022, 12, 3), - "otherComment")] - extracted_licensing_info = [ExtractedLicensingInfo("licenseId", "licenseText")] - document = Document(creation_info, annotations=annotations, extracted_licensing_info=extracted_licensing_info, - relationships=relationships, packages=[package], files=[file], snippets=[snippet]) - # TODO: Enable validation once test data is valid, https://github.com/spdx/tools-python/issues/397 - write_document(document, temporary_file_path, validate=False) + document = document_fixture() + write_document(document, temporary_file_path, validate=True) with open(temporary_file_path) as written_file: written_json = json.load(written_file) diff --git a/tests/writer/tagvalue/test_package_writer.py b/tests/writer/tagvalue/test_package_writer.py index b00f5fc78..47ec20297 100644 --- a/tests/writer/tagvalue/test_package_writer.py +++ b/tests/writer/tagvalue/test_package_writer.py @@ -8,30 +8,14 @@ # 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 datetime import datetime from unittest.mock import patch, mock_open, call -from spdx.model.actor import ActorType, Actor -from spdx.model.checksum import Checksum, ChecksumAlgorithm -from spdx.model.license_expression import LicenseExpression -from spdx.model.package import PackagePurpose, Package, PackageVerificationCode, ExternalPackageRef, \ - ExternalPackageRefCategory -from spdx.model.spdx_no_assertion import SpdxNoAssertion -from spdx.model.spdx_none import SpdxNone +from tests.fixtures import package_fixture from spdx.writer.tagvalue.package_writer import write_package def test_package_writer(): - package = Package("SPDXRef-Package", "package name", "www.download.com", "version", "file_name", SpdxNoAssertion(), - Actor(ActorType.PERSON, "person name", "email@mail.com"), True, - PackageVerificationCode("85ed0817af83a24ad8da68c2b5094de69833983c"), - [Checksum(ChecksumAlgorithm.SHA1, "85ed0817af83a24ad8da68c2b5094de69833983c")], - "https://homepage.com", "source_info", None, [LicenseExpression("expression")], - SpdxNone(), "comment on license", "copyright", "summary", "description", "comment", - [ExternalPackageRef(ExternalPackageRefCategory.SECURITY, "cpe22Type", - "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", - "external package ref comment")], - ["text"], PackagePurpose.OTHER, datetime(2022, 1, 1), None, None) + package = package_fixture() m = mock_open() with patch('{}.open'.format(__name__), m, create=True): @@ -42,27 +26,30 @@ def test_package_writer(): handle = m() handle.write.assert_has_calls( [call('## Package Information\n'), - call('PackageName: package name\n'), + call('PackageName: packageName\n'), call('SPDXID: SPDXRef-Package\n'), - call('PackageVersion: version\n'), - call('PackageFileName: file_name\n'), - call('PackageSupplier: NOASSERTION\n'), - call('PackageOriginator: Person: person name (email@mail.com)\n'), - call('PackageDownloadLocation: www.download.com\n'), + call('PackageVersion: 12.2\n'), + call('PackageFileName: ./packageFileName\n'), + call('PackageSupplier: Person: supplierName (some@mail.com)\n'), + call('PackageOriginator: Person: originatorName (some@mail.com)\n'), + call('PackageDownloadLocation: https://download.com\n'), call('FilesAnalyzed: True\n'), - call('PackageVerificationCode: 85ed0817af83a24ad8da68c2b5094de69833983c\n'), - call('PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c\n'), + call('PackageVerificationCode: 85ed0817af83a24ad8da68c2b5094de69833983c (excludes: ./exclude.py)\n'), + call('PackageChecksum: SHA1: 71c4025dd9897b364f3ebbb42c484ff43d00791c\n'), call('PackageHomePage: https://homepage.com\n'), - call('PackageSourceInfo: source_info\n'), - call('PackageLicenseInfoFromFiles: expression\n'), - call('PackageLicenseDeclared: NONE\n'), - call('PackageLicenseComments: comment on license\n'), - call('PackageCopyrightText: copyright\n'), - call('PackageSummary: summary\n'), - call('PackageDescription: description\n'), - call('PackageComment: comment\n'), - call('ExternalRef: SECURITY cpe22Type cpe:/o:canonical:ubuntu_linux:10.04:-:lts\n'), - call('ExternalRefComment: external package ref comment\n'), - call('PackageAttributionText: text\n'), - call('PrimaryPackagePurpose: OTHER\n'), - call('ReleaseDate: 2022-01-01T00:00:00Z\n')]) + call('PackageSourceInfo: sourceInfo\n'), + call('PackageLicenseConcluded: packageLicenseConcluded\n'), + call('PackageLicenseInfoFromFiles: licenseInfoFromFile\n'), + call('PackageLicenseDeclared: packageLicenseDeclared\n'), + call('PackageLicenseComments: packageLicenseComment\n'), + call('PackageCopyrightText: packageCopyrightText\n'), + call('PackageSummary: packageSummary\n'), + call('PackageDescription: packageDescription\n'), + call('PackageComment: packageComment\n'), + call('ExternalRef: PACKAGE-MANAGER maven-central org.apache.tomcat:tomcat:9.0.0.M4\n'), + call('ExternalRefComment: externalPackageRefComment\n'), + call('PackageAttributionText: packageAttributionText\n'), + call('PrimaryPackagePurpose: SOURCE\n'), + call('ReleaseDate: 2022-12-01T00:00:00Z\n'), + call('BuiltDate: 2022-12-02T00:00:00Z\n'), + call('ValidUntilDate: 2022-12-03T00:00:00Z\n')]) diff --git a/tests/writer/tagvalue/test_tagvalue_writer.py b/tests/writer/tagvalue/test_tagvalue_writer.py index 1d4277f11..f6b1af6c2 100644 --- a/tests/writer/tagvalue/test_tagvalue_writer.py +++ b/tests/writer/tagvalue/test_tagvalue_writer.py @@ -10,21 +10,10 @@ # limitations under the License. import os -from datetime import datetime import pytest -from spdx.model.actor import Actor, ActorType -from spdx.model.annotation import Annotation, AnnotationType -from spdx.model.checksum import ChecksumAlgorithm, Checksum -from spdx.model.document import CreationInfo, Document -from spdx.model.external_document_ref import ExternalDocumentRef -from spdx.model.extracted_licensing_info import ExtractedLicensingInfo -from spdx.model.file import File -from spdx.model.package import Package -from spdx.model.relationship import RelationshipType, Relationship -from spdx.model.snippet import Snippet -from spdx.model.spdx_none import SpdxNone +from tests.fixtures import document_fixture from spdx.writer.tagvalue.tagvalue_writer import write_document_to_file @@ -36,26 +25,7 @@ def temporary_file_path() -> str: def test_write_tag_value(temporary_file_path: str): - creation_info = CreationInfo("spdxVersion", "documentId", "documentName", "documentNamespace", - [Actor(ActorType.TOOL, "tools-python", "tools-python@github.com")], - datetime(2022, 12, 1), document_comment="comment", data_license="dataLicense", - external_document_refs=[ExternalDocumentRef("docRefId", "externalDocumentUri", - Checksum(ChecksumAlgorithm.SHA1, - "externalRefSha1"))]) - package = Package("packageId", "packageName", SpdxNone()) - file = File("fileName", "fileId", [Checksum(ChecksumAlgorithm.SHA1, "fileSha1")]) - snippet = Snippet("snippetId", "fileId", (1, 2)) - annotations = [ - Annotation("documentId", AnnotationType.REVIEW, Actor(ActorType.PERSON, "reviewerName"), datetime(2022, 12, 2), - "reviewComment"), - Annotation("fileId", AnnotationType.OTHER, Actor(ActorType.TOOL, "toolName"), datetime(2022, 12, 3), - "otherComment")] - extracted_licensing_info = [ExtractedLicensingInfo("licenseId", "licenseText")] - relationships = [Relationship(creation_info.spdx_id, RelationshipType.DESCRIBES, "packageId"), - Relationship(creation_info.spdx_id, RelationshipType.DESCRIBES, "fileId", "relationshipComment"), - Relationship("relationshipOriginId", RelationshipType.AMENDS, "relationShipTargetId")] - document = Document(creation_info, annotations=annotations, extracted_licensing_info=extracted_licensing_info, - relationships=relationships, packages=[package], files=[file], snippets=[snippet]) + document = document_fixture() write_document_to_file(document, temporary_file_path) From 11727e27f1190e81a1fb6dbb5f3c46b87701dfd7 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 9 Jan 2023 16:35:19 +0100 Subject: [PATCH 146/362] [fix] delete unused imports Signed-off-by: Meret Behrens --- src/spdx/parser/jsonlikedict/json_like_dict_parser.py | 1 - src/spdx/validation/creation_info_validator.py | 1 - tests/parser/jsonlikedict/test_dict_parsing_functions.py | 2 -- 3 files changed, 4 deletions(-) diff --git a/src/spdx/parser/jsonlikedict/json_like_dict_parser.py b/src/spdx/parser/jsonlikedict/json_like_dict_parser.py index 213f9da50..70334ff7f 100644 --- a/src/spdx/parser/jsonlikedict/json_like_dict_parser.py +++ b/src/spdx/parser/jsonlikedict/json_like_dict_parser.py @@ -8,7 +8,6 @@ # 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 json from typing import Dict from spdx.model.document import Document diff --git a/src/spdx/validation/creation_info_validator.py b/src/spdx/validation/creation_info_validator.py index f4bfc81ed..fc54740f6 100644 --- a/src/spdx/validation/creation_info_validator.py +++ b/src/spdx/validation/creation_info_validator.py @@ -9,7 +9,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import re from typing import List from spdx.model.document import CreationInfo diff --git a/tests/parser/jsonlikedict/test_dict_parsing_functions.py b/tests/parser/jsonlikedict/test_dict_parsing_functions.py index 30232e6ae..93b569362 100644 --- a/tests/parser/jsonlikedict/test_dict_parsing_functions.py +++ b/tests/parser/jsonlikedict/test_dict_parsing_functions.py @@ -8,7 +8,6 @@ # 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 datetime import datetime from unittest import TestCase import pytest @@ -18,7 +17,6 @@ from spdx.parser.error import SPDXParsingError from spdx.parser.jsonlikedict.dict_parsing_functions import json_str_to_enum_name, \ parse_field_or_no_assertion, parse_field_or_no_assertion_or_none -from spdx.datetime_conversions import datetime_from_str def test_json_str_to_enum(): From 0d1dcf5195b60d25c0171335c858e6d165bb2346 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 11 Jan 2023 14:32:09 +0100 Subject: [PATCH 147/362] [issue-420] fix order of mocks to match input arguments Signed-off-by: Meret Behrens --- tests/model/test_creation_info.py | 2 +- tests/model/test_document.py | 12 ++++++------ tests/model/test_package.py | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/model/test_creation_info.py b/tests/model/test_creation_info.py index 68f5f323e..70be62dc7 100644 --- a/tests/model/test_creation_info.py +++ b/tests/model/test_creation_info.py @@ -7,8 +7,8 @@ from spdx.model.version import Version -@mock.patch('spdx.model.actor.Actor', autospec=True) @mock.patch('spdx.model.external_document_ref.ExternalDocumentRef', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_correct_initialization(actor, ext_ref): creation_info = CreationInfo("version", "id", "name", "namespace", [actor, actor], datetime(2022, 1, 1), "creator_comment", "CC0-1.1", [ext_ref, ext_ref], Version(6, 3), "doc_comment") diff --git a/tests/model/test_document.py b/tests/model/test_document.py index a27370db5..c60742587 100644 --- a/tests/model/test_document.py +++ b/tests/model/test_document.py @@ -5,13 +5,13 @@ from spdx.model.document import Document -@mock.patch('spdx.model.document.CreationInfo', autospec=True) -@mock.patch('spdx.model.package.Package', autospec=True) -@mock.patch('spdx.model.file.File', autospec=True) -@mock.patch('spdx.model.snippet.Snippet', autospec=True) -@mock.patch('spdx.model.annotation.Annotation', autospec=True) -@mock.patch('spdx.model.relationship.Relationship', autospec=True) @mock.patch('spdx.model.extracted_licensing_info.ExtractedLicensingInfo', autospec=True) +@mock.patch('spdx.model.relationship.Relationship', autospec=True) +@mock.patch('spdx.model.annotation.Annotation', autospec=True) +@mock.patch('spdx.model.snippet.Snippet', autospec=True) +@mock.patch('spdx.model.file.File', autospec=True) +@mock.patch('spdx.model.package.Package', autospec=True) +@mock.patch('spdx.model.document.CreationInfo', autospec=True) def test_correct_initialization(creation_info, package, file, snippet, annotation, relationship, extracted_lic): document = Document(creation_info, [package, package], [file, file], [snippet, snippet], [annotation, annotation], diff --git a/tests/model/test_package.py b/tests/model/test_package.py index bb0e92816..1fb1fb1f0 100644 --- a/tests/model/test_package.py +++ b/tests/model/test_package.py @@ -10,10 +10,10 @@ from spdx.model.spdx_none import SpdxNone -@mock.patch('spdx.model.actor.Actor', autospec=True) -@mock.patch('spdx.model.package.PackageVerificationCode', autospec=True) -@mock.patch('spdx.model.checksum.Checksum', autospec=True) @mock.patch('spdx.model.package.ExternalPackageRef', autospec=True) +@mock.patch('spdx.model.checksum.Checksum', autospec=True) +@mock.patch('spdx.model.package.PackageVerificationCode', autospec=True) +@mock.patch('spdx.model.actor.Actor', autospec=True) def test_correct_initialization(actor, verif_code, checksum, ext_ref): package = Package("id", "name", SpdxNoAssertion(), "version", "file_name", SpdxNoAssertion(), actor, True, verif_code, [checksum], "homepage", "source_info", None, [LicenseExpression("expression")], From 7b71d90fb7d32f2fd80b30a1f166238d361814e3 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 12 Jan 2023 08:37:18 +0100 Subject: [PATCH 148/362] [issue-392] make usage of license_concluded consistent Signed-off-by: Meret Behrens --- src/spdx/jsonschema/file_converter.py | 2 +- src/spdx/jsonschema/snippet_converter.py | 2 +- src/spdx/model/file.py | 4 ++-- src/spdx/model/snippet.py | 4 ++-- src/spdx/parser/jsonlikedict/file_parser.py | 2 +- src/spdx/parser/jsonlikedict/snippet_parser.py | 4 ++-- src/spdx/validation/file_validator.py | 2 +- src/spdx/validation/snippet_validator.py | 2 +- src/spdx/writer/tagvalue/file_writer.py | 2 +- src/spdx/writer/tagvalue/snippet_writer.py | 2 +- tests/fixtures.py | 8 ++++---- tests/jsonschema/test_file_converter.py | 8 ++++---- tests/jsonschema/test_snippet_converter.py | 8 ++++---- tests/model/test_file.py | 8 ++++---- tests/model/test_snippet.py | 8 ++++---- tests/parser/jsonlikedict/test_file_parser.py | 2 +- tests/parser/jsonlikedict/test_snippet_parser.py | 2 +- tests/writer/json/expected_results/expected.json | 2 +- 18 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/spdx/jsonschema/file_converter.py b/src/spdx/jsonschema/file_converter.py index 78af0b90f..5806ee604 100644 --- a/src/spdx/jsonschema/file_converter.py +++ b/src/spdx/jsonschema/file_converter.py @@ -62,7 +62,7 @@ def _get_property_value(self, file: Any, file_property: FileProperty, document: elif file_property == FileProperty.LICENSE_COMMENTS: return file.license_comment elif file_property == FileProperty.LICENSE_CONCLUDED: - return apply_if_present(str, file.concluded_license) + return apply_if_present(str, file.license_concluded) elif file_property == FileProperty.LICENSE_INFO_IN_FILES: if isinstance(file.license_info_in_file, list): return [str(license_expression) for license_expression in file.license_info_in_file] or None diff --git a/src/spdx/jsonschema/snippet_converter.py b/src/spdx/jsonschema/snippet_converter.py index 0d3013e53..90dc9883b 100644 --- a/src/spdx/jsonschema/snippet_converter.py +++ b/src/spdx/jsonschema/snippet_converter.py @@ -46,7 +46,7 @@ def _get_property_value(self, snippet: Snippet, snippet_property: SnippetPropert elif snippet_property == SnippetProperty.LICENSE_COMMENTS: return snippet.license_comment elif snippet_property == SnippetProperty.LICENSE_CONCLUDED: - return apply_if_present(str, snippet.concluded_license) + return apply_if_present(str, snippet.license_concluded) elif snippet_property == SnippetProperty.LICENSE_INFO_IN_SNIPPETS: if isinstance(snippet.license_info_in_snippet, list): return [str(license_expression) for license_expression in snippet.license_info_in_snippet] or None diff --git a/src/spdx/model/file.py b/src/spdx/model/file.py index fa23f54e5..77837756b 100644 --- a/src/spdx/model/file.py +++ b/src/spdx/model/file.py @@ -40,7 +40,7 @@ class File: spdx_id: str checksums: List[Checksum] file_type: List[FileType] = field(default_factory=list) - concluded_license: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None + license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None license_info_in_file: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = field( default_factory=list) license_comment: Optional[str] = None @@ -56,7 +56,7 @@ class File: # between the file and this package def __init__(self, name: str, spdx_id: str, checksums: List[Checksum], file_type: List[FileType] = None, - concluded_license: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, + license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, license_info_in_file: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = None, license_comment: Optional[str] = None, copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None, diff --git a/src/spdx/model/snippet.py b/src/spdx/model/snippet.py index 5fba0c69d..1d5fa6b46 100644 --- a/src/spdx/model/snippet.py +++ b/src/spdx/model/snippet.py @@ -24,7 +24,7 @@ class Snippet: file_spdx_id: str byte_range: Tuple[int, int] line_range: Optional[Tuple[int, int]] = None - concluded_license: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None + license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None license_info_in_snippet: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = None license_comment: Optional[str] = None copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None @@ -34,7 +34,7 @@ class Snippet: def __init__(self, spdx_id: str, file_spdx_id: str, byte_range: Tuple[int, int], line_range: Optional[Tuple[int, int]] = None, - concluded_license: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, + license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, license_info_in_snippet: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = None, license_comment: Optional[str] = None, copyright_text: Optional[str] = None, comment: Optional[str] = None, name: Optional[str] = None, attribution_texts: List[str] = None): diff --git a/src/spdx/parser/jsonlikedict/file_parser.py b/src/spdx/parser/jsonlikedict/file_parser.py index 44a62b5ab..57954c0dd 100644 --- a/src/spdx/parser/jsonlikedict/file_parser.py +++ b/src/spdx/parser/jsonlikedict/file_parser.py @@ -64,7 +64,7 @@ def parse_file(self, file_dict: Dict) -> Optional[File]: copyright_text=copyright_text, file_type=file_types, contributors=file_contributors, license_comment=license_comments, - concluded_license=license_concluded, + license_concluded=license_concluded, license_info_in_file=license_info_in_files, notice=notice_text) ) diff --git a/src/spdx/parser/jsonlikedict/snippet_parser.py b/src/spdx/parser/jsonlikedict/snippet_parser.py index f0a8fef9c..9dbb733c3 100644 --- a/src/spdx/parser/jsonlikedict/snippet_parser.py +++ b/src/spdx/parser/jsonlikedict/snippet_parser.py @@ -52,7 +52,7 @@ def parse_snippet(self, snippet_dict: Dict) -> Snippet: comment: Optional[str] = snippet_dict.get("comment") copyright_text: Optional[str] = snippet_dict.get("copyrightText") license_comment: Optional[str] = snippet_dict.get("licenseComments") - concluded_license: Optional[Union[ + license_concluded: Optional[Union[ LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger, snippet_dict.get( "licenseConcluded"), lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expression)) @@ -69,7 +69,7 @@ def parse_snippet(self, snippet_dict: Dict) -> Snippet: file_spdx_id=file_spdx_id, line_range=line_range, attribution_texts=attribution_texts, comment=comment, copyright_text=copyright_text, license_comment=license_comment, - concluded_license=concluded_license, + license_concluded=license_concluded, license_info_in_snippet=license_info)) return snippet diff --git a/src/spdx/validation/file_validator.py b/src/spdx/validation/file_validator.py index 33cc73242..cc4375ebd 100644 --- a/src/spdx/validation/file_validator.py +++ b/src/spdx/validation/file_validator.py @@ -65,7 +65,7 @@ def validate_file(file: File, context: Optional[ValidationContext] = None) -> Li validation_messages.extend(validate_checksums(file.checksums, file.spdx_id)) - validation_messages.extend(validate_license_expression(file.concluded_license)) + validation_messages.extend(validate_license_expression(file.license_concluded)) validation_messages.extend(validate_license_expressions(file.license_info_in_file)) diff --git a/src/spdx/validation/snippet_validator.py b/src/spdx/validation/snippet_validator.py index 3140a85a9..4d350f353 100644 --- a/src/spdx/validation/snippet_validator.py +++ b/src/spdx/validation/snippet_validator.py @@ -83,7 +83,7 @@ def validate_snippet(snippet: Snippet, context: Optional[ValidationContext] = No context) ) - validation_messages.extend(validate_license_expression(snippet.concluded_license)) + validation_messages.extend(validate_license_expression(snippet.license_concluded)) validation_messages.extend(validate_license_expressions(snippet.license_info_in_snippet)) diff --git a/src/spdx/writer/tagvalue/file_writer.py b/src/spdx/writer/tagvalue/file_writer.py index e281d4fd7..ce1ec0a28 100644 --- a/src/spdx/writer/tagvalue/file_writer.py +++ b/src/spdx/writer/tagvalue/file_writer.py @@ -28,7 +28,7 @@ def write_file(file: File, text_output: TextIO): for file_checksum in file.checksums: write_value("FileChecksum", write_checksum_to_tag_value(file_checksum), text_output) - write_license_expression("LicenseConcluded", file.concluded_license, text_output) + write_license_expression("LicenseConcluded", file.license_concluded, text_output) write_license_expression("LicenseInfoInFile", file.license_info_in_file, text_output) write_text_value("LicenseComments", file.license_comment, text_output) write_text_value("FileCopyrightText", file.copyright_text, text_output) diff --git a/src/spdx/writer/tagvalue/snippet_writer.py b/src/spdx/writer/tagvalue/snippet_writer.py index 7f5d8d600..dc8553e9f 100644 --- a/src/spdx/writer/tagvalue/snippet_writer.py +++ b/src/spdx/writer/tagvalue/snippet_writer.py @@ -23,7 +23,7 @@ def write_snippet(snippet: Snippet, output_text: TextIO): write_range("SnippetByteRange", snippet.byte_range, output_text) write_range("SnippetLineRange", snippet.line_range, output_text) - write_license_expression("SnippetLicenseConcluded", snippet.concluded_license, output_text) + write_license_expression("SnippetLicenseConcluded", snippet.license_concluded, output_text) write_license_expression("LicenseInfoInSnippet", snippet.license_info_in_snippet, output_text) write_text_value("SnippetLicenseComments", snippet.license_comment, output_text) write_text_value("SnippetCopyrightText", snippet.copyright_text, output_text) diff --git a/tests/fixtures.py b/tests/fixtures.py index 8bcb0f3d0..389fa56a0 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -53,7 +53,7 @@ def creation_info_fixture(spdx_version="SPDX-2.3", spdx_id="SPDXRef-DOCUMENT", n def file_fixture(name="./fileName.py", spdx_id="SPDXRef-File", checksums=None, file_type=None, - concluded_license=LicenseExpression("concludedLicenseExpression"), license_info_in_file=None, + license_concluded=LicenseExpression("licenseConcludedExpression"), license_info_in_file=None, license_comment="licenseComment", copyright_text="copyrightText", comment="fileComment", notice="fileNotice", contributors=None, attribution_texts=None) -> File: checksums = [checksum_fixture()] if checksums is None else checksums @@ -63,7 +63,7 @@ def file_fixture(name="./fileName.py", spdx_id="SPDXRef-File", checksums=None, f contributors = ["fileContributor"] if contributors is None else contributors attribution_texts = ["fileAttributionText"] if attribution_texts is None else attribution_texts return File(name=name, spdx_id=spdx_id, checksums=checksums, file_type=file_type, - concluded_license=concluded_license, license_info_in_file=license_info_in_file, + license_concluded=license_concluded, license_info_in_file=license_info_in_file, license_comment=license_comment, copyright_text=copyright_text, comment=comment, notice=notice, contributors=contributors, attribution_texts=attribution_texts) @@ -108,7 +108,7 @@ def external_package_ref_fixture(category=ExternalPackageRefCategory.PACKAGE_MAN def snippet_fixture(spdx_id="SPDXRef-Snippet", file_spdx_id="SPDXRef-File", byte_range=(1, 2), - line_range=(3, 4), concluded_license=LicenseExpression("snippetLicenseConcluded"), + line_range=(3, 4), license_concluded=LicenseExpression("snippetLicenseConcluded"), license_info_in_snippet=None, license_comment="snippetLicenseComment", copyright_text="licenseCopyrightText", comment="snippetComment", name="snippetName", attribution_texts=None) -> Snippet: @@ -116,7 +116,7 @@ def snippet_fixture(spdx_id="SPDXRef-Snippet", file_spdx_id="SPDXRef-File", byte LicenseExpression("licenseInfoInSnippet")] if license_info_in_snippet is None else license_info_in_snippet attribution_texts = ["snippetAttributionText"] if attribution_texts is None else attribution_texts return Snippet(spdx_id=spdx_id, file_spdx_id=file_spdx_id, byte_range=byte_range, line_range=line_range, - concluded_license=concluded_license, license_info_in_snippet=license_info_in_snippet, + license_concluded=license_concluded, license_info_in_snippet=license_info_in_snippet, license_comment=license_comment, copyright_text=copyright_text, comment=comment, name=name, attribution_texts=attribution_texts) diff --git a/tests/jsonschema/test_file_converter.py b/tests/jsonschema/test_file_converter.py index 452d08c51..f0e968ae6 100644 --- a/tests/jsonschema/test_file_converter.py +++ b/tests/jsonschema/test_file_converter.py @@ -73,7 +73,7 @@ def test_successful_conversion(converter: FileConverter): converter.annotation_converter.convert.return_value = "mock_converted_annotation" file = File(name="name", spdx_id="spdxId", checksums=[Checksum(ChecksumAlgorithm.SHA224, "sha224"), Checksum(ChecksumAlgorithm.MD2, "md2")], - file_type=[FileType.SPDX, FileType.OTHER], concluded_license=LicenseExpression("licenseExpression1"), + file_type=[FileType.SPDX, FileType.OTHER], license_concluded=LicenseExpression("licenseExpression1"), license_info_in_file=[LicenseExpression("licenseExpression2"), LicenseExpression("licenseExpression3")], license_comment="licenseComment", copyright_text="copyrightText", comment="comment", notice="notice", contributors=["contributor1", "contributor2"], @@ -103,7 +103,7 @@ def test_successful_conversion(converter: FileConverter): def test_null_values(converter: FileConverter): - file = file_fixture(copyright_text=None, concluded_license=None, license_comment=None, comment=None, notice=None, + file = file_fixture(copyright_text=None, license_concluded=None, license_comment=None, comment=None, notice=None, attribution_texts=[], checksums=[], contributors=[], file_type=[], license_info_in_file=[]) document = Document(creation_info_fixture(), files=[file]) @@ -123,7 +123,7 @@ def test_null_values(converter: FileConverter): def test_spdx_no_assertion(converter: FileConverter): - file = file_fixture(concluded_license=SpdxNoAssertion(), license_info_in_file=SpdxNoAssertion(), + file = file_fixture(license_concluded=SpdxNoAssertion(), license_info_in_file=SpdxNoAssertion(), copyright_text=SpdxNoAssertion()) document = Document(creation_info_fixture(), files=[file]) @@ -136,7 +136,7 @@ def test_spdx_no_assertion(converter: FileConverter): def test_spdx_none(converter: FileConverter): - file = file_fixture(concluded_license=SpdxNone(), license_info_in_file=SpdxNone(), copyright_text=SpdxNone()) + file = file_fixture(license_concluded=SpdxNone(), license_info_in_file=SpdxNone(), copyright_text=SpdxNone()) document = Document(creation_info_fixture(), files=[file]) converted_dict = converter.convert(file, document) diff --git a/tests/jsonschema/test_snippet_converter.py b/tests/jsonschema/test_snippet_converter.py index c29ad0806..788bb43b0 100644 --- a/tests/jsonschema/test_snippet_converter.py +++ b/tests/jsonschema/test_snippet_converter.py @@ -66,7 +66,7 @@ def test_successful_conversion(converter: SnippetConverter): converter.annotation_converter.convert.return_value = "mock_converted_annotation" file_spdx_id = "fileSpdxId" snippet = Snippet("spdxId", file_spdx_id=file_spdx_id, byte_range=(1, 2), line_range=(3, 4), - concluded_license=LicenseExpression("licenseExpression1"), + license_concluded=LicenseExpression("licenseExpression1"), license_info_in_snippet=[LicenseExpression("licenseExpression2"), LicenseExpression("licenseExpression3")], license_comment="licenseComment", copyright_text="copyrightText", comment="comment", name="name", @@ -98,7 +98,7 @@ def test_successful_conversion(converter: SnippetConverter): def test_null_values(converter: SnippetConverter): - snippet = snippet_fixture(concluded_license=None, license_comment=None, copyright_text=None, comment=None, + snippet = snippet_fixture(license_concluded=None, license_comment=None, copyright_text=None, comment=None, name=None, attribution_texts=[], license_info_in_snippet=[]) document = Document(creation_info_fixture(), snippets=[snippet]) @@ -115,7 +115,7 @@ def test_null_values(converter: SnippetConverter): def test_spdx_no_assertion(converter: SnippetConverter): - snippet = snippet_fixture(concluded_license=SpdxNoAssertion(), license_info_in_snippet=SpdxNoAssertion()) + snippet = snippet_fixture(license_concluded=SpdxNoAssertion(), license_info_in_snippet=SpdxNoAssertion()) document = Document(creation_info_fixture(), snippets=[snippet]) converted_dict = converter.convert(snippet, document) @@ -126,7 +126,7 @@ def test_spdx_no_assertion(converter: SnippetConverter): def test_spdx_none(converter: SnippetConverter): - snippet = snippet_fixture(concluded_license=SpdxNone(), license_info_in_snippet=SpdxNone()) + snippet = snippet_fixture(license_concluded=SpdxNone(), license_info_in_snippet=SpdxNone()) document = Document(creation_info_fixture(), snippets=[snippet]) converted_dict = converter.convert(snippet, document) diff --git a/tests/model/test_file.py b/tests/model/test_file.py index 40a77d74b..14d6b4c78 100644 --- a/tests/model/test_file.py +++ b/tests/model/test_file.py @@ -16,7 +16,7 @@ def test_correct_initialization(checksum): assert file.spdx_id == "id" assert file.checksums == [checksum, checksum] assert file.file_type == [FileType.OTHER, FileType.SPDX] - assert file.concluded_license == SpdxNone() + assert file.license_concluded == SpdxNone() assert file.license_info_in_file == SpdxNoAssertion() assert file.license_comment == "comment on license" assert file.copyright_text == "copyright" @@ -33,7 +33,7 @@ def test_correct_initialization_with_default_values(checksum): assert file.spdx_id == "id" assert file.checksums == [checksum, checksum] assert file.file_type == [] - assert file.concluded_license is None + assert file.license_concluded is None assert file.license_info_in_file == [] assert file.license_comment is None assert file.copyright_text is None @@ -68,9 +68,9 @@ def test_wrong_type_in_file_type(checksum): @mock.patch('spdx.model.checksum.Checksum', autospec=True) -def test_wrong_type_in_concluded_license(checksum): +def test_wrong_type_in_license_concluded(checksum): with pytest.raises(TypeError): - File("name", "id", [checksum], concluded_license="NONE") + File("name", "id", [checksum], license_concluded="NONE") @mock.patch('spdx.model.checksum.Checksum', autospec=True) diff --git a/tests/model/test_snippet.py b/tests/model/test_snippet.py index 779e0c6dc..23bc2b625 100644 --- a/tests/model/test_snippet.py +++ b/tests/model/test_snippet.py @@ -12,7 +12,7 @@ def test_correct_initialization(): assert snippet.file_spdx_id == "file_id" assert snippet.byte_range == (200, 400) assert snippet.line_range == (20, 40) - assert snippet.concluded_license == SpdxNone() + assert snippet.license_concluded == SpdxNone() assert snippet.license_info_in_snippet == SpdxNoAssertion() assert snippet.license_comment == "comment on license" assert snippet.copyright_text == "copyright" @@ -27,7 +27,7 @@ def test_correct_initialization_with_default_values(): assert snippet.file_spdx_id == "file_id" assert snippet.byte_range == (200, 400) assert snippet.line_range is None - assert snippet.concluded_license is None + assert snippet.license_concluded is None assert snippet.license_info_in_snippet is None assert snippet.license_comment is None assert snippet.copyright_text is None @@ -56,9 +56,9 @@ def test_wrong_type_in_line_range(): Snippet("id", "file_id", (200, 400), line_range=(20, "40")) -def test_wrong_type_in_concluded_license(): +def test_wrong_type_in_license_concluded(): with pytest.raises(TypeError): - Snippet("id", "file_id", (200, 400), concluded_license="NONE") + Snippet("id", "file_id", (200, 400), license_concluded="NONE") def test_wrong_type_in_license_info_in_snippet(): diff --git a/tests/parser/jsonlikedict/test_file_parser.py b/tests/parser/jsonlikedict/test_file_parser.py index ea8eabed9..8a06f63af 100644 --- a/tests/parser/jsonlikedict/test_file_parser.py +++ b/tests/parser/jsonlikedict/test_file_parser.py @@ -56,7 +56,7 @@ def test_parse_file(): assert file.file_type == [FileType.SOURCE] TestCase().assertCountEqual(file.contributors, ["The Regents of the University of California", "Modified by Paul Mundt lethal@linux-sh.org", "IBM Corporation"]) - assert file.concluded_license == LicenseExpression("(LGPL-2.0-only OR LicenseRef-2)") + assert file.license_concluded == LicenseExpression("(LGPL-2.0-only OR LicenseRef-2)") TestCase().assertCountEqual(file.license_info_in_file, [LicenseExpression("GPL-2.0-only"), LicenseExpression("LicenseRef-2")]) assert file.license_comment == "The concluded license was taken from the package level that the file was included in." diff --git a/tests/parser/jsonlikedict/test_snippet_parser.py b/tests/parser/jsonlikedict/test_snippet_parser.py index e9108eed7..1fe87ae1d 100644 --- a/tests/parser/jsonlikedict/test_snippet_parser.py +++ b/tests/parser/jsonlikedict/test_snippet_parser.py @@ -61,7 +61,7 @@ def test_parse_snippet(): assert snippet.line_range == (5, 23) assert snippet.file_spdx_id == "SPDXRef-DoapSource" assert snippet.license_info_in_snippet == [LicenseExpression("GPL-2.0-only")] - assert snippet.concluded_license == LicenseExpression("GPL-2.0-only") + assert snippet.license_concluded == LicenseExpression("GPL-2.0-only") assert snippet.attribution_texts == ["Some example attibution text."] diff --git a/tests/writer/json/expected_results/expected.json b/tests/writer/json/expected_results/expected.json index ee4b8e91a..5277817f7 100644 --- a/tests/writer/json/expected_results/expected.json +++ b/tests/writer/json/expected_results/expected.json @@ -54,7 +54,7 @@ "TEXT" ], "licenseComments": "licenseComment", - "licenseConcluded": "concludedLicenseExpression", + "licenseConcluded": "licenseConcludedExpression", "licenseInfoInFiles": [ "licenseInfoInFileExpression" ], From 3fbc88b4467611f83a15befeee1e7df2fd3bf1ca Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 12 Jan 2023 09:07:22 +0100 Subject: [PATCH 149/362] [issue-400] ignore duplicated relationships while parsing Signed-off-by: Meret Behrens --- .../jsonlikedict/dict_parsing_functions.py | 5 ++ .../jsonlikedict/relationship_parser.py | 9 +-- tests/parser/json/test_json_parser.py | 2 +- .../jsonlikedict/test_relationship_parser.py | 71 +++++++++++++------ 4 files changed, 59 insertions(+), 28 deletions(-) diff --git a/src/spdx/parser/jsonlikedict/dict_parsing_functions.py b/src/spdx/parser/jsonlikedict/dict_parsing_functions.py index f5d361067..c2cde1e9e 100644 --- a/src/spdx/parser/jsonlikedict/dict_parsing_functions.py +++ b/src/spdx/parser/jsonlikedict/dict_parsing_functions.py @@ -92,3 +92,8 @@ def parse_list_of_elements(list_of_elements: List[Dict], method_to_parse_element method_to_parse_element) raise_parsing_error_if_logger_has_messages(logger) return parsed_elements + + +def delete_duplicates_from_list(list_with_potential_duplicates: List[Any]) -> List[Any]: + list_without_duplicates = list(dict.fromkeys(list_with_potential_duplicates)) + return list_without_duplicates diff --git a/src/spdx/parser/jsonlikedict/relationship_parser.py b/src/spdx/parser/jsonlikedict/relationship_parser.py index f17a63738..2804eb612 100644 --- a/src/spdx/parser/jsonlikedict/relationship_parser.py +++ b/src/spdx/parser/jsonlikedict/relationship_parser.py @@ -13,9 +13,10 @@ from spdx.model.relationship import Relationship, RelationshipType from spdx.model.typing.constructor_type_errors import ConstructorTypeErrors from spdx.parser.error import SPDXParsingError -from spdx.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, \ +from spdx.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ + json_str_to_enum_name, \ construct_or_raise_parsing_error, \ - parse_field_or_log_error, parse_field_or_no_assertion_or_none + parse_field_or_log_error, parse_field_or_no_assertion_or_none, delete_duplicates_from_list from spdx.parser.logger import Logger @@ -31,7 +32,7 @@ def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]: relationships.extend( parse_field_or_log_error(self.logger, relationship_dicts, self.parse_relationship, [], True)) - document_describes: List[str] = input_doc_dict.get("documentDescribes", []) + document_describes: List[str] = delete_duplicates_from_list(input_doc_dict.get("documentDescribes", [])) doc_spdx_id: Optional[str] = input_doc_dict.get("SPDXID") relationships.extend( @@ -102,7 +103,7 @@ def parse_has_files(self, package_dicts: List[Dict], existing_relationships: Lis contains_relationships = [] for package in package_dicts: package_spdx_id: Optional[str] = package.get("SPDXID") - contained_files: Optional[str] = package.get("hasFiles") + contained_files: List[str] = delete_duplicates_from_list(package.get("hasFiles", [])) if not contained_files: continue for file_spdx_id in contained_files: diff --git a/tests/parser/json/test_json_parser.py b/tests/parser/json/test_json_parser.py index 3acbfa14e..6939db9f8 100644 --- a/tests/parser/json/test_json_parser.py +++ b/tests/parser/json/test_json_parser.py @@ -31,7 +31,7 @@ def test_parse_json_with_2_3_example(): assert len(doc.files) == 5 assert len(doc.packages) == 4 assert len(doc.snippets) == 1 - assert len(doc.relationships) == 23 + assert len(doc.relationships) == 13 assert len(doc.extracted_licensing_info) == 5 def test_parse_json_with_2_2_example(): diff --git a/tests/parser/jsonlikedict/test_relationship_parser.py b/tests/parser/jsonlikedict/test_relationship_parser.py index 20dc39a42..79dae728c 100644 --- a/tests/parser/jsonlikedict/test_relationship_parser.py +++ b/tests/parser/jsonlikedict/test_relationship_parser.py @@ -79,26 +79,39 @@ def test_parse_document_describes(): Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-Snippet")]) -def test_parse_document_describes_without_duplicating_relationships(): +@pytest.mark.parametrize("document_describes,relationships,parsed_relationships", + [(["SPDXRef-Package", "SPDXRef-File"], [ + {"spdxElementId": "SPDXRef-DOCUMENT", "relatedSpdxElement": "SPDXRef-Package", + "relationshipType": "DESCRIBES", "comment": "This relationship has a comment."}, + {"spdxElementId": "SPDXRef-File", "relatedSpdxElement": "SPDXRef-DOCUMENT", + "relationshipType": "DESCRIBED_BY", "comment": "This relationship has a comment."}], [ + Relationship(related_spdx_element_id="SPDXRef-Package", + relationship_type=RelationshipType.DESCRIBES, + spdx_element_id="SPDXRef-DOCUMENT", + comment="This relationship has a comment."), + Relationship(related_spdx_element_id="SPDXRef-DOCUMENT", + relationship_type=RelationshipType.DESCRIBED_BY, + spdx_element_id="SPDXRef-File", + comment="This relationship has a comment.")]), + (["SPDXRef-Package", "SPDXRef-File", "SPDXRef-Package"], [], [ + Relationship(related_spdx_element_id="SPDXRef-Package", + relationship_type=RelationshipType.DESCRIBES, + spdx_element_id="SPDXRef-DOCUMENT"), + Relationship(related_spdx_element_id="SPDXRef-File", + relationship_type=RelationshipType.DESCRIBES, + spdx_element_id="SPDXRef-DOCUMENT")])]) +def test_parse_document_describes_without_duplicating_relationships(document_describes, relationships, + parsed_relationships): relationship_parser = RelationshipParser() document_dict = { "SPDXID": "SPDXRef-DOCUMENT", - "documentDescribes": ["SPDXRef-Package", "SPDXRef-File"], - "relationships": [{"spdxElementId": "SPDXRef-DOCUMENT", "relatedSpdxElement": "SPDXRef-Package", - "relationshipType": "DESCRIBES", - "comment": "This relationship has a comment."}, - {"spdxElementId": "SPDXRef-File", "relatedSpdxElement": "SPDXRef-DOCUMENT", - "relationshipType": "DESCRIBED_BY", "comment": "This relationship has a comment."} - ]} + "documentDescribes": document_describes, + "relationships": relationships} relationships = relationship_parser.parse_all_relationships(document_dict) - assert len(relationships) == 2 - TestCase().assertCountEqual(relationships, [ - Relationship(related_spdx_element_id="SPDXRef-Package", relationship_type=RelationshipType.DESCRIBES, - spdx_element_id="SPDXRef-DOCUMENT", comment="This relationship has a comment."), - Relationship(related_spdx_element_id="SPDXRef-DOCUMENT", relationship_type=RelationshipType.DESCRIBED_BY, - spdx_element_id="SPDXRef-File", comment="This relationship has a comment.")]) + assert len(relationships) == len(parsed_relationships) + TestCase().assertCountEqual(relationships, parsed_relationships) def test_parse_has_files(): @@ -121,22 +134,34 @@ def test_parse_has_files(): related_spdx_element_id="SPDXRef-File2")]) -def test_parse_has_files_without_duplicating_relationships(): +@pytest.mark.parametrize("has_files,existing_relationships,contains_relationships", + [(["SPDXRef-File1", "SPDXRef-File2"], [ + Relationship(spdx_element_id="SPDXRef-Package", + relationship_type=RelationshipType.CONTAINS, + related_spdx_element_id="SPDXRef-File1", + comment="This relationship has a comment."), + Relationship(spdx_element_id="SPDXRef-File2", + relationship_type=RelationshipType.CONTAINED_BY, + related_spdx_element_id="SPDXRef-Package")], []), + (["SPDXRef-File1", "SPDXRef-File2", "SPDXRef-File1"], [], [ + Relationship(spdx_element_id="SPDXRef-Package", + relationship_type=RelationshipType.CONTAINS, + related_spdx_element_id="SPDXRef-File1"), + Relationship(spdx_element_id="SPDXRef-Package", + relationship_type=RelationshipType.CONTAINS, + related_spdx_element_id="SPDXRef-File2")])]) +def test_parse_has_files_without_duplicating_relationships(has_files, existing_relationships, + contains_relationships): relationship_parser = RelationshipParser() document_dict = { "packages": [{ "SPDXID": "SPDXRef-Package", - "hasFiles": ["SPDXRef-File1", "SPDXRef-File2"] + "hasFiles": has_files }] } - existing_relationships = [ - Relationship(spdx_element_id="SPDXRef-Package", relationship_type=RelationshipType.CONTAINS, - related_spdx_element_id="SPDXRef-File1", comment="This relationship has a comment."), - Relationship(spdx_element_id="SPDXRef-File2", relationship_type=RelationshipType.CONTAINED_BY, - related_spdx_element_id="SPDXRef-Package")] - relationships = relationship_parser.parse_has_files(document_dict.get("packages"), existing_relationships=existing_relationships) - assert len(relationships) == 0 + assert len(relationships) == len(contains_relationships) + TestCase().assertCountEqual(relationships, contains_relationships) From 1f46bd78258f7caee243e39f7bb0e0293e36f114 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 10 Jan 2023 17:01:19 +0100 Subject: [PATCH 150/362] also use the src-layout for tests Signed-off-by: Meret Behrens --- .github/workflows/install_and_test.yml | 2 +- tests/{jsonschema => spdx}/__init__.py | 0 tests/{ => spdx}/data/doc_parse/SBOMexpected.json | 0 tests/{ => spdx}/data/doc_parse/expected.json | 0 tests/{ => spdx}/data/doc_parse/spdx-expected.json | 0 .../{ => spdx}/data/doc_write/json-simple-multi-package.json | 0 tests/{ => spdx}/data/doc_write/json-simple-plus.json | 0 tests/{ => spdx}/data/doc_write/json-simple.json | 0 tests/{ => spdx}/data/doc_write/rdf-mini.json | 0 tests/{ => spdx}/data/doc_write/rdf-simple-plus.json | 0 tests/{ => spdx}/data/doc_write/rdf-simple.json | 0 tests/{ => spdx}/data/doc_write/tv-mini.tv | 0 tests/{ => spdx}/data/doc_write/tv-simple-plus.tv | 0 tests/{ => spdx}/data/doc_write/tv-simple.tv | 0 tests/{ => spdx}/data/doc_write/xml-simple-multi-package.xml | 0 tests/{ => spdx}/data/doc_write/xml-simple-plus.xml | 0 tests/{ => spdx}/data/doc_write/xml-simple.xml | 0 .../{ => spdx}/data/doc_write/yaml-simple-multi-package.yaml | 0 tests/{ => spdx}/data/doc_write/yaml-simple-plus.yaml | 0 tests/{ => spdx}/data/doc_write/yaml-simple.yaml | 0 tests/{ => spdx}/data/formats/SPDXJSONExample-v2.2.spdx.json | 0 tests/{ => spdx}/data/formats/SPDXJSONExample-v2.3.spdx.json | 0 tests/{ => spdx}/data/formats/SPDXJsonExample.json | 0 tests/{ => spdx}/data/formats/SPDXRdfExample.rdf | 0 tests/{ => spdx}/data/formats/SPDXSBOMExample.spdx.yml | 0 tests/{ => spdx}/data/formats/SPDXSBOMExample.tag | 0 tests/{ => spdx}/data/formats/SPDXSimpleTag.tag | 0 tests/{ => spdx}/data/formats/SPDXTagExample-v2.2.spdx | 0 tests/{ => spdx}/data/formats/SPDXTagExample-v2.3.spdx | 0 tests/{ => spdx}/data/formats/SPDXTagExample.tag | 0 tests/{ => spdx}/data/formats/SPDXXMLExample-v2.2.spdx.xml | 0 tests/{ => spdx}/data/formats/SPDXXMLExample-v2.3.spdx.xml | 0 tests/{ => spdx}/data/formats/SPDXXmlExample.xml | 0 tests/{ => spdx}/data/formats/SPDXYAMLExample-2.2.spdx.yaml | 0 tests/{ => spdx}/data/formats/SPDXYAMLExample-2.3.spdx.yaml | 0 tests/{ => spdx}/data/formats/SPDXYamlExample.yaml | 0 tests/{ => spdx}/fixtures.py | 0 tests/{model => spdx/jsonschema}/__init__.py | 0 tests/{ => spdx}/jsonschema/test_annotation_converter.py | 0 tests/{ => spdx}/jsonschema/test_checksum_converter.py | 0 tests/{ => spdx}/jsonschema/test_converter.py | 0 tests/{ => spdx}/jsonschema/test_creation_info_converter.py | 2 +- tests/{ => spdx}/jsonschema/test_document_converter.py | 4 ++-- .../jsonschema/test_external_document_ref_converter.py | 0 .../jsonschema/test_external_package_ref_converter.py | 0 .../jsonschema/test_extracted_licensing_info_converter.py | 2 +- tests/{ => spdx}/jsonschema/test_file_converter.py | 4 ++-- tests/{ => spdx}/jsonschema/test_package_converter.py | 4 ++-- .../jsonschema/test_package_verification_code_converter.py | 0 tests/{ => spdx}/jsonschema/test_relationship_converter.py | 2 +- tests/{ => spdx}/jsonschema/test_snippet_converter.py | 4 ++-- tests/{ => spdx}/mock_utils.py | 0 tests/{parser => spdx/model}/__init__.py | 0 tests/{ => spdx}/model/test_actor.py | 0 tests/{ => spdx}/model/test_annotation.py | 0 tests/{ => spdx}/model/test_checksum.py | 0 tests/{ => spdx}/model/test_creation_info.py | 0 tests/{ => spdx}/model/test_document.py | 0 tests/{ => spdx}/model/test_external_document_ref.py | 0 tests/{ => spdx}/model/test_external_package_reference.py | 0 tests/{ => spdx}/model/test_extracted_licensing_info.py | 0 tests/{ => spdx}/model/test_file.py | 0 tests/{ => spdx}/model/test_license.py | 0 tests/{ => spdx}/model/test_package.py | 0 tests/{ => spdx}/model/test_package_verification_code.py | 0 tests/{ => spdx}/model/test_relationship.py | 0 tests/{ => spdx}/model/test_snippet.py | 0 tests/{ => spdx}/model/test_version.py | 0 tests/{parser/json => spdx/parser}/__init__.py | 0 tests/{parser/jsonlikedict => spdx/parser/json}/__init__.py | 0 tests/{ => spdx}/parser/json/test_json_parser.py | 0 tests/{validation => spdx/parser/jsonlikedict}/__init__.py | 0 tests/{ => spdx}/parser/jsonlikedict/test_actor_parser.py | 0 .../{ => spdx}/parser/jsonlikedict/test_annotation_parser.py | 0 tests/{ => spdx}/parser/jsonlikedict/test_checksum_parser.py | 0 .../parser/jsonlikedict/test_creation_info_parser.py | 0 .../parser/jsonlikedict/test_dict_parsing_functions.py | 0 .../jsonlikedict/test_extracted_licensing_info_parser.py | 0 tests/{ => spdx}/parser/jsonlikedict/test_file_parser.py | 0 .../parser/jsonlikedict/test_license_expression_parser.py | 0 tests/{ => spdx}/parser/jsonlikedict/test_package_parser.py | 0 .../parser/jsonlikedict/test_relationship_parser.py | 0 tests/{ => spdx}/parser/jsonlikedict/test_snippet_parser.py | 0 tests/{ => spdx}/test_datetime_conversions.py | 0 tests/{writer => spdx/validation}/__init__.py | 0 tests/{ => spdx}/validation/test_actor_validator.py | 2 +- tests/{ => spdx}/validation/test_annotation_validator.py | 2 +- tests/{ => spdx}/validation/test_checksum_validator.py | 2 +- tests/{ => spdx}/validation/test_creation_info_validator.py | 2 +- tests/{ => spdx}/validation/test_document_validator.py | 2 +- .../validation/test_external_document_ref_validator.py | 2 +- .../validation/test_external_package_ref_validator.py | 2 +- .../validation/test_extracted_licensing_info_validator.py | 2 +- tests/{ => spdx}/validation/test_file_validator.py | 2 +- .../validation/test_license_expression_validator.py | 0 tests/{ => spdx}/validation/test_package_validator.py | 2 +- tests/{ => spdx}/validation/test_relationship_validator.py | 2 +- tests/{ => spdx}/validation/test_snippet_validator.py | 2 +- tests/{ => spdx}/validation/test_spdx_id_validators.py | 0 tests/{ => spdx}/validation/test_uri_validators.py | 0 tests/{writer/json => spdx/writer}/__init__.py | 0 .../json/expected_results => spdx/writer/json}/__init__.py | 0 .../writer/json/expected_results}/__init__.py | 0 tests/{ => spdx}/writer/json/expected_results/expected.json | 0 tests/{ => spdx}/writer/json/test_json_writer.py | 2 +- tests/spdx/writer/tagvalue/__init__.py | 0 .../writer/tagvalue/expected_results/expected_tag_value.spdx | 0 tests/{ => spdx}/writer/tagvalue/test_package_writer.py | 2 +- tests/{ => spdx}/writer/tagvalue/test_tagvalue_writer.py | 2 +- 109 files changed, 27 insertions(+), 27 deletions(-) rename tests/{jsonschema => spdx}/__init__.py (100%) rename tests/{ => spdx}/data/doc_parse/SBOMexpected.json (100%) rename tests/{ => spdx}/data/doc_parse/expected.json (100%) rename tests/{ => spdx}/data/doc_parse/spdx-expected.json (100%) rename tests/{ => spdx}/data/doc_write/json-simple-multi-package.json (100%) rename tests/{ => spdx}/data/doc_write/json-simple-plus.json (100%) rename tests/{ => spdx}/data/doc_write/json-simple.json (100%) rename tests/{ => spdx}/data/doc_write/rdf-mini.json (100%) rename tests/{ => spdx}/data/doc_write/rdf-simple-plus.json (100%) rename tests/{ => spdx}/data/doc_write/rdf-simple.json (100%) rename tests/{ => spdx}/data/doc_write/tv-mini.tv (100%) rename tests/{ => spdx}/data/doc_write/tv-simple-plus.tv (100%) rename tests/{ => spdx}/data/doc_write/tv-simple.tv (100%) rename tests/{ => spdx}/data/doc_write/xml-simple-multi-package.xml (100%) rename tests/{ => spdx}/data/doc_write/xml-simple-plus.xml (100%) rename tests/{ => spdx}/data/doc_write/xml-simple.xml (100%) rename tests/{ => spdx}/data/doc_write/yaml-simple-multi-package.yaml (100%) rename tests/{ => spdx}/data/doc_write/yaml-simple-plus.yaml (100%) rename tests/{ => spdx}/data/doc_write/yaml-simple.yaml (100%) rename tests/{ => spdx}/data/formats/SPDXJSONExample-v2.2.spdx.json (100%) rename tests/{ => spdx}/data/formats/SPDXJSONExample-v2.3.spdx.json (100%) rename tests/{ => spdx}/data/formats/SPDXJsonExample.json (100%) rename tests/{ => spdx}/data/formats/SPDXRdfExample.rdf (100%) rename tests/{ => spdx}/data/formats/SPDXSBOMExample.spdx.yml (100%) rename tests/{ => spdx}/data/formats/SPDXSBOMExample.tag (100%) rename tests/{ => spdx}/data/formats/SPDXSimpleTag.tag (100%) rename tests/{ => spdx}/data/formats/SPDXTagExample-v2.2.spdx (100%) rename tests/{ => spdx}/data/formats/SPDXTagExample-v2.3.spdx (100%) rename tests/{ => spdx}/data/formats/SPDXTagExample.tag (100%) rename tests/{ => spdx}/data/formats/SPDXXMLExample-v2.2.spdx.xml (100%) rename tests/{ => spdx}/data/formats/SPDXXMLExample-v2.3.spdx.xml (100%) rename tests/{ => spdx}/data/formats/SPDXXmlExample.xml (100%) rename tests/{ => spdx}/data/formats/SPDXYAMLExample-2.2.spdx.yaml (100%) rename tests/{ => spdx}/data/formats/SPDXYAMLExample-2.3.spdx.yaml (100%) rename tests/{ => spdx}/data/formats/SPDXYamlExample.yaml (100%) rename tests/{ => spdx}/fixtures.py (100%) rename tests/{model => spdx/jsonschema}/__init__.py (100%) rename tests/{ => spdx}/jsonschema/test_annotation_converter.py (100%) rename tests/{ => spdx}/jsonschema/test_checksum_converter.py (100%) rename tests/{ => spdx}/jsonschema/test_converter.py (100%) rename tests/{ => spdx}/jsonschema/test_creation_info_converter.py (98%) rename tests/{ => spdx}/jsonschema/test_document_converter.py (98%) rename tests/{ => spdx}/jsonschema/test_external_document_ref_converter.py (100%) rename tests/{ => spdx}/jsonschema/test_external_package_ref_converter.py (100%) rename tests/{ => spdx}/jsonschema/test_extracted_licensing_info_converter.py (98%) rename tests/{ => spdx}/jsonschema/test_file_converter.py (98%) rename tests/{ => spdx}/jsonschema/test_package_converter.py (98%) rename tests/{ => spdx}/jsonschema/test_package_verification_code_converter.py (100%) rename tests/{ => spdx}/jsonschema/test_relationship_converter.py (98%) rename tests/{ => spdx}/jsonschema/test_snippet_converter.py (98%) rename tests/{ => spdx}/mock_utils.py (100%) rename tests/{parser => spdx/model}/__init__.py (100%) rename tests/{ => spdx}/model/test_actor.py (100%) rename tests/{ => spdx}/model/test_annotation.py (100%) rename tests/{ => spdx}/model/test_checksum.py (100%) rename tests/{ => spdx}/model/test_creation_info.py (100%) rename tests/{ => spdx}/model/test_document.py (100%) rename tests/{ => spdx}/model/test_external_document_ref.py (100%) rename tests/{ => spdx}/model/test_external_package_reference.py (100%) rename tests/{ => spdx}/model/test_extracted_licensing_info.py (100%) rename tests/{ => spdx}/model/test_file.py (100%) rename tests/{ => spdx}/model/test_license.py (100%) rename tests/{ => spdx}/model/test_package.py (100%) rename tests/{ => spdx}/model/test_package_verification_code.py (100%) rename tests/{ => spdx}/model/test_relationship.py (100%) rename tests/{ => spdx}/model/test_snippet.py (100%) rename tests/{ => spdx}/model/test_version.py (100%) rename tests/{parser/json => spdx/parser}/__init__.py (100%) rename tests/{parser/jsonlikedict => spdx/parser/json}/__init__.py (100%) rename tests/{ => spdx}/parser/json/test_json_parser.py (100%) rename tests/{validation => spdx/parser/jsonlikedict}/__init__.py (100%) rename tests/{ => spdx}/parser/jsonlikedict/test_actor_parser.py (100%) rename tests/{ => spdx}/parser/jsonlikedict/test_annotation_parser.py (100%) rename tests/{ => spdx}/parser/jsonlikedict/test_checksum_parser.py (100%) rename tests/{ => spdx}/parser/jsonlikedict/test_creation_info_parser.py (100%) rename tests/{ => spdx}/parser/jsonlikedict/test_dict_parsing_functions.py (100%) rename tests/{ => spdx}/parser/jsonlikedict/test_extracted_licensing_info_parser.py (100%) rename tests/{ => spdx}/parser/jsonlikedict/test_file_parser.py (100%) rename tests/{ => spdx}/parser/jsonlikedict/test_license_expression_parser.py (100%) rename tests/{ => spdx}/parser/jsonlikedict/test_package_parser.py (100%) rename tests/{ => spdx}/parser/jsonlikedict/test_relationship_parser.py (100%) rename tests/{ => spdx}/parser/jsonlikedict/test_snippet_parser.py (100%) rename tests/{ => spdx}/test_datetime_conversions.py (100%) rename tests/{writer => spdx/validation}/__init__.py (100%) rename tests/{ => spdx}/validation/test_actor_validator.py (97%) rename tests/{ => spdx}/validation/test_annotation_validator.py (95%) rename tests/{ => spdx}/validation/test_checksum_validator.py (99%) rename tests/{ => spdx}/validation/test_creation_info_validator.py (97%) rename tests/{ => spdx}/validation/test_document_validator.py (97%) rename tests/{ => spdx}/validation/test_external_document_ref_validator.py (94%) rename tests/{ => spdx}/validation/test_external_package_ref_validator.py (97%) rename tests/{ => spdx}/validation/test_extracted_licensing_info_validator.py (97%) rename tests/{ => spdx}/validation/test_file_validator.py (97%) rename tests/{ => spdx}/validation/test_license_expression_validator.py (100%) rename tests/{ => spdx}/validation/test_package_validator.py (96%) rename tests/{ => spdx}/validation/test_relationship_validator.py (98%) rename tests/{ => spdx}/validation/test_snippet_validator.py (97%) rename tests/{ => spdx}/validation/test_spdx_id_validators.py (100%) rename tests/{ => spdx}/validation/test_uri_validators.py (100%) rename tests/{writer/json => spdx/writer}/__init__.py (100%) rename tests/{writer/json/expected_results => spdx/writer/json}/__init__.py (100%) rename tests/{writer/tagvalue => spdx/writer/json/expected_results}/__init__.py (100%) rename tests/{ => spdx}/writer/json/expected_results/expected.json (100%) rename tests/{ => spdx}/writer/json/test_json_writer.py (97%) create mode 100644 tests/spdx/writer/tagvalue/__init__.py rename tests/{ => spdx}/writer/tagvalue/expected_results/expected_tag_value.spdx (100%) rename tests/{ => spdx}/writer/tagvalue/test_package_writer.py (98%) rename tests/{ => spdx}/writer/tagvalue/test_tagvalue_writer.py (96%) diff --git a/.github/workflows/install_and_test.yml b/.github/workflows/install_and_test.yml index eb3539380..77078c5f6 100644 --- a/.github/workflows/install_and_test.yml +++ b/.github/workflows/install_and_test.yml @@ -27,4 +27,4 @@ jobs: - name: Run tests run: pytest - name: Run CLI - run: pyspdxtools -i ./tests/data/formats/SPDXJSONExample-v2.3.spdx.json + run: pyspdxtools -i ./tests/spdx/data/formats/SPDXJSONExample-v2.3.spdx.json diff --git a/tests/jsonschema/__init__.py b/tests/spdx/__init__.py similarity index 100% rename from tests/jsonschema/__init__.py rename to tests/spdx/__init__.py diff --git a/tests/data/doc_parse/SBOMexpected.json b/tests/spdx/data/doc_parse/SBOMexpected.json similarity index 100% rename from tests/data/doc_parse/SBOMexpected.json rename to tests/spdx/data/doc_parse/SBOMexpected.json diff --git a/tests/data/doc_parse/expected.json b/tests/spdx/data/doc_parse/expected.json similarity index 100% rename from tests/data/doc_parse/expected.json rename to tests/spdx/data/doc_parse/expected.json diff --git a/tests/data/doc_parse/spdx-expected.json b/tests/spdx/data/doc_parse/spdx-expected.json similarity index 100% rename from tests/data/doc_parse/spdx-expected.json rename to tests/spdx/data/doc_parse/spdx-expected.json diff --git a/tests/data/doc_write/json-simple-multi-package.json b/tests/spdx/data/doc_write/json-simple-multi-package.json similarity index 100% rename from tests/data/doc_write/json-simple-multi-package.json rename to tests/spdx/data/doc_write/json-simple-multi-package.json diff --git a/tests/data/doc_write/json-simple-plus.json b/tests/spdx/data/doc_write/json-simple-plus.json similarity index 100% rename from tests/data/doc_write/json-simple-plus.json rename to tests/spdx/data/doc_write/json-simple-plus.json diff --git a/tests/data/doc_write/json-simple.json b/tests/spdx/data/doc_write/json-simple.json similarity index 100% rename from tests/data/doc_write/json-simple.json rename to tests/spdx/data/doc_write/json-simple.json diff --git a/tests/data/doc_write/rdf-mini.json b/tests/spdx/data/doc_write/rdf-mini.json similarity index 100% rename from tests/data/doc_write/rdf-mini.json rename to tests/spdx/data/doc_write/rdf-mini.json diff --git a/tests/data/doc_write/rdf-simple-plus.json b/tests/spdx/data/doc_write/rdf-simple-plus.json similarity index 100% rename from tests/data/doc_write/rdf-simple-plus.json rename to tests/spdx/data/doc_write/rdf-simple-plus.json diff --git a/tests/data/doc_write/rdf-simple.json b/tests/spdx/data/doc_write/rdf-simple.json similarity index 100% rename from tests/data/doc_write/rdf-simple.json rename to tests/spdx/data/doc_write/rdf-simple.json diff --git a/tests/data/doc_write/tv-mini.tv b/tests/spdx/data/doc_write/tv-mini.tv similarity index 100% rename from tests/data/doc_write/tv-mini.tv rename to tests/spdx/data/doc_write/tv-mini.tv diff --git a/tests/data/doc_write/tv-simple-plus.tv b/tests/spdx/data/doc_write/tv-simple-plus.tv similarity index 100% rename from tests/data/doc_write/tv-simple-plus.tv rename to tests/spdx/data/doc_write/tv-simple-plus.tv diff --git a/tests/data/doc_write/tv-simple.tv b/tests/spdx/data/doc_write/tv-simple.tv similarity index 100% rename from tests/data/doc_write/tv-simple.tv rename to tests/spdx/data/doc_write/tv-simple.tv diff --git a/tests/data/doc_write/xml-simple-multi-package.xml b/tests/spdx/data/doc_write/xml-simple-multi-package.xml similarity index 100% rename from tests/data/doc_write/xml-simple-multi-package.xml rename to tests/spdx/data/doc_write/xml-simple-multi-package.xml diff --git a/tests/data/doc_write/xml-simple-plus.xml b/tests/spdx/data/doc_write/xml-simple-plus.xml similarity index 100% rename from tests/data/doc_write/xml-simple-plus.xml rename to tests/spdx/data/doc_write/xml-simple-plus.xml diff --git a/tests/data/doc_write/xml-simple.xml b/tests/spdx/data/doc_write/xml-simple.xml similarity index 100% rename from tests/data/doc_write/xml-simple.xml rename to tests/spdx/data/doc_write/xml-simple.xml diff --git a/tests/data/doc_write/yaml-simple-multi-package.yaml b/tests/spdx/data/doc_write/yaml-simple-multi-package.yaml similarity index 100% rename from tests/data/doc_write/yaml-simple-multi-package.yaml rename to tests/spdx/data/doc_write/yaml-simple-multi-package.yaml diff --git a/tests/data/doc_write/yaml-simple-plus.yaml b/tests/spdx/data/doc_write/yaml-simple-plus.yaml similarity index 100% rename from tests/data/doc_write/yaml-simple-plus.yaml rename to tests/spdx/data/doc_write/yaml-simple-plus.yaml diff --git a/tests/data/doc_write/yaml-simple.yaml b/tests/spdx/data/doc_write/yaml-simple.yaml similarity index 100% rename from tests/data/doc_write/yaml-simple.yaml rename to tests/spdx/data/doc_write/yaml-simple.yaml diff --git a/tests/data/formats/SPDXJSONExample-v2.2.spdx.json b/tests/spdx/data/formats/SPDXJSONExample-v2.2.spdx.json similarity index 100% rename from tests/data/formats/SPDXJSONExample-v2.2.spdx.json rename to tests/spdx/data/formats/SPDXJSONExample-v2.2.spdx.json diff --git a/tests/data/formats/SPDXJSONExample-v2.3.spdx.json b/tests/spdx/data/formats/SPDXJSONExample-v2.3.spdx.json similarity index 100% rename from tests/data/formats/SPDXJSONExample-v2.3.spdx.json rename to tests/spdx/data/formats/SPDXJSONExample-v2.3.spdx.json diff --git a/tests/data/formats/SPDXJsonExample.json b/tests/spdx/data/formats/SPDXJsonExample.json similarity index 100% rename from tests/data/formats/SPDXJsonExample.json rename to tests/spdx/data/formats/SPDXJsonExample.json diff --git a/tests/data/formats/SPDXRdfExample.rdf b/tests/spdx/data/formats/SPDXRdfExample.rdf similarity index 100% rename from tests/data/formats/SPDXRdfExample.rdf rename to tests/spdx/data/formats/SPDXRdfExample.rdf diff --git a/tests/data/formats/SPDXSBOMExample.spdx.yml b/tests/spdx/data/formats/SPDXSBOMExample.spdx.yml similarity index 100% rename from tests/data/formats/SPDXSBOMExample.spdx.yml rename to tests/spdx/data/formats/SPDXSBOMExample.spdx.yml diff --git a/tests/data/formats/SPDXSBOMExample.tag b/tests/spdx/data/formats/SPDXSBOMExample.tag similarity index 100% rename from tests/data/formats/SPDXSBOMExample.tag rename to tests/spdx/data/formats/SPDXSBOMExample.tag diff --git a/tests/data/formats/SPDXSimpleTag.tag b/tests/spdx/data/formats/SPDXSimpleTag.tag similarity index 100% rename from tests/data/formats/SPDXSimpleTag.tag rename to tests/spdx/data/formats/SPDXSimpleTag.tag diff --git a/tests/data/formats/SPDXTagExample-v2.2.spdx b/tests/spdx/data/formats/SPDXTagExample-v2.2.spdx similarity index 100% rename from tests/data/formats/SPDXTagExample-v2.2.spdx rename to tests/spdx/data/formats/SPDXTagExample-v2.2.spdx diff --git a/tests/data/formats/SPDXTagExample-v2.3.spdx b/tests/spdx/data/formats/SPDXTagExample-v2.3.spdx similarity index 100% rename from tests/data/formats/SPDXTagExample-v2.3.spdx rename to tests/spdx/data/formats/SPDXTagExample-v2.3.spdx diff --git a/tests/data/formats/SPDXTagExample.tag b/tests/spdx/data/formats/SPDXTagExample.tag similarity index 100% rename from tests/data/formats/SPDXTagExample.tag rename to tests/spdx/data/formats/SPDXTagExample.tag diff --git a/tests/data/formats/SPDXXMLExample-v2.2.spdx.xml b/tests/spdx/data/formats/SPDXXMLExample-v2.2.spdx.xml similarity index 100% rename from tests/data/formats/SPDXXMLExample-v2.2.spdx.xml rename to tests/spdx/data/formats/SPDXXMLExample-v2.2.spdx.xml diff --git a/tests/data/formats/SPDXXMLExample-v2.3.spdx.xml b/tests/spdx/data/formats/SPDXXMLExample-v2.3.spdx.xml similarity index 100% rename from tests/data/formats/SPDXXMLExample-v2.3.spdx.xml rename to tests/spdx/data/formats/SPDXXMLExample-v2.3.spdx.xml diff --git a/tests/data/formats/SPDXXmlExample.xml b/tests/spdx/data/formats/SPDXXmlExample.xml similarity index 100% rename from tests/data/formats/SPDXXmlExample.xml rename to tests/spdx/data/formats/SPDXXmlExample.xml diff --git a/tests/data/formats/SPDXYAMLExample-2.2.spdx.yaml b/tests/spdx/data/formats/SPDXYAMLExample-2.2.spdx.yaml similarity index 100% rename from tests/data/formats/SPDXYAMLExample-2.2.spdx.yaml rename to tests/spdx/data/formats/SPDXYAMLExample-2.2.spdx.yaml diff --git a/tests/data/formats/SPDXYAMLExample-2.3.spdx.yaml b/tests/spdx/data/formats/SPDXYAMLExample-2.3.spdx.yaml similarity index 100% rename from tests/data/formats/SPDXYAMLExample-2.3.spdx.yaml rename to tests/spdx/data/formats/SPDXYAMLExample-2.3.spdx.yaml diff --git a/tests/data/formats/SPDXYamlExample.yaml b/tests/spdx/data/formats/SPDXYamlExample.yaml similarity index 100% rename from tests/data/formats/SPDXYamlExample.yaml rename to tests/spdx/data/formats/SPDXYamlExample.yaml diff --git a/tests/fixtures.py b/tests/spdx/fixtures.py similarity index 100% rename from tests/fixtures.py rename to tests/spdx/fixtures.py diff --git a/tests/model/__init__.py b/tests/spdx/jsonschema/__init__.py similarity index 100% rename from tests/model/__init__.py rename to tests/spdx/jsonschema/__init__.py diff --git a/tests/jsonschema/test_annotation_converter.py b/tests/spdx/jsonschema/test_annotation_converter.py similarity index 100% rename from tests/jsonschema/test_annotation_converter.py rename to tests/spdx/jsonschema/test_annotation_converter.py diff --git a/tests/jsonschema/test_checksum_converter.py b/tests/spdx/jsonschema/test_checksum_converter.py similarity index 100% rename from tests/jsonschema/test_checksum_converter.py rename to tests/spdx/jsonschema/test_checksum_converter.py diff --git a/tests/jsonschema/test_converter.py b/tests/spdx/jsonschema/test_converter.py similarity index 100% rename from tests/jsonschema/test_converter.py rename to tests/spdx/jsonschema/test_converter.py diff --git a/tests/jsonschema/test_creation_info_converter.py b/tests/spdx/jsonschema/test_creation_info_converter.py similarity index 98% rename from tests/jsonschema/test_creation_info_converter.py rename to tests/spdx/jsonschema/test_creation_info_converter.py index 47b825802..7e20cbbbe 100644 --- a/tests/jsonschema/test_creation_info_converter.py +++ b/tests/spdx/jsonschema/test_creation_info_converter.py @@ -18,7 +18,7 @@ from spdx.model.actor import Actor, ActorType from spdx.model.document import CreationInfo from spdx.model.version import Version -from tests.fixtures import creation_info_fixture +from tests.spdx.fixtures import creation_info_fixture @pytest.fixture diff --git a/tests/jsonschema/test_document_converter.py b/tests/spdx/jsonschema/test_document_converter.py similarity index 98% rename from tests/jsonschema/test_document_converter.py rename to tests/spdx/jsonschema/test_document_converter.py index 95bf0dd15..0d5603fb4 100644 --- a/tests/jsonschema/test_document_converter.py +++ b/tests/spdx/jsonschema/test_document_converter.py @@ -24,9 +24,9 @@ from spdx.model.document import Document from spdx.model.extracted_licensing_info import ExtractedLicensingInfo from spdx.model.relationship import Relationship, RelationshipType -from tests.fixtures import creation_info_fixture, file_fixture, package_fixture, external_document_ref_fixture, \ +from tests.spdx.fixtures import creation_info_fixture, file_fixture, package_fixture, external_document_ref_fixture, \ snippet_fixture, annotation_fixture, document_fixture, relationship_fixture -from tests.mock_utils import assert_mock_method_called_with_arguments, assert_no_mock_methods_called +from tests.spdx.mock_utils import assert_mock_method_called_with_arguments, assert_no_mock_methods_called @pytest.fixture diff --git a/tests/jsonschema/test_external_document_ref_converter.py b/tests/spdx/jsonschema/test_external_document_ref_converter.py similarity index 100% rename from tests/jsonschema/test_external_document_ref_converter.py rename to tests/spdx/jsonschema/test_external_document_ref_converter.py diff --git a/tests/jsonschema/test_external_package_ref_converter.py b/tests/spdx/jsonschema/test_external_package_ref_converter.py similarity index 100% rename from tests/jsonschema/test_external_package_ref_converter.py rename to tests/spdx/jsonschema/test_external_package_ref_converter.py diff --git a/tests/jsonschema/test_extracted_licensing_info_converter.py b/tests/spdx/jsonschema/test_extracted_licensing_info_converter.py similarity index 98% rename from tests/jsonschema/test_extracted_licensing_info_converter.py rename to tests/spdx/jsonschema/test_extracted_licensing_info_converter.py index 52fef8f80..05ec55a5f 100644 --- a/tests/jsonschema/test_extracted_licensing_info_converter.py +++ b/tests/spdx/jsonschema/test_extracted_licensing_info_converter.py @@ -14,7 +14,7 @@ from spdx.jsonschema.extracted_licensing_info_properties import ExtractedLicensingInfoProperty from spdx.model.extracted_licensing_info import ExtractedLicensingInfo from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING -from tests.fixtures import extracted_licensing_info_fixture +from tests.spdx.fixtures import extracted_licensing_info_fixture @pytest.fixture diff --git a/tests/jsonschema/test_file_converter.py b/tests/spdx/jsonschema/test_file_converter.py similarity index 98% rename from tests/jsonschema/test_file_converter.py rename to tests/spdx/jsonschema/test_file_converter.py index f0e968ae6..f5d167b1f 100644 --- a/tests/jsonschema/test_file_converter.py +++ b/tests/spdx/jsonschema/test_file_converter.py @@ -26,8 +26,8 @@ from spdx.model.license_expression import LicenseExpression from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING from spdx.model.spdx_none import SpdxNone, SPDX_NONE_STRING -from tests.fixtures import creation_info_fixture, file_fixture, annotation_fixture, document_fixture -from tests.mock_utils import assert_mock_method_called_with_arguments +from tests.spdx.fixtures import creation_info_fixture, file_fixture, annotation_fixture, document_fixture +from tests.spdx.mock_utils import assert_mock_method_called_with_arguments @pytest.fixture diff --git a/tests/jsonschema/test_package_converter.py b/tests/spdx/jsonschema/test_package_converter.py similarity index 98% rename from tests/jsonschema/test_package_converter.py rename to tests/spdx/jsonschema/test_package_converter.py index f6c757a17..ba0332ee0 100644 --- a/tests/jsonschema/test_package_converter.py +++ b/tests/spdx/jsonschema/test_package_converter.py @@ -27,9 +27,9 @@ from spdx.model.relationship import RelationshipType from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING from spdx.model.spdx_none import SpdxNone, SPDX_NONE_STRING -from tests.fixtures import creation_info_fixture, package_fixture, external_package_ref_fixture, document_fixture, \ +from tests.spdx.fixtures import creation_info_fixture, package_fixture, external_package_ref_fixture, document_fixture, \ annotation_fixture, file_fixture, relationship_fixture, snippet_fixture -from tests.mock_utils import assert_mock_method_called_with_arguments +from tests.spdx.mock_utils import assert_mock_method_called_with_arguments @pytest.fixture diff --git a/tests/jsonschema/test_package_verification_code_converter.py b/tests/spdx/jsonschema/test_package_verification_code_converter.py similarity index 100% rename from tests/jsonschema/test_package_verification_code_converter.py rename to tests/spdx/jsonschema/test_package_verification_code_converter.py diff --git a/tests/jsonschema/test_relationship_converter.py b/tests/spdx/jsonschema/test_relationship_converter.py similarity index 98% rename from tests/jsonschema/test_relationship_converter.py rename to tests/spdx/jsonschema/test_relationship_converter.py index 98ff88e46..f6d3d5050 100644 --- a/tests/jsonschema/test_relationship_converter.py +++ b/tests/spdx/jsonschema/test_relationship_converter.py @@ -15,7 +15,7 @@ from spdx.model.relationship import Relationship, RelationshipType from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING from spdx.model.spdx_none import SpdxNone, SPDX_NONE_STRING -from tests.fixtures import relationship_fixture +from tests.spdx.fixtures import relationship_fixture @pytest.fixture diff --git a/tests/jsonschema/test_snippet_converter.py b/tests/spdx/jsonschema/test_snippet_converter.py similarity index 98% rename from tests/jsonschema/test_snippet_converter.py rename to tests/spdx/jsonschema/test_snippet_converter.py index 788bb43b0..35f988f64 100644 --- a/tests/jsonschema/test_snippet_converter.py +++ b/tests/spdx/jsonschema/test_snippet_converter.py @@ -25,8 +25,8 @@ from spdx.model.snippet import Snippet from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING from spdx.model.spdx_none import SpdxNone, SPDX_NONE_STRING -from tests.fixtures import creation_info_fixture, snippet_fixture, document_fixture, annotation_fixture -from tests.mock_utils import assert_mock_method_called_with_arguments +from tests.spdx.fixtures import creation_info_fixture, snippet_fixture, document_fixture, annotation_fixture +from tests.spdx.mock_utils import assert_mock_method_called_with_arguments @pytest.fixture diff --git a/tests/mock_utils.py b/tests/spdx/mock_utils.py similarity index 100% rename from tests/mock_utils.py rename to tests/spdx/mock_utils.py diff --git a/tests/parser/__init__.py b/tests/spdx/model/__init__.py similarity index 100% rename from tests/parser/__init__.py rename to tests/spdx/model/__init__.py diff --git a/tests/model/test_actor.py b/tests/spdx/model/test_actor.py similarity index 100% rename from tests/model/test_actor.py rename to tests/spdx/model/test_actor.py diff --git a/tests/model/test_annotation.py b/tests/spdx/model/test_annotation.py similarity index 100% rename from tests/model/test_annotation.py rename to tests/spdx/model/test_annotation.py diff --git a/tests/model/test_checksum.py b/tests/spdx/model/test_checksum.py similarity index 100% rename from tests/model/test_checksum.py rename to tests/spdx/model/test_checksum.py diff --git a/tests/model/test_creation_info.py b/tests/spdx/model/test_creation_info.py similarity index 100% rename from tests/model/test_creation_info.py rename to tests/spdx/model/test_creation_info.py diff --git a/tests/model/test_document.py b/tests/spdx/model/test_document.py similarity index 100% rename from tests/model/test_document.py rename to tests/spdx/model/test_document.py diff --git a/tests/model/test_external_document_ref.py b/tests/spdx/model/test_external_document_ref.py similarity index 100% rename from tests/model/test_external_document_ref.py rename to tests/spdx/model/test_external_document_ref.py diff --git a/tests/model/test_external_package_reference.py b/tests/spdx/model/test_external_package_reference.py similarity index 100% rename from tests/model/test_external_package_reference.py rename to tests/spdx/model/test_external_package_reference.py diff --git a/tests/model/test_extracted_licensing_info.py b/tests/spdx/model/test_extracted_licensing_info.py similarity index 100% rename from tests/model/test_extracted_licensing_info.py rename to tests/spdx/model/test_extracted_licensing_info.py diff --git a/tests/model/test_file.py b/tests/spdx/model/test_file.py similarity index 100% rename from tests/model/test_file.py rename to tests/spdx/model/test_file.py diff --git a/tests/model/test_license.py b/tests/spdx/model/test_license.py similarity index 100% rename from tests/model/test_license.py rename to tests/spdx/model/test_license.py diff --git a/tests/model/test_package.py b/tests/spdx/model/test_package.py similarity index 100% rename from tests/model/test_package.py rename to tests/spdx/model/test_package.py diff --git a/tests/model/test_package_verification_code.py b/tests/spdx/model/test_package_verification_code.py similarity index 100% rename from tests/model/test_package_verification_code.py rename to tests/spdx/model/test_package_verification_code.py diff --git a/tests/model/test_relationship.py b/tests/spdx/model/test_relationship.py similarity index 100% rename from tests/model/test_relationship.py rename to tests/spdx/model/test_relationship.py diff --git a/tests/model/test_snippet.py b/tests/spdx/model/test_snippet.py similarity index 100% rename from tests/model/test_snippet.py rename to tests/spdx/model/test_snippet.py diff --git a/tests/model/test_version.py b/tests/spdx/model/test_version.py similarity index 100% rename from tests/model/test_version.py rename to tests/spdx/model/test_version.py diff --git a/tests/parser/json/__init__.py b/tests/spdx/parser/__init__.py similarity index 100% rename from tests/parser/json/__init__.py rename to tests/spdx/parser/__init__.py diff --git a/tests/parser/jsonlikedict/__init__.py b/tests/spdx/parser/json/__init__.py similarity index 100% rename from tests/parser/jsonlikedict/__init__.py rename to tests/spdx/parser/json/__init__.py diff --git a/tests/parser/json/test_json_parser.py b/tests/spdx/parser/json/test_json_parser.py similarity index 100% rename from tests/parser/json/test_json_parser.py rename to tests/spdx/parser/json/test_json_parser.py diff --git a/tests/validation/__init__.py b/tests/spdx/parser/jsonlikedict/__init__.py similarity index 100% rename from tests/validation/__init__.py rename to tests/spdx/parser/jsonlikedict/__init__.py diff --git a/tests/parser/jsonlikedict/test_actor_parser.py b/tests/spdx/parser/jsonlikedict/test_actor_parser.py similarity index 100% rename from tests/parser/jsonlikedict/test_actor_parser.py rename to tests/spdx/parser/jsonlikedict/test_actor_parser.py diff --git a/tests/parser/jsonlikedict/test_annotation_parser.py b/tests/spdx/parser/jsonlikedict/test_annotation_parser.py similarity index 100% rename from tests/parser/jsonlikedict/test_annotation_parser.py rename to tests/spdx/parser/jsonlikedict/test_annotation_parser.py diff --git a/tests/parser/jsonlikedict/test_checksum_parser.py b/tests/spdx/parser/jsonlikedict/test_checksum_parser.py similarity index 100% rename from tests/parser/jsonlikedict/test_checksum_parser.py rename to tests/spdx/parser/jsonlikedict/test_checksum_parser.py diff --git a/tests/parser/jsonlikedict/test_creation_info_parser.py b/tests/spdx/parser/jsonlikedict/test_creation_info_parser.py similarity index 100% rename from tests/parser/jsonlikedict/test_creation_info_parser.py rename to tests/spdx/parser/jsonlikedict/test_creation_info_parser.py diff --git a/tests/parser/jsonlikedict/test_dict_parsing_functions.py b/tests/spdx/parser/jsonlikedict/test_dict_parsing_functions.py similarity index 100% rename from tests/parser/jsonlikedict/test_dict_parsing_functions.py rename to tests/spdx/parser/jsonlikedict/test_dict_parsing_functions.py diff --git a/tests/parser/jsonlikedict/test_extracted_licensing_info_parser.py b/tests/spdx/parser/jsonlikedict/test_extracted_licensing_info_parser.py similarity index 100% rename from tests/parser/jsonlikedict/test_extracted_licensing_info_parser.py rename to tests/spdx/parser/jsonlikedict/test_extracted_licensing_info_parser.py diff --git a/tests/parser/jsonlikedict/test_file_parser.py b/tests/spdx/parser/jsonlikedict/test_file_parser.py similarity index 100% rename from tests/parser/jsonlikedict/test_file_parser.py rename to tests/spdx/parser/jsonlikedict/test_file_parser.py diff --git a/tests/parser/jsonlikedict/test_license_expression_parser.py b/tests/spdx/parser/jsonlikedict/test_license_expression_parser.py similarity index 100% rename from tests/parser/jsonlikedict/test_license_expression_parser.py rename to tests/spdx/parser/jsonlikedict/test_license_expression_parser.py diff --git a/tests/parser/jsonlikedict/test_package_parser.py b/tests/spdx/parser/jsonlikedict/test_package_parser.py similarity index 100% rename from tests/parser/jsonlikedict/test_package_parser.py rename to tests/spdx/parser/jsonlikedict/test_package_parser.py diff --git a/tests/parser/jsonlikedict/test_relationship_parser.py b/tests/spdx/parser/jsonlikedict/test_relationship_parser.py similarity index 100% rename from tests/parser/jsonlikedict/test_relationship_parser.py rename to tests/spdx/parser/jsonlikedict/test_relationship_parser.py diff --git a/tests/parser/jsonlikedict/test_snippet_parser.py b/tests/spdx/parser/jsonlikedict/test_snippet_parser.py similarity index 100% rename from tests/parser/jsonlikedict/test_snippet_parser.py rename to tests/spdx/parser/jsonlikedict/test_snippet_parser.py diff --git a/tests/test_datetime_conversions.py b/tests/spdx/test_datetime_conversions.py similarity index 100% rename from tests/test_datetime_conversions.py rename to tests/spdx/test_datetime_conversions.py diff --git a/tests/writer/__init__.py b/tests/spdx/validation/__init__.py similarity index 100% rename from tests/writer/__init__.py rename to tests/spdx/validation/__init__.py diff --git a/tests/validation/test_actor_validator.py b/tests/spdx/validation/test_actor_validator.py similarity index 97% rename from tests/validation/test_actor_validator.py rename to tests/spdx/validation/test_actor_validator.py index f7f7e22da..dbee17547 100644 --- a/tests/validation/test_actor_validator.py +++ b/tests/spdx/validation/test_actor_validator.py @@ -16,7 +16,7 @@ from spdx.model.actor import ActorType from spdx.validation.actor_validator import validate_actor from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.fixtures import actor_fixture +from tests.spdx.fixtures import actor_fixture def test_valid_actor_person(): diff --git a/tests/validation/test_annotation_validator.py b/tests/spdx/validation/test_annotation_validator.py similarity index 95% rename from tests/validation/test_annotation_validator.py rename to tests/spdx/validation/test_annotation_validator.py index b6a624855..b37a6860d 100644 --- a/tests/validation/test_annotation_validator.py +++ b/tests/spdx/validation/test_annotation_validator.py @@ -17,7 +17,7 @@ from spdx.model.document import Document from spdx.validation.annotation_validator import validate_annotation from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.fixtures import document_fixture, annotation_fixture, file_fixture +from tests.spdx.fixtures import document_fixture, annotation_fixture, file_fixture def test_valid_annotation(): diff --git a/tests/validation/test_checksum_validator.py b/tests/spdx/validation/test_checksum_validator.py similarity index 99% rename from tests/validation/test_checksum_validator.py rename to tests/spdx/validation/test_checksum_validator.py index c57e7b022..99a127db5 100644 --- a/tests/validation/test_checksum_validator.py +++ b/tests/spdx/validation/test_checksum_validator.py @@ -16,7 +16,7 @@ from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.validation.checksum_validator import validate_checksum from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.fixtures import checksum_fixture +from tests.spdx.fixtures import checksum_fixture @pytest.mark.parametrize("checksum", diff --git a/tests/validation/test_creation_info_validator.py b/tests/spdx/validation/test_creation_info_validator.py similarity index 97% rename from tests/validation/test_creation_info_validator.py rename to tests/spdx/validation/test_creation_info_validator.py index 323252f97..7ad38e5fd 100644 --- a/tests/validation/test_creation_info_validator.py +++ b/tests/spdx/validation/test_creation_info_validator.py @@ -15,7 +15,7 @@ from spdx.validation.creation_info_validator import validate_creation_info from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.fixtures import creation_info_fixture +from tests.spdx.fixtures import creation_info_fixture def test_valid_creation_info(): diff --git a/tests/validation/test_document_validator.py b/tests/spdx/validation/test_document_validator.py similarity index 97% rename from tests/validation/test_document_validator.py rename to tests/spdx/validation/test_document_validator.py index 6156f4ca4..c3b805587 100644 --- a/tests/validation/test_document_validator.py +++ b/tests/spdx/validation/test_document_validator.py @@ -16,7 +16,7 @@ from spdx.model.document import Document, CreationInfo from spdx.validation.document_validator import validate_full_spdx_document from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.fixtures import document_fixture, creation_info_fixture +from tests.spdx.fixtures import document_fixture, creation_info_fixture def test_valid_document(): diff --git a/tests/validation/test_external_document_ref_validator.py b/tests/spdx/validation/test_external_document_ref_validator.py similarity index 94% rename from tests/validation/test_external_document_ref_validator.py rename to tests/spdx/validation/test_external_document_ref_validator.py index 870e80f06..5010f69cb 100644 --- a/tests/validation/test_external_document_ref_validator.py +++ b/tests/spdx/validation/test_external_document_ref_validator.py @@ -13,7 +13,7 @@ from spdx.validation.external_document_ref_validator import validate_external_document_ref from spdx.validation.validation_message import ValidationMessage -from tests.fixtures import external_document_ref_fixture +from tests.spdx.fixtures import external_document_ref_fixture def test_valid_external_document_ref(): diff --git a/tests/validation/test_external_package_ref_validator.py b/tests/spdx/validation/test_external_package_ref_validator.py similarity index 97% rename from tests/validation/test_external_package_ref_validator.py rename to tests/spdx/validation/test_external_package_ref_validator.py index f347f9a7c..ee0c3865b 100644 --- a/tests/validation/test_external_package_ref_validator.py +++ b/tests/spdx/validation/test_external_package_ref_validator.py @@ -15,7 +15,7 @@ from spdx.validation.external_package_ref_validator import validate_external_package_ref from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.fixtures import external_package_ref_fixture +from tests.spdx.fixtures import external_package_ref_fixture def test_valid_external_package_ref(): diff --git a/tests/validation/test_extracted_licensing_info_validator.py b/tests/spdx/validation/test_extracted_licensing_info_validator.py similarity index 97% rename from tests/validation/test_extracted_licensing_info_validator.py rename to tests/spdx/validation/test_extracted_licensing_info_validator.py index 2017c151c..a85068f59 100644 --- a/tests/validation/test_extracted_licensing_info_validator.py +++ b/tests/spdx/validation/test_extracted_licensing_info_validator.py @@ -15,7 +15,7 @@ from spdx.validation.extracted_licensing_info_validator import validate_extracted_licensing_info from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.fixtures import extracted_licensing_info_fixture +from tests.spdx.fixtures import extracted_licensing_info_fixture def test_valid_extracted_licensing_info(): diff --git a/tests/validation/test_file_validator.py b/tests/spdx/validation/test_file_validator.py similarity index 97% rename from tests/validation/test_file_validator.py rename to tests/spdx/validation/test_file_validator.py index 433fd1145..040edde6e 100644 --- a/tests/validation/test_file_validator.py +++ b/tests/spdx/validation/test_file_validator.py @@ -16,7 +16,7 @@ from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.validation.file_validator import validate_file_within_document from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.fixtures import file_fixture, document_fixture +from tests.spdx.fixtures import file_fixture, document_fixture def test_valid_file(): diff --git a/tests/validation/test_license_expression_validator.py b/tests/spdx/validation/test_license_expression_validator.py similarity index 100% rename from tests/validation/test_license_expression_validator.py rename to tests/spdx/validation/test_license_expression_validator.py diff --git a/tests/validation/test_package_validator.py b/tests/spdx/validation/test_package_validator.py similarity index 96% rename from tests/validation/test_package_validator.py rename to tests/spdx/validation/test_package_validator.py index 929f17bcc..0bbb8d77d 100644 --- a/tests/validation/test_package_validator.py +++ b/tests/spdx/validation/test_package_validator.py @@ -18,7 +18,7 @@ from spdx.model.spdx_none import SpdxNone from spdx.validation.package_validator import validate_package_within_document from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.fixtures import package_fixture, package_verification_code_fixture, document_fixture +from tests.spdx.fixtures import package_fixture, package_verification_code_fixture, document_fixture def test_valid_package(): diff --git a/tests/validation/test_relationship_validator.py b/tests/spdx/validation/test_relationship_validator.py similarity index 98% rename from tests/validation/test_relationship_validator.py rename to tests/spdx/validation/test_relationship_validator.py index 684334b7f..c93c2e24c 100644 --- a/tests/validation/test_relationship_validator.py +++ b/tests/spdx/validation/test_relationship_validator.py @@ -19,7 +19,7 @@ from spdx.model.spdx_none import SpdxNone from spdx.validation.relationship_validator import validate_relationship from spdx.validation.validation_message import ValidationMessage, SpdxElementType, ValidationContext -from tests.fixtures import document_fixture, relationship_fixture +from tests.spdx.fixtures import document_fixture, relationship_fixture @pytest.mark.parametrize("related_spdx_element", diff --git a/tests/validation/test_snippet_validator.py b/tests/spdx/validation/test_snippet_validator.py similarity index 97% rename from tests/validation/test_snippet_validator.py rename to tests/spdx/validation/test_snippet_validator.py index 46c72ae0d..f6594d93d 100644 --- a/tests/validation/test_snippet_validator.py +++ b/tests/spdx/validation/test_snippet_validator.py @@ -15,7 +15,7 @@ from spdx.validation.snippet_validator import validate_snippet_within_document from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.fixtures import document_fixture, snippet_fixture +from tests.spdx.fixtures import document_fixture, snippet_fixture def test_valid_snippet(): diff --git a/tests/validation/test_spdx_id_validators.py b/tests/spdx/validation/test_spdx_id_validators.py similarity index 100% rename from tests/validation/test_spdx_id_validators.py rename to tests/spdx/validation/test_spdx_id_validators.py diff --git a/tests/validation/test_uri_validators.py b/tests/spdx/validation/test_uri_validators.py similarity index 100% rename from tests/validation/test_uri_validators.py rename to tests/spdx/validation/test_uri_validators.py diff --git a/tests/writer/json/__init__.py b/tests/spdx/writer/__init__.py similarity index 100% rename from tests/writer/json/__init__.py rename to tests/spdx/writer/__init__.py diff --git a/tests/writer/json/expected_results/__init__.py b/tests/spdx/writer/json/__init__.py similarity index 100% rename from tests/writer/json/expected_results/__init__.py rename to tests/spdx/writer/json/__init__.py diff --git a/tests/writer/tagvalue/__init__.py b/tests/spdx/writer/json/expected_results/__init__.py similarity index 100% rename from tests/writer/tagvalue/__init__.py rename to tests/spdx/writer/json/expected_results/__init__.py diff --git a/tests/writer/json/expected_results/expected.json b/tests/spdx/writer/json/expected_results/expected.json similarity index 100% rename from tests/writer/json/expected_results/expected.json rename to tests/spdx/writer/json/expected_results/expected.json diff --git a/tests/writer/json/test_json_writer.py b/tests/spdx/writer/json/test_json_writer.py similarity index 97% rename from tests/writer/json/test_json_writer.py rename to tests/spdx/writer/json/test_json_writer.py index 280e0fa4e..2bfeb5aa5 100644 --- a/tests/writer/json/test_json_writer.py +++ b/tests/spdx/writer/json/test_json_writer.py @@ -14,7 +14,7 @@ import pytest from spdx.writer.json.json_writer import write_document -from tests.fixtures import document_fixture +from tests.spdx.fixtures import document_fixture @pytest.fixture diff --git a/tests/spdx/writer/tagvalue/__init__.py b/tests/spdx/writer/tagvalue/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/writer/tagvalue/expected_results/expected_tag_value.spdx b/tests/spdx/writer/tagvalue/expected_results/expected_tag_value.spdx similarity index 100% rename from tests/writer/tagvalue/expected_results/expected_tag_value.spdx rename to tests/spdx/writer/tagvalue/expected_results/expected_tag_value.spdx diff --git a/tests/writer/tagvalue/test_package_writer.py b/tests/spdx/writer/tagvalue/test_package_writer.py similarity index 98% rename from tests/writer/tagvalue/test_package_writer.py rename to tests/spdx/writer/tagvalue/test_package_writer.py index 47ec20297..eb87a6f13 100644 --- a/tests/writer/tagvalue/test_package_writer.py +++ b/tests/spdx/writer/tagvalue/test_package_writer.py @@ -10,7 +10,7 @@ # limitations under the License. from unittest.mock import patch, mock_open, call -from tests.fixtures import package_fixture +from tests.spdx.fixtures import package_fixture from spdx.writer.tagvalue.package_writer import write_package diff --git a/tests/writer/tagvalue/test_tagvalue_writer.py b/tests/spdx/writer/tagvalue/test_tagvalue_writer.py similarity index 96% rename from tests/writer/tagvalue/test_tagvalue_writer.py rename to tests/spdx/writer/tagvalue/test_tagvalue_writer.py index f6b1af6c2..556b724ac 100644 --- a/tests/writer/tagvalue/test_tagvalue_writer.py +++ b/tests/spdx/writer/tagvalue/test_tagvalue_writer.py @@ -13,7 +13,7 @@ import pytest -from tests.fixtures import document_fixture +from tests.spdx.fixtures import document_fixture from spdx.writer.tagvalue.tagvalue_writer import write_document_to_file From 9ae0ebc9e18b549c2106579e8bde1be84dc4b7f0 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 10 Jan 2023 13:18:48 +0100 Subject: [PATCH 151/362] move typing to common module to prepare for the prototype of spdx 3.0 Signed-off-by: Meret Behrens --- src/{spdx/model/typing => common}/__init__.py | 0 src/common/typing/__init__.py | 0 src/{spdx/model => common}/typing/constructor_type_errors.py | 0 .../model => common}/typing/dataclass_with_properties.py | 0 src/{spdx/model => common}/typing/type_checks.py | 2 +- src/spdx/model/actor.py | 4 ++-- src/spdx/model/annotation.py | 4 ++-- src/spdx/model/checksum.py | 4 ++-- src/spdx/model/document.py | 4 ++-- src/spdx/model/external_document_ref.py | 4 ++-- src/spdx/model/extracted_licensing_info.py | 4 ++-- src/spdx/model/file.py | 4 ++-- src/spdx/model/license_expression.py | 4 ++-- src/spdx/model/package.py | 4 ++-- src/spdx/model/relationship.py | 4 ++-- src/spdx/model/snippet.py | 4 ++-- src/spdx/parser/jsonlikedict/dict_parsing_functions.py | 2 +- src/spdx/parser/jsonlikedict/relationship_parser.py | 2 +- tests/spdx/jsonschema/test_converter.py | 4 ++-- 19 files changed, 27 insertions(+), 27 deletions(-) rename src/{spdx/model/typing => common}/__init__.py (100%) create mode 100644 src/common/typing/__init__.py rename src/{spdx/model => common}/typing/constructor_type_errors.py (100%) rename src/{spdx/model => common}/typing/dataclass_with_properties.py (100%) rename src/{spdx/model => common}/typing/type_checks.py (93%) diff --git a/src/spdx/model/typing/__init__.py b/src/common/__init__.py similarity index 100% rename from src/spdx/model/typing/__init__.py rename to src/common/__init__.py diff --git a/src/common/typing/__init__.py b/src/common/typing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/spdx/model/typing/constructor_type_errors.py b/src/common/typing/constructor_type_errors.py similarity index 100% rename from src/spdx/model/typing/constructor_type_errors.py rename to src/common/typing/constructor_type_errors.py diff --git a/src/spdx/model/typing/dataclass_with_properties.py b/src/common/typing/dataclass_with_properties.py similarity index 100% rename from src/spdx/model/typing/dataclass_with_properties.py rename to src/common/typing/dataclass_with_properties.py diff --git a/src/spdx/model/typing/type_checks.py b/src/common/typing/type_checks.py similarity index 93% rename from src/spdx/model/typing/type_checks.py rename to src/common/typing/type_checks.py index b06ab5d34..71a741f04 100644 --- a/src/spdx/model/typing/type_checks.py +++ b/src/common/typing/type_checks.py @@ -1,6 +1,6 @@ from typing import Any, Dict -from spdx.model.typing.constructor_type_errors import ConstructorTypeErrors +from common.typing.constructor_type_errors import ConstructorTypeErrors def check_types_and_set_values(instance_under_construction: Any, local_variables: Dict) -> None: diff --git a/src/spdx/model/actor.py b/src/spdx/model/actor.py index f058b0b03..4dc3ac3fe 100644 --- a/src/spdx/model/actor.py +++ b/src/spdx/model/actor.py @@ -11,8 +11,8 @@ from enum import Enum, auto from typing import Optional -from spdx.model.typing.dataclass_with_properties import dataclass_with_properties -from spdx.model.typing.type_checks import check_types_and_set_values +from common.typing.dataclass_with_properties import dataclass_with_properties +from common.typing.type_checks import check_types_and_set_values class ActorType(Enum): diff --git a/src/spdx/model/annotation.py b/src/spdx/model/annotation.py index 0e9d2e66b..ad44204ad 100644 --- a/src/spdx/model/annotation.py +++ b/src/spdx/model/annotation.py @@ -12,8 +12,8 @@ from enum import Enum, auto from spdx.model.actor import Actor -from spdx.model.typing.dataclass_with_properties import dataclass_with_properties -from spdx.model.typing.type_checks import check_types_and_set_values +from common.typing.dataclass_with_properties import dataclass_with_properties +from common.typing.type_checks import check_types_and_set_values class AnnotationType(Enum): diff --git a/src/spdx/model/checksum.py b/src/spdx/model/checksum.py index 775892b08..94039b42e 100644 --- a/src/spdx/model/checksum.py +++ b/src/spdx/model/checksum.py @@ -10,8 +10,8 @@ # limitations under the License. from enum import auto, Enum -from spdx.model.typing.dataclass_with_properties import dataclass_with_properties -from spdx.model.typing.type_checks import check_types_and_set_values +from common.typing.dataclass_with_properties import dataclass_with_properties +from common.typing.type_checks import check_types_and_set_values class ChecksumAlgorithm(Enum): diff --git a/src/spdx/model/document.py b/src/spdx/model/document.py index 1dfac798e..c5147ddcb 100644 --- a/src/spdx/model/document.py +++ b/src/spdx/model/document.py @@ -14,14 +14,14 @@ from spdx.model.actor import Actor from spdx.model.annotation import Annotation -from spdx.model.typing.dataclass_with_properties import dataclass_with_properties +from common.typing.dataclass_with_properties import dataclass_with_properties from spdx.model.external_document_ref import ExternalDocumentRef from spdx.model.extracted_licensing_info import ExtractedLicensingInfo from spdx.model.file import File from spdx.model.package import Package from spdx.model.relationship import Relationship from spdx.model.snippet import Snippet -from spdx.model.typing.type_checks import check_types_and_set_values +from common.typing.type_checks import check_types_and_set_values from spdx.model.version import Version diff --git a/src/spdx/model/external_document_ref.py b/src/spdx/model/external_document_ref.py index 6e3b807bb..2e33ec470 100644 --- a/src/spdx/model/external_document_ref.py +++ b/src/spdx/model/external_document_ref.py @@ -10,8 +10,8 @@ # limitations under the License. from spdx.model.checksum import Checksum -from spdx.model.typing.dataclass_with_properties import dataclass_with_properties -from spdx.model.typing.type_checks import check_types_and_set_values +from common.typing.dataclass_with_properties import dataclass_with_properties +from common.typing.type_checks import check_types_and_set_values @dataclass_with_properties diff --git a/src/spdx/model/extracted_licensing_info.py b/src/spdx/model/extracted_licensing_info.py index 97574b4a0..a6c9030e3 100644 --- a/src/spdx/model/extracted_licensing_info.py +++ b/src/spdx/model/extracted_licensing_info.py @@ -12,8 +12,8 @@ from typing import Optional, List, Union from spdx.model.spdx_no_assertion import SpdxNoAssertion -from spdx.model.typing.dataclass_with_properties import dataclass_with_properties -from spdx.model.typing.type_checks import check_types_and_set_values +from common.typing.dataclass_with_properties import dataclass_with_properties +from common.typing.type_checks import check_types_and_set_values @dataclass_with_properties diff --git a/src/spdx/model/file.py b/src/spdx/model/file.py index 77837756b..2bfa6c0b8 100644 --- a/src/spdx/model/file.py +++ b/src/spdx/model/file.py @@ -13,11 +13,11 @@ from typing import Optional, List, Union from spdx.model.checksum import Checksum -from spdx.model.typing.dataclass_with_properties import dataclass_with_properties +from common.typing.dataclass_with_properties import dataclass_with_properties from spdx.model.license_expression import LicenseExpression from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone -from spdx.model.typing.type_checks import check_types_and_set_values +from common.typing.type_checks import check_types_and_set_values class FileType(Enum): diff --git a/src/spdx/model/license_expression.py b/src/spdx/model/license_expression.py index 291242b63..74c44fcd6 100644 --- a/src/spdx/model/license_expression.py +++ b/src/spdx/model/license_expression.py @@ -9,8 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from spdx.model.typing.dataclass_with_properties import dataclass_with_properties -from spdx.model.typing.type_checks import check_types_and_set_values +from common.typing.dataclass_with_properties import dataclass_with_properties +from common.typing.type_checks import check_types_and_set_values @dataclass_with_properties diff --git a/src/spdx/model/package.py b/src/spdx/model/package.py index 6a1836fdd..bbeff650a 100644 --- a/src/spdx/model/package.py +++ b/src/spdx/model/package.py @@ -15,11 +15,11 @@ from spdx.model.actor import Actor from spdx.model.checksum import Checksum -from spdx.model.typing.dataclass_with_properties import dataclass_with_properties +from common.typing.dataclass_with_properties import dataclass_with_properties from spdx.model.license_expression import LicenseExpression from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone -from spdx.model.typing.type_checks import check_types_and_set_values +from common.typing.type_checks import check_types_and_set_values class PackagePurpose(Enum): diff --git a/src/spdx/model/relationship.py b/src/spdx/model/relationship.py index 6e639af70..25c9647be 100644 --- a/src/spdx/model/relationship.py +++ b/src/spdx/model/relationship.py @@ -13,8 +13,8 @@ from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone -from spdx.model.typing.dataclass_with_properties import dataclass_with_properties -from spdx.model.typing.type_checks import check_types_and_set_values +from common.typing.dataclass_with_properties import dataclass_with_properties +from common.typing.type_checks import check_types_and_set_values class RelationshipType(Enum): diff --git a/src/spdx/model/snippet.py b/src/spdx/model/snippet.py index 1d5fa6b46..8100502e5 100644 --- a/src/spdx/model/snippet.py +++ b/src/spdx/model/snippet.py @@ -11,11 +11,11 @@ from dataclasses import field from typing import Tuple, Optional, List, Union -from spdx.model.typing.dataclass_with_properties import dataclass_with_properties +from common.typing.dataclass_with_properties import dataclass_with_properties from spdx.model.license_expression import LicenseExpression from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone -from spdx.model.typing.type_checks import check_types_and_set_values +from common.typing.type_checks import check_types_and_set_values @dataclass_with_properties diff --git a/src/spdx/parser/jsonlikedict/dict_parsing_functions.py b/src/spdx/parser/jsonlikedict/dict_parsing_functions.py index c2cde1e9e..6414fee2a 100644 --- a/src/spdx/parser/jsonlikedict/dict_parsing_functions.py +++ b/src/spdx/parser/jsonlikedict/dict_parsing_functions.py @@ -12,7 +12,7 @@ from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone -from spdx.model.typing.constructor_type_errors import ConstructorTypeErrors +from common.typing.constructor_type_errors import ConstructorTypeErrors from spdx.parser.error import SPDXParsingError from spdx.parser.logger import Logger diff --git a/src/spdx/parser/jsonlikedict/relationship_parser.py b/src/spdx/parser/jsonlikedict/relationship_parser.py index 2804eb612..2550f63c5 100644 --- a/src/spdx/parser/jsonlikedict/relationship_parser.py +++ b/src/spdx/parser/jsonlikedict/relationship_parser.py @@ -11,7 +11,7 @@ from typing import Dict, List, Optional from spdx.model.relationship import Relationship, RelationshipType -from spdx.model.typing.constructor_type_errors import ConstructorTypeErrors +from common.typing.constructor_type_errors import ConstructorTypeErrors from spdx.parser.error import SPDXParsingError from spdx.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ json_str_to_enum_name, \ diff --git a/tests/spdx/jsonschema/test_converter.py b/tests/spdx/jsonschema/test_converter.py index 7a5b4783c..daf337491 100644 --- a/tests/spdx/jsonschema/test_converter.py +++ b/tests/spdx/jsonschema/test_converter.py @@ -17,8 +17,8 @@ from spdx.jsonschema.json_property import JsonProperty from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.model.document import Document -from spdx.model.typing.dataclass_with_properties import dataclass_with_properties -from spdx.model.typing.type_checks import check_types_and_set_values +from common.typing.dataclass_with_properties import dataclass_with_properties +from common.typing.type_checks import check_types_and_set_values class TestPropertyType(JsonProperty): From ce6f3d70396ecf8406044759ad109b8a10160f4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 24 Jan 2023 13:04:52 +0100 Subject: [PATCH 152/362] [issue-373] implement externalPackageRef validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- .../external_package_ref_validator.py | 118 +++++++++++++++++- 1 file changed, 112 insertions(+), 6 deletions(-) diff --git a/src/spdx/validation/external_package_ref_validator.py b/src/spdx/validation/external_package_ref_validator.py index 10ff0ee15..d51b9107c 100644 --- a/src/spdx/validation/external_package_ref_validator.py +++ b/src/spdx/validation/external_package_ref_validator.py @@ -8,12 +8,23 @@ # 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 re from typing import List -from spdx.model.package import ExternalPackageRef -from spdx.validation.validation_message import ValidationMessage +from spdx.model.package import ExternalPackageRef, ExternalPackageRefCategory +from spdx.validation.uri_validators import validate_url, validate_uri +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType + +CPE22TYPE_REGEX = r'^c[pP][eE]:/[AHOaho]?(:[A-Za-z0-9._\-~%]*){0,6}$' +CPE23TYPE_REGEX = r'^cpe:2\.3:[aho\*\-](:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#$$%&\'\(\)\+,\/:;<=>@\[\]\^`\{\|}~]))+(\?*|\*?))|[\*\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\*\-]))(:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#$$%&\'\(\)\+,\/:;<=>@\[\]\^`\{\|}~]))+(\?*|\*?))|[\*\-])){4}$' +MAVEN_CENTRAL_REGEX = r'^[^:]+:[^:]+(:[^:]+)?$' +NPM_REGEX = r'^[^@]+@[^@]+$' +NUGET_REGEX = r'^[^/]+/[^/]+$' +BOWER_REGEX = r'^[^#]+#[^#]+$' +PURL_REGEX = None # TODO +SWH_REGEX = r'^swh:1:(snp|rel|rev|dir|cnt):[0-9a-fA-F]{40}$' +GITOID_REGEX = r'^gitoid:(blob|tree|commit|tag):(sha1:[0-9a-fA-F]{40}|sha256:[0-9a-fA-F]{64})$' def validate_external_package_refs(external_package_refs: List[ExternalPackageRef], parent_id: str) -> List[ ValidationMessage]: @@ -23,7 +34,102 @@ def validate_external_package_refs(external_package_refs: List[ExternalPackageRe return validation_messages - def validate_external_package_ref(external_package_ref: ExternalPackageRef, parent_id: str) -> List[ValidationMessage]: - # TODO: https://github.com/spdx/tools-python/issues/373 - return [] + validation_messages = [] + context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.EXTERNAL_PACKAGE_REF, full_element=external_package_ref) + + if external_package_ref.category == ExternalPackageRefCategory.SECURITY: + if external_package_ref.reference_type == "cpe22Type": + if not re.match(CPE22TYPE_REGEX, external_package_ref.locator): + validation_messages.append( + ValidationMessage(f'externalPackageRef locator of type "cpe22Type" must conform with the regex {CPE22TYPE_REGEX}, but is: {external_package_ref.locator}', context) + ) + elif external_package_ref.reference_type == "cpe23Type": + if not re.match(CPE23TYPE_REGEX, external_package_ref.locator): + validation_messages.append( + ValidationMessage( + f'externalPackageRef locator of type "cpe23Type" must conform with the regex {CPE23TYPE_REGEX}, but is: {external_package_ref.locator}', + context) + ) + elif external_package_ref.reference_type in ["advisory", "fix", "url"]: + for message in validate_url(external_package_ref.locator): + validation_messages.append( + ValidationMessage(f'externalPackageRef locator of type "{external_package_ref.reference_type}" {message}', context) + ) + elif external_package_ref.reference_type == "swid": + for message in validate_uri(external_package_ref.locator): + validation_messages.append( + ValidationMessage(f'externalPackageRef locator of type "{external_package_ref.reference_type}" {message}', + context) + ) + else: + validation_messages.append( + ValidationMessage(f"externalPackageRef type in category SECURITY must be one of [cpe22Type, cpe23Type, advisory, fix, url, swid], but is: {external_package_ref.reference_type}", context) + ) + + elif external_package_ref.category == ExternalPackageRefCategory.PACKAGE_MANAGER: + if external_package_ref.reference_type == "maven-central": + if not re.match(MAVEN_CENTRAL_REGEX, external_package_ref.locator): + validation_messages.append( + ValidationMessage( + f'externalPackageRef locator of type "maven-central" must conform with the regex {MAVEN_CENTRAL_REGEX}, but is: {external_package_ref.locator}', + context) + ) + elif external_package_ref.reference_type == "npm": + if not re.match(NPM_REGEX, external_package_ref.locator): + validation_messages.append( + ValidationMessage( + f'externalPackageRef locator of type "npm" must conform with the regex {NPM_REGEX}, but is: {external_package_ref.locator}', + context) + ) + elif external_package_ref.reference_type == "nuget": + if not re.match(NUGET_REGEX, external_package_ref.locator): + validation_messages.append( + ValidationMessage( + f'externalPackageRef locator of type "nuget" must conform with the regex {NUGET_REGEX}, but is: {external_package_ref.locator}', + context) + ) + elif external_package_ref.reference_type == "bower": + if not re.match(BOWER_REGEX, external_package_ref.locator): + validation_messages.append( + ValidationMessage( + f'externalPackageRef locator of type "bower" must conform with the regex {BOWER_REGEX}, but is: {external_package_ref.locator}', + context) + ) + elif external_package_ref.reference_type == "purl": + pass + else: + validation_messages.append( + ValidationMessage( + f"externalPackageRef type in category PACKAGE_MANAGER must be one of [maven-central, npm, nuget, bower, purl], but is: {external_package_ref.reference_type}", context) + ) + + elif external_package_ref.category == ExternalPackageRefCategory.PERSISTENT_ID: + if external_package_ref.reference_type == "swh": + if not re.match(SWH_REGEX, external_package_ref.locator): + validation_messages.append( + ValidationMessage( + f'externalPackageRef locator of type "swh" must conform with the regex {SWH_REGEX}, but is: {external_package_ref.locator}', + context) + ) + elif external_package_ref.reference_type == "gitoid": + if not re.match(GITOID_REGEX, external_package_ref.locator): + validation_messages.append( + ValidationMessage( + f'externalPackageRef locator of type "gitoid" must conform with the regex {GITOID_REGEX}, but is: {external_package_ref.locator}', + context) + ) + else: + validation_messages.append( + ValidationMessage( + f"externalPackageRef type in category PERSISTENT_ID must be one of [swh, gitoid], but is: {external_package_ref.reference_type}", + context) + ) + elif external_package_ref.category == ExternalPackageRefCategory.OTHER: + if " " in external_package_ref.locator: + validation_messages.append( + ValidationMessage(f"externalPackageRef locator must contain no spaces, but is: {external_package_ref.locator}", context) + ) + + + return validation_messages From 29cb44362359bd02db70d207880aceffdc909283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 24 Jan 2023 15:18:48 +0100 Subject: [PATCH 153/362] [issue-373] add positive externalPackageRef validation tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- .../test_external_package_ref_validator.py | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/tests/spdx/validation/test_external_package_ref_validator.py b/tests/spdx/validation/test_external_package_ref_validator.py index ee0c3865b..541ff812c 100644 --- a/tests/spdx/validation/test_external_package_ref_validator.py +++ b/tests/spdx/validation/test_external_package_ref_validator.py @@ -13,13 +13,33 @@ import pytest +from spdx.model.package import ExternalPackageRef, ExternalPackageRefCategory from spdx.validation.external_package_ref_validator import validate_external_package_ref from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.spdx.fixtures import external_package_ref_fixture - -def test_valid_external_package_ref(): - external_package_ref = external_package_ref_fixture() +@pytest.mark.parametrize("category, reference_type, locator", + [(ExternalPackageRefCategory.SECURITY, "cpe22Type", "cpe:/o:canonical:ubuntu_linux:10.04:-:lts"), + (ExternalPackageRefCategory.SECURITY, "cpe23Type", "cpe:2.3:o:canonical:ubuntu_linux:10.04:-:lts:*:*:*:*:*"), + (ExternalPackageRefCategory.SECURITY, "advisory", "https://nvd.nist.gov/vuln/detail/CVE-2020-28498"), + (ExternalPackageRefCategory.SECURITY, "fix", "https://github.com/indutny/elliptic/commit/441b7428"), + (ExternalPackageRefCategory.SECURITY, "swid", "swid:2df9de35-0aff-4a86-ace6-f7dddd1ade4c"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "maven-central", "org.apache.tomcat:tomcat:9.0.0.M4"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "npm", "http-server@0.3.0"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "nuget", "Microsoft.AspNet.MVC/5.0.0"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "bower", "modernizr#2.6.2"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "purl", "pkg:docker/debian@sha256:2f04d3d33b6027bb74ecc81397abe780649ec89f1a2af18d7022737d0482cefe"), + (ExternalPackageRefCategory.PERSISTENT_ID, "swh", "swh:1:cnt:94a9ed024d3859793618152ea559a168bbcbb5e2"), + (ExternalPackageRefCategory.PERSISTENT_ID, "swh", "swh:1:dir:d198bc9d7a6bcf6db04f476d29314f157507d505"), + (ExternalPackageRefCategory.PERSISTENT_ID, "swh", "swh:1:rev:309cf2674ee7a0749978cf8265ab91a60aea0f7d"), + (ExternalPackageRefCategory.PERSISTENT_ID, "swh", "swh:1:rel:22ece559cc7cc2364edc5e5593d63ae8bd229f9f"), + (ExternalPackageRefCategory.PERSISTENT_ID, "swh", "swh:1:snp:c7c108084bc0bf3d81436bf980b46e98bd338453"), + (ExternalPackageRefCategory.PERSISTENT_ID, "gitoid", "gitoid:blob:sha1:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64"), + (ExternalPackageRefCategory.PERSISTENT_ID, "gitoid", "gitoid:blob:sha256:3557f7eb43c621c71483743d4b37059bb80933e7f71277c0c3b3846159d1f61c"), + (ExternalPackageRefCategory.OTHER, "some idstring", "#//string-withOUT!Spaces\\?") + ]) +def test_valid_external_package_ref(category, reference_type, locator): + external_package_ref = ExternalPackageRef(category, reference_type, locator, "externalPackageRef comment") validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, "parent_id") assert validation_messages == [] From 740f61ed7fa3b4efeb8b5088ac3c6fd4942c32b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 24 Jan 2023 16:56:43 +0100 Subject: [PATCH 154/362] [issue-373] add negative externalPackageRef validation tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- .../external_package_ref_validator.py | 2 +- .../test_external_package_ref_validator.py | 71 +++++++++++++++++-- 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/src/spdx/validation/external_package_ref_validator.py b/src/spdx/validation/external_package_ref_validator.py index d51b9107c..49471d6ad 100644 --- a/src/spdx/validation/external_package_ref_validator.py +++ b/src/spdx/validation/external_package_ref_validator.py @@ -128,7 +128,7 @@ def validate_external_package_ref(external_package_ref: ExternalPackageRef, pare elif external_package_ref.category == ExternalPackageRefCategory.OTHER: if " " in external_package_ref.locator: validation_messages.append( - ValidationMessage(f"externalPackageRef locator must contain no spaces, but is: {external_package_ref.locator}", context) + ValidationMessage(f"externalPackageRef type in category OTHER must contain no spaces, but is: {external_package_ref.locator}", context) ) diff --git a/tests/spdx/validation/test_external_package_ref_validator.py b/tests/spdx/validation/test_external_package_ref_validator.py index 541ff812c..bc451d430 100644 --- a/tests/spdx/validation/test_external_package_ref_validator.py +++ b/tests/spdx/validation/test_external_package_ref_validator.py @@ -16,13 +16,14 @@ from spdx.model.package import ExternalPackageRef, ExternalPackageRefCategory from spdx.validation.external_package_ref_validator import validate_external_package_ref from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.spdx.fixtures import external_package_ref_fixture + @pytest.mark.parametrize("category, reference_type, locator", [(ExternalPackageRefCategory.SECURITY, "cpe22Type", "cpe:/o:canonical:ubuntu_linux:10.04:-:lts"), (ExternalPackageRefCategory.SECURITY, "cpe23Type", "cpe:2.3:o:canonical:ubuntu_linux:10.04:-:lts:*:*:*:*:*"), (ExternalPackageRefCategory.SECURITY, "advisory", "https://nvd.nist.gov/vuln/detail/CVE-2020-28498"), (ExternalPackageRefCategory.SECURITY, "fix", "https://github.com/indutny/elliptic/commit/441b7428"), + (ExternalPackageRefCategory.SECURITY, "url", "https://github.com/christianlundkvist/blog/blob/master/2020_05_26_secp256k1_twist_attacks/secp256k1_twist_attacks.md"), (ExternalPackageRefCategory.SECURITY, "swid", "swid:2df9de35-0aff-4a86-ace6-f7dddd1ade4c"), (ExternalPackageRefCategory.PACKAGE_MANAGER, "maven-central", "org.apache.tomcat:tomcat:9.0.0.M4"), (ExternalPackageRefCategory.PACKAGE_MANAGER, "npm", "http-server@0.3.0"), @@ -45,13 +46,69 @@ def test_valid_external_package_ref(category, reference_type, locator): assert validation_messages == [] -@pytest.mark.parametrize("external_package_ref, expected_message", - [(external_package_ref_fixture(), - "TBD"), +@pytest.mark.parametrize("category, reference_type, locator, expected_message", + [(ExternalPackageRefCategory.SECURITY, "cpe22Typo", "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", + "externalPackageRef type in category SECURITY must be one of [cpe22Type, cpe23Type, advisory, fix, url, swid], but is: cpe22Typo"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "nugat", "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", + "externalPackageRef type in category PACKAGE_MANAGER must be one of [maven-central, npm, nuget, bower, purl], but is: nugat"), + (ExternalPackageRefCategory.PERSISTENT_ID, "git-oid", "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", + "externalPackageRef type in category PERSISTENT_ID must be one of [swh, gitoid], but is: git-oid") + ]) +def test_invalid_external_package_ref_types(category, reference_type, locator, expected_message): + external_package_ref = ExternalPackageRef(category, reference_type, locator, "externalPackageRef comment") + parent_id = "SPDXRef-Package" + validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, parent_id) + + expected = ValidationMessage(expected_message, + ValidationContext(parent_id=parent_id, + element_type=SpdxElementType.EXTERNAL_PACKAGE_REF, + full_element=external_package_ref)) + + assert validation_messages == [expected] + + +CPE22TYPE_REGEX = r'^c[pP][eE]:/[AHOaho]?(:[A-Za-z0-9._\-~%]*){0,6}$' +CPE23TYPE_REGEX = r'^cpe:2\.3:[aho\*\-](:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#$$%&\'\(\)\+,\/:;<=>@\[\]\^`\{\|}~]))+(\?*|\*?))|[\*\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\*\-]))(:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#$$%&\'\(\)\+,\/:;<=>@\[\]\^`\{\|}~]))+(\?*|\*?))|[\*\-])){4}$' +MAVEN_CENTRAL_REGEX = r'^[^:]+:[^:]+(:[^:]+)?$' +NPM_REGEX = r'^[^@]+@[^@]+$' +NUGET_REGEX = r'^[^/]+/[^/]+$' +BOWER_REGEX = r'^[^#]+#[^#]+$' +PURL_REGEX = None # TODO: add negative test for purl +SWH_REGEX = r'^swh:1:(snp|rel|rev|dir|cnt):[0-9a-fA-F]{40}$' +GITOID_REGEX = r'^gitoid:(blob|tree|commit|tag):(sha1:[0-9a-fA-F]{40}|sha256:[0-9a-fA-F]{64})$' + +@pytest.mark.parametrize("category, reference_type, locator, expected_message", + [(ExternalPackageRefCategory.SECURITY, "cpe22Type", "cpe:o:canonical:ubuntu_linux:10.04:-:lts", + f'externalPackageRef locator of type "cpe22Type" must conform with the regex {CPE22TYPE_REGEX}, but is: cpe:o:canonical:ubuntu_linux:10.04:-:lts'), + (ExternalPackageRefCategory.SECURITY, "cpe23Type", "cpe:2.3:/o:canonical:ubuntu_linux:10.04:-:lts:*:*:*:*:*", + f'externalPackageRef locator of type "cpe23Type" must conform with the regex {CPE23TYPE_REGEX}, but is: cpe:2.3:/o:canonical:ubuntu_linux:10.04:-:lts:*:*:*:*:*'), + (ExternalPackageRefCategory.SECURITY, "advisory", "http://locatorurl", + f'externalPackageRef locator of type "advisory" must be a valid URL, but is: http://locatorurl'), + (ExternalPackageRefCategory.SECURITY, "fix", "http://fixurl", + f'externalPackageRef locator of type "fix" must be a valid URL, but is: http://fixurl'), + (ExternalPackageRefCategory.SECURITY, "url", "http://url", + f'externalPackageRef locator of type "url" must be a valid URL, but is: http://url'), + (ExternalPackageRefCategory.SECURITY, "swid", "2df9de35-0aff-4a86-ace6-f7dddd1ade4c", + f'externalPackageRef locator of type "swid" must be a valid URI specified in RFC-3986, but is: 2df9de35-0aff-4a86-ace6-f7dddd1ade4c'), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "maven-central", "org.apache.tomcat:tomcat:tomcat:9.0.0.M4", + f'externalPackageRef locator of type "maven-central" must conform with the regex {MAVEN_CENTRAL_REGEX}, but is: org.apache.tomcat:tomcat:tomcat:9.0.0.M4'), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "npm", "http-server:0.3.0", + f'externalPackageRef locator of type "npm" must conform with the regex {NPM_REGEX}, but is: http-server:0.3.0'), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "nuget", "Microsoft.AspNet.MVC@5.0.0", + f'externalPackageRef locator of type "nuget" must conform with the regex {NUGET_REGEX}, but is: Microsoft.AspNet.MVC@5.0.0'), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "bower", "modernizr:2.6.2", + f'externalPackageRef locator of type "bower" must conform with the regex {BOWER_REGEX}, but is: modernizr:2.6.2'), + (ExternalPackageRefCategory.PERSISTENT_ID, "swh", "swh:cnt:94a9ed024d3859793618152ea559a168bbcbb5e2", + f'externalPackageRef locator of type "swh" must conform with the regex {SWH_REGEX}, but is: swh:cnt:94a9ed024d3859793618152ea559a168bbcbb5e2'), + (ExternalPackageRefCategory.PERSISTENT_ID, "gitoid", "gitoid:blob:sha1:3557f7eb43c621c71483743d4b37059bb80933e7f71277c0c3b3846159d1f61c", + f'externalPackageRef locator of type "gitoid" must conform with the regex {GITOID_REGEX}, but is: gitoid:blob:sha1:3557f7eb43c621c71483743d4b37059bb80933e7f71277c0c3b3846159d1f61c'), + (ExternalPackageRefCategory.PERSISTENT_ID, "gitoid", "gitoid:blob:sha256:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64", + f'externalPackageRef locator of type "gitoid" must conform with the regex {GITOID_REGEX}, but is: gitoid:blob:sha256:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64'), + (ExternalPackageRefCategory.OTHER, "id string", "locator string", + "externalPackageRef type in category OTHER must contain no spaces, but is: locator string"), ]) -@pytest.mark.skip( - "add tests once external package ref validation is implemented: https://github.com/spdx/tools-python/issues/373") -def test_invalid_external_package_ref(external_package_ref, expected_message): +def test_invalid_external_package_ref_locators(category, reference_type, locator, expected_message): + external_package_ref = ExternalPackageRef(category, reference_type, locator, "externalPackageRef comment") parent_id = "SPDXRef-Package" validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, parent_id) From f874c5e34989261e8032782271122e94bc95f424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 25 Jan 2023 10:51:38 +0100 Subject: [PATCH 155/362] [issue-373] add purl and tests, reformat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- .../external_package_ref_validator.py | 40 +++++--- .../test_external_package_ref_validator.py | 93 ++++++++++++++----- 2 files changed, 97 insertions(+), 36 deletions(-) diff --git a/src/spdx/validation/external_package_ref_validator.py b/src/spdx/validation/external_package_ref_validator.py index 49471d6ad..092429461 100644 --- a/src/spdx/validation/external_package_ref_validator.py +++ b/src/spdx/validation/external_package_ref_validator.py @@ -15,17 +15,17 @@ from spdx.validation.uri_validators import validate_url, validate_uri from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType - CPE22TYPE_REGEX = r'^c[pP][eE]:/[AHOaho]?(:[A-Za-z0-9._\-~%]*){0,6}$' CPE23TYPE_REGEX = r'^cpe:2\.3:[aho\*\-](:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#$$%&\'\(\)\+,\/:;<=>@\[\]\^`\{\|}~]))+(\?*|\*?))|[\*\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\*\-]))(:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#$$%&\'\(\)\+,\/:;<=>@\[\]\^`\{\|}~]))+(\?*|\*?))|[\*\-])){4}$' MAVEN_CENTRAL_REGEX = r'^[^:]+:[^:]+(:[^:]+)?$' NPM_REGEX = r'^[^@]+@[^@]+$' NUGET_REGEX = r'^[^/]+/[^/]+$' BOWER_REGEX = r'^[^#]+#[^#]+$' -PURL_REGEX = None # TODO +PURL_REGEX = r'^pkg:.+(\/.+)?\/.+(@.+)?(\?.+)?(#.+)?$' SWH_REGEX = r'^swh:1:(snp|rel|rev|dir|cnt):[0-9a-fA-F]{40}$' GITOID_REGEX = r'^gitoid:(blob|tree|commit|tag):(sha1:[0-9a-fA-F]{40}|sha256:[0-9a-fA-F]{64})$' + def validate_external_package_refs(external_package_refs: List[ExternalPackageRef], parent_id: str) -> List[ ValidationMessage]: validation_messages = [] @@ -34,15 +34,19 @@ def validate_external_package_refs(external_package_refs: List[ExternalPackageRe return validation_messages + def validate_external_package_ref(external_package_ref: ExternalPackageRef, parent_id: str) -> List[ValidationMessage]: validation_messages = [] - context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.EXTERNAL_PACKAGE_REF, full_element=external_package_ref) + context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.EXTERNAL_PACKAGE_REF, + full_element=external_package_ref) if external_package_ref.category == ExternalPackageRefCategory.SECURITY: if external_package_ref.reference_type == "cpe22Type": if not re.match(CPE22TYPE_REGEX, external_package_ref.locator): validation_messages.append( - ValidationMessage(f'externalPackageRef locator of type "cpe22Type" must conform with the regex {CPE22TYPE_REGEX}, but is: {external_package_ref.locator}', context) + ValidationMessage( + f'externalPackageRef locator of type "cpe22Type" must conform with the regex {CPE22TYPE_REGEX}, but is: {external_package_ref.locator}', + context) ) elif external_package_ref.reference_type == "cpe23Type": if not re.match(CPE23TYPE_REGEX, external_package_ref.locator): @@ -54,17 +58,22 @@ def validate_external_package_ref(external_package_ref: ExternalPackageRef, pare elif external_package_ref.reference_type in ["advisory", "fix", "url"]: for message in validate_url(external_package_ref.locator): validation_messages.append( - ValidationMessage(f'externalPackageRef locator of type "{external_package_ref.reference_type}" {message}', context) + ValidationMessage( + f'externalPackageRef locator of type "{external_package_ref.reference_type}" {message}', + context) ) elif external_package_ref.reference_type == "swid": for message in validate_uri(external_package_ref.locator): validation_messages.append( - ValidationMessage(f'externalPackageRef locator of type "{external_package_ref.reference_type}" {message}', - context) + ValidationMessage( + f'externalPackageRef locator of type "{external_package_ref.reference_type}" {message}', + context) ) else: validation_messages.append( - ValidationMessage(f"externalPackageRef type in category SECURITY must be one of [cpe22Type, cpe23Type, advisory, fix, url, swid], but is: {external_package_ref.reference_type}", context) + ValidationMessage( + f"externalPackageRef type in category SECURITY must be one of [cpe22Type, cpe23Type, advisory, fix, url, swid], but is: {external_package_ref.reference_type}", + context) ) elif external_package_ref.category == ExternalPackageRefCategory.PACKAGE_MANAGER: @@ -97,11 +106,17 @@ def validate_external_package_ref(external_package_ref: ExternalPackageRef, pare context) ) elif external_package_ref.reference_type == "purl": - pass + if not re.match(PURL_REGEX, external_package_ref.locator): + validation_messages.append( + ValidationMessage( + f'externalPackageRef locator of type "purl" must conform with the regex {PURL_REGEX}, but is: {external_package_ref.locator}', + context) + ) else: validation_messages.append( ValidationMessage( - f"externalPackageRef type in category PACKAGE_MANAGER must be one of [maven-central, npm, nuget, bower, purl], but is: {external_package_ref.reference_type}", context) + f"externalPackageRef type in category PACKAGE_MANAGER must be one of [maven-central, npm, nuget, bower, purl], but is: {external_package_ref.reference_type}", + context) ) elif external_package_ref.category == ExternalPackageRefCategory.PERSISTENT_ID: @@ -128,8 +143,9 @@ def validate_external_package_ref(external_package_ref: ExternalPackageRef, pare elif external_package_ref.category == ExternalPackageRefCategory.OTHER: if " " in external_package_ref.locator: validation_messages.append( - ValidationMessage(f"externalPackageRef type in category OTHER must contain no spaces, but is: {external_package_ref.locator}", context) + ValidationMessage( + f"externalPackageRef type in category OTHER must contain no spaces, but is: {external_package_ref.locator}", + context) ) - return validation_messages diff --git a/tests/spdx/validation/test_external_package_ref_validator.py b/tests/spdx/validation/test_external_package_ref_validator.py index bc451d430..86288fdc7 100644 --- a/tests/spdx/validation/test_external_package_ref_validator.py +++ b/tests/spdx/validation/test_external_package_ref_validator.py @@ -19,24 +19,58 @@ @pytest.mark.parametrize("category, reference_type, locator", - [(ExternalPackageRefCategory.SECURITY, "cpe22Type", "cpe:/o:canonical:ubuntu_linux:10.04:-:lts"), - (ExternalPackageRefCategory.SECURITY, "cpe23Type", "cpe:2.3:o:canonical:ubuntu_linux:10.04:-:lts:*:*:*:*:*"), - (ExternalPackageRefCategory.SECURITY, "advisory", "https://nvd.nist.gov/vuln/detail/CVE-2020-28498"), - (ExternalPackageRefCategory.SECURITY, "fix", "https://github.com/indutny/elliptic/commit/441b7428"), - (ExternalPackageRefCategory.SECURITY, "url", "https://github.com/christianlundkvist/blog/blob/master/2020_05_26_secp256k1_twist_attacks/secp256k1_twist_attacks.md"), + [(ExternalPackageRefCategory.SECURITY, "cpe22Type", + "cpe:/o:canonical:ubuntu_linux:10.04:-:lts"), + (ExternalPackageRefCategory.SECURITY, "cpe23Type", + "cpe:2.3:o:canonical:ubuntu_linux:10.04:-:lts:*:*:*:*:*"), + (ExternalPackageRefCategory.SECURITY, "advisory", + "https://nvd.nist.gov/vuln/detail/CVE-2020-28498"), + (ExternalPackageRefCategory.SECURITY, "fix", + "https://github.com/indutny/elliptic/commit/441b7428"), + (ExternalPackageRefCategory.SECURITY, "url", + "https://github.com/christianlundkvist/blog/blob/master/2020_05_26_secp256k1_twist_attacks/secp256k1_twist_attacks.md"), (ExternalPackageRefCategory.SECURITY, "swid", "swid:2df9de35-0aff-4a86-ace6-f7dddd1ade4c"), - (ExternalPackageRefCategory.PACKAGE_MANAGER, "maven-central", "org.apache.tomcat:tomcat:9.0.0.M4"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "maven-central", + "org.apache.tomcat:tomcat:9.0.0.M4"), (ExternalPackageRefCategory.PACKAGE_MANAGER, "npm", "http-server@0.3.0"), (ExternalPackageRefCategory.PACKAGE_MANAGER, "nuget", "Microsoft.AspNet.MVC/5.0.0"), (ExternalPackageRefCategory.PACKAGE_MANAGER, "bower", "modernizr#2.6.2"), - (ExternalPackageRefCategory.PACKAGE_MANAGER, "purl", "pkg:docker/debian@sha256:2f04d3d33b6027bb74ecc81397abe780649ec89f1a2af18d7022737d0482cefe"), - (ExternalPackageRefCategory.PERSISTENT_ID, "swh", "swh:1:cnt:94a9ed024d3859793618152ea559a168bbcbb5e2"), - (ExternalPackageRefCategory.PERSISTENT_ID, "swh", "swh:1:dir:d198bc9d7a6bcf6db04f476d29314f157507d505"), - (ExternalPackageRefCategory.PERSISTENT_ID, "swh", "swh:1:rev:309cf2674ee7a0749978cf8265ab91a60aea0f7d"), - (ExternalPackageRefCategory.PERSISTENT_ID, "swh", "swh:1:rel:22ece559cc7cc2364edc5e5593d63ae8bd229f9f"), - (ExternalPackageRefCategory.PERSISTENT_ID, "swh", "swh:1:snp:c7c108084bc0bf3d81436bf980b46e98bd338453"), - (ExternalPackageRefCategory.PERSISTENT_ID, "gitoid", "gitoid:blob:sha1:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64"), - (ExternalPackageRefCategory.PERSISTENT_ID, "gitoid", "gitoid:blob:sha256:3557f7eb43c621c71483743d4b37059bb80933e7f71277c0c3b3846159d1f61c"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "purl", + "pkg:docker/debian@sha256:2f04d3d33b6027bb74ecc81397abe780649ec89f1a2af18d7022737d0482cefe"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "purl", + "pkg:bitbucket/birkenfeld/pygments-main@244fd47e07d1014f0aed9c"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "purl", + "pkg:deb/debian/curl@7.50.3-1?arch=i386&distro=jessie"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "purl", + "pkg:docker/customer/dockerimage@sha256:244fd47e07d1004f0aed9c?repository_url=gcr.io"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "purl", + "pkg:gem/jruby-launcher@1.1.2?platform=java"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "purl", "pkg:gem/ruby-advisory-db-check@0.12.4"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "purl", + "pkg:github/package-url/purl-spec@244fd47e07d1004f0aed9c"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "purl", + "pkg:golang/google.golang.org/genproto#googleapis/api/annotations"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "purl", + "pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?repository_url=repo.spring.io%2Frelease"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "purl", "pkg:npm/%40angular/animation@12.3.1"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "purl", + "pkg:nuget/EnterpriseLibrary.Common@6.0.1304"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "purl", + "pkg:rpm/fedora/curl@7.50.3-1.fc25?arch=i386&distro=fedora-25"), + (ExternalPackageRefCategory.PERSISTENT_ID, "swh", + "swh:1:cnt:94a9ed024d3859793618152ea559a168bbcbb5e2"), + (ExternalPackageRefCategory.PERSISTENT_ID, "swh", + "swh:1:dir:d198bc9d7a6bcf6db04f476d29314f157507d505"), + (ExternalPackageRefCategory.PERSISTENT_ID, "swh", + "swh:1:rev:309cf2674ee7a0749978cf8265ab91a60aea0f7d"), + (ExternalPackageRefCategory.PERSISTENT_ID, "swh", + "swh:1:rel:22ece559cc7cc2364edc5e5593d63ae8bd229f9f"), + (ExternalPackageRefCategory.PERSISTENT_ID, "swh", + "swh:1:snp:c7c108084bc0bf3d81436bf980b46e98bd338453"), + (ExternalPackageRefCategory.PERSISTENT_ID, "gitoid", + "gitoid:blob:sha1:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64"), + (ExternalPackageRefCategory.PERSISTENT_ID, "gitoid", + "gitoid:blob:sha256:3557f7eb43c621c71483743d4b37059bb80933e7f71277c0c3b3846159d1f61c"), (ExternalPackageRefCategory.OTHER, "some idstring", "#//string-withOUT!Spaces\\?") ]) def test_valid_external_package_ref(category, reference_type, locator): @@ -47,11 +81,14 @@ def test_valid_external_package_ref(category, reference_type, locator): @pytest.mark.parametrize("category, reference_type, locator, expected_message", - [(ExternalPackageRefCategory.SECURITY, "cpe22Typo", "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", - "externalPackageRef type in category SECURITY must be one of [cpe22Type, cpe23Type, advisory, fix, url, swid], but is: cpe22Typo"), - (ExternalPackageRefCategory.PACKAGE_MANAGER, "nugat", "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", + [( + ExternalPackageRefCategory.SECURITY, "cpe22Typo", "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", + "externalPackageRef type in category SECURITY must be one of [cpe22Type, cpe23Type, advisory, fix, url, swid], but is: cpe22Typo"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "nugat", + "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", "externalPackageRef type in category PACKAGE_MANAGER must be one of [maven-central, npm, nuget, bower, purl], but is: nugat"), - (ExternalPackageRefCategory.PERSISTENT_ID, "git-oid", "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", + (ExternalPackageRefCategory.PERSISTENT_ID, "git-oid", + "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", "externalPackageRef type in category PERSISTENT_ID must be one of [swh, gitoid], but is: git-oid") ]) def test_invalid_external_package_ref_types(category, reference_type, locator, expected_message): @@ -73,14 +110,16 @@ def test_invalid_external_package_ref_types(category, reference_type, locator, e NPM_REGEX = r'^[^@]+@[^@]+$' NUGET_REGEX = r'^[^/]+/[^/]+$' BOWER_REGEX = r'^[^#]+#[^#]+$' -PURL_REGEX = None # TODO: add negative test for purl +PURL_REGEX = r'^pkg:.+(\/.+)?\/.+(@.+)?(\?.+)?(#.+)?$' SWH_REGEX = r'^swh:1:(snp|rel|rev|dir|cnt):[0-9a-fA-F]{40}$' GITOID_REGEX = r'^gitoid:(blob|tree|commit|tag):(sha1:[0-9a-fA-F]{40}|sha256:[0-9a-fA-F]{64})$' + @pytest.mark.parametrize("category, reference_type, locator, expected_message", [(ExternalPackageRefCategory.SECURITY, "cpe22Type", "cpe:o:canonical:ubuntu_linux:10.04:-:lts", f'externalPackageRef locator of type "cpe22Type" must conform with the regex {CPE22TYPE_REGEX}, but is: cpe:o:canonical:ubuntu_linux:10.04:-:lts'), - (ExternalPackageRefCategory.SECURITY, "cpe23Type", "cpe:2.3:/o:canonical:ubuntu_linux:10.04:-:lts:*:*:*:*:*", + (ExternalPackageRefCategory.SECURITY, "cpe23Type", + "cpe:2.3:/o:canonical:ubuntu_linux:10.04:-:lts:*:*:*:*:*", f'externalPackageRef locator of type "cpe23Type" must conform with the regex {CPE23TYPE_REGEX}, but is: cpe:2.3:/o:canonical:ubuntu_linux:10.04:-:lts:*:*:*:*:*'), (ExternalPackageRefCategory.SECURITY, "advisory", "http://locatorurl", f'externalPackageRef locator of type "advisory" must be a valid URL, but is: http://locatorurl'), @@ -90,7 +129,8 @@ def test_invalid_external_package_ref_types(category, reference_type, locator, e f'externalPackageRef locator of type "url" must be a valid URL, but is: http://url'), (ExternalPackageRefCategory.SECURITY, "swid", "2df9de35-0aff-4a86-ace6-f7dddd1ade4c", f'externalPackageRef locator of type "swid" must be a valid URI specified in RFC-3986, but is: 2df9de35-0aff-4a86-ace6-f7dddd1ade4c'), - (ExternalPackageRefCategory.PACKAGE_MANAGER, "maven-central", "org.apache.tomcat:tomcat:tomcat:9.0.0.M4", + (ExternalPackageRefCategory.PACKAGE_MANAGER, "maven-central", + "org.apache.tomcat:tomcat:tomcat:9.0.0.M4", f'externalPackageRef locator of type "maven-central" must conform with the regex {MAVEN_CENTRAL_REGEX}, but is: org.apache.tomcat:tomcat:tomcat:9.0.0.M4'), (ExternalPackageRefCategory.PACKAGE_MANAGER, "npm", "http-server:0.3.0", f'externalPackageRef locator of type "npm" must conform with the regex {NPM_REGEX}, but is: http-server:0.3.0'), @@ -98,11 +138,16 @@ def test_invalid_external_package_ref_types(category, reference_type, locator, e f'externalPackageRef locator of type "nuget" must conform with the regex {NUGET_REGEX}, but is: Microsoft.AspNet.MVC@5.0.0'), (ExternalPackageRefCategory.PACKAGE_MANAGER, "bower", "modernizr:2.6.2", f'externalPackageRef locator of type "bower" must conform with the regex {BOWER_REGEX}, but is: modernizr:2.6.2'), - (ExternalPackageRefCategory.PERSISTENT_ID, "swh", "swh:cnt:94a9ed024d3859793618152ea559a168bbcbb5e2", + (ExternalPackageRefCategory.PACKAGE_MANAGER, "purl", "pkg:npm@12.3.1", + f'externalPackageRef locator of type "purl" must conform with the regex {PURL_REGEX}, but is: pkg:npm@12.3.1'), + (ExternalPackageRefCategory.PERSISTENT_ID, "swh", + "swh:cnt:94a9ed024d3859793618152ea559a168bbcbb5e2", f'externalPackageRef locator of type "swh" must conform with the regex {SWH_REGEX}, but is: swh:cnt:94a9ed024d3859793618152ea559a168bbcbb5e2'), - (ExternalPackageRefCategory.PERSISTENT_ID, "gitoid", "gitoid:blob:sha1:3557f7eb43c621c71483743d4b37059bb80933e7f71277c0c3b3846159d1f61c", + (ExternalPackageRefCategory.PERSISTENT_ID, "gitoid", + "gitoid:blob:sha1:3557f7eb43c621c71483743d4b37059bb80933e7f71277c0c3b3846159d1f61c", f'externalPackageRef locator of type "gitoid" must conform with the regex {GITOID_REGEX}, but is: gitoid:blob:sha1:3557f7eb43c621c71483743d4b37059bb80933e7f71277c0c3b3846159d1f61c'), - (ExternalPackageRefCategory.PERSISTENT_ID, "gitoid", "gitoid:blob:sha256:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64", + (ExternalPackageRefCategory.PERSISTENT_ID, "gitoid", + "gitoid:blob:sha256:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64", f'externalPackageRef locator of type "gitoid" must conform with the regex {GITOID_REGEX}, but is: gitoid:blob:sha256:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64'), (ExternalPackageRefCategory.OTHER, "id string", "locator string", "externalPackageRef type in category OTHER must contain no spaces, but is: locator string"), From 81dc6a5ae50a60c449c0c5af82b1a046e2ef0dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 25 Jan 2023 12:37:22 +0100 Subject: [PATCH 156/362] [issue-373] refactoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- .../external_package_ref_validator.py | 178 +++++++----------- .../test_external_package_ref_validator.py | 2 +- 2 files changed, 70 insertions(+), 110 deletions(-) diff --git a/src/spdx/validation/external_package_ref_validator.py b/src/spdx/validation/external_package_ref_validator.py index 092429461..cf9df0f2a 100644 --- a/src/spdx/validation/external_package_ref_validator.py +++ b/src/spdx/validation/external_package_ref_validator.py @@ -36,116 +36,76 @@ def validate_external_package_refs(external_package_refs: List[ExternalPackageRe def validate_external_package_ref(external_package_ref: ExternalPackageRef, parent_id: str) -> List[ValidationMessage]: - validation_messages = [] context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.EXTERNAL_PACKAGE_REF, full_element=external_package_ref) - if external_package_ref.category == ExternalPackageRefCategory.SECURITY: - if external_package_ref.reference_type == "cpe22Type": - if not re.match(CPE22TYPE_REGEX, external_package_ref.locator): - validation_messages.append( - ValidationMessage( - f'externalPackageRef locator of type "cpe22Type" must conform with the regex {CPE22TYPE_REGEX}, but is: {external_package_ref.locator}', - context) - ) - elif external_package_ref.reference_type == "cpe23Type": - if not re.match(CPE23TYPE_REGEX, external_package_ref.locator): - validation_messages.append( - ValidationMessage( - f'externalPackageRef locator of type "cpe23Type" must conform with the regex {CPE23TYPE_REGEX}, but is: {external_package_ref.locator}', - context) - ) - elif external_package_ref.reference_type in ["advisory", "fix", "url"]: - for message in validate_url(external_package_ref.locator): - validation_messages.append( - ValidationMessage( - f'externalPackageRef locator of type "{external_package_ref.reference_type}" {message}', - context) - ) - elif external_package_ref.reference_type == "swid": - for message in validate_uri(external_package_ref.locator): - validation_messages.append( - ValidationMessage( - f'externalPackageRef locator of type "{external_package_ref.reference_type}" {message}', - context) - ) - else: - validation_messages.append( - ValidationMessage( - f"externalPackageRef type in category SECURITY must be one of [cpe22Type, cpe23Type, advisory, fix, url, swid], but is: {external_package_ref.reference_type}", - context) - ) - - elif external_package_ref.category == ExternalPackageRefCategory.PACKAGE_MANAGER: - if external_package_ref.reference_type == "maven-central": - if not re.match(MAVEN_CENTRAL_REGEX, external_package_ref.locator): - validation_messages.append( - ValidationMessage( - f'externalPackageRef locator of type "maven-central" must conform with the regex {MAVEN_CENTRAL_REGEX}, but is: {external_package_ref.locator}', - context) - ) - elif external_package_ref.reference_type == "npm": - if not re.match(NPM_REGEX, external_package_ref.locator): - validation_messages.append( - ValidationMessage( - f'externalPackageRef locator of type "npm" must conform with the regex {NPM_REGEX}, but is: {external_package_ref.locator}', - context) - ) - elif external_package_ref.reference_type == "nuget": - if not re.match(NUGET_REGEX, external_package_ref.locator): - validation_messages.append( - ValidationMessage( - f'externalPackageRef locator of type "nuget" must conform with the regex {NUGET_REGEX}, but is: {external_package_ref.locator}', - context) - ) - elif external_package_ref.reference_type == "bower": - if not re.match(BOWER_REGEX, external_package_ref.locator): - validation_messages.append( - ValidationMessage( - f'externalPackageRef locator of type "bower" must conform with the regex {BOWER_REGEX}, but is: {external_package_ref.locator}', - context) - ) - elif external_package_ref.reference_type == "purl": - if not re.match(PURL_REGEX, external_package_ref.locator): - validation_messages.append( - ValidationMessage( - f'externalPackageRef locator of type "purl" must conform with the regex {PURL_REGEX}, but is: {external_package_ref.locator}', - context) - ) - else: - validation_messages.append( - ValidationMessage( - f"externalPackageRef type in category PACKAGE_MANAGER must be one of [maven-central, npm, nuget, bower, purl], but is: {external_package_ref.reference_type}", - context) - ) - - elif external_package_ref.category == ExternalPackageRefCategory.PERSISTENT_ID: - if external_package_ref.reference_type == "swh": - if not re.match(SWH_REGEX, external_package_ref.locator): - validation_messages.append( - ValidationMessage( - f'externalPackageRef locator of type "swh" must conform with the regex {SWH_REGEX}, but is: {external_package_ref.locator}', - context) - ) - elif external_package_ref.reference_type == "gitoid": - if not re.match(GITOID_REGEX, external_package_ref.locator): - validation_messages.append( - ValidationMessage( - f'externalPackageRef locator of type "gitoid" must conform with the regex {GITOID_REGEX}, but is: {external_package_ref.locator}', - context) - ) - else: - validation_messages.append( - ValidationMessage( - f"externalPackageRef type in category PERSISTENT_ID must be one of [swh, gitoid], but is: {external_package_ref.reference_type}", - context) - ) - elif external_package_ref.category == ExternalPackageRefCategory.OTHER: - if " " in external_package_ref.locator: - validation_messages.append( - ValidationMessage( - f"externalPackageRef type in category OTHER must contain no spaces, but is: {external_package_ref.locator}", - context) - ) + category = external_package_ref.category + locator = external_package_ref.locator + reference_type = external_package_ref.reference_type - return validation_messages + if category == ExternalPackageRefCategory.SECURITY: + if reference_type == "cpe22Type": + return validate_against_regex(locator, CPE22TYPE_REGEX, "cpe22Type", context) + if reference_type == "cpe23Type": + return validate_against_regex(locator, CPE23TYPE_REGEX, "cpe23Type", context) + if reference_type in ["advisory", "fix", "url"]: + if validate_url(locator): + return [ValidationMessage( + f'externalPackageRef locator of type "{reference_type}" must be a valid URL, but is: {locator}', + context)] + return [] + if reference_type == "swid": + if validate_uri(locator) or not locator.startswith("swid"): + return [ValidationMessage( + f'externalPackageRef locator of type "swid" must be a valid URI with scheme swid, but is: {locator}', + context)] + return [] + + return [ValidationMessage( + f"externalPackageRef type in category SECURITY must be one of [cpe22Type, cpe23Type, advisory, fix, url, swid], but is: {reference_type}", + context)] + + if category == ExternalPackageRefCategory.PACKAGE_MANAGER: + if reference_type == "maven-central": + return validate_against_regex(locator, MAVEN_CENTRAL_REGEX, "maven-central", context) + if reference_type == "npm": + return validate_against_regex(locator, NPM_REGEX, "npm", context) + if reference_type == "nuget": + return validate_against_regex(locator, NUGET_REGEX, "nuget", context) + if reference_type == "bower": + return validate_against_regex(locator, BOWER_REGEX, "bower", context) + if reference_type == "purl": + return validate_against_regex(locator, PURL_REGEX, "purl", context) + + return [ValidationMessage( + f"externalPackageRef type in category PACKAGE_MANAGER must be one of [maven-central, npm, nuget, bower, purl], but is: {reference_type}", + context)] + + if category == ExternalPackageRefCategory.PERSISTENT_ID: + if reference_type == "swh": + return validate_against_regex(locator, SWH_REGEX, "swh", context) + if reference_type == "gitoid": + return validate_against_regex(locator, GITOID_REGEX, "gitoid", context) + + return [ValidationMessage( + f"externalPackageRef type in category PERSISTENT_ID must be one of [swh, gitoid], but is: {reference_type}", + context)] + + if category == ExternalPackageRefCategory.OTHER: + if " " in locator: + return [ValidationMessage( + f"externalPackageRef type in category OTHER must contain no spaces, but is: {locator}", + context)] + return [] + + + +def validate_against_regex(string_to_validate: str, regex: str, type_name: str, context: ValidationContext) -> List[ + ValidationMessage]: + if not re.match(regex, string_to_validate): + return [ValidationMessage( + f'externalPackageRef locator of type "{type_name}" must conform with the regex {regex}, but is: {string_to_validate}', + context) + ] + + return [] diff --git a/tests/spdx/validation/test_external_package_ref_validator.py b/tests/spdx/validation/test_external_package_ref_validator.py index 86288fdc7..a47d4c31f 100644 --- a/tests/spdx/validation/test_external_package_ref_validator.py +++ b/tests/spdx/validation/test_external_package_ref_validator.py @@ -128,7 +128,7 @@ def test_invalid_external_package_ref_types(category, reference_type, locator, e (ExternalPackageRefCategory.SECURITY, "url", "http://url", f'externalPackageRef locator of type "url" must be a valid URL, but is: http://url'), (ExternalPackageRefCategory.SECURITY, "swid", "2df9de35-0aff-4a86-ace6-f7dddd1ade4c", - f'externalPackageRef locator of type "swid" must be a valid URI specified in RFC-3986, but is: 2df9de35-0aff-4a86-ace6-f7dddd1ade4c'), + f'externalPackageRef locator of type "swid" must be a valid URI with scheme swid, but is: 2df9de35-0aff-4a86-ace6-f7dddd1ade4c'), (ExternalPackageRefCategory.PACKAGE_MANAGER, "maven-central", "org.apache.tomcat:tomcat:tomcat:9.0.0.M4", f'externalPackageRef locator of type "maven-central" must conform with the regex {MAVEN_CENTRAL_REGEX}, but is: org.apache.tomcat:tomcat:tomcat:9.0.0.M4'), From 0f8669123157e8dc8067aaca4c897e1fb7453e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 25 Jan 2023 14:09:11 +0100 Subject: [PATCH 157/362] [issue-373, review] import regex expressions in tests instead of redefining them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- .../test_external_package_ref_validator.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/tests/spdx/validation/test_external_package_ref_validator.py b/tests/spdx/validation/test_external_package_ref_validator.py index a47d4c31f..893294fe2 100644 --- a/tests/spdx/validation/test_external_package_ref_validator.py +++ b/tests/spdx/validation/test_external_package_ref_validator.py @@ -14,7 +14,8 @@ import pytest from spdx.model.package import ExternalPackageRef, ExternalPackageRefCategory -from spdx.validation.external_package_ref_validator import validate_external_package_ref +from spdx.validation.external_package_ref_validator import validate_external_package_ref, CPE22TYPE_REGEX, \ + CPE23TYPE_REGEX, MAVEN_CENTRAL_REGEX, NPM_REGEX, NUGET_REGEX, BOWER_REGEX, PURL_REGEX, SWH_REGEX, GITOID_REGEX from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType @@ -104,17 +105,6 @@ def test_invalid_external_package_ref_types(category, reference_type, locator, e assert validation_messages == [expected] -CPE22TYPE_REGEX = r'^c[pP][eE]:/[AHOaho]?(:[A-Za-z0-9._\-~%]*){0,6}$' -CPE23TYPE_REGEX = r'^cpe:2\.3:[aho\*\-](:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#$$%&\'\(\)\+,\/:;<=>@\[\]\^`\{\|}~]))+(\?*|\*?))|[\*\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\*\-]))(:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#$$%&\'\(\)\+,\/:;<=>@\[\]\^`\{\|}~]))+(\?*|\*?))|[\*\-])){4}$' -MAVEN_CENTRAL_REGEX = r'^[^:]+:[^:]+(:[^:]+)?$' -NPM_REGEX = r'^[^@]+@[^@]+$' -NUGET_REGEX = r'^[^/]+/[^/]+$' -BOWER_REGEX = r'^[^#]+#[^#]+$' -PURL_REGEX = r'^pkg:.+(\/.+)?\/.+(@.+)?(\?.+)?(#.+)?$' -SWH_REGEX = r'^swh:1:(snp|rel|rev|dir|cnt):[0-9a-fA-F]{40}$' -GITOID_REGEX = r'^gitoid:(blob|tree|commit|tag):(sha1:[0-9a-fA-F]{40}|sha256:[0-9a-fA-F]{64})$' - - @pytest.mark.parametrize("category, reference_type, locator, expected_message", [(ExternalPackageRefCategory.SECURITY, "cpe22Type", "cpe:o:canonical:ubuntu_linux:10.04:-:lts", f'externalPackageRef locator of type "cpe22Type" must conform with the regex {CPE22TYPE_REGEX}, but is: cpe:o:canonical:ubuntu_linux:10.04:-:lts'), From 6b3899f96e549dd414543176c81dbca09d4b19ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 25 Jan 2023 14:32:55 +0100 Subject: [PATCH 158/362] [issue-373, review] refactoring using dictionaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/model/package.py | 9 +- .../external_package_ref_validator.py | 89 ++++++++----------- .../test_external_package_ref_validator.py | 8 +- 3 files changed, 48 insertions(+), 58 deletions(-) diff --git a/src/spdx/model/package.py b/src/spdx/model/package.py index bbeff650a..0a621701b 100644 --- a/src/spdx/model/package.py +++ b/src/spdx/model/package.py @@ -11,7 +11,7 @@ from dataclasses import field from datetime import datetime from enum import Enum, auto -from typing import Optional, Union, List +from typing import Optional, Union, List, Dict from spdx.model.actor import Actor from spdx.model.checksum import Checksum @@ -54,6 +54,13 @@ class ExternalPackageRefCategory(Enum): OTHER = auto() +CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES: Dict[ExternalPackageRefCategory, List[str]] = { + ExternalPackageRefCategory.SECURITY : ["cpe22Type", "cpe23Type", "advisory", "fix", "url", "swid"], + ExternalPackageRefCategory.PACKAGE_MANAGER : ["maven-central", "npm", "nuget", "bower", "purl"], + ExternalPackageRefCategory.PERSISTENT_ID : ["swh", "gitoid"] +} + + @dataclass_with_properties class ExternalPackageRef: category: ExternalPackageRefCategory diff --git a/src/spdx/validation/external_package_ref_validator.py b/src/spdx/validation/external_package_ref_validator.py index cf9df0f2a..7be94f6ef 100644 --- a/src/spdx/validation/external_package_ref_validator.py +++ b/src/spdx/validation/external_package_ref_validator.py @@ -9,9 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. import re -from typing import List +from typing import List, Dict -from spdx.model.package import ExternalPackageRef, ExternalPackageRefCategory +from spdx.model.package import ExternalPackageRef, ExternalPackageRefCategory, CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES from spdx.validation.uri_validators import validate_url, validate_uri from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType @@ -25,6 +25,18 @@ SWH_REGEX = r'^swh:1:(snp|rel|rev|dir|cnt):[0-9a-fA-F]{40}$' GITOID_REGEX = r'^gitoid:(blob|tree|commit|tag):(sha1:[0-9a-fA-F]{40}|sha256:[0-9a-fA-F]{64})$' +TYPE_TO_REGEX: Dict[str, str] = { + "cpe22Type": CPE22TYPE_REGEX, + "cpe23Type": CPE23TYPE_REGEX, + "maven-central": MAVEN_CENTRAL_REGEX, + "npm": NPM_REGEX, + "nuget": NUGET_REGEX, + "bower": BOWER_REGEX, + "purl": PURL_REGEX, + "swh": SWH_REGEX, + "gitoid": GITOID_REGEX +} + def validate_external_package_refs(external_package_refs: List[ExternalPackageRef], parent_id: str) -> List[ ValidationMessage]: @@ -43,69 +55,40 @@ def validate_external_package_ref(external_package_ref: ExternalPackageRef, pare locator = external_package_ref.locator reference_type = external_package_ref.reference_type - if category == ExternalPackageRefCategory.SECURITY: - if reference_type == "cpe22Type": - return validate_against_regex(locator, CPE22TYPE_REGEX, "cpe22Type", context) - if reference_type == "cpe23Type": - return validate_against_regex(locator, CPE23TYPE_REGEX, "cpe23Type", context) - if reference_type in ["advisory", "fix", "url"]: - if validate_url(locator): - return [ValidationMessage( - f'externalPackageRef locator of type "{reference_type}" must be a valid URL, but is: {locator}', - context)] - return [] - if reference_type == "swid": - if validate_uri(locator) or not locator.startswith("swid"): - return [ValidationMessage( - f'externalPackageRef locator of type "swid" must be a valid URI with scheme swid, but is: {locator}', - context)] - return [] - - return [ValidationMessage( - f"externalPackageRef type in category SECURITY must be one of [cpe22Type, cpe23Type, advisory, fix, url, swid], but is: {reference_type}", - context)] - - if category == ExternalPackageRefCategory.PACKAGE_MANAGER: - if reference_type == "maven-central": - return validate_against_regex(locator, MAVEN_CENTRAL_REGEX, "maven-central", context) - if reference_type == "npm": - return validate_against_regex(locator, NPM_REGEX, "npm", context) - if reference_type == "nuget": - return validate_against_regex(locator, NUGET_REGEX, "nuget", context) - if reference_type == "bower": - return validate_against_regex(locator, BOWER_REGEX, "bower", context) - if reference_type == "purl": - return validate_against_regex(locator, PURL_REGEX, "purl", context) + if category == ExternalPackageRefCategory.OTHER: + if " " in locator: + return [ValidationMessage( + f"externalPackageRef locator in category OTHER must contain no spaces, but is: {locator}", + context)] + return [] + if reference_type not in CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES[category]: return [ValidationMessage( - f"externalPackageRef type in category PACKAGE_MANAGER must be one of [maven-central, npm, nuget, bower, purl], but is: {reference_type}", + f"externalPackageRef type in category {category.name} must be one of {CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES[category]}, but is: {reference_type}", context)] - if category == ExternalPackageRefCategory.PERSISTENT_ID: - if reference_type == "swh": - return validate_against_regex(locator, SWH_REGEX, "swh", context) - if reference_type == "gitoid": - return validate_against_regex(locator, GITOID_REGEX, "gitoid", context) - - return [ValidationMessage( - f"externalPackageRef type in category PERSISTENT_ID must be one of [swh, gitoid], but is: {reference_type}", - context)] + if reference_type in ["advisory", "fix", "url"]: + if validate_url(locator): + return [ValidationMessage( + f'externalPackageRef locator of type "{reference_type}" must be a valid URL, but is: {locator}', + context)] + return [] - if category == ExternalPackageRefCategory.OTHER: - if " " in locator: + if reference_type == "swid": + if validate_uri(locator) or not locator.startswith("swid"): return [ValidationMessage( - f"externalPackageRef type in category OTHER must contain no spaces, but is: {locator}", + f'externalPackageRef locator of type "swid" must be a valid URI with scheme swid, but is: {locator}', context)] return [] + return validate_against_regex(locator, reference_type, context) -def validate_against_regex(string_to_validate: str, regex: str, type_name: str, context: ValidationContext) -> List[ +def validate_against_regex(string_to_validate: str, reference_type: str, context: ValidationContext) -> List[ ValidationMessage]: + regex = TYPE_TO_REGEX[reference_type] if not re.match(regex, string_to_validate): return [ValidationMessage( - f'externalPackageRef locator of type "{type_name}" must conform with the regex {regex}, but is: {string_to_validate}', - context) - ] - + f'externalPackageRef locator of type "{reference_type}" must conform with the regex {regex}, but is: {string_to_validate}', + context)] return [] diff --git a/tests/spdx/validation/test_external_package_ref_validator.py b/tests/spdx/validation/test_external_package_ref_validator.py index 893294fe2..f0085c868 100644 --- a/tests/spdx/validation/test_external_package_ref_validator.py +++ b/tests/spdx/validation/test_external_package_ref_validator.py @@ -84,13 +84,13 @@ def test_valid_external_package_ref(category, reference_type, locator): @pytest.mark.parametrize("category, reference_type, locator, expected_message", [( ExternalPackageRefCategory.SECURITY, "cpe22Typo", "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", - "externalPackageRef type in category SECURITY must be one of [cpe22Type, cpe23Type, advisory, fix, url, swid], but is: cpe22Typo"), + "externalPackageRef type in category SECURITY must be one of ['cpe22Type', 'cpe23Type', 'advisory', 'fix', 'url', 'swid'], but is: cpe22Typo"), (ExternalPackageRefCategory.PACKAGE_MANAGER, "nugat", "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", - "externalPackageRef type in category PACKAGE_MANAGER must be one of [maven-central, npm, nuget, bower, purl], but is: nugat"), + "externalPackageRef type in category PACKAGE_MANAGER must be one of ['maven-central', 'npm', 'nuget', 'bower', 'purl'], but is: nugat"), (ExternalPackageRefCategory.PERSISTENT_ID, "git-oid", "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", - "externalPackageRef type in category PERSISTENT_ID must be one of [swh, gitoid], but is: git-oid") + "externalPackageRef type in category PERSISTENT_ID must be one of ['swh', 'gitoid'], but is: git-oid") ]) def test_invalid_external_package_ref_types(category, reference_type, locator, expected_message): external_package_ref = ExternalPackageRef(category, reference_type, locator, "externalPackageRef comment") @@ -140,7 +140,7 @@ def test_invalid_external_package_ref_types(category, reference_type, locator, e "gitoid:blob:sha256:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64", f'externalPackageRef locator of type "gitoid" must conform with the regex {GITOID_REGEX}, but is: gitoid:blob:sha256:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64'), (ExternalPackageRefCategory.OTHER, "id string", "locator string", - "externalPackageRef type in category OTHER must contain no spaces, but is: locator string"), + "externalPackageRef locator in category OTHER must contain no spaces, but is: locator string"), ]) def test_invalid_external_package_ref_locators(category, reference_type, locator, expected_message): external_package_ref = ExternalPackageRef(category, reference_type, locator, "externalPackageRef comment") From 83ef78864e6c4b49b062acc6a4c47b4fcf2a6e1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 25 Jan 2023 14:57:38 +0100 Subject: [PATCH 159/362] [issue-443] change file name validation logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/validation/file_validator.py | 4 ++-- tests/spdx/validation/test_file_validator.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/spdx/validation/file_validator.py b/src/spdx/validation/file_validator.py index cc4375ebd..16845e55d 100644 --- a/src/spdx/validation/file_validator.py +++ b/src/spdx/validation/file_validator.py @@ -50,10 +50,10 @@ def validate_file(file: File, context: Optional[ValidationContext] = None) -> Li if not context: context = ValidationContext(spdx_id=file.spdx_id, element_type=SpdxElementType.FILE, full_element=file) - if not file.name.startswith("./"): + if file.name.startswith("/"): validation_messages.append( ValidationMessage( - f'file name must be a relative path to the file, starting with "./", but is: {file.name}', context) + f'file name must not be an absolute path starting with "/", but is: {file.name}', context) ) if ChecksumAlgorithm.SHA1 not in [checksum.algorithm for checksum in file.checksums]: diff --git a/tests/spdx/validation/test_file_validator.py b/tests/spdx/validation/test_file_validator.py index 040edde6e..d06a24d63 100644 --- a/tests/spdx/validation/test_file_validator.py +++ b/tests/spdx/validation/test_file_validator.py @@ -27,9 +27,9 @@ def test_valid_file(): @pytest.mark.parametrize("file_input, spdx_id, expected_message", - [(file_fixture(name="invalid file name"), file_fixture().spdx_id, - 'file name must be a relative path to the file, starting with "./", but is: invalid file name'), - ( + [(file_fixture(name="/invalid/file/name"), file_fixture().spdx_id, + f'file name must not be an absolute path starting with "/", but is: /invalid/file/name'), + ( file_fixture(checksums=[Checksum(ChecksumAlgorithm.MD2, "d4c41ce30a517d6ce9d79c8c17bb4b66")]), file_fixture().spdx_id, f'checksums must contain a SHA1 algorithm checksum, but only contains: []') From 34fabfde078eb9d040bdfa594dbc39a9e2f238ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 25 Jan 2023 15:35:18 +0100 Subject: [PATCH 160/362] [issue-442] change CLI to read version from document MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit also print validation output to stderr Signed-off-by: Armin Tänzer --- src/spdx/clitools/pyspdxtools.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/spdx/clitools/pyspdxtools.py b/src/spdx/clitools/pyspdxtools.py index 1f98b624a..d292d004f 100644 --- a/src/spdx/clitools/pyspdxtools.py +++ b/src/spdx/clitools/pyspdxtools.py @@ -26,7 +26,7 @@ @click.command() @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("--version", help='The SPDX version to be used during parsing and validation (format "SPDX-2.3"). Will be read from the document if not provided.', default=None) @click.option("--novalidation", is_flag=True, help="Don't validate the provided document.") def main(infile: str, outfile: str, version: str, novalidation: bool): """ @@ -39,27 +39,29 @@ def main(infile: str, outfile: str, version: str, novalidation: bool): if outfile == "-": tagvalue_writer.write_document(document, sys.stdout) - print("") if not novalidation: + if not version: + version = document.creation_info.spdx_version + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, version) if validation_messages: - print("The document is invalid. The following issues have been found:") + print("The document is invalid. The following issues have been found:", file=sys.stderr) for message in validation_messages: - print(message.validation_message) + print(message.validation_message, file=sys.stderr) sys.exit(1) else: - print("The document is valid.") + print("The document is valid.", file=sys.stderr) if outfile and outfile != "-": write_file(document, outfile, validate=False) except NotImplementedError as err: - print(err.args[0]) + print(err.args[0], file=sys.stderr) print("Please note that this project is currently undergoing a major refactoring and therefore missing " "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.") + "In the meantime, please use the PyPI release version 0.7.0.", file=sys.stderr) sys.exit(1) From d53df99760737ff707e2d8d5267a0dc4474dab20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 26 Jan 2023 08:52:54 +0100 Subject: [PATCH 161/362] [issue-442, review] except SPDXParsingError and delete CLI prompt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/clitools/pyspdxtools.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/spdx/clitools/pyspdxtools.py b/src/spdx/clitools/pyspdxtools.py index d292d004f..695cf99e8 100644 --- a/src/spdx/clitools/pyspdxtools.py +++ b/src/spdx/clitools/pyspdxtools.py @@ -16,6 +16,7 @@ import click from spdx.model.document import Document +from spdx.parser.error import SPDXParsingError from spdx.parser.parse_anything import parse_file from spdx.validation.document_validator import validate_full_spdx_document from spdx.validation.validation_message import ValidationMessage @@ -24,7 +25,7 @@ @click.command() -@click.option("--infile", "-i", prompt="input file path", help="The file containing the document to be validated or converted.") +@click.option("--infile", "-i", 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"). Will be read from the document if not provided.', default=None) @click.option("--novalidation", is_flag=True, help="Don't validate the provided document.") @@ -64,6 +65,12 @@ def main(infile: str, outfile: str, version: str, novalidation: bool): "In the meantime, please use the PyPI release version 0.7.0.", file=sys.stderr) sys.exit(1) + except SPDXParsingError as err: + print("There have been issues while parsing the provided document:", file=sys.stderr) + for message in err.get_messages(): + print(message, file=sys.stderr) + sys.exit(1) + if __name__ == "__main__": main() From 603fce4967ded13ba1d00b4b1df423689ad98831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 26 Jan 2023 16:28:32 +0100 Subject: [PATCH 162/362] [issue-10] use license_expression library for LicenseExpression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- pyproject.toml | 2 +- src/spdx/jsonschema/optional_utils.py | 2 +- src/spdx/model/file.py | 2 +- src/spdx/model/license_expression.py | 26 --------------- src/spdx/model/package.py | 2 +- src/spdx/model/snippet.py | 2 +- src/spdx/parser/jsonlikedict/file_parser.py | 2 +- .../jsonlikedict/license_expression_parser.py | 32 +++++++++++++------ .../parser/jsonlikedict/package_parser.py | 2 +- .../parser/jsonlikedict/snippet_parser.py | 2 +- .../license_expression_validator.py | 2 +- src/spdx/writer/tagvalue/file_writer.py | 8 ++--- src/spdx/writer/tagvalue/package_writer.py | 11 ++++--- src/spdx/writer/tagvalue/snippet_writer.py | 8 ++--- .../tagvalue_writer_helper_functions.py | 15 ++------- tests/spdx/fixtures.py | 32 +++++++++++-------- tests/spdx/jsonschema/test_file_converter.py | 10 +++--- .../spdx/jsonschema/test_package_converter.py | 17 +++++----- .../spdx/jsonschema/test_snippet_converter.py | 13 ++++---- tests/spdx/model/test_package.py | 6 ++-- .../parser/jsonlikedict/test_file_parser.py | 6 ++-- .../test_license_expression_parser.py | 29 +++++++++-------- .../jsonlikedict/test_package_parser.py | 10 +++--- .../jsonlikedict/test_snippet_parser.py | 6 ++-- .../test_license_expression_validator.py | 4 +-- .../spdx/validation/test_package_validator.py | 7 ++-- tests/spdx/validation/test_uri_validators.py | 2 +- .../json/expected_results/expected.json | 20 ++++-------- .../writer/tagvalue/test_package_writer.py | 7 ++-- 29 files changed, 132 insertions(+), 155 deletions(-) delete mode 100644 src/spdx/model/license_expression.py diff --git a/pyproject.toml b/pyproject.toml index 352933462..b7f176add 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ ] urls = {Homepage = "https://github.com/spdx/tools-python"} requires-python = ">=3.7" -dependencies = ["ply", "rdflib", "click", "pyyaml", "xmltodict", "typeguard", "uritools"] +dependencies = ["ply", "rdflib", "click", "pyyaml", "xmltodict", "typeguard", "uritools", "license_expression"] dynamic = ["version"] [project.optional-dependencies] diff --git a/src/spdx/jsonschema/optional_utils.py b/src/spdx/jsonschema/optional_utils.py index 81c0ba041..14824ed9a 100644 --- a/src/spdx/jsonschema/optional_utils.py +++ b/src/spdx/jsonschema/optional_utils.py @@ -18,4 +18,4 @@ def apply_if_present(function: Callable[[T], S], optional_value: Optional[T]) -> """ Apply the passed function to the optional value if it is not None. Else returns None. """ - return function(optional_value) if optional_value else None + return function(optional_value) if optional_value is not None else None diff --git a/src/spdx/model/file.py b/src/spdx/model/file.py index 2bfa6c0b8..eb9b0d094 100644 --- a/src/spdx/model/file.py +++ b/src/spdx/model/file.py @@ -14,7 +14,7 @@ from spdx.model.checksum import Checksum from common.typing.dataclass_with_properties import dataclass_with_properties -from spdx.model.license_expression import LicenseExpression +from license_expression import LicenseExpression from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone from common.typing.type_checks import check_types_and_set_values diff --git a/src/spdx/model/license_expression.py b/src/spdx/model/license_expression.py deleted file mode 100644 index 74c44fcd6..000000000 --- a/src/spdx/model/license_expression.py +++ /dev/null @@ -1,26 +0,0 @@ -# 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 common.typing.dataclass_with_properties import dataclass_with_properties -from common.typing.type_checks import check_types_and_set_values - - -@dataclass_with_properties -class LicenseExpression: - """So far, this just holds a string with the license expression. The ticket for adding license expression support - is https://github.com/spdx/tools-python/issues/10.""" - expression_string: str - - def __init__(self, expression_string: str): - check_types_and_set_values(self, locals()) - - def __str__(self): - return self.expression_string diff --git a/src/spdx/model/package.py b/src/spdx/model/package.py index 0a621701b..50bd802b1 100644 --- a/src/spdx/model/package.py +++ b/src/spdx/model/package.py @@ -16,7 +16,7 @@ from spdx.model.actor import Actor from spdx.model.checksum import Checksum from common.typing.dataclass_with_properties import dataclass_with_properties -from spdx.model.license_expression import LicenseExpression +from license_expression import LicenseExpression from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone from common.typing.type_checks import check_types_and_set_values diff --git a/src/spdx/model/snippet.py b/src/spdx/model/snippet.py index 8100502e5..6a6974a6c 100644 --- a/src/spdx/model/snippet.py +++ b/src/spdx/model/snippet.py @@ -12,7 +12,7 @@ from typing import Tuple, Optional, List, Union from common.typing.dataclass_with_properties import dataclass_with_properties -from spdx.model.license_expression import LicenseExpression +from license_expression import LicenseExpression from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone from common.typing.type_checks import check_types_and_set_values diff --git a/src/spdx/parser/jsonlikedict/file_parser.py b/src/spdx/parser/jsonlikedict/file_parser.py index 57954c0dd..156fae676 100644 --- a/src/spdx/parser/jsonlikedict/file_parser.py +++ b/src/spdx/parser/jsonlikedict/file_parser.py @@ -12,7 +12,7 @@ from spdx.model.checksum import Checksum from spdx.model.file import File, FileType -from spdx.model.license_expression import LicenseExpression +from license_expression import LicenseExpression from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone from spdx.parser.jsonlikedict.checksum_parser import ChecksumParser diff --git a/src/spdx/parser/jsonlikedict/license_expression_parser.py b/src/spdx/parser/jsonlikedict/license_expression_parser.py index 663d53af8..796690eda 100644 --- a/src/spdx/parser/jsonlikedict/license_expression_parser.py +++ b/src/spdx/parser/jsonlikedict/license_expression_parser.py @@ -10,23 +10,37 @@ # limitations under the License. from typing import Union, List -from spdx.model.license_expression import LicenseExpression +from license_expression import LicenseExpression, Licensing, ExpressionError + +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone from spdx.parser.error import SPDXParsingError -from spdx.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error, append_parsed_field_or_log_error, \ +from spdx.parser.jsonlikedict.dict_parsing_functions import append_parsed_field_or_log_error, \ raise_parsing_error_if_logger_has_messages from spdx.parser.logger import Logger class LicenseExpressionParser: @staticmethod - def parse_license_expression(license_expression_str_or_list: str) -> LicenseExpression: - license_expression = construct_or_raise_parsing_error(LicenseExpression, - dict(expression_string=license_expression_str_or_list)) + def parse_license_expression(license_expression_str: str) -> Union[LicenseExpression, SpdxNone, SpdxNoAssertion]: + if isinstance(license_expression_str, str): + if license_expression_str.upper() == "NOASSERTION": + return SpdxNoAssertion() + if license_expression_str.upper() == "NONE": + return SpdxNone() + + try: + license_expression = Licensing().parse(license_expression_str) + except ExpressionError as err: + raise SPDXParsingError([f"Error parsing LicenseExpression: {err.args[0]}: {license_expression_str}"]) + return license_expression - def parse_license_expressions(self, license_expression_str_or_list: Union[str, List[str]]) -> Union[LicenseExpression, List[LicenseExpression]]: - if isinstance(license_expression_str_or_list, str): + def parse_license_expressions(self, license_expression_str_or_list: Union[str, List[str]]) -> Union[ + LicenseExpression, SpdxNone, SpdxNoAssertion, List[Union[LicenseExpression, SpdxNone, SpdxNoAssertion]]]: + if not isinstance(license_expression_str_or_list, List): return self.parse_license_expression(license_expression_str_or_list) + license_expressions = [] logger = Logger() for license_expression_str in license_expression_str_or_list: @@ -34,7 +48,7 @@ def parse_license_expressions(self, license_expression_str_or_list: Union[str, L license_expressions = append_parsed_field_or_log_error(logger, license_expressions, license_expression_str, self.parse_license_expression) - except SPDXParsingError as err: - logger.append(err.get_messages()) + except ExpressionError as err: + logger.append(err.args[0]) raise_parsing_error_if_logger_has_messages(logger) return license_expressions diff --git a/src/spdx/parser/jsonlikedict/package_parser.py b/src/spdx/parser/jsonlikedict/package_parser.py index d2677f5c3..c57edd977 100644 --- a/src/spdx/parser/jsonlikedict/package_parser.py +++ b/src/spdx/parser/jsonlikedict/package_parser.py @@ -12,7 +12,7 @@ from typing import Dict, List, Optional, Union from spdx.model.actor import Actor -from spdx.model.license_expression import LicenseExpression +from license_expression import LicenseExpression from spdx.model.package import Package, ExternalPackageRef, PackageVerificationCode, PackagePurpose, \ ExternalPackageRefCategory from spdx.model.spdx_no_assertion import SpdxNoAssertion diff --git a/src/spdx/parser/jsonlikedict/snippet_parser.py b/src/spdx/parser/jsonlikedict/snippet_parser.py index 9dbb733c3..63964dcd4 100644 --- a/src/spdx/parser/jsonlikedict/snippet_parser.py +++ b/src/spdx/parser/jsonlikedict/snippet_parser.py @@ -11,7 +11,7 @@ from enum import auto, Enum from typing import Dict, Tuple, List, Optional, Union -from spdx.model.license_expression import LicenseExpression +from license_expression import LicenseExpression from spdx.model.snippet import Snippet from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone diff --git a/src/spdx/validation/license_expression_validator.py b/src/spdx/validation/license_expression_validator.py index 3146e6f00..0d6d0e687 100644 --- a/src/spdx/validation/license_expression_validator.py +++ b/src/spdx/validation/license_expression_validator.py @@ -11,7 +11,7 @@ from typing import List, Optional, Union -from spdx.model.license_expression import LicenseExpression +from license_expression import LicenseExpression from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone from spdx.validation.validation_message import ValidationMessage diff --git a/src/spdx/writer/tagvalue/file_writer.py b/src/spdx/writer/tagvalue/file_writer.py index ce1ec0a28..0198b57ca 100644 --- a/src/spdx/writer/tagvalue/file_writer.py +++ b/src/spdx/writer/tagvalue/file_writer.py @@ -11,9 +11,8 @@ from typing import TextIO from spdx.model.file import File -from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, \ - write_license_expression from spdx.writer.tagvalue.checksum_writer import write_checksum_to_tag_value +from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value def write_file(file: File, text_output: TextIO): @@ -28,8 +27,9 @@ def write_file(file: File, text_output: TextIO): for file_checksum in file.checksums: write_value("FileChecksum", write_checksum_to_tag_value(file_checksum), text_output) - write_license_expression("LicenseConcluded", file.license_concluded, text_output) - write_license_expression("LicenseInfoInFile", file.license_info_in_file, text_output) + write_value("LicenseConcluded", file.license_concluded, text_output) + for license_info in file.license_info_in_file: + write_value("LicenseInfoInFile", license_info, text_output) write_text_value("LicenseComments", file.license_comment, text_output) write_text_value("FileCopyrightText", file.copyright_text, text_output) diff --git a/src/spdx/writer/tagvalue/package_writer.py b/src/spdx/writer/tagvalue/package_writer.py index 045e509e4..74e4adeb6 100644 --- a/src/spdx/writer/tagvalue/package_writer.py +++ b/src/spdx/writer/tagvalue/package_writer.py @@ -12,9 +12,9 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.model.package import Package, PackageVerificationCode -from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, \ - write_license_expression, transform_enum_name_to_tv, write_actor from spdx.writer.tagvalue.checksum_writer import write_checksum_to_tag_value +from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, \ + transform_enum_name_to_tv, write_actor def write_package(package: Package, text_output: TextIO): @@ -39,9 +39,10 @@ def write_package(package: Package, text_output: TextIO): write_value("PackageHomePage", package.homepage, text_output) write_text_value("PackageSourceInfo", package.source_info, text_output) - write_license_expression("PackageLicenseConcluded", package.license_concluded, text_output) - write_license_expression("PackageLicenseInfoFromFiles", package.license_info_from_files, text_output) - write_license_expression("PackageLicenseDeclared", package.license_declared, text_output) + write_value("PackageLicenseConcluded", package.license_concluded, text_output) + for license_info in package.license_info_from_files: + write_value("PackageLicenseInfoFromFiles", license_info, text_output) + write_value("PackageLicenseDeclared", package.license_declared, text_output) write_text_value("PackageLicenseComments", package.license_comment, text_output) write_text_value("PackageCopyrightText", package.copyright_text, text_output) diff --git a/src/spdx/writer/tagvalue/snippet_writer.py b/src/spdx/writer/tagvalue/snippet_writer.py index dc8553e9f..f20630913 100644 --- a/src/spdx/writer/tagvalue/snippet_writer.py +++ b/src/spdx/writer/tagvalue/snippet_writer.py @@ -11,8 +11,7 @@ from typing import TextIO from spdx.model.snippet import Snippet -from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, write_range, \ - write_license_expression +from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, write_range def write_snippet(snippet: Snippet, output_text: TextIO): @@ -23,8 +22,9 @@ def write_snippet(snippet: Snippet, output_text: TextIO): write_range("SnippetByteRange", snippet.byte_range, output_text) write_range("SnippetLineRange", snippet.line_range, output_text) - write_license_expression("SnippetLicenseConcluded", snippet.license_concluded, output_text) - write_license_expression("LicenseInfoInSnippet", snippet.license_info_in_snippet, output_text) + write_value("SnippetLicenseConcluded", snippet.license_concluded, output_text) + for license_info in snippet.license_info_in_snippet: + write_value("LicenseInfoInSnippet", license_info, output_text) write_text_value("SnippetLicenseComments", snippet.license_comment, output_text) write_text_value("SnippetCopyrightText", snippet.copyright_text, output_text) diff --git a/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py b/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py index 445b6c207..8ac9abc39 100644 --- a/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py +++ b/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py @@ -12,7 +12,7 @@ from spdx.model.actor import Actor from spdx.model.file import File -from spdx.model.license_expression import LicenseExpression +from license_expression import LicenseExpression from spdx.model.package import Package from spdx.model.relationship import Relationship from spdx.model.snippet import Snippet @@ -25,7 +25,7 @@ def write_separator(out: TextIO): def write_value(tag: str, value: Optional[Union[bool, str, SpdxNone, SpdxNoAssertion]], out: TextIO): - if value: + if value is not None: out.write(f"{tag}: {value}\n") @@ -65,17 +65,6 @@ def write_actor(tag: str, element_to_write: Optional[Union[Actor, SpdxNoAssertio write_value(tag, element_to_write, text_output) -def write_license_expression(tag: str, element_to_write: Optional[Union[ - List[LicenseExpression], LicenseExpression, SpdxNoAssertion, SpdxNone]], text_output: TextIO): - if isinstance(element_to_write, (SpdxNone, SpdxNoAssertion, str)): - write_value(tag, element_to_write, text_output) - elif isinstance(element_to_write, LicenseExpression): - write_value(tag, element_to_write.expression_string, text_output) - elif isinstance(element_to_write, list): - for element in element_to_write: - write_value(tag, element.expression_string, text_output) - - def scan_relationships(relationships: List[Relationship], packages: List[Package], files: List[File]) \ -> Tuple[List, Dict]: contained_files_by_package_id = dict() diff --git a/tests/spdx/fixtures.py b/tests/spdx/fixtures.py index 389fa56a0..094347db7 100644 --- a/tests/spdx/fixtures.py +++ b/tests/spdx/fixtures.py @@ -10,6 +10,8 @@ # limitations under the License. from datetime import datetime +from license_expression import Licensing + from spdx.model.actor import Actor, ActorType from spdx.model.annotation import Annotation, AnnotationType from spdx.model.checksum import Checksum, ChecksumAlgorithm @@ -17,7 +19,6 @@ from spdx.model.external_document_ref import ExternalDocumentRef from spdx.model.extracted_licensing_info import ExtractedLicensingInfo from spdx.model.file import File, FileType -from spdx.model.license_expression import LicenseExpression from spdx.model.package import Package, PackageVerificationCode, PackagePurpose, ExternalPackageRef, \ ExternalPackageRefCategory from spdx.model.relationship import Relationship, RelationshipType @@ -36,7 +37,8 @@ def checksum_fixture(algorithm=ChecksumAlgorithm.SHA1, value="71c4025dd9897b364f return Checksum(algorithm, value) -def package_verification_code_fixture(value="85ed0817af83a24ad8da68c2b5094de69833983c", excluded_files=None) -> PackageVerificationCode: +def package_verification_code_fixture(value="85ed0817af83a24ad8da68c2b5094de69833983c", + excluded_files=None) -> PackageVerificationCode: excluded_files = ["./exclude.py"] if excluded_files is None else excluded_files return PackageVerificationCode(value, excluded_files) @@ -48,18 +50,19 @@ def creation_info_fixture(spdx_version="SPDX-2.3", spdx_id="SPDXRef-DOCUMENT", n creators = [actor_fixture(name="creatorName")] if creators is None else creators external_document_refs = [ external_document_ref_fixture()] if external_document_refs is None else external_document_refs - return CreationInfo(spdx_version, spdx_id, name, document_namespace, creators, created, creator_comment, data_license, + return CreationInfo(spdx_version, spdx_id, name, document_namespace, creators, created, creator_comment, + data_license, external_document_refs, license_list_version, document_comment) def file_fixture(name="./fileName.py", spdx_id="SPDXRef-File", checksums=None, file_type=None, - license_concluded=LicenseExpression("licenseConcludedExpression"), license_info_in_file=None, + license_concluded=Licensing().parse("MIT and GPL-2.0"), license_info_in_file=None, license_comment="licenseComment", copyright_text="copyrightText", comment="fileComment", notice="fileNotice", contributors=None, attribution_texts=None) -> File: checksums = [checksum_fixture()] if checksums is None else checksums file_type = [FileType.TEXT] if file_type is None else file_type - license_info_in_file = [ - LicenseExpression("licenseInfoInFileExpression")] if license_info_in_file is None else license_info_in_file + license_info_in_file = [Licensing().parse("MIT"), + Licensing().parse("GPL-2.0")] if license_info_in_file is None else license_info_in_file contributors = ["fileContributor"] if contributors is None else contributors attribution_texts = ["fileAttributionText"] if attribution_texts is None else attribution_texts return File(name=name, spdx_id=spdx_id, checksums=checksums, file_type=file_type, @@ -73,16 +76,16 @@ def package_fixture(spdx_id="SPDXRef-Package", name="packageName", download_loca supplier=actor_fixture(name="supplierName"), originator=actor_fixture(name="originatorName"), files_analyzed=True, verification_code=package_verification_code_fixture(), checksums=None, homepage="https://homepage.com", source_info="sourceInfo", - license_concluded=LicenseExpression("packageLicenseConcluded"), license_info_from_files=None, - license_declared=LicenseExpression("packageLicenseDeclared"), + license_concluded=Licensing().parse("MIT and GPL-2.0"), license_info_from_files=None, + license_declared=Licensing().parse("MIT and GPL-2.0"), license_comment="packageLicenseComment", copyright_text="packageCopyrightText", summary="packageSummary", description="packageDescription", comment="packageComment", external_references=None, attribution_texts=None, primary_package_purpose=PackagePurpose.SOURCE, release_date=datetime(2022, 12, 1), built_date=datetime(2022, 12, 2), valid_until_date=datetime(2022, 12, 3)) -> Package: checksums = [checksum_fixture()] if checksums is None else checksums - license_info_from_files = [ - LicenseExpression("licenseInfoFromFile")] if license_info_from_files is None else license_info_from_files + license_info_from_files = [Licensing().parse("MIT"), Licensing().parse( + "GPL-2.0")] if license_info_from_files is None else license_info_from_files external_references = [external_package_ref_fixture()] if external_references is None else external_references attribution_texts = ["packageAttributionText"] if attribution_texts is None else attribution_texts return Package(spdx_id=spdx_id, name=name, download_location=download_location, version=version, @@ -108,12 +111,12 @@ def external_package_ref_fixture(category=ExternalPackageRefCategory.PACKAGE_MAN def snippet_fixture(spdx_id="SPDXRef-Snippet", file_spdx_id="SPDXRef-File", byte_range=(1, 2), - line_range=(3, 4), license_concluded=LicenseExpression("snippetLicenseConcluded"), + line_range=(3, 4), license_concluded=Licensing().parse("MIT and GPL-2.0"), license_info_in_snippet=None, license_comment="snippetLicenseComment", copyright_text="licenseCopyrightText", comment="snippetComment", name="snippetName", attribution_texts=None) -> Snippet: - license_info_in_snippet = [ - LicenseExpression("licenseInfoInSnippet")] if license_info_in_snippet is None else license_info_in_snippet + license_info_in_snippet = [Licensing().parse("MIT"), Licensing().parse( + "GPL-2.0")] if license_info_in_snippet is None else license_info_in_snippet attribution_texts = ["snippetAttributionText"] if attribution_texts is None else attribution_texts return Snippet(spdx_id=spdx_id, file_spdx_id=file_spdx_id, byte_range=byte_range, line_range=line_range, license_concluded=license_concluded, license_info_in_snippet=license_info_in_snippet, @@ -128,7 +131,8 @@ def annotation_fixture(spdx_id="SPDXRef-File", annotation_type=AnnotationType.RE annotation_date=annotation_date, annotation_comment=annotation_comment) -def extracted_licensing_info_fixture(license_id="LicenseRef-1", extracted_text="extractedText", license_name="licenseName", +def extracted_licensing_info_fixture(license_id="LicenseRef-1", extracted_text="extractedText", + license_name="licenseName", cross_references=None, comment="licenseComment") -> ExtractedLicensingInfo: cross_references = ["https://see.also"] if cross_references is None else cross_references return ExtractedLicensingInfo(license_id=license_id, extracted_text=extracted_text, license_name=license_name, diff --git a/tests/spdx/jsonschema/test_file_converter.py b/tests/spdx/jsonschema/test_file_converter.py index f5d167b1f..b169a9d44 100644 --- a/tests/spdx/jsonschema/test_file_converter.py +++ b/tests/spdx/jsonschema/test_file_converter.py @@ -23,7 +23,7 @@ from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.model.document import Document from spdx.model.file import File, FileType -from spdx.model.license_expression import LicenseExpression +from license_expression import LicenseExpression, Licensing from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING from spdx.model.spdx_none import SpdxNone, SPDX_NONE_STRING from tests.spdx.fixtures import creation_info_fixture, file_fixture, annotation_fixture, document_fixture @@ -73,8 +73,8 @@ def test_successful_conversion(converter: FileConverter): converter.annotation_converter.convert.return_value = "mock_converted_annotation" file = File(name="name", spdx_id="spdxId", checksums=[Checksum(ChecksumAlgorithm.SHA224, "sha224"), Checksum(ChecksumAlgorithm.MD2, "md2")], - file_type=[FileType.SPDX, FileType.OTHER], license_concluded=LicenseExpression("licenseExpression1"), - license_info_in_file=[LicenseExpression("licenseExpression2"), LicenseExpression("licenseExpression3")], + file_type=[FileType.SPDX, FileType.OTHER], license_concluded=Licensing().parse("MIT and GPL-2.0"), + license_info_in_file=[Licensing().parse("MIT"), Licensing().parse("GPL-2.0")], license_comment="licenseComment", copyright_text="copyrightText", comment="comment", notice="notice", contributors=["contributor1", "contributor2"], attribution_texts=["attributionText1", "attributionText2"]) @@ -96,8 +96,8 @@ def test_successful_conversion(converter: FileConverter): converter.json_property_name(FileProperty.FILE_NAME): "name", converter.json_property_name(FileProperty.FILE_TYPES): ["SPDX", "OTHER"], converter.json_property_name(FileProperty.LICENSE_COMMENTS): "licenseComment", - converter.json_property_name(FileProperty.LICENSE_CONCLUDED): "licenseExpression1", - converter.json_property_name(FileProperty.LICENSE_INFO_IN_FILES): ["licenseExpression2", "licenseExpression3"], + converter.json_property_name(FileProperty.LICENSE_CONCLUDED): "MIT AND GPL-2.0", + converter.json_property_name(FileProperty.LICENSE_INFO_IN_FILES): ["MIT", "GPL-2.0"], converter.json_property_name(FileProperty.NOTICE_TEXT): "notice" } diff --git a/tests/spdx/jsonschema/test_package_converter.py b/tests/spdx/jsonschema/test_package_converter.py index ba0332ee0..fae81f6db 100644 --- a/tests/spdx/jsonschema/test_package_converter.py +++ b/tests/spdx/jsonschema/test_package_converter.py @@ -22,7 +22,7 @@ from spdx.model.annotation import Annotation, AnnotationType from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.model.document import Document -from spdx.model.license_expression import LicenseExpression +from license_expression import LicenseExpression, Licensing from spdx.model.package import Package, PackageVerificationCode, PackagePurpose from spdx.model.relationship import RelationshipType from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING @@ -100,10 +100,10 @@ def test_successful_conversion(converter: PackageConverter): verification_code=PackageVerificationCode("value"), checksums=[Checksum(ChecksumAlgorithm.SHA1, "sha1"), Checksum(ChecksumAlgorithm.BLAKE2B_256, "blake")], homepage="homepage", - source_info="sourceInfo", license_concluded=LicenseExpression("licenseExpression1"), - license_info_from_files=[LicenseExpression("licenseExpression2"), - LicenseExpression("licenseExpression3")], - license_declared=LicenseExpression("licenseExpression4"), license_comment="licenseComment", + source_info="sourceInfo", license_concluded=Licensing().parse("MIT and GPL-2.0"), + license_info_from_files=[Licensing().parse("MIT"), + Licensing().parse("GPL-2.0")], + license_declared=Licensing().parse("MIT or GPL-2.0 "), license_comment="licenseComment", copyright_text="copyrightText", summary="summary", description="description", comment="comment", external_references=[external_package_ref_fixture()], attribution_texts=["attributionText1", "attributionText2"], @@ -131,10 +131,9 @@ def test_successful_conversion(converter: PackageConverter): converter.json_property_name(PackageProperty.CHECKSUMS): ["mock_converted_checksum", "mock_converted_checksum"], converter.json_property_name(PackageProperty.HOMEPAGE): "homepage", converter.json_property_name(PackageProperty.SOURCE_INFO): "sourceInfo", - converter.json_property_name(PackageProperty.LICENSE_CONCLUDED): "licenseExpression1", - converter.json_property_name(PackageProperty.LICENSE_INFO_FROM_FILES): ["licenseExpression2", - "licenseExpression3"], - converter.json_property_name(PackageProperty.LICENSE_DECLARED): "licenseExpression4", + converter.json_property_name(PackageProperty.LICENSE_CONCLUDED): "MIT AND GPL-2.0", + converter.json_property_name(PackageProperty.LICENSE_INFO_FROM_FILES): ["MIT", "GPL-2.0"], + converter.json_property_name(PackageProperty.LICENSE_DECLARED): "MIT OR GPL-2.0", converter.json_property_name(PackageProperty.LICENSE_COMMENTS): "licenseComment", converter.json_property_name(PackageProperty.COPYRIGHT_TEXT): "copyrightText", converter.json_property_name(PackageProperty.SUMMARY): "summary", diff --git a/tests/spdx/jsonschema/test_snippet_converter.py b/tests/spdx/jsonschema/test_snippet_converter.py index 35f988f64..082cd1a90 100644 --- a/tests/spdx/jsonschema/test_snippet_converter.py +++ b/tests/spdx/jsonschema/test_snippet_converter.py @@ -21,7 +21,7 @@ from spdx.model.actor import Actor, ActorType from spdx.model.annotation import Annotation, AnnotationType from spdx.model.document import Document -from spdx.model.license_expression import LicenseExpression +from license_expression import LicenseExpression, Licensing from spdx.model.snippet import Snippet from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING from spdx.model.spdx_none import SpdxNone, SPDX_NONE_STRING @@ -66,9 +66,9 @@ def test_successful_conversion(converter: SnippetConverter): converter.annotation_converter.convert.return_value = "mock_converted_annotation" file_spdx_id = "fileSpdxId" snippet = Snippet("spdxId", file_spdx_id=file_spdx_id, byte_range=(1, 2), line_range=(3, 4), - license_concluded=LicenseExpression("licenseExpression1"), - license_info_in_snippet=[LicenseExpression("licenseExpression2"), - LicenseExpression("licenseExpression3")], + license_concluded=Licensing().parse("MIT and GPL-2.0"), + license_info_in_snippet=[Licensing().parse("MIT"), + Licensing().parse("GPL-2.0")], license_comment="licenseComment", copyright_text="copyrightText", comment="comment", name="name", attribution_texts=["attributionText1", "attributionText2"]) @@ -84,9 +84,8 @@ def test_successful_conversion(converter: SnippetConverter): converter.json_property_name(SnippetProperty.COMMENT): "comment", converter.json_property_name(SnippetProperty.COPYRIGHT_TEXT): "copyrightText", converter.json_property_name(SnippetProperty.LICENSE_COMMENTS): "licenseComment", - converter.json_property_name(SnippetProperty.LICENSE_CONCLUDED): "licenseExpression1", - converter.json_property_name(SnippetProperty.LICENSE_INFO_IN_SNIPPETS): ["licenseExpression2", - "licenseExpression3"], + converter.json_property_name(SnippetProperty.LICENSE_CONCLUDED): "MIT AND GPL-2.0", + converter.json_property_name(SnippetProperty.LICENSE_INFO_IN_SNIPPETS): ["MIT", "GPL-2.0"], converter.json_property_name(SnippetProperty.NAME): "name", converter.json_property_name(SnippetProperty.RANGES): [ {"startPointer": {"reference": file_spdx_id, "offset": 1}, diff --git a/tests/spdx/model/test_package.py b/tests/spdx/model/test_package.py index 1fb1fb1f0..cf05cf89f 100644 --- a/tests/spdx/model/test_package.py +++ b/tests/spdx/model/test_package.py @@ -4,7 +4,7 @@ import pytest from spdx.model.checksum import Checksum, ChecksumAlgorithm -from spdx.model.license_expression import LicenseExpression +from license_expression import LicenseExpression, Licensing from spdx.model.package import Package, PackagePurpose from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone @@ -16,7 +16,7 @@ @mock.patch('spdx.model.actor.Actor', autospec=True) def test_correct_initialization(actor, verif_code, checksum, ext_ref): package = Package("id", "name", SpdxNoAssertion(), "version", "file_name", SpdxNoAssertion(), actor, True, - verif_code, [checksum], "homepage", "source_info", None, [LicenseExpression("expression")], + verif_code, [checksum], "homepage", "source_info", None, [Licensing().parse("license and expression")], SpdxNone(), "comment on license", "copyright", "summary", "description", "comment", [ext_ref, ext_ref], ["text"], PackagePurpose.OTHER, datetime(2022, 1, 1), None, None) assert package.spdx_id == "id" @@ -32,7 +32,7 @@ def test_correct_initialization(actor, verif_code, checksum, ext_ref): assert package.homepage == "homepage" assert package.source_info == "source_info" assert package.license_concluded is None - assert package.license_info_from_files == [LicenseExpression("expression")] + assert package.license_info_from_files == [Licensing().parse("license and expression")] assert package.license_declared == SpdxNone() assert package.license_comment == "comment on license" assert package.copyright_text == "copyright" diff --git a/tests/spdx/parser/jsonlikedict/test_file_parser.py b/tests/spdx/parser/jsonlikedict/test_file_parser.py index 8a06f63af..3050dcaec 100644 --- a/tests/spdx/parser/jsonlikedict/test_file_parser.py +++ b/tests/spdx/parser/jsonlikedict/test_file_parser.py @@ -14,7 +14,7 @@ from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.model.file import FileType -from spdx.model.license_expression import LicenseExpression +from license_expression import LicenseExpression, Licensing from spdx.parser.error import SPDXParsingError from spdx.parser.jsonlikedict.dict_parsing_functions import parse_list_of_elements from spdx.parser.jsonlikedict.file_parser import FileParser @@ -56,9 +56,9 @@ def test_parse_file(): assert file.file_type == [FileType.SOURCE] TestCase().assertCountEqual(file.contributors, ["The Regents of the University of California", "Modified by Paul Mundt lethal@linux-sh.org", "IBM Corporation"]) - assert file.license_concluded == LicenseExpression("(LGPL-2.0-only OR LicenseRef-2)") + assert file.license_concluded == Licensing().parse("(LGPL-2.0-only OR LicenseRef-2)") TestCase().assertCountEqual(file.license_info_in_file, - [LicenseExpression("GPL-2.0-only"), LicenseExpression("LicenseRef-2")]) + [Licensing().parse("GPL-2.0-only"), Licensing().parse("LicenseRef-2")]) assert file.license_comment == "The concluded license was taken from the package level that the file was included in." assert file.attribution_texts == ["Some attribution text."] diff --git a/tests/spdx/parser/jsonlikedict/test_license_expression_parser.py b/tests/spdx/parser/jsonlikedict/test_license_expression_parser.py index b21583426..5eddd06d1 100644 --- a/tests/spdx/parser/jsonlikedict/test_license_expression_parser.py +++ b/tests/spdx/parser/jsonlikedict/test_license_expression_parser.py @@ -11,16 +11,18 @@ from unittest import TestCase import pytest +from license_expression import Licensing -from spdx.model.license_expression import LicenseExpression +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone from spdx.parser.error import SPDXParsingError from spdx.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser @pytest.mark.parametrize("invalid_license_expression,expected_message", - [(56, ["Error while constructing LicenseExpression: ['SetterError LicenseExpression: " - 'type of argument "expression_string" must be str; got int instead: 56\']'] - ), ]) + [(56, + ["Error parsing LicenseExpression: expression must be a string and not: : 56"]), + ]) def test_parse_invalid_license_expression(invalid_license_expression, expected_message): license_expression_parser = LicenseExpressionParser() @@ -32,22 +34,21 @@ def test_parse_invalid_license_expression(invalid_license_expression, expected_m def test_parse_license_expressions(): license_expression_parser = LicenseExpressionParser() - license_expressions_list = ["First License", "Second License", "Third License"] + license_expressions_list = ["First License", "Second License", "NONE", "NOASSERTION"] license_expressions = license_expression_parser.parse_license_expressions(license_expressions_list) - assert len(license_expressions) == 3 + assert len(license_expressions) == 4 TestCase().assertCountEqual(license_expressions, - [LicenseExpression("First License"), LicenseExpression("Second License"), - LicenseExpression("Third License")]) + [Licensing().parse("First License"), Licensing().parse("Second License"), + SpdxNone(), SpdxNoAssertion()]) -@pytest.mark.parametrize("invalid_license_expressions,expected_message", [(["First Expression", 4, 6], - [ - "Error while constructing LicenseExpression: ['SetterError LicenseExpression: " - 'type of argument "expression_string" must be str; got int instead: 4\']', - "Error while constructing LicenseExpression: ['SetterError LicenseExpression: " - 'type of argument "expression_string" must be str; got int instead: 6\']'])]) +@pytest.mark.parametrize("invalid_license_expressions, expected_message", + [(["First Expression", 4, 6], + ["Error parsing LicenseExpression: expression must be a string and not: : 4", + "Error parsing LicenseExpression: expression must be a string and not: : 6"]) + ]) def test_parse_invalid_license_expressions(invalid_license_expressions, expected_message): license_expression_parser = LicenseExpressionParser() diff --git a/tests/spdx/parser/jsonlikedict/test_package_parser.py b/tests/spdx/parser/jsonlikedict/test_package_parser.py index 6839af812..fa9408dd7 100644 --- a/tests/spdx/parser/jsonlikedict/test_package_parser.py +++ b/tests/spdx/parser/jsonlikedict/test_package_parser.py @@ -15,7 +15,7 @@ from spdx.model.actor import Actor, ActorType from spdx.model.checksum import Checksum, ChecksumAlgorithm -from spdx.model.license_expression import LicenseExpression +from license_expression import LicenseExpression, Licensing from spdx.model.package import PackageVerificationCode, ExternalPackageRef, ExternalPackageRefCategory, PackagePurpose from spdx.parser.error import SPDXParsingError from spdx.parser.jsonlikedict.dict_parsing_functions import parse_list_of_elements @@ -101,11 +101,11 @@ def test_parse_package(): "aaabd89c926ab525c242e6621f2f5fa73aa4afe3d9e24aed727faaadd6af38b620bdb623dd2b4788b1c8086984af8706")]) assert package.homepage == "http://ftp.gnu.org/gnu/glibc" assert package.source_info == "uses glibc-2_11-branch from git://sourceware.org/git/glibc.git." - assert package.license_concluded == LicenseExpression("(LGPL-2.0-only OR LicenseRef-3)") + assert package.license_concluded == Licensing().parse("(LGPL-2.0-only OR LicenseRef-3)") TestCase().assertCountEqual(package.license_info_from_files, - [LicenseExpression("GPL-2.0-only"), LicenseExpression("LicenseRef-2"), - LicenseExpression("LicenseRef-1")]) - assert package.license_declared == LicenseExpression("(LGPL-2.0-only AND LicenseRef-3)") + [Licensing().parse("GPL-2.0-only"), Licensing().parse("LicenseRef-2"), + Licensing().parse("LicenseRef-1")]) + assert package.license_declared == Licensing().parse("(LGPL-2.0-only AND LicenseRef-3)") assert package.license_comment == "The license for this project changed with the release of version x.y. The version of the project included here post-dates the license change." assert package.copyright_text == "Copyright 2008-2010 John Smith" assert package.summary == "GNU C library." diff --git a/tests/spdx/parser/jsonlikedict/test_snippet_parser.py b/tests/spdx/parser/jsonlikedict/test_snippet_parser.py index 1fe87ae1d..9bbe71a26 100644 --- a/tests/spdx/parser/jsonlikedict/test_snippet_parser.py +++ b/tests/spdx/parser/jsonlikedict/test_snippet_parser.py @@ -12,7 +12,7 @@ import pytest -from spdx.model.license_expression import LicenseExpression +from license_expression import LicenseExpression, Licensing from spdx.parser.error import SPDXParsingError from spdx.parser.jsonlikedict.snippet_parser import SnippetParser @@ -60,8 +60,8 @@ def test_parse_snippet(): assert snippet.byte_range == (310, 420) assert snippet.line_range == (5, 23) assert snippet.file_spdx_id == "SPDXRef-DoapSource" - assert snippet.license_info_in_snippet == [LicenseExpression("GPL-2.0-only")] - assert snippet.license_concluded == LicenseExpression("GPL-2.0-only") + assert snippet.license_info_in_snippet == [Licensing().parse("GPL-2.0-only")] + assert snippet.license_concluded == Licensing().parse("GPL-2.0-only") assert snippet.attribution_texts == ["Some example attibution text."] diff --git a/tests/spdx/validation/test_license_expression_validator.py b/tests/spdx/validation/test_license_expression_validator.py index ac13a537a..6167fd50a 100644 --- a/tests/spdx/validation/test_license_expression_validator.py +++ b/tests/spdx/validation/test_license_expression_validator.py @@ -11,13 +11,13 @@ from typing import List -from spdx.model.license_expression import LicenseExpression +from license_expression import LicenseExpression, Licensing from spdx.validation.license_expression_validator import validate_license_expression from spdx.validation.validation_message import ValidationMessage def test_valid_license_expression(): - license_expression = LicenseExpression("LicenseRef-1") + license_expression = Licensing().parse("something") validation_messages: List[ValidationMessage] = validate_license_expression(license_expression) assert validation_messages == [] diff --git a/tests/spdx/validation/test_package_validator.py b/tests/spdx/validation/test_package_validator.py index 0bbb8d77d..3a4133872 100644 --- a/tests/spdx/validation/test_package_validator.py +++ b/tests/spdx/validation/test_package_validator.py @@ -12,8 +12,8 @@ from typing import List import pytest +from license_expression import Licensing -from spdx.model.license_expression import LicenseExpression from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone from spdx.validation.package_validator import validate_package_within_document @@ -39,9 +39,10 @@ def test_valid_package(): verification_code=None), 'license_info_from_files must be None if files_analyzed is False, but is: NOASSERTION'), (package_fixture(files_analyzed=False, - license_info_from_files=[LicenseExpression("some_license")], + license_info_from_files=[Licensing().parse("some_license")], verification_code=None), - 'license_info_from_files must be None if files_analyzed is False, but is: [LicenseExpression(expression_string=\'some_license\')]') + "license_info_from_files must be None if files_analyzed is False, but is: [LicenseSymbol('some_license', " + "is_exception=False)]") ]) def test_invalid_package(package_input, expected_message): validation_messages: List[ValidationMessage] = validate_package_within_document(package_input, diff --git a/tests/spdx/validation/test_uri_validators.py b/tests/spdx/validation/test_uri_validators.py index 5eac56145..a2a582292 100644 --- a/tests/spdx/validation/test_uri_validators.py +++ b/tests/spdx/validation/test_uri_validators.py @@ -22,7 +22,7 @@ def test_valid_url(input_value): # TODO: more negative examples: https://github.com/spdx/tools-python/issues/377 -@pytest.mark.parametrize("input_value", [":::::", ]) +@pytest.mark.parametrize("input_value", [":::::", "http://testurl"]) def test_invalid_url(input_value): assert validate_url(input_value) == [f"must be a valid URL, but is: {input_value}"] diff --git a/tests/spdx/writer/json/expected_results/expected.json b/tests/spdx/writer/json/expected_results/expected.json index 5277817f7..9e9a9c45a 100644 --- a/tests/spdx/writer/json/expected_results/expected.json +++ b/tests/spdx/writer/json/expected_results/expected.json @@ -54,10 +54,8 @@ "TEXT" ], "licenseComments": "licenseComment", - "licenseConcluded": "licenseConcludedExpression", - "licenseInfoInFiles": [ - "licenseInfoInFileExpression" - ], + "licenseConcluded": "MIT AND GPL-2.0", + "licenseInfoInFiles": ["MIT", "GPL-2.0"], "noticeText": "fileNotice" } ], @@ -101,11 +99,9 @@ "filesAnalyzed": true, "homepage": "https://homepage.com", "licenseComments": "packageLicenseComment", - "licenseConcluded": "packageLicenseConcluded", - "licenseDeclared": "packageLicenseDeclared", - "licenseInfoFromFiles": [ - "licenseInfoFromFile" - ], + "licenseConcluded": "MIT AND GPL-2.0", + "licenseDeclared": "MIT AND GPL-2.0", + "licenseInfoFromFiles": ["MIT", "GPL-2.0"], "name": "packageName", "originator": "Person: originatorName (some@mail.com)", "packageFileName": "./packageFileName", @@ -141,10 +137,8 @@ "comment": "snippetComment", "copyrightText": "licenseCopyrightText", "licenseComments": "snippetLicenseComment", - "licenseConcluded": "snippetLicenseConcluded", - "licenseInfoInSnippets": [ - "licenseInfoInSnippet" - ], + "licenseConcluded": "MIT AND GPL-2.0", + "licenseInfoInSnippets": ["MIT", "GPL-2.0"], "name": "snippetName", "ranges": [ { diff --git a/tests/spdx/writer/tagvalue/test_package_writer.py b/tests/spdx/writer/tagvalue/test_package_writer.py index eb87a6f13..c7d9ec0c0 100644 --- a/tests/spdx/writer/tagvalue/test_package_writer.py +++ b/tests/spdx/writer/tagvalue/test_package_writer.py @@ -38,9 +38,10 @@ def test_package_writer(): call('PackageChecksum: SHA1: 71c4025dd9897b364f3ebbb42c484ff43d00791c\n'), call('PackageHomePage: https://homepage.com\n'), call('PackageSourceInfo: sourceInfo\n'), - call('PackageLicenseConcluded: packageLicenseConcluded\n'), - call('PackageLicenseInfoFromFiles: licenseInfoFromFile\n'), - call('PackageLicenseDeclared: packageLicenseDeclared\n'), + call('PackageLicenseConcluded: MIT AND GPL-2.0\n'), + call('PackageLicenseInfoFromFiles: MIT\n'), + call('PackageLicenseInfoFromFiles: GPL-2.0\n'), + call('PackageLicenseDeclared: MIT AND GPL-2.0\n'), call('PackageLicenseComments: packageLicenseComment\n'), call('PackageCopyrightText: packageCopyrightText\n'), call('PackageSummary: packageSummary\n'), From c0226209296a08a8afc208acc28c1e19a0bd4c55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 27 Jan 2023 11:30:26 +0100 Subject: [PATCH 163/362] [issue-10, review] remove error handling in parse_license_expressions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit as they are already caught by append_parsed_field_or_log_error Signed-off-by: Armin Tänzer --- .../parser/jsonlikedict/license_expression_parser.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/spdx/parser/jsonlikedict/license_expression_parser.py b/src/spdx/parser/jsonlikedict/license_expression_parser.py index 796690eda..2e3f41c11 100644 --- a/src/spdx/parser/jsonlikedict/license_expression_parser.py +++ b/src/spdx/parser/jsonlikedict/license_expression_parser.py @@ -44,11 +44,8 @@ def parse_license_expressions(self, license_expression_str_or_list: Union[str, L license_expressions = [] logger = Logger() for license_expression_str in license_expression_str_or_list: - try: - license_expressions = append_parsed_field_or_log_error(logger, license_expressions, - license_expression_str, - self.parse_license_expression) - except ExpressionError as err: - logger.append(err.args[0]) + license_expressions = append_parsed_field_or_log_error(logger, license_expressions, + license_expression_str, + self.parse_license_expression) raise_parsing_error_if_logger_has_messages(logger) return license_expressions From 22d83c66bda73e87be8f31ebf8926d3ec4dbac9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 27 Jan 2023 11:55:44 +0100 Subject: [PATCH 164/362] [issue-10, review] add write_license_info_list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit to handle lists of licenses with NONE and NOASSERTION values Signed-off-by: Armin Tänzer --- src/spdx/writer/tagvalue/file_writer.py | 5 ++--- src/spdx/writer/tagvalue/package_writer.py | 5 ++--- src/spdx/writer/tagvalue/snippet_writer.py | 6 +++--- .../tagvalue/tagvalue_writer_helper_functions.py | 11 ++++++++++- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/spdx/writer/tagvalue/file_writer.py b/src/spdx/writer/tagvalue/file_writer.py index 0198b57ca..ee99645eb 100644 --- a/src/spdx/writer/tagvalue/file_writer.py +++ b/src/spdx/writer/tagvalue/file_writer.py @@ -12,7 +12,7 @@ from spdx.model.file import File from spdx.writer.tagvalue.checksum_writer import write_checksum_to_tag_value -from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value +from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, write_license_info_list def write_file(file: File, text_output: TextIO): @@ -28,8 +28,7 @@ def write_file(file: File, text_output: TextIO): write_value("FileChecksum", write_checksum_to_tag_value(file_checksum), text_output) write_value("LicenseConcluded", file.license_concluded, text_output) - for license_info in file.license_info_in_file: - write_value("LicenseInfoInFile", license_info, text_output) + write_license_info_list("LicenseInfoInFile", file.license_info_in_file, text_output) write_text_value("LicenseComments", file.license_comment, text_output) write_text_value("FileCopyrightText", file.copyright_text, text_output) diff --git a/src/spdx/writer/tagvalue/package_writer.py b/src/spdx/writer/tagvalue/package_writer.py index 74e4adeb6..b5abeb1c7 100644 --- a/src/spdx/writer/tagvalue/package_writer.py +++ b/src/spdx/writer/tagvalue/package_writer.py @@ -14,7 +14,7 @@ from spdx.model.package import Package, PackageVerificationCode from spdx.writer.tagvalue.checksum_writer import write_checksum_to_tag_value from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, \ - transform_enum_name_to_tv, write_actor + transform_enum_name_to_tv, write_actor, write_license_info_list def write_package(package: Package, text_output: TextIO): @@ -40,8 +40,7 @@ def write_package(package: Package, text_output: TextIO): write_text_value("PackageSourceInfo", package.source_info, text_output) write_value("PackageLicenseConcluded", package.license_concluded, text_output) - for license_info in package.license_info_from_files: - write_value("PackageLicenseInfoFromFiles", license_info, text_output) + write_license_info_list("PackageLicenseInfoFromFiles", package.license_info_from_files, text_output) write_value("PackageLicenseDeclared", package.license_declared, text_output) write_text_value("PackageLicenseComments", package.license_comment, text_output) write_text_value("PackageCopyrightText", package.copyright_text, text_output) diff --git a/src/spdx/writer/tagvalue/snippet_writer.py b/src/spdx/writer/tagvalue/snippet_writer.py index f20630913..3c6a73e24 100644 --- a/src/spdx/writer/tagvalue/snippet_writer.py +++ b/src/spdx/writer/tagvalue/snippet_writer.py @@ -11,7 +11,8 @@ from typing import TextIO from spdx.model.snippet import Snippet -from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, write_range +from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, write_range, \ + write_license_info_list def write_snippet(snippet: Snippet, output_text: TextIO): @@ -23,8 +24,7 @@ def write_snippet(snippet: Snippet, output_text: TextIO): write_range("SnippetLineRange", snippet.line_range, output_text) write_value("SnippetLicenseConcluded", snippet.license_concluded, output_text) - for license_info in snippet.license_info_in_snippet: - write_value("LicenseInfoInSnippet", license_info, output_text) + write_license_info_list("LicenseInfoInSnippet", snippet.license_info_in_snippet, output_text) write_text_value("SnippetLicenseComments", snippet.license_comment, output_text) write_text_value("SnippetCopyrightText", snippet.copyright_text, output_text) diff --git a/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py b/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py index 8ac9abc39..4bf7c71c3 100644 --- a/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py +++ b/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py @@ -24,7 +24,7 @@ def write_separator(out: TextIO): out.write("\n") -def write_value(tag: str, value: Optional[Union[bool, str, SpdxNone, SpdxNoAssertion]], out: TextIO): +def write_value(tag: str, value: Optional[Union[bool, str, SpdxNone, SpdxNoAssertion, LicenseExpression]], out: TextIO): if value is not None: out.write(f"{tag}: {value}\n") @@ -58,6 +58,15 @@ def write_list_of_elements(list_of_elements: List[Any], write_method: Callable[[ write_separator(text_output) +def write_license_info_list(tag: str, license_infos: Union[SpdxNone, SpdxNoAssertion, List[LicenseExpression]], text_output: TextIO): + if isinstance(license_infos, (SpdxNone, SpdxNoAssertion)): + write_value(tag, license_infos, text_output) + return + + for license_info in license_infos: + write_value(tag, license_info, text_output) + + def write_actor(tag: str, element_to_write: Optional[Union[Actor, SpdxNoAssertion]], text_output: TextIO): if isinstance(element_to_write, Actor): write_value(tag, element_to_write.to_serialized_string(), text_output) From b1cd78c368c8094fdf598ea8f01da0a47c463959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 27 Jan 2023 11:56:58 +0100 Subject: [PATCH 165/362] [refactor] make naming consistent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/writer/tagvalue/snippet_writer.py | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/spdx/writer/tagvalue/snippet_writer.py b/src/spdx/writer/tagvalue/snippet_writer.py index 3c6a73e24..62355d5d6 100644 --- a/src/spdx/writer/tagvalue/snippet_writer.py +++ b/src/spdx/writer/tagvalue/snippet_writer.py @@ -15,21 +15,21 @@ write_license_info_list -def write_snippet(snippet: Snippet, output_text: TextIO): - output_text.write("## Snippet Information\n") +def write_snippet(snippet: Snippet, text_output: TextIO): + text_output.write("## Snippet Information\n") - write_value("SnippetSPDXID", snippet.spdx_id, output_text) - write_value("SnippetFromFileSPDXID", snippet.file_spdx_id, output_text) - write_range("SnippetByteRange", snippet.byte_range, output_text) - write_range("SnippetLineRange", snippet.line_range, output_text) + write_value("SnippetSPDXID", snippet.spdx_id, text_output) + write_value("SnippetFromFileSPDXID", snippet.file_spdx_id, text_output) + write_range("SnippetByteRange", snippet.byte_range, text_output) + write_range("SnippetLineRange", snippet.line_range, text_output) - write_value("SnippetLicenseConcluded", snippet.license_concluded, output_text) - write_license_info_list("LicenseInfoInSnippet", snippet.license_info_in_snippet, output_text) - write_text_value("SnippetLicenseComments", snippet.license_comment, output_text) - write_text_value("SnippetCopyrightText", snippet.copyright_text, output_text) + write_value("SnippetLicenseConcluded", snippet.license_concluded, text_output) + write_license_info_list("LicenseInfoInSnippet", snippet.license_info_in_snippet, text_output) + write_text_value("SnippetLicenseComments", snippet.license_comment, text_output) + write_text_value("SnippetCopyrightText", snippet.copyright_text, text_output) - write_text_value("SnippetComment", snippet.comment, output_text) - write_value("SnippetName", snippet.name, output_text) + write_text_value("SnippetComment", snippet.comment, text_output) + write_value("SnippetName", snippet.name, text_output) for attribution_text in snippet.attribution_texts: - write_text_value("SnippetAttributionText", attribution_text, output_text) + write_text_value("SnippetAttributionText", attribution_text, text_output) From 9b3365f9d5f3440398574734ba9979ee6efd61b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 31 Jan 2023 12:54:08 +0100 Subject: [PATCH 166/362] [issue-457] add "no #" requirement to validation messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit also make swid accept non-absolute URIs as I did not find information on that this would be forbidden Signed-off-by: Armin Tänzer --- src/spdx/validation/external_package_ref_validator.py | 4 +++- src/spdx/validation/uri_validators.py | 2 +- tests/spdx/validation/test_creation_info_validator.py | 2 +- tests/spdx/validation/test_uri_validators.py | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/spdx/validation/external_package_ref_validator.py b/src/spdx/validation/external_package_ref_validator.py index 7be94f6ef..bff504d97 100644 --- a/src/spdx/validation/external_package_ref_validator.py +++ b/src/spdx/validation/external_package_ref_validator.py @@ -11,6 +11,8 @@ import re from typing import List, Dict +import uritools + from spdx.model.package import ExternalPackageRef, ExternalPackageRefCategory, CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES from spdx.validation.uri_validators import validate_url, validate_uri from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType @@ -75,7 +77,7 @@ def validate_external_package_ref(external_package_ref: ExternalPackageRef, pare return [] if reference_type == "swid": - if validate_uri(locator) or not locator.startswith("swid"): + if not uritools.isuri(locator) or not locator.startswith("swid"): return [ValidationMessage( f'externalPackageRef locator of type "swid" must be a valid URI with scheme swid, but is: {locator}', context)] diff --git a/src/spdx/validation/uri_validators.py b/src/spdx/validation/uri_validators.py index 701ec3cee..3033e0458 100644 --- a/src/spdx/validation/uri_validators.py +++ b/src/spdx/validation/uri_validators.py @@ -38,7 +38,7 @@ def validate_download_location(location: str) -> List[str]: def validate_uri(uri: str) -> List[str]: if not isabsuri(uri): - return [f"must be a valid URI specified in RFC-3986, but is: {uri}"] + return [f"must be a valid URI specified in RFC-3986 and must contain no fragment (#), but is: {uri}"] else: split = urisplit(uri) if split.scheme is None: diff --git a/tests/spdx/validation/test_creation_info_validator.py b/tests/spdx/validation/test_creation_info_validator.py index 7ad38e5fd..0fb4fc746 100644 --- a/tests/spdx/validation/test_creation_info_validator.py +++ b/tests/spdx/validation/test_creation_info_validator.py @@ -32,7 +32,7 @@ def test_valid_creation_info(): (creation_info_fixture(data_license="MIT"), "SPDXRef-DOCUMENT", 'data_license must be "CC0-1.0", but is: MIT'), (creation_info_fixture(document_namespace="some_namespace"), "SPDXRef-DOCUMENT", - "document_namespace must be a valid URI specified in RFC-3986, but is: some_namespace"), + "document_namespace must be a valid URI specified in RFC-3986 and must contain no fragment (#), but is: some_namespace"), ]) def test_invalid_creation_info(creation_info_input, expected_message, spdx_id): validation_messages: List[ValidationMessage] = validate_creation_info(creation_info_input) diff --git a/tests/spdx/validation/test_uri_validators.py b/tests/spdx/validation/test_uri_validators.py index a2a582292..a692ee8c7 100644 --- a/tests/spdx/validation/test_uri_validators.py +++ b/tests/spdx/validation/test_uri_validators.py @@ -99,7 +99,7 @@ def test_valid_uri(input_value): def test_invalid_uri(input_value): message = validate_uri(input_value) - assert message == [f"must be a valid URI specified in RFC-3986, but is: {input_value}"] + assert message == [f"must be a valid URI specified in RFC-3986 and must contain no fragment (#), but is: {input_value}"] @pytest.mark.parametrize("input_value", ["://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82..."]) From 036d21c4060d5a3cf30d6584913e8576f1d55f64 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 26 Jan 2023 17:27:53 +0100 Subject: [PATCH 167/362] delete unused dependencies und update README.md Signed-off-by: Meret Behrens --- README.md | 5 +++-- pyproject.toml | 14 +++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e281354ac..cb8e9d3c2 100644 --- a/README.md +++ b/README.md @@ -75,11 +75,12 @@ instead of `bin`. # Dependencies -* PLY: https://pypi.python.org/pypi/ply/ used for parsing. -* rdflib: https://pypi.python.org/pypi/rdflib/ for handling RDF. * PyYAML: https://pypi.org/project/PyYAML/ for handling YAML. * xmltodict: https://pypi.org/project/xmltodict/ for handling XML. * click: https://pypi.org/project/click/ for creating the CLI interface. +* rdflib: https://pypi.python.org/pypi/rdflib/ for handling RDF. +* typeguard: https://pypi.org/project/typeguard/ for using typehints. +* uritools: https://pypi.org/project/uritools/ for validation of URIs. # Support diff --git a/pyproject.toml b/pyproject.toml index b7f176add..fd2a6a867 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta" [project] name = "spdx-tools" -authors = [{name = "Ahmed H. Ismail", email = "ahm3d.hisham@gmail.com"}] +authors = [{ name = "Ahmed H. Ismail", email = "ahm3d.hisham@gmail.com" }] maintainers = [ - {name = "Philippe Ombredanne", email = "pombredanne@gmail.com"}, - {name = "SPDX group at the Linux Foundation and others"}, + { name = "Philippe Ombredanne", email = "pombredanne@gmail.com" }, + { name = "SPDX group at the Linux Foundation and others" }, ] -license = {text = "Apache-2.0"} +license = { text = "Apache-2.0" } description = "SPDX parser and tools." readme = "README.md" classifiers = [ @@ -22,9 +22,9 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", ] -urls = {Homepage = "https://github.com/spdx/tools-python"} +urls = { Homepage = "https://github.com/spdx/tools-python" } requires-python = ">=3.7" -dependencies = ["ply", "rdflib", "click", "pyyaml", "xmltodict", "typeguard", "uritools", "license_expression"] +dependencies = ["click", "pyyaml", "xmltodict", "rdflib", "typeguard", "uritools", "license_expression"] dynamic = ["version"] [project.optional-dependencies] @@ -41,7 +41,7 @@ include-package-data = true where = ["src"] [tool.setuptools_scm] -git_describe_command = ["git", "describe", "--dirty", "--tags", "--long", "--match", "v[0-9]*"] # `python3.6` tag falsely matches to the default one, clrearly a bug in setuptools_scm +git_describe_command = ["git", "describe", "--dirty", "--tags", "--long", "--match", "v[0-9]*"] # `python3.6` tag falsely matches to the default one, clearly a bug in setuptools_scm [tool.aliases] release = "clean --all sdist --formats=gztar bdist_wheel" From a91aa5a9aec530d97489d3c0b3660baf36c9f1f0 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 23 Jan 2023 15:49:30 +0100 Subject: [PATCH 168/362] [issue-407] add writer for rdf, start with simple graph Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/__init__.py | 0 src/spdx/writer/rdf/rdf_writer.py | 24 ++++++++++++++++++++ tests/spdx/writer/rdf/__init__.py | 0 tests/spdx/writer/rdf/test_rdf_writer.py | 29 ++++++++++++++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 src/spdx/writer/rdf/__init__.py create mode 100644 src/spdx/writer/rdf/rdf_writer.py create mode 100644 tests/spdx/writer/rdf/__init__.py create mode 100644 tests/spdx/writer/rdf/test_rdf_writer.py diff --git a/src/spdx/writer/rdf/__init__.py b/src/spdx/writer/rdf/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/spdx/writer/rdf/rdf_writer.py b/src/spdx/writer/rdf/rdf_writer.py new file mode 100644 index 000000000..09332d16e --- /dev/null +++ b/src/spdx/writer/rdf/rdf_writer.py @@ -0,0 +1,24 @@ +# Copyright (c) 2023 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 rdflib import Graph, Namespace, URIRef, RDF + +from spdx.model.document import Document + + +def write_document_to_file(document: Document, file_name: str): + spdx_namespace = Namespace("http://spdx.org/rdf/terms#") + doc_node = URIRef("http://www.spdx.org/tools#SPDXRef-DOCUMENT") + graph = Graph() + graph.bind("spdx", spdx_namespace) + graph.add((doc_node, RDF.type, spdx_namespace.SpdxDocument)) + + + graph.serialize(file_name, "pretty-xml", encoding="UTF-8") diff --git a/tests/spdx/writer/rdf/__init__.py b/tests/spdx/writer/rdf/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/spdx/writer/rdf/test_rdf_writer.py b/tests/spdx/writer/rdf/test_rdf_writer.py new file mode 100644 index 000000000..8677deeb3 --- /dev/null +++ b/tests/spdx/writer/rdf/test_rdf_writer.py @@ -0,0 +1,29 @@ +# Copyright (c) 2023 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. +import os + +import pytest + +from tests.spdx.fixtures import document_fixture + +from spdx.model.document import Document +from spdx.writer.rdf.rdf_writer import write_document_to_file + +@pytest.fixture +def temporary_file_path() -> str: + temporary_file_path = "temp_test_rdf_writer_output.rdf.xml" + yield temporary_file_path + os.remove(temporary_file_path) +def test_write_document_to_file(temporary_file_path: str): + + document: Document = document_fixture() + + write_document_to_file(document, temporary_file_path) From 60e1a8b45c986b6982c00e98e7d4d336809599d6 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 24 Jan 2023 09:10:02 +0100 Subject: [PATCH 169/362] [issue-407] add writer for creation information Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/creation_info_writer.py | 41 +++++++++++++++++++ src/spdx/writer/rdf/rdf_writer.py | 12 +++--- src/spdx/writer/rdf/writer_utils.py | 15 +++++++ .../writer/rdf/test_creation_info_writer.py | 37 +++++++++++++++++ 4 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 src/spdx/writer/rdf/creation_info_writer.py create mode 100644 src/spdx/writer/rdf/writer_utils.py create mode 100644 tests/spdx/writer/rdf/test_creation_info_writer.py diff --git a/src/spdx/writer/rdf/creation_info_writer.py b/src/spdx/writer/rdf/creation_info_writer.py new file mode 100644 index 000000000..34c126141 --- /dev/null +++ b/src/spdx/writer/rdf/creation_info_writer.py @@ -0,0 +1,41 @@ +# Copyright (c) 2023 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 rdflib import Graph, BNode, RDF, Literal, RDFS, URIRef + +from spdx.datetime_conversions import datetime_to_iso_string +from spdx.model.document import CreationInfo +from spdx.writer.rdf.writer_utils import spdx_namespace + + +def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): + doc_node = URIRef(f"{creation_info.document_namespace}#{creation_info.spdx_id}") + graph.add((doc_node, RDF.type, spdx_namespace().SpdxDocument)) + graph.add((doc_node, spdx_namespace().specVersion, Literal(creation_info.spdx_version))) + graph.add((doc_node, spdx_namespace().dataLicense, Literal(creation_info.data_license))) + graph.add((doc_node, spdx_namespace().name, Literal(creation_info.name))) + + creation_info_node = BNode() + graph.add((creation_info_node, RDF.type, spdx_namespace().CreationInfo)) + + graph.add((creation_info_node, spdx_namespace().created, Literal(datetime_to_iso_string(creation_info.created)))) + + for creator in creation_info.creators: + graph.add((creation_info_node, spdx_namespace().creator, Literal(creator.to_serialized_string()))) + + graph.add( + (creation_info_node, spdx_namespace().licenseListVersion, Literal(str(creation_info.license_list_version)))) + + if creation_info.creator_comment: + graph.add((creation_info_node, RDFS.comment, Literal(creation_info.creator_comment))) + + graph.add((doc_node, spdx_namespace().creationInfo, creation_info_node)) + + return diff --git a/src/spdx/writer/rdf/rdf_writer.py b/src/spdx/writer/rdf/rdf_writer.py index 09332d16e..640b90ca3 100644 --- a/src/spdx/writer/rdf/rdf_writer.py +++ b/src/spdx/writer/rdf/rdf_writer.py @@ -8,17 +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 rdflib import Graph, Namespace, URIRef, RDF +from rdflib import Graph +from rdflib.compare import to_isomorphic from spdx.model.document import Document +from spdx.writer.rdf.creation_info_writer import add_creation_info_to_graph +from spdx.writer.rdf.writer_utils import spdx_namespace def write_document_to_file(document: Document, file_name: str): - spdx_namespace = Namespace("http://spdx.org/rdf/terms#") - doc_node = URIRef("http://www.spdx.org/tools#SPDXRef-DOCUMENT") graph = Graph() - graph.bind("spdx", spdx_namespace) - graph.add((doc_node, RDF.type, spdx_namespace.SpdxDocument)) + add_creation_info_to_graph(document.creation_info, graph) + graph = to_isomorphic(graph) + graph.bind("spdx", spdx_namespace()) graph.serialize(file_name, "pretty-xml", encoding="UTF-8") diff --git a/src/spdx/writer/rdf/writer_utils.py b/src/spdx/writer/rdf/writer_utils.py new file mode 100644 index 000000000..a718276ea --- /dev/null +++ b/src/spdx/writer/rdf/writer_utils.py @@ -0,0 +1,15 @@ +# Copyright (c) 2023 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 rdflib import Namespace + + +def spdx_namespace(): + return Namespace("http://spdx.org/rdf/terms#") diff --git a/tests/spdx/writer/rdf/test_creation_info_writer.py b/tests/spdx/writer/rdf/test_creation_info_writer.py new file mode 100644 index 000000000..2ff6fdfd8 --- /dev/null +++ b/tests/spdx/writer/rdf/test_creation_info_writer.py @@ -0,0 +1,37 @@ +# Copyright (c) 2023 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 datetime import datetime + +from rdflib import Graph, Literal, RDFS, URIRef + +from spdx.datetime_conversions import datetime_to_iso_string +from spdx.writer.rdf.creation_info_writer import add_creation_info_to_graph +from spdx.writer.rdf.writer_utils import spdx_namespace +from tests.spdx.fixtures import creation_info_fixture + + +def test_add_creation_info_to_graph(): + graph = Graph() + creation_info = creation_info_fixture() + + add_creation_info_to_graph(creation_info, graph) + + assert (None, None, spdx_namespace().SpdxDocument) in graph + assert (URIRef(f"{creation_info.document_namespace}#{creation_info.spdx_id}"), None, None) in graph + assert (None, spdx_namespace().creationInfo, None) in graph + assert (None, spdx_namespace().name, Literal("documentName")) in graph + assert (None, spdx_namespace().specVersion, Literal("SPDX-2.3")) in graph + + assert (None, None, spdx_namespace().CreationInfo) in graph + assert (None, spdx_namespace().created, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph + assert (None, RDFS.comment, Literal("creatorComment")) in graph + assert (None, spdx_namespace().licenseListVersion, Literal("3.19")) in graph + assert (None, spdx_namespace().creator, Literal("Person: creatorName (some@mail.com)")) in graph From 76eb155f3b853c67d76d7fb9de52c68e05936e15 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 24 Jan 2023 12:26:12 +0100 Subject: [PATCH 170/362] [issue-407] add writer for annotations Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/annotation_writer.py | 26 +++++++++++++++ .../spdx/writer/rdf/test_annotation_writer.py | 32 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/spdx/writer/rdf/annotation_writer.py create mode 100644 tests/spdx/writer/rdf/test_annotation_writer.py diff --git a/src/spdx/writer/rdf/annotation_writer.py b/src/spdx/writer/rdf/annotation_writer.py new file mode 100644 index 000000000..845dc9f47 --- /dev/null +++ b/src/spdx/writer/rdf/annotation_writer.py @@ -0,0 +1,26 @@ +# Copyright (c) 2023 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 rdflib import Graph, Literal, RDFS, URIRef, RDF + +from spdx.datetime_conversions import datetime_to_iso_string +from spdx.model.annotation import Annotation +from spdx.writer.rdf.writer_utils import spdx_namespace + + +def add_annotation_info_to_graph(annotation: Annotation, graph: Graph): + annotation_node = URIRef(annotation.spdx_id) + + graph.add((annotation_node, RDF.type, spdx_namespace().Annotation)) + graph.add((annotation_node, spdx_namespace().annotationType, Literal(annotation.annotation_type.name))) + graph.add((annotation_node, spdx_namespace().annotator, Literal(annotation.annotator.to_serialized_string()))) + graph.add( + (annotation_node, spdx_namespace().annotationDate, Literal(datetime_to_iso_string(annotation.annotation_date)))) + graph.add((annotation_node, RDFS.comment, Literal(annotation.annotation_comment))) diff --git a/tests/spdx/writer/rdf/test_annotation_writer.py b/tests/spdx/writer/rdf/test_annotation_writer.py new file mode 100644 index 000000000..67397610c --- /dev/null +++ b/tests/spdx/writer/rdf/test_annotation_writer.py @@ -0,0 +1,32 @@ +# Copyright (c) 2023 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 datetime import datetime + +from rdflib import Graph, Literal, RDFS + +from spdx.datetime_conversions import datetime_to_iso_string +from spdx.model.annotation import AnnotationType +from spdx.writer.rdf.annotation_writer import add_annotation_info_to_graph +from spdx.writer.rdf.writer_utils import spdx_namespace +from tests.spdx.fixtures import annotation_fixture + + +def test_add_annotation_info_to_graph(): + graph = Graph() + annotation = annotation_fixture() + + add_annotation_info_to_graph(annotation, graph) + + assert (None, None, spdx_namespace().Annotation) in graph + assert (None, spdx_namespace().annotationType, Literal(AnnotationType.REVIEW.name)) in graph + assert (None, spdx_namespace().annotationDate, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph + assert (None, spdx_namespace().annotator, Literal("Person: annotatorName")) + assert (None, RDFS.comment, Literal("annotationComment")) in graph From b9a93268c91e47dacff4c8234f98cbabe4eeb187 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 24 Jan 2023 12:39:19 +0100 Subject: [PATCH 171/362] [issue-407] connect annotation with spdx element that is annotated Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/annotation_writer.py | 10 ++++++---- src/spdx/writer/rdf/rdf_writer.py | 3 +++ tests/spdx/writer/rdf/test_annotation_writer.py | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/spdx/writer/rdf/annotation_writer.py b/src/spdx/writer/rdf/annotation_writer.py index 845dc9f47..aa13dd311 100644 --- a/src/spdx/writer/rdf/annotation_writer.py +++ b/src/spdx/writer/rdf/annotation_writer.py @@ -8,19 +8,21 @@ # 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 rdflib import Graph, Literal, RDFS, URIRef, RDF +from rdflib import Graph, Literal, RDFS, URIRef, RDF, BNode from spdx.datetime_conversions import datetime_to_iso_string from spdx.model.annotation import Annotation from spdx.writer.rdf.writer_utils import spdx_namespace -def add_annotation_info_to_graph(annotation: Annotation, graph: Graph): - annotation_node = URIRef(annotation.spdx_id) - +def add_annotation_info_to_graph(annotation: Annotation, graph: Graph, doc_namespace: str): + annotation_resource = URIRef(f"{doc_namespace}#{annotation.spdx_id}") + annotation_node = BNode() graph.add((annotation_node, RDF.type, spdx_namespace().Annotation)) graph.add((annotation_node, spdx_namespace().annotationType, Literal(annotation.annotation_type.name))) graph.add((annotation_node, spdx_namespace().annotator, Literal(annotation.annotator.to_serialized_string()))) graph.add( (annotation_node, spdx_namespace().annotationDate, Literal(datetime_to_iso_string(annotation.annotation_date)))) graph.add((annotation_node, RDFS.comment, Literal(annotation.annotation_comment))) + + graph.add((annotation_resource, spdx_namespace().annotation, annotation_node)) diff --git a/src/spdx/writer/rdf/rdf_writer.py b/src/spdx/writer/rdf/rdf_writer.py index 640b90ca3..fb3ebd2a5 100644 --- a/src/spdx/writer/rdf/rdf_writer.py +++ b/src/spdx/writer/rdf/rdf_writer.py @@ -12,6 +12,7 @@ from rdflib.compare import to_isomorphic from spdx.model.document import Document +from spdx.writer.rdf.annotation_writer import add_annotation_info_to_graph from spdx.writer.rdf.creation_info_writer import add_creation_info_to_graph from spdx.writer.rdf.writer_utils import spdx_namespace @@ -20,6 +21,8 @@ def write_document_to_file(document: Document, file_name: str): graph = Graph() add_creation_info_to_graph(document.creation_info, graph) + for annotation in document.annotations: + add_annotation_info_to_graph(annotation, graph, document.creation_info.document_namespace) graph = to_isomorphic(graph) graph.bind("spdx", spdx_namespace()) diff --git a/tests/spdx/writer/rdf/test_annotation_writer.py b/tests/spdx/writer/rdf/test_annotation_writer.py index 67397610c..2ff7ba640 100644 --- a/tests/spdx/writer/rdf/test_annotation_writer.py +++ b/tests/spdx/writer/rdf/test_annotation_writer.py @@ -23,7 +23,7 @@ def test_add_annotation_info_to_graph(): graph = Graph() annotation = annotation_fixture() - add_annotation_info_to_graph(annotation, graph) + add_annotation_info_to_graph(annotation, graph, "anyURI") assert (None, None, spdx_namespace().Annotation) in graph assert (None, spdx_namespace().annotationType, Literal(AnnotationType.REVIEW.name)) in graph From 2a4b4f2cae041094a56205e15107803fe7518ad5 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 24 Jan 2023 12:52:50 +0100 Subject: [PATCH 172/362] [issue-407] add annotation_type as resource not Literal Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/annotation_writer.py | 2 +- tests/spdx/writer/rdf/test_annotation_writer.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/spdx/writer/rdf/annotation_writer.py b/src/spdx/writer/rdf/annotation_writer.py index aa13dd311..502e50f4b 100644 --- a/src/spdx/writer/rdf/annotation_writer.py +++ b/src/spdx/writer/rdf/annotation_writer.py @@ -19,7 +19,7 @@ def add_annotation_info_to_graph(annotation: Annotation, graph: Graph, doc_names annotation_resource = URIRef(f"{doc_namespace}#{annotation.spdx_id}") annotation_node = BNode() graph.add((annotation_node, RDF.type, spdx_namespace().Annotation)) - graph.add((annotation_node, spdx_namespace().annotationType, Literal(annotation.annotation_type.name))) + graph.add((annotation_node, spdx_namespace().annotationType, spdx_namespace()[f"annotationType_{annotation.annotation_type.name.lower()}"])) graph.add((annotation_node, spdx_namespace().annotator, Literal(annotation.annotator.to_serialized_string()))) graph.add( (annotation_node, spdx_namespace().annotationDate, Literal(datetime_to_iso_string(annotation.annotation_date)))) diff --git a/tests/spdx/writer/rdf/test_annotation_writer.py b/tests/spdx/writer/rdf/test_annotation_writer.py index 2ff7ba640..2ec235876 100644 --- a/tests/spdx/writer/rdf/test_annotation_writer.py +++ b/tests/spdx/writer/rdf/test_annotation_writer.py @@ -13,7 +13,6 @@ from rdflib import Graph, Literal, RDFS from spdx.datetime_conversions import datetime_to_iso_string -from spdx.model.annotation import AnnotationType from spdx.writer.rdf.annotation_writer import add_annotation_info_to_graph from spdx.writer.rdf.writer_utils import spdx_namespace from tests.spdx.fixtures import annotation_fixture @@ -26,7 +25,7 @@ def test_add_annotation_info_to_graph(): add_annotation_info_to_graph(annotation, graph, "anyURI") assert (None, None, spdx_namespace().Annotation) in graph - assert (None, spdx_namespace().annotationType, Literal(AnnotationType.REVIEW.name)) in graph + assert (None, spdx_namespace().annotationType, spdx_namespace().annotationType_review) in graph assert (None, spdx_namespace().annotationDate, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph assert (None, spdx_namespace().annotator, Literal("Person: annotatorName")) assert (None, RDFS.comment, Literal("annotationComment")) in graph From 922a2c84b1cfe48a8868d7d894eacfd7579b353c Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 24 Jan 2023 13:47:30 +0100 Subject: [PATCH 173/362] [issue-407] add relationship writer Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/relationship_writer.py | 25 +++++++++++++++++++ .../writer/rdf/test_relationship_writer.py | 24 ++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/spdx/writer/rdf/relationship_writer.py create mode 100644 tests/spdx/writer/rdf/test_relationship_writer.py diff --git a/src/spdx/writer/rdf/relationship_writer.py b/src/spdx/writer/rdf/relationship_writer.py new file mode 100644 index 000000000..d8761b1aa --- /dev/null +++ b/src/spdx/writer/rdf/relationship_writer.py @@ -0,0 +1,25 @@ +# Copyright (c) 2023 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 rdflib import Graph, BNode, RDF, URIRef + +from spdx.model.relationship import Relationship +from spdx.writer.rdf.writer_utils import spdx_namespace + + +def add_relationship_info_to_graph(relationship: Relationship, graph: Graph, doc_namespace: str): + + relationship_node = BNode() + graph.add((relationship_node, RDF.type, spdx_namespace().Relationship)) + graph.add((relationship_node, spdx_namespace().relationshipType, spdx_namespace()[f"relationshipType_{relationship.relationship_type.name.lower()}"])) + graph.add((relationship_node, spdx_namespace().relatedSpdxElement, URIRef(f"{doc_namespace}#{relationship.related_spdx_element_id}"))) + + relationship_resource = URIRef(f"{doc_namespace}#{relationship.spdx_element_id}") + graph.add((relationship_resource, spdx_namespace().relationship, relationship_node)) diff --git a/tests/spdx/writer/rdf/test_relationship_writer.py b/tests/spdx/writer/rdf/test_relationship_writer.py new file mode 100644 index 000000000..9063f8d89 --- /dev/null +++ b/tests/spdx/writer/rdf/test_relationship_writer.py @@ -0,0 +1,24 @@ +# Copyright (c) 2023 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 rdflib import Graph + +from spdx.writer.rdf.relationship_writer import add_relationship_info_to_graph +from spdx.writer.rdf.writer_utils import spdx_namespace +from tests.spdx.fixtures import relationship_fixture + + +def test_add_relationship_info_to_graph(): + relationship = relationship_fixture() + graph = Graph() + add_relationship_info_to_graph(relationship, graph, "anyURI") + + assert (None, spdx_namespace().relationshipType, spdx_namespace().relationshipType_describes) in graph + assert (None, spdx_namespace().relatedSpdxElement, None) in graph From ce2995d7859de084b3a5f28b98f65d54e5ce337b Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 24 Jan 2023 13:54:14 +0100 Subject: [PATCH 174/362] [issue-407] add relationship writer to document writer Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/rdf_writer.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/spdx/writer/rdf/rdf_writer.py b/src/spdx/writer/rdf/rdf_writer.py index fb3ebd2a5..93ae6c059 100644 --- a/src/spdx/writer/rdf/rdf_writer.py +++ b/src/spdx/writer/rdf/rdf_writer.py @@ -14,16 +14,19 @@ from spdx.model.document import Document from spdx.writer.rdf.annotation_writer import add_annotation_info_to_graph from spdx.writer.rdf.creation_info_writer import add_creation_info_to_graph +from spdx.writer.rdf.relationship_writer import add_relationship_info_to_graph from spdx.writer.rdf.writer_utils import spdx_namespace def write_document_to_file(document: Document, file_name: str): graph = Graph() - + doc_namespace = document.creation_info.document_namespace add_creation_info_to_graph(document.creation_info, graph) for annotation in document.annotations: - add_annotation_info_to_graph(annotation, graph, document.creation_info.document_namespace) + add_annotation_info_to_graph(annotation, graph, doc_namespace) + for relationship in document.relationships: + add_relationship_info_to_graph(relationship, graph, doc_namespace) graph = to_isomorphic(graph) graph.bind("spdx", spdx_namespace()) graph.serialize(file_name, "pretty-xml", encoding="UTF-8") From ea865b7108a9228d29fc281761e83bd4844fb16e Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 24 Jan 2023 14:46:00 +0100 Subject: [PATCH 175/362] [issue-407] add checksum writer Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/checksum_writer.py | 24 +++++++++++++++++ tests/spdx/writer/rdf/test_checksum_writer.py | 26 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 src/spdx/writer/rdf/checksum_writer.py create mode 100644 tests/spdx/writer/rdf/test_checksum_writer.py diff --git a/src/spdx/writer/rdf/checksum_writer.py b/src/spdx/writer/rdf/checksum_writer.py new file mode 100644 index 000000000..db102f6e6 --- /dev/null +++ b/src/spdx/writer/rdf/checksum_writer.py @@ -0,0 +1,24 @@ +# Copyright (c) 2023 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 rdflib import Graph, URIRef, BNode, RDF, Literal + +from spdx.model.checksum import Checksum +from spdx.writer.rdf.writer_utils import spdx_namespace + + +def add_checksum_information_to_graph(checksum: Checksum, graph: Graph, parent_node: URIRef): + checksum_node = BNode() + graph.add((checksum_node, RDF.type, spdx_namespace().Checksum)) + graph.add((checksum_node, spdx_namespace().algorithm, + spdx_namespace()[f"checksumAlgorithm_{checksum.algorithm.name.lower()}"])) + graph.add((checksum_node, spdx_namespace().checksumValue, Literal(checksum.value))) + + graph.add((parent_node, spdx_namespace().checksum, checksum_node)) diff --git a/tests/spdx/writer/rdf/test_checksum_writer.py b/tests/spdx/writer/rdf/test_checksum_writer.py new file mode 100644 index 000000000..a7571f768 --- /dev/null +++ b/tests/spdx/writer/rdf/test_checksum_writer.py @@ -0,0 +1,26 @@ +# Copyright (c) 2023 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 rdflib import Graph, URIRef, Literal + +from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph +from spdx.writer.rdf.writer_utils import spdx_namespace +from tests.spdx.fixtures import checksum_fixture + + +def test_add_checksum_information_to_graph(): + graph = Graph() + checksum = checksum_fixture() + + add_checksum_information_to_graph(checksum, graph, URIRef("TestURI")) + + assert (None, None, spdx_namespace().Checksum) in graph + assert (None, spdx_namespace().algorithm, spdx_namespace().checksumAlgorithm_sha1) in graph + assert (None, spdx_namespace().checksumValue, Literal("71c4025dd9897b364f3ebbb42c484ff43d00791c")) in graph From 47fde839c5abd7f6e1ee522090727fd04662535d Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 24 Jan 2023 15:49:22 +0100 Subject: [PATCH 176/362] [issue-407] add file writer Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/file_writer.py | 54 +++++++++++++++++++++++ src/spdx/writer/rdf/rdf_writer.py | 6 +++ tests/spdx/writer/rdf/test_file_writer.py | 35 +++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 src/spdx/writer/rdf/file_writer.py create mode 100644 tests/spdx/writer/rdf/test_file_writer.py diff --git a/src/spdx/writer/rdf/file_writer.py b/src/spdx/writer/rdf/file_writer.py new file mode 100644 index 000000000..aa1a34722 --- /dev/null +++ b/src/spdx/writer/rdf/file_writer.py @@ -0,0 +1,54 @@ +# Copyright (c) 2023 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 typing import Any, Union + +from rdflib import Graph, BNode, URIRef, Literal, RDF, RDFS +from rdflib.term import Node + +from spdx.model.file import File +from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph +from spdx.writer.rdf.writer_utils import spdx_namespace + + +def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str): + file_resource = URIRef(f"{doc_namespace}#{file.spdx_id}") + graph.add((file_resource, RDF.type, spdx_namespace().File)) + graph.add((file_resource, spdx_namespace().fileName, Literal(file.name))) + for file_type in file.file_type: + graph.add((file_resource, spdx_namespace().fileType, spdx_namespace()[f"fileType_{file_type.name.lower()}"])) + + for checksum in file.checksums: + add_checksum_information_to_graph(checksum, graph, file_resource) + + # as long as we don't have a proper handling of the licenses we simply write literals here + add_literal_value_if_exists(graph, file_resource, spdx_namespace().licenseConcluded, file.license_concluded) + add_literal_value_if_exists(graph, file_resource, spdx_namespace().licenseInfoInFile, file.license_info_in_file) + + add_literal_value_if_exists(graph, file_resource, spdx_namespace().licenseComments, file.license_comment) + add_literal_value_if_exists(graph, file_resource, spdx_namespace().copyrightText, file.copyright_text) + add_literal_value_if_exists(graph, file_resource, RDFS.comment, file.comment) + add_literal_value_if_exists(graph, file_resource, spdx_namespace().noticeText, file.notice) + for contributor in file.contributors: + graph.add((file_resource, spdx_namespace().fileContributor, Literal(contributor))) + for attribution_text in file.attribution_texts: + graph.add((file_resource, spdx_namespace().attributionText, Literal(attribution_text))) + + +def add_literal_value_if_exists(graph: Graph, parent: Node, predicate: Node, value: Union[Any, list]): + if not value: + return + if not isinstance(value, list): + graph.add((parent, predicate, Literal(str(value)))) + return + + for element in value: + element_triple = (parent, predicate, Literal(str(element))) + graph.add(element_triple) diff --git a/src/spdx/writer/rdf/rdf_writer.py b/src/spdx/writer/rdf/rdf_writer.py index 93ae6c059..4553496f4 100644 --- a/src/spdx/writer/rdf/rdf_writer.py +++ b/src/spdx/writer/rdf/rdf_writer.py @@ -14,6 +14,7 @@ from spdx.model.document import Document from spdx.writer.rdf.annotation_writer import add_annotation_info_to_graph from spdx.writer.rdf.creation_info_writer import add_creation_info_to_graph +from spdx.writer.rdf.file_writer import add_file_information_to_graph from spdx.writer.rdf.relationship_writer import add_relationship_info_to_graph from spdx.writer.rdf.writer_utils import spdx_namespace @@ -25,8 +26,13 @@ def write_document_to_file(document: Document, file_name: str): for annotation in document.annotations: add_annotation_info_to_graph(annotation, graph, doc_namespace) + for file in document.files: + add_file_information_to_graph(file, graph, doc_namespace) + for relationship in document.relationships: add_relationship_info_to_graph(relationship, graph, doc_namespace) + + graph = to_isomorphic(graph) graph.bind("spdx", spdx_namespace()) graph.serialize(file_name, "pretty-xml", encoding="UTF-8") diff --git a/tests/spdx/writer/rdf/test_file_writer.py b/tests/spdx/writer/rdf/test_file_writer.py new file mode 100644 index 000000000..07c0abba6 --- /dev/null +++ b/tests/spdx/writer/rdf/test_file_writer.py @@ -0,0 +1,35 @@ +# Copyright (c) 2023 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 rdflib import Graph, Literal, RDFS, RDF, URIRef + +from spdx.writer.rdf.file_writer import add_file_information_to_graph +from spdx.writer.rdf.writer_utils import spdx_namespace +from tests.spdx.fixtures import file_fixture + + +def test_add_file_information_to_graph(): + graph = Graph() + file = file_fixture() + + add_file_information_to_graph(file, graph, "anyURI") + + assert (URIRef("anyURI#SPDXRef-File"), RDF.type, spdx_namespace().File) in graph + assert (None, spdx_namespace().fileName, Literal("./fileName.py")) in graph + assert (None, spdx_namespace().fileType, spdx_namespace().fileType_text) in graph + assert (None, spdx_namespace().licenseComments, Literal("licenseComment")) in graph + assert (None, spdx_namespace().licenseConcluded, Literal("licenseConcludedExpression")) in graph + assert (None, spdx_namespace().licenseInfoInFile, Literal("licenseInfoInFileExpression")) in graph + assert (None, spdx_namespace().copyrightText, Literal("copyrightText")) in graph + assert (None, RDFS.comment, Literal("fileComment")) in graph + assert (None, spdx_namespace().noticeText, Literal("fileNotice")) in graph + assert (None, spdx_namespace().fileContributor, Literal("fileContributor")) in graph + assert (None, spdx_namespace().checksum, None) in graph + assert (None, spdx_namespace().attributionText, Literal("fileAttributionText")) in graph From 8dbeb2e207f50f1f57115e59bc7a00ac2e98b47d Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 24 Jan 2023 16:47:38 +0100 Subject: [PATCH 177/362] [issue-407] fix annotation test Signed-off-by: Meret Behrens --- tests/spdx/writer/rdf/test_annotation_writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/spdx/writer/rdf/test_annotation_writer.py b/tests/spdx/writer/rdf/test_annotation_writer.py index 2ec235876..c0d9e5fd0 100644 --- a/tests/spdx/writer/rdf/test_annotation_writer.py +++ b/tests/spdx/writer/rdf/test_annotation_writer.py @@ -27,5 +27,5 @@ def test_add_annotation_info_to_graph(): assert (None, None, spdx_namespace().Annotation) in graph assert (None, spdx_namespace().annotationType, spdx_namespace().annotationType_review) in graph assert (None, spdx_namespace().annotationDate, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph - assert (None, spdx_namespace().annotator, Literal("Person: annotatorName")) + assert (None, spdx_namespace().annotator, Literal("Person: annotatorName (some@mail.com)")) in graph assert (None, RDFS.comment, Literal("annotationComment")) in graph From 516bc035a801af6614ed98ac66cd9f42ceb1f6f9 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 25 Jan 2023 08:53:45 +0100 Subject: [PATCH 178/362] [issue-407] also use add_value_if_exists in creation information Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/creation_info_writer.py | 10 ++++------ src/spdx/writer/rdf/file_writer.py | 12 +----------- src/spdx/writer/rdf/writer_utils.py | 18 +++++++++++++++--- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/spdx/writer/rdf/creation_info_writer.py b/src/spdx/writer/rdf/creation_info_writer.py index 34c126141..acaf8e46f 100644 --- a/src/spdx/writer/rdf/creation_info_writer.py +++ b/src/spdx/writer/rdf/creation_info_writer.py @@ -12,7 +12,7 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.model.document import CreationInfo -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import spdx_namespace(), add_literal_value_if_exists def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): @@ -21,6 +21,7 @@ def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): graph.add((doc_node, spdx_namespace().specVersion, Literal(creation_info.spdx_version))) graph.add((doc_node, spdx_namespace().dataLicense, Literal(creation_info.data_license))) graph.add((doc_node, spdx_namespace().name, Literal(creation_info.name))) + add_literal_value_if_exists(graph, doc_node, RDFS.comment, creation_info.document_comment) creation_info_node = BNode() graph.add((creation_info_node, RDF.type, spdx_namespace().CreationInfo)) @@ -30,11 +31,8 @@ def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): for creator in creation_info.creators: graph.add((creation_info_node, spdx_namespace().creator, Literal(creator.to_serialized_string()))) - graph.add( - (creation_info_node, spdx_namespace().licenseListVersion, Literal(str(creation_info.license_list_version)))) - - if creation_info.creator_comment: - graph.add((creation_info_node, RDFS.comment, Literal(creation_info.creator_comment))) + add_literal_value_if_exists(graph, creation_info_node, spdx_namespace().licenseListVersion, creation_info.license_list_version) + add_literal_value_if_exists(graph, creation_info_node, RDFS.comment, creation_info.creator_comment) graph.add((doc_node, spdx_namespace().creationInfo, creation_info_node)) diff --git a/src/spdx/writer/rdf/file_writer.py b/src/spdx/writer/rdf/file_writer.py index aa1a34722..c446546ee 100644 --- a/src/spdx/writer/rdf/file_writer.py +++ b/src/spdx/writer/rdf/file_writer.py @@ -15,7 +15,7 @@ from spdx.model.file import File from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value_if_exists def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str): @@ -42,13 +42,3 @@ def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str): graph.add((file_resource, spdx_namespace().attributionText, Literal(attribution_text))) -def add_literal_value_if_exists(graph: Graph, parent: Node, predicate: Node, value: Union[Any, list]): - if not value: - return - if not isinstance(value, list): - graph.add((parent, predicate, Literal(str(value)))) - return - - for element in value: - element_triple = (parent, predicate, Literal(str(element))) - graph.add(element_triple) diff --git a/src/spdx/writer/rdf/writer_utils.py b/src/spdx/writer/rdf/writer_utils.py index a718276ea..a4848d492 100644 --- a/src/spdx/writer/rdf/writer_utils.py +++ b/src/spdx/writer/rdf/writer_utils.py @@ -8,8 +8,20 @@ # 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 rdflib import Namespace +from typing import Union, Any +from rdflib import Namespace, Graph, Literal +from rdflib.term import Node -def spdx_namespace(): - return Namespace("http://spdx.org/rdf/terms#") + + +def add_literal_value_if_exists(graph: Graph, parent: Node, predicate: Node, value: Union[Any, list]): + if not value: + return + if not isinstance(value, list): + graph.add((parent, predicate, Literal(str(value)))) + return + + for element in value: + element_triple = (parent, predicate, Literal(str(element))) + graph.add(element_triple) From f5303ebc4b06bafdfd7a3325507f86c4b0c55c2e Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 25 Jan 2023 14:50:20 +0100 Subject: [PATCH 179/362] [issue-407] turn spdx_namespace into a variable Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/annotation_writer.py | 10 ++++---- src/spdx/writer/rdf/checksum_writer.py | 10 ++++---- src/spdx/writer/rdf/creation_info_writer.py | 20 ++++++++-------- src/spdx/writer/rdf/file_writer.py | 24 +++++++++---------- src/spdx/writer/rdf/rdf_writer.py | 8 ++++++- src/spdx/writer/rdf/relationship_writer.py | 8 +++---- src/spdx/writer/rdf/writer_utils.py | 1 + .../spdx/writer/rdf/test_annotation_writer.py | 8 +++---- tests/spdx/writer/rdf/test_checksum_writer.py | 6 ++--- .../writer/rdf/test_creation_info_writer.py | 16 ++++++------- tests/spdx/writer/rdf/test_file_writer.py | 22 ++++++++--------- tests/spdx/writer/rdf/test_rdf_writer.py | 2 +- .../writer/rdf/test_relationship_writer.py | 4 ++-- 13 files changed, 72 insertions(+), 67 deletions(-) diff --git a/src/spdx/writer/rdf/annotation_writer.py b/src/spdx/writer/rdf/annotation_writer.py index 502e50f4b..c62febd1c 100644 --- a/src/spdx/writer/rdf/annotation_writer.py +++ b/src/spdx/writer/rdf/annotation_writer.py @@ -18,11 +18,11 @@ def add_annotation_info_to_graph(annotation: Annotation, graph: Graph, doc_namespace: str): annotation_resource = URIRef(f"{doc_namespace}#{annotation.spdx_id}") annotation_node = BNode() - graph.add((annotation_node, RDF.type, spdx_namespace().Annotation)) - graph.add((annotation_node, spdx_namespace().annotationType, spdx_namespace()[f"annotationType_{annotation.annotation_type.name.lower()}"])) - graph.add((annotation_node, spdx_namespace().annotator, Literal(annotation.annotator.to_serialized_string()))) + graph.add((annotation_node, RDF.type, spdx_namespace.Annotation)) + graph.add((annotation_node, spdx_namespace.annotationType, spdx_namespace[f"annotationType_{annotation.annotation_type.name.lower()}"])) + graph.add((annotation_node, spdx_namespace.annotator, Literal(annotation.annotator.to_serialized_string()))) graph.add( - (annotation_node, spdx_namespace().annotationDate, Literal(datetime_to_iso_string(annotation.annotation_date)))) + (annotation_node, spdx_namespace.annotationDate, Literal(datetime_to_iso_string(annotation.annotation_date)))) graph.add((annotation_node, RDFS.comment, Literal(annotation.annotation_comment))) - graph.add((annotation_resource, spdx_namespace().annotation, annotation_node)) + graph.add((annotation_resource, spdx_namespace.annotation, annotation_node)) diff --git a/src/spdx/writer/rdf/checksum_writer.py b/src/spdx/writer/rdf/checksum_writer.py index db102f6e6..354c91fde 100644 --- a/src/spdx/writer/rdf/checksum_writer.py +++ b/src/spdx/writer/rdf/checksum_writer.py @@ -16,9 +16,9 @@ def add_checksum_information_to_graph(checksum: Checksum, graph: Graph, parent_node: URIRef): checksum_node = BNode() - graph.add((checksum_node, RDF.type, spdx_namespace().Checksum)) - graph.add((checksum_node, spdx_namespace().algorithm, - spdx_namespace()[f"checksumAlgorithm_{checksum.algorithm.name.lower()}"])) - graph.add((checksum_node, spdx_namespace().checksumValue, Literal(checksum.value))) + graph.add((checksum_node, RDF.type, spdx_namespace.Checksum)) + graph.add((checksum_node, spdx_namespace.algorithm, + spdx_namespace[f"checksumAlgorithm_{checksum.algorithm.name.lower()}"])) + graph.add((checksum_node, spdx_namespace.checksumValue, Literal(checksum.value))) - graph.add((parent_node, spdx_namespace().checksum, checksum_node)) + graph.add((parent_node, spdx_namespace.checksum, checksum_node)) diff --git a/src/spdx/writer/rdf/creation_info_writer.py b/src/spdx/writer/rdf/creation_info_writer.py index acaf8e46f..95052923d 100644 --- a/src/spdx/writer/rdf/creation_info_writer.py +++ b/src/spdx/writer/rdf/creation_info_writer.py @@ -12,28 +12,28 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.model.document import CreationInfo -from spdx.writer.rdf.writer_utils import spdx_namespace(), add_literal_value_if_exists +from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value_if_exists def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): doc_node = URIRef(f"{creation_info.document_namespace}#{creation_info.spdx_id}") - graph.add((doc_node, RDF.type, spdx_namespace().SpdxDocument)) - graph.add((doc_node, spdx_namespace().specVersion, Literal(creation_info.spdx_version))) - graph.add((doc_node, spdx_namespace().dataLicense, Literal(creation_info.data_license))) - graph.add((doc_node, spdx_namespace().name, Literal(creation_info.name))) + graph.add((doc_node, RDF.type, spdx_namespace.SpdxDocument)) + graph.add((doc_node, spdx_namespace.specVersion, Literal(creation_info.spdx_version))) + graph.add((doc_node, spdx_namespace.dataLicense, Literal(creation_info.data_license))) + graph.add((doc_node, spdx_namespace.name, Literal(creation_info.name))) add_literal_value_if_exists(graph, doc_node, RDFS.comment, creation_info.document_comment) creation_info_node = BNode() - graph.add((creation_info_node, RDF.type, spdx_namespace().CreationInfo)) + graph.add((creation_info_node, RDF.type, spdx_namespace.CreationInfo)) - graph.add((creation_info_node, spdx_namespace().created, Literal(datetime_to_iso_string(creation_info.created)))) + graph.add((creation_info_node, spdx_namespace.created, Literal(datetime_to_iso_string(creation_info.created)))) for creator in creation_info.creators: - graph.add((creation_info_node, spdx_namespace().creator, Literal(creator.to_serialized_string()))) + graph.add((creation_info_node, spdx_namespace.creator, Literal(creator.to_serialized_string()))) - add_literal_value_if_exists(graph, creation_info_node, spdx_namespace().licenseListVersion, creation_info.license_list_version) + add_literal_value_if_exists(graph, creation_info_node, spdx_namespace.licenseListVersion, creation_info.license_list_version) add_literal_value_if_exists(graph, creation_info_node, RDFS.comment, creation_info.creator_comment) - graph.add((doc_node, spdx_namespace().creationInfo, creation_info_node)) + graph.add((doc_node, spdx_namespace.creationInfo, creation_info_node)) return diff --git a/src/spdx/writer/rdf/file_writer.py b/src/spdx/writer/rdf/file_writer.py index c446546ee..bd0b266d0 100644 --- a/src/spdx/writer/rdf/file_writer.py +++ b/src/spdx/writer/rdf/file_writer.py @@ -8,10 +8,8 @@ # 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 typing import Any, Union -from rdflib import Graph, BNode, URIRef, Literal, RDF, RDFS -from rdflib.term import Node +from rdflib import Graph, URIRef, Literal, RDF, RDFS from spdx.model.file import File from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph @@ -20,25 +18,25 @@ def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str): file_resource = URIRef(f"{doc_namespace}#{file.spdx_id}") - graph.add((file_resource, RDF.type, spdx_namespace().File)) - graph.add((file_resource, spdx_namespace().fileName, Literal(file.name))) + graph.add((file_resource, RDF.type, spdx_namespace.File)) + graph.add((file_resource, spdx_namespace.fileName, Literal(file.name))) for file_type in file.file_type: - graph.add((file_resource, spdx_namespace().fileType, spdx_namespace()[f"fileType_{file_type.name.lower()}"])) + graph.add((file_resource, spdx_namespace.fileType, spdx_namespace[f"fileType_{file_type.name.lower()}"])) for checksum in file.checksums: add_checksum_information_to_graph(checksum, graph, file_resource) # as long as we don't have a proper handling of the licenses we simply write literals here - add_literal_value_if_exists(graph, file_resource, spdx_namespace().licenseConcluded, file.license_concluded) - add_literal_value_if_exists(graph, file_resource, spdx_namespace().licenseInfoInFile, file.license_info_in_file) + add_literal_value_if_exists(graph, file_resource, spdx_namespace.licenseConcluded, file.license_concluded) + add_literal_value_if_exists(graph, file_resource, spdx_namespace.licenseInfoInFile, file.license_info_in_file) - add_literal_value_if_exists(graph, file_resource, spdx_namespace().licenseComments, file.license_comment) - add_literal_value_if_exists(graph, file_resource, spdx_namespace().copyrightText, file.copyright_text) + add_literal_value_if_exists(graph, file_resource, spdx_namespace.licenseComments, file.license_comment) + add_literal_value_if_exists(graph, file_resource, spdx_namespace.copyrightText, file.copyright_text) add_literal_value_if_exists(graph, file_resource, RDFS.comment, file.comment) - add_literal_value_if_exists(graph, file_resource, spdx_namespace().noticeText, file.notice) + add_literal_value_if_exists(graph, file_resource, spdx_namespace.noticeText, file.notice) for contributor in file.contributors: - graph.add((file_resource, spdx_namespace().fileContributor, Literal(contributor))) + graph.add((file_resource, spdx_namespace.fileContributor, Literal(contributor))) for attribution_text in file.attribution_texts: - graph.add((file_resource, spdx_namespace().attributionText, Literal(attribution_text))) + graph.add((file_resource, spdx_namespace.attributionText, Literal(attribution_text))) diff --git a/src/spdx/writer/rdf/rdf_writer.py b/src/spdx/writer/rdf/rdf_writer.py index 4553496f4..32e620eb5 100644 --- a/src/spdx/writer/rdf/rdf_writer.py +++ b/src/spdx/writer/rdf/rdf_writer.py @@ -15,6 +15,7 @@ from spdx.writer.rdf.annotation_writer import add_annotation_info_to_graph from spdx.writer.rdf.creation_info_writer import add_creation_info_to_graph from spdx.writer.rdf.file_writer import add_file_information_to_graph +from spdx.writer.rdf.package_writer import add_package_information_to_graph from spdx.writer.rdf.relationship_writer import add_relationship_info_to_graph from spdx.writer.rdf.writer_utils import spdx_namespace @@ -29,10 +30,15 @@ def write_document_to_file(document: Document, file_name: str): for file in document.files: add_file_information_to_graph(file, graph, doc_namespace) + for package in document.packages: + add_package_information_to_graph(package, graph, doc_namespace) + for relationship in document.relationships: add_relationship_info_to_graph(relationship, graph, doc_namespace) + # once all elements are added to the graph we probably need some logic to inline, Files, Packages, Snippets + # pseudocode: graph.add(URI of element, spdx_namespace.file/package/snippet, file_node/package_node/snippet_node) graph = to_isomorphic(graph) - graph.bind("spdx", spdx_namespace()) + graph.bind("spdx", spdx_namespace) graph.serialize(file_name, "pretty-xml", encoding="UTF-8") diff --git a/src/spdx/writer/rdf/relationship_writer.py b/src/spdx/writer/rdf/relationship_writer.py index d8761b1aa..4003b8e3a 100644 --- a/src/spdx/writer/rdf/relationship_writer.py +++ b/src/spdx/writer/rdf/relationship_writer.py @@ -17,9 +17,9 @@ def add_relationship_info_to_graph(relationship: Relationship, graph: Graph, doc_namespace: str): relationship_node = BNode() - graph.add((relationship_node, RDF.type, spdx_namespace().Relationship)) - graph.add((relationship_node, spdx_namespace().relationshipType, spdx_namespace()[f"relationshipType_{relationship.relationship_type.name.lower()}"])) - graph.add((relationship_node, spdx_namespace().relatedSpdxElement, URIRef(f"{doc_namespace}#{relationship.related_spdx_element_id}"))) + graph.add((relationship_node, RDF.type, spdx_namespace.Relationship)) + graph.add((relationship_node, spdx_namespace.relationshipType, spdx_namespace[f"relationshipType_{relationship.relationship_type.name.lower()}"])) + graph.add((relationship_node, spdx_namespace.relatedSpdxElement, URIRef(f"{doc_namespace}#{relationship.related_spdx_element_id}"))) relationship_resource = URIRef(f"{doc_namespace}#{relationship.spdx_element_id}") - graph.add((relationship_resource, spdx_namespace().relationship, relationship_node)) + graph.add((relationship_resource, spdx_namespace.relationship, relationship_node)) diff --git a/src/spdx/writer/rdf/writer_utils.py b/src/spdx/writer/rdf/writer_utils.py index a4848d492..dd0c181ac 100644 --- a/src/spdx/writer/rdf/writer_utils.py +++ b/src/spdx/writer/rdf/writer_utils.py @@ -13,6 +13,7 @@ from rdflib import Namespace, Graph, Literal from rdflib.term import Node +spdx_namespace = Namespace("http://spdx.org/rdf/terms#") def add_literal_value_if_exists(graph: Graph, parent: Node, predicate: Node, value: Union[Any, list]): diff --git a/tests/spdx/writer/rdf/test_annotation_writer.py b/tests/spdx/writer/rdf/test_annotation_writer.py index c0d9e5fd0..b8bdc7654 100644 --- a/tests/spdx/writer/rdf/test_annotation_writer.py +++ b/tests/spdx/writer/rdf/test_annotation_writer.py @@ -24,8 +24,8 @@ def test_add_annotation_info_to_graph(): add_annotation_info_to_graph(annotation, graph, "anyURI") - assert (None, None, spdx_namespace().Annotation) in graph - assert (None, spdx_namespace().annotationType, spdx_namespace().annotationType_review) in graph - assert (None, spdx_namespace().annotationDate, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph - assert (None, spdx_namespace().annotator, Literal("Person: annotatorName (some@mail.com)")) in graph + assert (None, None, spdx_namespace.Annotation) in graph + assert (None, spdx_namespace.annotationType, spdx_namespace.annotationType_review) in graph + assert (None, spdx_namespace.annotationDate, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph + assert (None, spdx_namespace.annotator, Literal("Person: annotatorName (some@mail.com)")) in graph assert (None, RDFS.comment, Literal("annotationComment")) in graph diff --git a/tests/spdx/writer/rdf/test_checksum_writer.py b/tests/spdx/writer/rdf/test_checksum_writer.py index a7571f768..0aeacbbe7 100644 --- a/tests/spdx/writer/rdf/test_checksum_writer.py +++ b/tests/spdx/writer/rdf/test_checksum_writer.py @@ -21,6 +21,6 @@ def test_add_checksum_information_to_graph(): add_checksum_information_to_graph(checksum, graph, URIRef("TestURI")) - assert (None, None, spdx_namespace().Checksum) in graph - assert (None, spdx_namespace().algorithm, spdx_namespace().checksumAlgorithm_sha1) in graph - assert (None, spdx_namespace().checksumValue, Literal("71c4025dd9897b364f3ebbb42c484ff43d00791c")) in graph + assert (None, None, spdx_namespace.Checksum) in graph + assert (None, spdx_namespace.algorithm, spdx_namespace.checksumAlgorithm_sha1) in graph + assert (None, spdx_namespace.checksumValue, Literal("71c4025dd9897b364f3ebbb42c484ff43d00791c")) in graph diff --git a/tests/spdx/writer/rdf/test_creation_info_writer.py b/tests/spdx/writer/rdf/test_creation_info_writer.py index 2ff6fdfd8..0a14169c5 100644 --- a/tests/spdx/writer/rdf/test_creation_info_writer.py +++ b/tests/spdx/writer/rdf/test_creation_info_writer.py @@ -24,14 +24,14 @@ def test_add_creation_info_to_graph(): add_creation_info_to_graph(creation_info, graph) - assert (None, None, spdx_namespace().SpdxDocument) in graph + assert (None, None, spdx_namespace.SpdxDocument) in graph assert (URIRef(f"{creation_info.document_namespace}#{creation_info.spdx_id}"), None, None) in graph - assert (None, spdx_namespace().creationInfo, None) in graph - assert (None, spdx_namespace().name, Literal("documentName")) in graph - assert (None, spdx_namespace().specVersion, Literal("SPDX-2.3")) in graph + assert (None, spdx_namespace.creationInfo, None) in graph + assert (None, spdx_namespace.name, Literal("documentName")) in graph + assert (None, spdx_namespace.specVersion, Literal("SPDX-2.3")) in graph - assert (None, None, spdx_namespace().CreationInfo) in graph - assert (None, spdx_namespace().created, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph + assert (None, None, spdx_namespace.CreationInfo) in graph + assert (None, spdx_namespace.created, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph assert (None, RDFS.comment, Literal("creatorComment")) in graph - assert (None, spdx_namespace().licenseListVersion, Literal("3.19")) in graph - assert (None, spdx_namespace().creator, Literal("Person: creatorName (some@mail.com)")) in graph + assert (None, spdx_namespace.licenseListVersion, Literal("3.19")) in graph + assert (None, spdx_namespace.creator, Literal("Person: creatorName (some@mail.com)")) in graph diff --git a/tests/spdx/writer/rdf/test_file_writer.py b/tests/spdx/writer/rdf/test_file_writer.py index 07c0abba6..5e07c427d 100644 --- a/tests/spdx/writer/rdf/test_file_writer.py +++ b/tests/spdx/writer/rdf/test_file_writer.py @@ -21,15 +21,15 @@ def test_add_file_information_to_graph(): add_file_information_to_graph(file, graph, "anyURI") - assert (URIRef("anyURI#SPDXRef-File"), RDF.type, spdx_namespace().File) in graph - assert (None, spdx_namespace().fileName, Literal("./fileName.py")) in graph - assert (None, spdx_namespace().fileType, spdx_namespace().fileType_text) in graph - assert (None, spdx_namespace().licenseComments, Literal("licenseComment")) in graph - assert (None, spdx_namespace().licenseConcluded, Literal("licenseConcludedExpression")) in graph - assert (None, spdx_namespace().licenseInfoInFile, Literal("licenseInfoInFileExpression")) in graph - assert (None, spdx_namespace().copyrightText, Literal("copyrightText")) in graph + assert (URIRef("anyURI#SPDXRef-File"), RDF.type, spdx_namespace.File) in graph + assert (None, spdx_namespace.fileName, Literal("./fileName.py")) in graph + assert (None, spdx_namespace.fileType, spdx_namespace.fileType_text) in graph + assert (None, spdx_namespace.licenseComments, Literal("licenseComment")) in graph + assert (None, spdx_namespace.licenseConcluded, Literal("licenseConcludedExpression")) in graph + assert (None, spdx_namespace.licenseInfoInFile, Literal("licenseInfoInFileExpression")) in graph + assert (None, spdx_namespace.copyrightText, Literal("copyrightText")) in graph assert (None, RDFS.comment, Literal("fileComment")) in graph - assert (None, spdx_namespace().noticeText, Literal("fileNotice")) in graph - assert (None, spdx_namespace().fileContributor, Literal("fileContributor")) in graph - assert (None, spdx_namespace().checksum, None) in graph - assert (None, spdx_namespace().attributionText, Literal("fileAttributionText")) in graph + assert (None, spdx_namespace.noticeText, Literal("fileNotice")) in graph + assert (None, spdx_namespace.fileContributor, Literal("fileContributor")) in graph + assert (None, spdx_namespace.checksum, None) in graph + assert (None, spdx_namespace.attributionText, Literal("fileAttributionText")) in graph diff --git a/tests/spdx/writer/rdf/test_rdf_writer.py b/tests/spdx/writer/rdf/test_rdf_writer.py index 8677deeb3..ee817e0c7 100644 --- a/tests/spdx/writer/rdf/test_rdf_writer.py +++ b/tests/spdx/writer/rdf/test_rdf_writer.py @@ -17,13 +17,13 @@ from spdx.model.document import Document from spdx.writer.rdf.rdf_writer import write_document_to_file + @pytest.fixture def temporary_file_path() -> str: temporary_file_path = "temp_test_rdf_writer_output.rdf.xml" yield temporary_file_path os.remove(temporary_file_path) def test_write_document_to_file(temporary_file_path: str): - document: Document = document_fixture() write_document_to_file(document, temporary_file_path) diff --git a/tests/spdx/writer/rdf/test_relationship_writer.py b/tests/spdx/writer/rdf/test_relationship_writer.py index 9063f8d89..61e786102 100644 --- a/tests/spdx/writer/rdf/test_relationship_writer.py +++ b/tests/spdx/writer/rdf/test_relationship_writer.py @@ -20,5 +20,5 @@ def test_add_relationship_info_to_graph(): graph = Graph() add_relationship_info_to_graph(relationship, graph, "anyURI") - assert (None, spdx_namespace().relationshipType, spdx_namespace().relationshipType_describes) in graph - assert (None, spdx_namespace().relatedSpdxElement, None) in graph + assert (None, spdx_namespace.relationshipType, spdx_namespace.relationshipType_describes) in graph + assert (None, spdx_namespace.relatedSpdxElement, None) in graph From 2dc5fa6ce46b7cd5a0982f6027f33999a3f5b6fb Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 25 Jan 2023 08:56:05 +0100 Subject: [PATCH 180/362] [issue-407, refactor] rename function Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/creation_info_writer.py | 8 ++++---- src/spdx/writer/rdf/file_writer.py | 14 +++++++------- src/spdx/writer/rdf/writer_utils.py | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/spdx/writer/rdf/creation_info_writer.py b/src/spdx/writer/rdf/creation_info_writer.py index 95052923d..e26307b60 100644 --- a/src/spdx/writer/rdf/creation_info_writer.py +++ b/src/spdx/writer/rdf/creation_info_writer.py @@ -12,7 +12,7 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.model.document import CreationInfo -from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value_if_exists +from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): @@ -21,7 +21,7 @@ def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): graph.add((doc_node, spdx_namespace.specVersion, Literal(creation_info.spdx_version))) graph.add((doc_node, spdx_namespace.dataLicense, Literal(creation_info.data_license))) graph.add((doc_node, spdx_namespace.name, Literal(creation_info.name))) - add_literal_value_if_exists(graph, doc_node, RDFS.comment, creation_info.document_comment) + add_literal_value(graph, doc_node, RDFS.comment, creation_info.document_comment) creation_info_node = BNode() graph.add((creation_info_node, RDF.type, spdx_namespace.CreationInfo)) @@ -31,8 +31,8 @@ def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): for creator in creation_info.creators: graph.add((creation_info_node, spdx_namespace.creator, Literal(creator.to_serialized_string()))) - add_literal_value_if_exists(graph, creation_info_node, spdx_namespace.licenseListVersion, creation_info.license_list_version) - add_literal_value_if_exists(graph, creation_info_node, RDFS.comment, creation_info.creator_comment) + add_literal_value(graph, creation_info_node, spdx_namespace.licenseListVersion, creation_info.license_list_version) + add_literal_value(graph, creation_info_node, RDFS.comment, creation_info.creator_comment) graph.add((doc_node, spdx_namespace.creationInfo, creation_info_node)) diff --git a/src/spdx/writer/rdf/file_writer.py b/src/spdx/writer/rdf/file_writer.py index bd0b266d0..02a0c1191 100644 --- a/src/spdx/writer/rdf/file_writer.py +++ b/src/spdx/writer/rdf/file_writer.py @@ -13,7 +13,7 @@ from spdx.model.file import File from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph -from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value_if_exists +from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str): @@ -27,13 +27,13 @@ def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str): add_checksum_information_to_graph(checksum, graph, file_resource) # as long as we don't have a proper handling of the licenses we simply write literals here - add_literal_value_if_exists(graph, file_resource, spdx_namespace.licenseConcluded, file.license_concluded) - add_literal_value_if_exists(graph, file_resource, spdx_namespace.licenseInfoInFile, file.license_info_in_file) + add_literal_value(graph, file_resource, spdx_namespace.licenseConcluded, file.license_concluded) + add_literal_value(graph, file_resource, spdx_namespace.licenseInfoInFile, file.license_info_in_file) - add_literal_value_if_exists(graph, file_resource, spdx_namespace.licenseComments, file.license_comment) - add_literal_value_if_exists(graph, file_resource, spdx_namespace.copyrightText, file.copyright_text) - add_literal_value_if_exists(graph, file_resource, RDFS.comment, file.comment) - add_literal_value_if_exists(graph, file_resource, spdx_namespace.noticeText, file.notice) + add_literal_value(graph, file_resource, spdx_namespace.licenseComments, file.license_comment) + add_literal_value(graph, file_resource, spdx_namespace.copyrightText, file.copyright_text) + add_literal_value(graph, file_resource, RDFS.comment, file.comment) + add_literal_value(graph, file_resource, spdx_namespace.noticeText, file.notice) for contributor in file.contributors: graph.add((file_resource, spdx_namespace.fileContributor, Literal(contributor))) for attribution_text in file.attribution_texts: diff --git a/src/spdx/writer/rdf/writer_utils.py b/src/spdx/writer/rdf/writer_utils.py index dd0c181ac..6db9fddcd 100644 --- a/src/spdx/writer/rdf/writer_utils.py +++ b/src/spdx/writer/rdf/writer_utils.py @@ -16,7 +16,7 @@ spdx_namespace = Namespace("http://spdx.org/rdf/terms#") -def add_literal_value_if_exists(graph: Graph, parent: Node, predicate: Node, value: Union[Any, list]): +def add_literal_value(graph: Graph, parent: Node, predicate: Node, value: Union[Any, list]): if not value: return if not isinstance(value, list): From 22c636337448d4089cfa55910340db9a6ea2949b Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 25 Jan 2023 14:17:17 +0100 Subject: [PATCH 181/362] [issue-407] add helper functions to handle SpdxNoAssertion and SpdxNone Signed-off-by: Meret Behrens --- src/spdx/model/actor.py | 3 +++ src/spdx/writer/rdf/file_writer.py | 6 +++--- src/spdx/writer/rdf/writer_utils.py | 30 +++++++++++++++++++++++++++-- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/spdx/model/actor.py b/src/spdx/model/actor.py index 4dc3ac3fe..022954f3a 100644 --- a/src/spdx/model/actor.py +++ b/src/spdx/model/actor.py @@ -36,3 +36,6 @@ def to_serialized_string(self) -> str: """ optional_email = f" ({self.email})" if self.email else "" return "".join([f"{self.actor_type.name.title()}:", f" {self.name}", optional_email]) + + def __str__(self): + return self.to_serialized_string() diff --git a/src/spdx/writer/rdf/file_writer.py b/src/spdx/writer/rdf/file_writer.py index 02a0c1191..54b32013a 100644 --- a/src/spdx/writer/rdf/file_writer.py +++ b/src/spdx/writer/rdf/file_writer.py @@ -13,7 +13,7 @@ from spdx.model.file import File from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph -from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value +from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value, add_literal_or_no_assertion_or_none def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str): @@ -27,8 +27,8 @@ def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str): add_checksum_information_to_graph(checksum, graph, file_resource) # as long as we don't have a proper handling of the licenses we simply write literals here - add_literal_value(graph, file_resource, spdx_namespace.licenseConcluded, file.license_concluded) - add_literal_value(graph, file_resource, spdx_namespace.licenseInfoInFile, file.license_info_in_file) + add_literal_or_no_assertion_or_none(graph, file_resource, spdx_namespace.licenseConcluded, file.license_concluded) + add_literal_or_no_assertion_or_none(graph, file_resource, spdx_namespace.licenseInfoInFile, file.license_info_in_file) add_literal_value(graph, file_resource, spdx_namespace.licenseComments, file.license_comment) add_literal_value(graph, file_resource, spdx_namespace.copyrightText, file.copyright_text) diff --git a/src/spdx/writer/rdf/writer_utils.py b/src/spdx/writer/rdf/writer_utils.py index 6db9fddcd..1f3c20c18 100644 --- a/src/spdx/writer/rdf/writer_utils.py +++ b/src/spdx/writer/rdf/writer_utils.py @@ -8,15 +8,20 @@ # 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 typing import Union, Any +from datetime import datetime +from typing import Union, Any, Optional from rdflib import Namespace, Graph, Literal from rdflib.term import Node +from spdx.datetime_conversions import datetime_to_iso_string +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone + spdx_namespace = Namespace("http://spdx.org/rdf/terms#") -def add_literal_value(graph: Graph, parent: Node, predicate: Node, value: Union[Any, list]): +def add_literal_value(graph: Graph, parent: Node, predicate: Node, value: Any): if not value: return if not isinstance(value, list): @@ -26,3 +31,24 @@ def add_literal_value(graph: Graph, parent: Node, predicate: Node, value: Union[ for element in value: element_triple = (parent, predicate, Literal(str(element))) graph.add(element_triple) + +def add_literal_or_no_assertion_or_none(graph: Graph, parent: Node, predicate: Node, value: Any): + if not value: + return + if isinstance(value, SpdxNone): + graph.add((parent, predicate, spdx_namespace.none)) + return + add_literal_or_no_assertion(graph, parent, predicate, value) + +def add_literal_or_no_assertion(graph: Graph, parent: Node, predicate: Node, value: Any): + if not value: + return + if isinstance(value, SpdxNoAssertion): + graph.add((parent, predicate, spdx_namespace.noassertion)) + return + add_literal_value(graph,parent, predicate, value) + +def add_datetime_to_graph(graph: Graph, parent: Node, predicate: Node, value: Optional[datetime]): + if not value: + return + graph.add((parent, predicate, Literal(datetime_to_iso_string(value)))) From d8f1085121aae8584406a19682ed3cd8e2435509 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 25 Jan 2023 14:17:48 +0100 Subject: [PATCH 182/362] [issue-407] add package writer Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/package_writer.py | 94 ++++++++++++++++++++ tests/spdx/writer/rdf/test_package_writer.py | 76 ++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 src/spdx/writer/rdf/package_writer.py create mode 100644 tests/spdx/writer/rdf/test_package_writer.py diff --git a/src/spdx/writer/rdf/package_writer.py b/src/spdx/writer/rdf/package_writer.py new file mode 100644 index 000000000..0aa25b2fc --- /dev/null +++ b/src/spdx/writer/rdf/package_writer.py @@ -0,0 +1,94 @@ +# Copyright (c) 2023 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 typing import List + +from rdflib import Graph, URIRef, RDF, Literal, XSD, BNode, DOAP, RDFS, Namespace + +from spdx.writer.casing_tools import snake_case_to_camel_case +from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph + +from spdx.model.package import Package, PackageVerificationCode, ExternalPackageRef +from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value, add_literal_or_no_assertion_or_none, \ + add_datetime_to_graph + + +def add_package_information_to_graph(package: Package, graph: Graph, doc_namespace: str): + package_resource = URIRef(f"{doc_namespace}#{package.spdx_id}") + graph.add((package_resource, RDF.type, spdx_namespace.Package)) + + graph.add((package_resource, spdx_namespace.name, Literal(package.name))) + add_literal_value(graph, package_resource, spdx_namespace.versionInfo, package.version) + add_literal_value(graph, package_resource, spdx_namespace.packageFileName, package.file_name) + add_literal_value(graph, package_resource, spdx_namespace.supplier, package.supplier) + add_literal_value(graph, package_resource, spdx_namespace.originator, package.originator) + add_literal_or_no_assertion_or_none(graph, package_resource, spdx_namespace.downloadLocation, + package.download_location) + graph.add((package_resource, spdx_namespace.filesAnalyzed, Literal(package.files_analyzed, datatype=XSD.boolean))) + add_package_verification_code_to_graph(package.verification_code, graph, package_resource) + for checksum in package.checksums: + add_checksum_information_to_graph(checksum, graph, package_resource) + + add_literal_value(graph, package_resource, DOAP.homepage, package.homepage) + add_literal_value(graph, package_resource, spdx_namespace.sourceInfo, package.source_info) + add_literal_or_no_assertion_or_none(graph, package_resource, spdx_namespace.licenseConcluded, + package.license_concluded) + add_literal_or_no_assertion_or_none(graph, package_resource, spdx_namespace.licenseInfoFromFiles, + package.license_info_from_files) + add_literal_or_no_assertion_or_none(graph, package_resource, spdx_namespace.licenseDeclared, + package.license_declared) + add_literal_value(graph, package_resource, spdx_namespace.licenseComments, package.license_comment) + add_literal_value(graph, package_resource, spdx_namespace.copyrightText, package.copyright_text) + add_literal_value(graph, package_resource, spdx_namespace.summary, package.summary) + add_literal_value(graph, package_resource, spdx_namespace.description, package.description) + add_literal_value(graph, package_resource, RDFS.comment, package.comment) + for external_reference in package.external_references: + add_external_package_ref_to_graph(graph, external_reference, package_resource) + for attribution_text in package.attribution_texts: + add_literal_value(graph, package_resource, spdx_namespace.attributionText, attribution_text) + if package.primary_package_purpose: + graph.add((package_resource, spdx_namespace.primaryPackagePurpose, + spdx_namespace[f"packagePurpose_{snake_case_to_camel_case(package.primary_package_purpose.name)}"])) + + add_datetime_to_graph(graph, package_resource, spdx_namespace.releaseDate, package.release_date) + add_datetime_to_graph(graph, package_resource, spdx_namespace.builtDate, package.built_date) + add_datetime_to_graph(graph, package_resource, spdx_namespace.validUntilDate, package.valid_until_date) + + +def add_package_verification_code_to_graph(package_verification_code: PackageVerificationCode, graph: Graph, + package_resource: URIRef): + if not package_verification_code: + return + package_verification_code_node = BNode() + graph.add((package_verification_code_node, RDF.type, spdx_namespace.PackageVerificationCode)) + graph.add((package_verification_code_node, spdx_namespace.packageVerificationCodeValue, + Literal(package_verification_code.value))) + for excluded_file in package_verification_code.excluded_files: + graph.add((package_verification_code_node, spdx_namespace.packageVerificationCodeExcludedFile, + Literal(excluded_file))) + + graph.add((package_resource, spdx_namespace.packageVerificationCode, package_verification_code_node)) + + +def add_external_package_ref_to_graph(graph: Graph, external_package_ref: ExternalPackageRef, + package_resource: URIRef): + external_package_ref_node = BNode() + graph.add((external_package_ref_node, RDF.type, spdx_namespace.ExternalRef)) + graph.add((external_package_ref_node, spdx_namespace.referenceCategory, + spdx_namespace[f"referenceCategory_{snake_case_to_camel_case(external_package_ref.category.name)}"])) + # the referenceType should either be f"http://spdx.org/rdf/references/{location}" for listed locations + # or f"{doc_namespace}#type" for unlisted locations, as a first attempt we simply write a Literal + # TODO: open issue + graph.add((external_package_ref_node, spdx_namespace.referenceType, Literal(external_package_ref.reference_type))) + graph.add((external_package_ref_node, spdx_namespace.referenceLocator, Literal(external_package_ref.locator))) + if external_package_ref.comment: + graph.add((external_package_ref_node, RDFS.comment, Literal(external_package_ref.comment))) + + graph.add((package_resource, spdx_namespace.externalRef, external_package_ref_node)) diff --git a/tests/spdx/writer/rdf/test_package_writer.py b/tests/spdx/writer/rdf/test_package_writer.py new file mode 100644 index 000000000..eb69bf246 --- /dev/null +++ b/tests/spdx/writer/rdf/test_package_writer.py @@ -0,0 +1,76 @@ +# Copyright (c) 2023 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 datetime import datetime + +from rdflib import Graph, URIRef, RDF, Literal, XSD, RDFS, DOAP + +from spdx.datetime_conversions import datetime_to_iso_string +from spdx.writer.rdf.package_writer import add_package_information_to_graph, add_external_package_ref_to_graph, \ + add_package_verification_code_to_graph +from spdx.writer.rdf.writer_utils import spdx_namespace +from tests.spdx.fixtures import package_fixture, external_package_ref_fixture, package_verification_code_fixture + + +def test_add_package_information_to_graph(): + graph = Graph() + package = package_fixture() + + add_package_information_to_graph(package, graph, "anyURI") + + assert (URIRef("anyURI#SPDXRef-Package"), RDF.type, spdx_namespace.Package) in graph + assert (None, spdx_namespace.name, Literal("packageName")) in graph + assert (None, spdx_namespace.versionInfo, Literal("12.2")) in graph + assert (None, spdx_namespace.packageFileName, Literal("./packageFileName")) in graph + assert (None, spdx_namespace.supplier, Literal("Person: supplierName (some@mail.com)")) in graph + assert (None, spdx_namespace.originator, Literal("Person: originatorName (some@mail.com)")) in graph + assert (None, spdx_namespace.downloadLocation, Literal("https://download.com")) in graph + assert (None, spdx_namespace.filesAnalyzed, Literal("true", datatype=XSD.boolean)) in graph + assert (URIRef("anyURI#SPDXRef-Package"), spdx_namespace.packageVerificationCode, None) in graph + assert (URIRef("anyURI#SPDXRef-Package"), spdx_namespace.checksum, None) in graph + assert (None, DOAP.homepage, Literal("https://homepage.com")) in graph + assert (None, spdx_namespace.sourceInfo, Literal("sourceInfo")) in graph + assert (None, spdx_namespace.licenseConcluded, Literal("packageLicenseConcluded")) in graph + assert (None, spdx_namespace.licenseInfoFromFiles, Literal("licenseInfoFromFile")) in graph + assert (None, spdx_namespace.licenseDeclared, Literal("packageLicenseDeclared")) in graph + assert (None, spdx_namespace.licenseComments, Literal("packageLicenseComment")) in graph + assert (None, spdx_namespace.copyrightText, Literal("packageCopyrightText")) in graph + assert (None, spdx_namespace.summary, Literal("packageSummary")) in graph + assert (None, spdx_namespace.description, Literal("packageDescription")) in graph + assert (None, RDFS.comment, Literal("packageComment")) in graph + assert (URIRef("anyURI#SPDXRef-Package"), spdx_namespace.externalRef, None) in graph + assert (None, spdx_namespace.attributionText, Literal("packageAttributionText")) + assert (None, spdx_namespace.primaryPackagePurpose, spdx_namespace.primaryPackagePurpose_source) + assert (None, spdx_namespace.releaseDate, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph + assert (None, spdx_namespace.builtDate, Literal(datetime_to_iso_string(datetime(2022, 12, 2)))) in graph + assert (None, spdx_namespace.validUntilDate, Literal(datetime_to_iso_string(datetime(2022, 12, 3)))) in graph + +def test_add_package_verification_code_to_graph(): + graph = Graph() + verification_code = package_verification_code_fixture() + + add_package_verification_code_to_graph(verification_code, graph, URIRef("anyURI")) + + assert (None, None, spdx_namespace.PackageVerificationCode) in graph + assert (None, spdx_namespace.packageVerificationCodeValue, Literal("85ed0817af83a24ad8da68c2b5094de69833983c")) in graph + assert (None, spdx_namespace.packageVerificationCodeExcludedFile, Literal("./exclude.py")) in graph + + +def test_external_package_ref_to_graph(): + graph = Graph() + external_reference = external_package_ref_fixture() + + add_external_package_ref_to_graph(graph, external_reference, URIRef("anyURI")) + + assert (None, None, spdx_namespace.ExternalRef) in graph + assert (None, spdx_namespace.referenceCategory, spdx_namespace.referenceCategory_packageManager) in graph + assert (None, spdx_namespace.referenceType, Literal("maven-central") ) in graph + assert (None, spdx_namespace.referenceLocator, Literal("org.apache.tomcat:tomcat:9.0.0.M4")) in graph + assert (None, RDFS.comment, Literal("externalPackageRefComment")) in graph From 670096b9f3981fbef68e7d5d807294fd1ebae34a Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 25 Jan 2023 14:21:18 +0100 Subject: [PATCH 183/362] [issue-407] use helper method to transform enum names correctly Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/annotation_writer.py | 4 +++- src/spdx/writer/rdf/checksum_writer.py | 3 ++- src/spdx/writer/rdf/file_writer.py | 9 +++++---- src/spdx/writer/rdf/relationship_writer.py | 8 +++++--- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/spdx/writer/rdf/annotation_writer.py b/src/spdx/writer/rdf/annotation_writer.py index c62febd1c..cc9f64e40 100644 --- a/src/spdx/writer/rdf/annotation_writer.py +++ b/src/spdx/writer/rdf/annotation_writer.py @@ -12,6 +12,7 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.model.annotation import Annotation +from spdx.writer.casing_tools import snake_case_to_camel_case from spdx.writer.rdf.writer_utils import spdx_namespace @@ -19,7 +20,8 @@ def add_annotation_info_to_graph(annotation: Annotation, graph: Graph, doc_names annotation_resource = URIRef(f"{doc_namespace}#{annotation.spdx_id}") annotation_node = BNode() graph.add((annotation_node, RDF.type, spdx_namespace.Annotation)) - graph.add((annotation_node, spdx_namespace.annotationType, spdx_namespace[f"annotationType_{annotation.annotation_type.name.lower()}"])) + graph.add((annotation_node, spdx_namespace.annotationType, + spdx_namespace[f"annotationType_{snake_case_to_camel_case(annotation.annotation_type.name)}"])) graph.add((annotation_node, spdx_namespace.annotator, Literal(annotation.annotator.to_serialized_string()))) graph.add( (annotation_node, spdx_namespace.annotationDate, Literal(datetime_to_iso_string(annotation.annotation_date)))) diff --git a/src/spdx/writer/rdf/checksum_writer.py b/src/spdx/writer/rdf/checksum_writer.py index 354c91fde..4cc9d3369 100644 --- a/src/spdx/writer/rdf/checksum_writer.py +++ b/src/spdx/writer/rdf/checksum_writer.py @@ -11,6 +11,7 @@ from rdflib import Graph, URIRef, BNode, RDF, Literal from spdx.model.checksum import Checksum +from spdx.writer.casing_tools import snake_case_to_camel_case from spdx.writer.rdf.writer_utils import spdx_namespace @@ -18,7 +19,7 @@ def add_checksum_information_to_graph(checksum: Checksum, graph: Graph, parent_n checksum_node = BNode() graph.add((checksum_node, RDF.type, spdx_namespace.Checksum)) graph.add((checksum_node, spdx_namespace.algorithm, - spdx_namespace[f"checksumAlgorithm_{checksum.algorithm.name.lower()}"])) + spdx_namespace[f"checksumAlgorithm_{snake_case_to_camel_case(checksum.algorithm.name)}"])) graph.add((checksum_node, spdx_namespace.checksumValue, Literal(checksum.value))) graph.add((parent_node, spdx_namespace.checksum, checksum_node)) diff --git a/src/spdx/writer/rdf/file_writer.py b/src/spdx/writer/rdf/file_writer.py index 54b32013a..14b9d05c4 100644 --- a/src/spdx/writer/rdf/file_writer.py +++ b/src/spdx/writer/rdf/file_writer.py @@ -12,6 +12,7 @@ from rdflib import Graph, URIRef, Literal, RDF, RDFS from spdx.model.file import File +from spdx.writer.casing_tools import snake_case_to_camel_case from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value, add_literal_or_no_assertion_or_none @@ -21,14 +22,16 @@ def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str): graph.add((file_resource, RDF.type, spdx_namespace.File)) graph.add((file_resource, spdx_namespace.fileName, Literal(file.name))) for file_type in file.file_type: - graph.add((file_resource, spdx_namespace.fileType, spdx_namespace[f"fileType_{file_type.name.lower()}"])) + graph.add((file_resource, spdx_namespace.fileType, + spdx_namespace[f"fileType_{snake_case_to_camel_case(file_type.name)}"])) for checksum in file.checksums: add_checksum_information_to_graph(checksum, graph, file_resource) # as long as we don't have a proper handling of the licenses we simply write literals here add_literal_or_no_assertion_or_none(graph, file_resource, spdx_namespace.licenseConcluded, file.license_concluded) - add_literal_or_no_assertion_or_none(graph, file_resource, spdx_namespace.licenseInfoInFile, file.license_info_in_file) + add_literal_or_no_assertion_or_none(graph, file_resource, spdx_namespace.licenseInfoInFile, + file.license_info_in_file) add_literal_value(graph, file_resource, spdx_namespace.licenseComments, file.license_comment) add_literal_value(graph, file_resource, spdx_namespace.copyrightText, file.copyright_text) @@ -38,5 +41,3 @@ def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str): graph.add((file_resource, spdx_namespace.fileContributor, Literal(contributor))) for attribution_text in file.attribution_texts: graph.add((file_resource, spdx_namespace.attributionText, Literal(attribution_text))) - - diff --git a/src/spdx/writer/rdf/relationship_writer.py b/src/spdx/writer/rdf/relationship_writer.py index 4003b8e3a..29fbb0d5e 100644 --- a/src/spdx/writer/rdf/relationship_writer.py +++ b/src/spdx/writer/rdf/relationship_writer.py @@ -11,15 +11,17 @@ from rdflib import Graph, BNode, RDF, URIRef from spdx.model.relationship import Relationship +from spdx.writer.casing_tools import snake_case_to_camel_case from spdx.writer.rdf.writer_utils import spdx_namespace def add_relationship_info_to_graph(relationship: Relationship, graph: Graph, doc_namespace: str): - relationship_node = BNode() graph.add((relationship_node, RDF.type, spdx_namespace.Relationship)) - graph.add((relationship_node, spdx_namespace.relationshipType, spdx_namespace[f"relationshipType_{relationship.relationship_type.name.lower()}"])) - graph.add((relationship_node, spdx_namespace.relatedSpdxElement, URIRef(f"{doc_namespace}#{relationship.related_spdx_element_id}"))) + graph.add((relationship_node, spdx_namespace.relationshipType, + spdx_namespace[f"relationshipType_{snake_case_to_camel_case(relationship.relationship_type.name)}"])) + graph.add((relationship_node, spdx_namespace.relatedSpdxElement, + URIRef(f"{doc_namespace}#{relationship.related_spdx_element_id}"))) relationship_resource = URIRef(f"{doc_namespace}#{relationship.spdx_element_id}") graph.add((relationship_resource, spdx_namespace.relationship, relationship_node)) From 31ecf0c2aed7722d3fadead9480318c2a6f8cd88 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 25 Jan 2023 14:41:59 +0100 Subject: [PATCH 184/362] [issue-407] add transformation from ChecksumAlgorithm to rdf string Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/checksum_writer.py | 14 ++++++--- tests/spdx/writer/rdf/test_checksum_writer.py | 31 ++++++++++++++++++- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/spdx/writer/rdf/checksum_writer.py b/src/spdx/writer/rdf/checksum_writer.py index 4cc9d3369..75b4fdd2b 100644 --- a/src/spdx/writer/rdf/checksum_writer.py +++ b/src/spdx/writer/rdf/checksum_writer.py @@ -10,16 +10,22 @@ # limitations under the License. from rdflib import Graph, URIRef, BNode, RDF, Literal -from spdx.model.checksum import Checksum -from spdx.writer.casing_tools import snake_case_to_camel_case +from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.writer.rdf.writer_utils import spdx_namespace def add_checksum_information_to_graph(checksum: Checksum, graph: Graph, parent_node: URIRef): checksum_node = BNode() graph.add((checksum_node, RDF.type, spdx_namespace.Checksum)) - graph.add((checksum_node, spdx_namespace.algorithm, - spdx_namespace[f"checksumAlgorithm_{snake_case_to_camel_case(checksum.algorithm.name)}"])) + graph.add((checksum_node, spdx_namespace.algorithm, algorithm_to_rdf_string(checksum.algorithm))) graph.add((checksum_node, spdx_namespace.checksumValue, Literal(checksum.value))) graph.add((parent_node, spdx_namespace.checksum, checksum_node)) + +def algorithm_to_rdf_string(algorithm: ChecksumAlgorithm) -> URIRef: + if "BLAKE2B" in algorithm.name: + algorithm_rdf_string = algorithm.name.replace("_","").lower() + else: + algorithm_rdf_string = algorithm.name.lower() + + return spdx_namespace[f"checksumAlgorithm_{algorithm_rdf_string}"] diff --git a/tests/spdx/writer/rdf/test_checksum_writer.py b/tests/spdx/writer/rdf/test_checksum_writer.py index 0aeacbbe7..487b248fd 100644 --- a/tests/spdx/writer/rdf/test_checksum_writer.py +++ b/tests/spdx/writer/rdf/test_checksum_writer.py @@ -8,9 +8,11 @@ # 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 pytest from rdflib import Graph, URIRef, Literal -from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph +from spdx.model.checksum import ChecksumAlgorithm +from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph, algorithm_to_rdf_string from spdx.writer.rdf.writer_utils import spdx_namespace from tests.spdx.fixtures import checksum_fixture @@ -24,3 +26,30 @@ def test_add_checksum_information_to_graph(): assert (None, None, spdx_namespace.Checksum) in graph assert (None, spdx_namespace.algorithm, spdx_namespace.checksumAlgorithm_sha1) in graph assert (None, spdx_namespace.checksumValue, Literal("71c4025dd9897b364f3ebbb42c484ff43d00791c")) in graph + + +@pytest.mark.parametrize("algorithm,expected", [(ChecksumAlgorithm.SHA1, spdx_namespace.checksumAlgorithm_sha1), + (ChecksumAlgorithm.SHA224, spdx_namespace.checksumAlgorithm_sha224), + (ChecksumAlgorithm.SHA256, spdx_namespace.checksumAlgorithm_sha256), + (ChecksumAlgorithm.SHA384, spdx_namespace.checksumAlgorithm_sha384), + (ChecksumAlgorithm.SHA512, spdx_namespace.checksumAlgorithm_sha512), + (ChecksumAlgorithm.SHA3_256, spdx_namespace.checksumAlgorithm_sha3_256), + (ChecksumAlgorithm.SHA3_384, spdx_namespace.checksumAlgorithm_sha3_384), + (ChecksumAlgorithm.SHA3_512, spdx_namespace.checksumAlgorithm_sha3_512), + (ChecksumAlgorithm.BLAKE2B_256, + spdx_namespace.checksumAlgorithm_blake2b256), + (ChecksumAlgorithm.BLAKE2B_384, + spdx_namespace.checksumAlgorithm_blake2b384), + (ChecksumAlgorithm.BLAKE2B_512, + spdx_namespace.checksumAlgorithm_blake2b512), + (ChecksumAlgorithm.BLAKE3, spdx_namespace.checksumAlgorithm_blake3), + (ChecksumAlgorithm.MD2, spdx_namespace.checksumAlgorithm_md2), + (ChecksumAlgorithm.MD4, spdx_namespace.checksumAlgorithm_md4), + (ChecksumAlgorithm.MD5, spdx_namespace.checksumAlgorithm_md5), + (ChecksumAlgorithm.MD6, spdx_namespace.checksumAlgorithm_md6), + (ChecksumAlgorithm.ADLER32, spdx_namespace.checksumAlgorithm_adler32) + ]) +def test_algorithm_to_rdf_string(algorithm, expected): + rdf_element = algorithm_to_rdf_string(algorithm) + + assert rdf_element == expected From 4284dae336aed71a31ffc1b25ccc941372c202b6 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 25 Jan 2023 15:29:41 +0100 Subject: [PATCH 185/362] [issue-407] add snippet writer Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/rdf_writer.py | 3 ++ src/spdx/writer/rdf/snippet_writer.py | 41 ++++++++++++++++++++ tests/spdx/writer/rdf/test_snippet_writer.py | 34 ++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 src/spdx/writer/rdf/snippet_writer.py create mode 100644 tests/spdx/writer/rdf/test_snippet_writer.py diff --git a/src/spdx/writer/rdf/rdf_writer.py b/src/spdx/writer/rdf/rdf_writer.py index 32e620eb5..4085af8b3 100644 --- a/src/spdx/writer/rdf/rdf_writer.py +++ b/src/spdx/writer/rdf/rdf_writer.py @@ -17,6 +17,7 @@ from spdx.writer.rdf.file_writer import add_file_information_to_graph from spdx.writer.rdf.package_writer import add_package_information_to_graph from spdx.writer.rdf.relationship_writer import add_relationship_info_to_graph +from spdx.writer.rdf.snippet_writer import add_snippet_information_to_graph from spdx.writer.rdf.writer_utils import spdx_namespace @@ -36,6 +37,8 @@ def write_document_to_file(document: Document, file_name: str): for relationship in document.relationships: add_relationship_info_to_graph(relationship, graph, doc_namespace) + for snippet in document.snippets: + add_snippet_information_to_graph(snippet, graph, doc_namespace) # once all elements are added to the graph we probably need some logic to inline, Files, Packages, Snippets # pseudocode: graph.add(URI of element, spdx_namespace.file/package/snippet, file_node/package_node/snippet_node) diff --git a/src/spdx/writer/rdf/snippet_writer.py b/src/spdx/writer/rdf/snippet_writer.py new file mode 100644 index 000000000..0f159aac9 --- /dev/null +++ b/src/spdx/writer/rdf/snippet_writer.py @@ -0,0 +1,41 @@ +# Copyright (c) 2023 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 typing import Tuple, Optional + +from rdflib import Graph, URIRef, RDF, RDFS, Literal +from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_or_no_assertion_or_none, add_literal_value + +from spdx.model.snippet import Snippet + + +def add_snippet_information_to_graph(snippet: Snippet, graph: Graph, doc_namespace: str): + snippet_resource = URIRef(f"{doc_namespace}#{snippet.spdx_id}") + graph.add((snippet_resource, RDF.type, spdx_namespace.Snippet)) + + graph.add((snippet_resource, spdx_namespace.snippetFromFile, URIRef(snippet.file_spdx_id))) + add_range_to_graph(graph, snippet_resource, snippet.byte_range, snippet.file_spdx_id) + add_range_to_graph(graph, snippet_resource, snippet.line_range, snippet.file_spdx_id) + add_literal_or_no_assertion_or_none(graph, snippet_resource, spdx_namespace.licenseConcluded, + snippet.license_concluded) + add_literal_or_no_assertion_or_none(graph, snippet_resource, spdx_namespace.licenseInfoInSnippet, + snippet.license_info_in_snippet) + add_literal_value(graph, snippet_resource, spdx_namespace.licenseComments, snippet.license_comment) + add_literal_value(graph, snippet_resource, spdx_namespace.copyrightText, snippet.copyright_text) + add_literal_value(graph, snippet_resource, RDFS.comment, snippet.comment) + add_literal_value(graph, snippet_resource, spdx_namespace.name, snippet.name) + for attribution_text in snippet.attribution_texts: + graph.add((snippet_resource, spdx_namespace.attributionText, Literal(attribution_text))) + + +def add_range_to_graph(graph: Graph, snippet_resource: URIRef, range_information: Optional[Tuple[int, int]], + file_spdx_id: str): + # TODO: implement writer for ranges (https://github.com/spdx/tools-python/issues/274) + pass diff --git a/tests/spdx/writer/rdf/test_snippet_writer.py b/tests/spdx/writer/rdf/test_snippet_writer.py new file mode 100644 index 000000000..e38f31d72 --- /dev/null +++ b/tests/spdx/writer/rdf/test_snippet_writer.py @@ -0,0 +1,34 @@ +# Copyright (c) 2023 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 rdflib import Graph, URIRef, RDF, Literal, RDFS +from spdx.writer.rdf.writer_utils import spdx_namespace + +from spdx.writer.rdf.snippet_writer import add_snippet_information_to_graph +from tests.spdx.fixtures import snippet_fixture + + +def test_add_snippet_information_to_graph(): + graph = Graph() + snippet = snippet_fixture() + + add_snippet_information_to_graph(snippet, graph, "anyURI") + + assert (URIRef("anyURI#SPDXRef-Snippet"), RDF.type, spdx_namespace.Snippet) in graph + assert (None, spdx_namespace.snippetFromFile, URIRef(snippet.file_spdx_id)) in graph + assert (None, spdx_namespace.licenseConcluded, Literal("snippetLicenseConcluded")) in graph + assert (None, spdx_namespace.licenseInfoInSnippet, Literal("licenseInfoInSnippet")) in graph + assert (None, spdx_namespace.licenseComments, Literal("snippetLicenseComment")) in graph + assert (None, spdx_namespace.copyrightText, Literal("licenseCopyrightText")) in graph + assert (None, spdx_namespace.name, Literal("snippetName")) in graph + assert (None, spdx_namespace.attributionText, Literal("snippetAttributionText")) in graph + assert (None, RDFS.comment, Literal("snippetComment")) in graph + + From 566ed1196b378887a2581ab4f54c83ded9abb6d3 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 25 Jan 2023 17:05:11 +0100 Subject: [PATCH 186/362] [issue-407] add extracted licensing info writer Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/creation_info_writer.py | 2 +- .../rdf/extracted_licensing_info_writer.py | 33 +++++++++++++++++++ src/spdx/writer/rdf/rdf_writer.py | 7 +++- .../test_extracted_licensing_info_writer.py | 30 +++++++++++++++++ 4 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 src/spdx/writer/rdf/extracted_licensing_info_writer.py create mode 100644 tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py diff --git a/src/spdx/writer/rdf/creation_info_writer.py b/src/spdx/writer/rdf/creation_info_writer.py index e26307b60..48a55b95a 100644 --- a/src/spdx/writer/rdf/creation_info_writer.py +++ b/src/spdx/writer/rdf/creation_info_writer.py @@ -36,4 +36,4 @@ def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): graph.add((doc_node, spdx_namespace.creationInfo, creation_info_node)) - return + return doc_node diff --git a/src/spdx/writer/rdf/extracted_licensing_info_writer.py b/src/spdx/writer/rdf/extracted_licensing_info_writer.py new file mode 100644 index 000000000..a1a456a50 --- /dev/null +++ b/src/spdx/writer/rdf/extracted_licensing_info_writer.py @@ -0,0 +1,33 @@ +# Copyright (c) 2023 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 rdflib import Graph, URIRef, RDF, BNode, RDFS, Literal +from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value, add_literal_or_no_assertion + +from spdx.model.extracted_licensing_info import ExtractedLicensingInfo + + +def add_extracted_licensing_info_to_graph(extracted_licensing_info: ExtractedLicensingInfo, graph: Graph, doc_node): + if extracted_licensing_info.license_id: + extracted_licensing_info_resource = URIRef(extracted_licensing_info.license_id) + graph.add((extracted_licensing_info_resource, RDF.type, spdx_namespace.ExtractedLicensingInfo)) + else: + extracted_licensing_info_resource = BNode() + add_literal_value(graph, extracted_licensing_info_resource, spdx_namespace.licenseId, + extracted_licensing_info.license_id) + add_literal_value(graph, extracted_licensing_info_resource, spdx_namespace.extractedText, + extracted_licensing_info.extracted_text) + add_literal_or_no_assertion(graph, extracted_licensing_info_resource, spdx_namespace.name, + extracted_licensing_info.license_name) + for cross_reference in extracted_licensing_info.cross_references: + graph.add((extracted_licensing_info_resource, RDFS.seeAlso, Literal(cross_reference))) + add_literal_value(graph, extracted_licensing_info_resource, RDFS.comment, extracted_licensing_info.comment) + + graph.add((doc_node, spdx_namespace.hasExtractedLicensingInfo, extracted_licensing_info_resource)) diff --git a/src/spdx/writer/rdf/rdf_writer.py b/src/spdx/writer/rdf/rdf_writer.py index 4085af8b3..4ddd44506 100644 --- a/src/spdx/writer/rdf/rdf_writer.py +++ b/src/spdx/writer/rdf/rdf_writer.py @@ -14,6 +14,7 @@ from spdx.model.document import Document from spdx.writer.rdf.annotation_writer import add_annotation_info_to_graph from spdx.writer.rdf.creation_info_writer import add_creation_info_to_graph +from spdx.writer.rdf.extracted_licensing_info_writer import add_extracted_licensing_info_to_graph from spdx.writer.rdf.file_writer import add_file_information_to_graph from spdx.writer.rdf.package_writer import add_package_information_to_graph from spdx.writer.rdf.relationship_writer import add_relationship_info_to_graph @@ -24,7 +25,7 @@ def write_document_to_file(document: Document, file_name: str): graph = Graph() doc_namespace = document.creation_info.document_namespace - add_creation_info_to_graph(document.creation_info, graph) + doc_node = add_creation_info_to_graph(document.creation_info, graph) for annotation in document.annotations: add_annotation_info_to_graph(annotation, graph, doc_namespace) @@ -39,6 +40,10 @@ def write_document_to_file(document: Document, file_name: str): for snippet in document.snippets: add_snippet_information_to_graph(snippet, graph, doc_namespace) + + for extracted_licensing_info in document.extracted_licensing_info: + add_extracted_licensing_info_to_graph(extracted_licensing_info, graph, doc_node) + # once all elements are added to the graph we probably need some logic to inline, Files, Packages, Snippets # pseudocode: graph.add(URI of element, spdx_namespace.file/package/snippet, file_node/package_node/snippet_node) diff --git a/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py b/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py new file mode 100644 index 000000000..95a3fc3a6 --- /dev/null +++ b/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py @@ -0,0 +1,30 @@ +# Copyright (c) 2023 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 rdflib import Graph, Literal, RDFS, URIRef +from spdx.writer.rdf.writer_utils import spdx_namespace + +from spdx.writer.rdf.extracted_licensing_info_writer import add_extracted_licensing_info_to_graph +from tests.spdx.fixtures import extracted_licensing_info_fixture + + +def test_add_extracted_licensing_info_to_graph(): + graph = Graph() + extracted_licensing_info = extracted_licensing_info_fixture() + + add_extracted_licensing_info_to_graph(extracted_licensing_info, graph, doc_node=URIRef("anyURI")) + + assert (URIRef("anyURI"), spdx_namespace.hasExtractedLicensingInfo, None) in graph + assert (None, None, spdx_namespace.ExtractedLicensingInfo) in graph + assert (None, spdx_namespace.licenseId, Literal("LicenseRef-1")) in graph + assert (None, spdx_namespace.extractedText, Literal("extractedText")) in graph + assert (None, RDFS.seeAlso, Literal("https://see.also")) in graph + assert (None, spdx_namespace.name, Literal("licenseName")) in graph + assert (None, RDFS.comment, Literal("licenseComment")) in graph From fced6d0097052485f59dae173dab77ff3f30d02c Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 26 Jan 2023 09:13:10 +0100 Subject: [PATCH 187/362] [issue-407] add external document ref writer Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/creation_info_writer.py | 4 +++ .../rdf/external_document_ref_writer.py | 25 +++++++++++++++ .../rdf/test_external_document_ref_writer.py | 31 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 src/spdx/writer/rdf/external_document_ref_writer.py create mode 100644 tests/spdx/writer/rdf/test_external_document_ref_writer.py diff --git a/src/spdx/writer/rdf/creation_info_writer.py b/src/spdx/writer/rdf/creation_info_writer.py index 48a55b95a..99b640211 100644 --- a/src/spdx/writer/rdf/creation_info_writer.py +++ b/src/spdx/writer/rdf/creation_info_writer.py @@ -12,6 +12,7 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.model.document import CreationInfo +from spdx.writer.rdf.external_document_ref_writer import add_external_document_ref_to_graph from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value @@ -36,4 +37,7 @@ def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): graph.add((doc_node, spdx_namespace.creationInfo, creation_info_node)) + for external_document_ref in creation_info.external_document_refs: + add_external_document_ref_to_graph(external_document_ref, graph, doc_node, creation_info.document_namespace) + return doc_node diff --git a/src/spdx/writer/rdf/external_document_ref_writer.py b/src/spdx/writer/rdf/external_document_ref_writer.py new file mode 100644 index 000000000..084e47ffc --- /dev/null +++ b/src/spdx/writer/rdf/external_document_ref_writer.py @@ -0,0 +1,25 @@ +# Copyright (c) 2023 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 rdflib import Graph, URIRef, RDF + +from spdx.model.external_document_ref import ExternalDocumentRef +from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph +from spdx.writer.rdf.writer_utils import spdx_namespace + + +def add_external_document_ref_to_graph(external_document_ref: ExternalDocumentRef, graph: Graph, doc_node: URIRef, + doc_namespace: str): + external_document_ref_resource = URIRef(f"{doc_namespace}#{external_document_ref.document_ref_id}") + graph.add((external_document_ref_resource, RDF.type, spdx_namespace.ExternalDocumentRef)) + graph.add((external_document_ref_resource, spdx_namespace.spdxDocument, URIRef(external_document_ref.document_uri))) + add_checksum_information_to_graph(external_document_ref.checksum, graph, external_document_ref_resource) + + graph.add((doc_node, spdx_namespace.externalDocumentRef, external_document_ref_resource)) diff --git a/tests/spdx/writer/rdf/test_external_document_ref_writer.py b/tests/spdx/writer/rdf/test_external_document_ref_writer.py new file mode 100644 index 000000000..3722fd545 --- /dev/null +++ b/tests/spdx/writer/rdf/test_external_document_ref_writer.py @@ -0,0 +1,31 @@ +# Copyright (c) 2023 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 rdflib import Graph, URIRef +from spdx.writer.rdf.writer_utils import spdx_namespace + +from spdx.writer.rdf.external_document_ref_writer import add_external_document_ref_to_graph +from tests.spdx.fixtures import external_document_ref_fixture + + +def test_add_external_document_ref_to_graph(): + graph = Graph() + external_document_ref = external_document_ref_fixture() + + add_external_document_ref_to_graph(external_document_ref, graph, URIRef("anyURI"), "anyURI") + + assert (None, spdx_namespace.externalDocumentRef, URIRef("anyURI#DocumentRef-external")) in graph + assert (None, None, spdx_namespace.ExternalDocumentRef) in graph + assert (None, spdx_namespace.checksum, None) in graph + assert (None, None, spdx_namespace.Checksum) in graph + assert (None, spdx_namespace.spdxDocument, URIRef("https://namespace.com")) in graph + + + From 8e619738ea7c7d16b289ee2b9010e592d48eb256 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 26 Jan 2023 11:25:28 +0100 Subject: [PATCH 188/362] [issue-407] bind DOAP namespace to "doap" prefix Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/rdf_writer.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/spdx/writer/rdf/rdf_writer.py b/src/spdx/writer/rdf/rdf_writer.py index 4ddd44506..381b9eb00 100644 --- a/src/spdx/writer/rdf/rdf_writer.py +++ b/src/spdx/writer/rdf/rdf_writer.py @@ -8,7 +8,7 @@ # 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 rdflib import Graph +from rdflib import Graph, DOAP from rdflib.compare import to_isomorphic from spdx.model.document import Document @@ -49,4 +49,5 @@ def write_document_to_file(document: Document, file_name: str): graph = to_isomorphic(graph) graph.bind("spdx", spdx_namespace) - graph.serialize(file_name, "pretty-xml", encoding="UTF-8") + graph.bind("doap", DOAP) + graph.serialize(file_name, "pretty-xml", encoding="UTF-8", max_depth=100) From 242c0694e8eb2f8a9c80693a633e7110a8ff1087 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 26 Jan 2023 12:23:00 +0100 Subject: [PATCH 189/362] [issue-407] fix handling of spdx id Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/annotation_writer.py | 9 +++++--- .../rdf/extracted_licensing_info_writer.py | 5 +++-- src/spdx/writer/rdf/file_writer.py | 9 +++++--- src/spdx/writer/rdf/package_writer.py | 9 ++++---- src/spdx/writer/rdf/rdf_writer.py | 18 +++++++-------- src/spdx/writer/rdf/relationship_writer.py | 22 ++++++++++++++----- src/spdx/writer/rdf/snippet_writer.py | 13 ++++++----- src/spdx/writer/rdf/writer_utils.py | 19 ++++++++++++++-- .../spdx/writer/rdf/test_annotation_writer.py | 2 +- .../test_extracted_licensing_info_writer.py | 2 +- tests/spdx/writer/rdf/test_file_writer.py | 2 +- tests/spdx/writer/rdf/test_package_writer.py | 8 ++++--- .../writer/rdf/test_relationship_writer.py | 2 +- tests/spdx/writer/rdf/test_snippet_writer.py | 4 +--- 14 files changed, 81 insertions(+), 43 deletions(-) diff --git a/src/spdx/writer/rdf/annotation_writer.py b/src/spdx/writer/rdf/annotation_writer.py index cc9f64e40..3268eeed5 100644 --- a/src/spdx/writer/rdf/annotation_writer.py +++ b/src/spdx/writer/rdf/annotation_writer.py @@ -8,16 +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 typing import Dict + from rdflib import Graph, Literal, RDFS, URIRef, RDF, BNode from spdx.datetime_conversions import datetime_to_iso_string from spdx.model.annotation import Annotation from spdx.writer.casing_tools import snake_case_to_camel_case -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import spdx_namespace, add_namespace_to_spdx_id -def add_annotation_info_to_graph(annotation: Annotation, graph: Graph, doc_namespace: str): - annotation_resource = URIRef(f"{doc_namespace}#{annotation.spdx_id}") +def add_annotation_info_to_graph(annotation: Annotation, graph: Graph, doc_namespace: str, + external_doc_namespaces: Dict[str, str]): + annotation_resource = URIRef(add_namespace_to_spdx_id(annotation.spdx_id, doc_namespace, external_doc_namespaces)) annotation_node = BNode() graph.add((annotation_node, RDF.type, spdx_namespace.Annotation)) graph.add((annotation_node, spdx_namespace.annotationType, diff --git a/src/spdx/writer/rdf/extracted_licensing_info_writer.py b/src/spdx/writer/rdf/extracted_licensing_info_writer.py index a1a456a50..3133387bc 100644 --- a/src/spdx/writer/rdf/extracted_licensing_info_writer.py +++ b/src/spdx/writer/rdf/extracted_licensing_info_writer.py @@ -14,9 +14,10 @@ from spdx.model.extracted_licensing_info import ExtractedLicensingInfo -def add_extracted_licensing_info_to_graph(extracted_licensing_info: ExtractedLicensingInfo, graph: Graph, doc_node): +def add_extracted_licensing_info_to_graph(extracted_licensing_info: ExtractedLicensingInfo, graph: Graph, doc_node, + doc_namespace: str): if extracted_licensing_info.license_id: - extracted_licensing_info_resource = URIRef(extracted_licensing_info.license_id) + extracted_licensing_info_resource = URIRef(f"{doc_namespace}#{extracted_licensing_info.license_id}") graph.add((extracted_licensing_info_resource, RDF.type, spdx_namespace.ExtractedLicensingInfo)) else: extracted_licensing_info_resource = BNode() diff --git a/src/spdx/writer/rdf/file_writer.py b/src/spdx/writer/rdf/file_writer.py index 14b9d05c4..5cfaa55a0 100644 --- a/src/spdx/writer/rdf/file_writer.py +++ b/src/spdx/writer/rdf/file_writer.py @@ -8,17 +8,20 @@ # 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 typing import Dict from rdflib import Graph, URIRef, Literal, RDF, RDFS from spdx.model.file import File from spdx.writer.casing_tools import snake_case_to_camel_case from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph -from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value, add_literal_or_no_assertion_or_none +from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value, add_literal_or_no_assertion_or_none, \ + add_namespace_to_spdx_id -def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str): - file_resource = URIRef(f"{doc_namespace}#{file.spdx_id}") +def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str, + external_doc_namespaces: Dict[str, str]): + file_resource = URIRef(add_namespace_to_spdx_id(file.spdx_id, doc_namespace, external_doc_namespaces)) graph.add((file_resource, RDF.type, spdx_namespace.File)) graph.add((file_resource, spdx_namespace.fileName, Literal(file.name))) for file_type in file.file_type: diff --git a/src/spdx/writer/rdf/package_writer.py b/src/spdx/writer/rdf/package_writer.py index 0aa25b2fc..8710fa263 100644 --- a/src/spdx/writer/rdf/package_writer.py +++ b/src/spdx/writer/rdf/package_writer.py @@ -8,7 +8,7 @@ # 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 typing import List +from typing import Dict from rdflib import Graph, URIRef, RDF, Literal, XSD, BNode, DOAP, RDFS, Namespace @@ -17,11 +17,12 @@ from spdx.model.package import Package, PackageVerificationCode, ExternalPackageRef from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value, add_literal_or_no_assertion_or_none, \ - add_datetime_to_graph + add_datetime_to_graph, add_namespace_to_spdx_id -def add_package_information_to_graph(package: Package, graph: Graph, doc_namespace: str): - package_resource = URIRef(f"{doc_namespace}#{package.spdx_id}") +def add_package_information_to_graph(package: Package, graph: Graph, doc_namespace: str, + external_doc_namespaces: Dict[str, str]): + package_resource = URIRef(add_namespace_to_spdx_id(package.spdx_id, doc_namespace, external_doc_namespaces)) graph.add((package_resource, RDF.type, spdx_namespace.Package)) graph.add((package_resource, spdx_namespace.name, Literal(package.name))) diff --git a/src/spdx/writer/rdf/rdf_writer.py b/src/spdx/writer/rdf/rdf_writer.py index 381b9eb00..6bfc44128 100644 --- a/src/spdx/writer/rdf/rdf_writer.py +++ b/src/spdx/writer/rdf/rdf_writer.py @@ -25,27 +25,27 @@ def write_document_to_file(document: Document, file_name: str): graph = Graph() doc_namespace = document.creation_info.document_namespace + external_doc_namespace_mapping = {external_doc_ref.document_ref_id: external_doc_ref.document_uri for + external_doc_ref + in document.creation_info.external_document_refs} doc_node = add_creation_info_to_graph(document.creation_info, graph) for annotation in document.annotations: - add_annotation_info_to_graph(annotation, graph, doc_namespace) + add_annotation_info_to_graph(annotation, graph, doc_namespace, external_doc_namespace_mapping) for file in document.files: - add_file_information_to_graph(file, graph, doc_namespace) + add_file_information_to_graph(file, graph, doc_namespace, external_doc_namespace_mapping) for package in document.packages: - add_package_information_to_graph(package, graph, doc_namespace) + add_package_information_to_graph(package, graph, doc_namespace, external_doc_namespace_mapping) for relationship in document.relationships: - add_relationship_info_to_graph(relationship, graph, doc_namespace) + add_relationship_info_to_graph(relationship, graph, doc_namespace, external_doc_namespace_mapping) for snippet in document.snippets: - add_snippet_information_to_graph(snippet, graph, doc_namespace) + add_snippet_information_to_graph(snippet, graph, doc_namespace, external_doc_namespace_mapping) for extracted_licensing_info in document.extracted_licensing_info: - add_extracted_licensing_info_to_graph(extracted_licensing_info, graph, doc_node) - - # once all elements are added to the graph we probably need some logic to inline, Files, Packages, Snippets - # pseudocode: graph.add(URI of element, spdx_namespace.file/package/snippet, file_node/package_node/snippet_node) + add_extracted_licensing_info_to_graph(extracted_licensing_info, graph, doc_node, doc_namespace) graph = to_isomorphic(graph) graph.bind("spdx", spdx_namespace) diff --git a/src/spdx/writer/rdf/relationship_writer.py b/src/spdx/writer/rdf/relationship_writer.py index 29fbb0d5e..a3a1be0ad 100644 --- a/src/spdx/writer/rdf/relationship_writer.py +++ b/src/spdx/writer/rdf/relationship_writer.py @@ -8,20 +8,32 @@ # 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 typing import Dict + from rdflib import Graph, BNode, RDF, URIRef from spdx.model.relationship import Relationship +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone from spdx.writer.casing_tools import snake_case_to_camel_case -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import spdx_namespace, add_namespace_to_spdx_id -def add_relationship_info_to_graph(relationship: Relationship, graph: Graph, doc_namespace: str): +def add_relationship_info_to_graph(relationship: Relationship, graph: Graph, doc_namespace: str, + external_doc_namespaces: Dict[str, str]): relationship_node = BNode() graph.add((relationship_node, RDF.type, spdx_namespace.Relationship)) graph.add((relationship_node, spdx_namespace.relationshipType, spdx_namespace[f"relationshipType_{snake_case_to_camel_case(relationship.relationship_type.name)}"])) - graph.add((relationship_node, spdx_namespace.relatedSpdxElement, - URIRef(f"{doc_namespace}#{relationship.related_spdx_element_id}"))) + if isinstance(relationship.related_spdx_element_id, SpdxNone): + graph.add((relationship_node, spdx_namespace.relatedSpdxElement, spdx_namespace.none)) + elif isinstance(relationship.related_spdx_element_id, SpdxNoAssertion): + graph.add((relationship_node, spdx_namespace.relatedSpdxElement, spdx_namespace.noassertion)) + else: + graph.add((relationship_node, spdx_namespace.relatedSpdxElement, + URIRef(add_namespace_to_spdx_id(relationship.related_spdx_element_id, doc_namespace, + external_doc_namespaces)))) - relationship_resource = URIRef(f"{doc_namespace}#{relationship.spdx_element_id}") + relationship_resource = URIRef( + add_namespace_to_spdx_id(relationship.spdx_element_id, doc_namespace, external_doc_namespaces)) graph.add((relationship_resource, spdx_namespace.relationship, relationship_node)) diff --git a/src/spdx/writer/rdf/snippet_writer.py b/src/spdx/writer/rdf/snippet_writer.py index 0f159aac9..ceb38be97 100644 --- a/src/spdx/writer/rdf/snippet_writer.py +++ b/src/spdx/writer/rdf/snippet_writer.py @@ -8,19 +8,22 @@ # 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 typing import Tuple, Optional +from typing import Tuple, Optional, Dict from rdflib import Graph, URIRef, RDF, RDFS, Literal -from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_or_no_assertion_or_none, add_literal_value +from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_or_no_assertion_or_none, add_literal_value, \ + add_namespace_to_spdx_id from spdx.model.snippet import Snippet -def add_snippet_information_to_graph(snippet: Snippet, graph: Graph, doc_namespace: str): - snippet_resource = URIRef(f"{doc_namespace}#{snippet.spdx_id}") +def add_snippet_information_to_graph(snippet: Snippet, graph: Graph, doc_namespace: str, + external_doc_namespaces: Dict[str, str]): + snippet_resource = URIRef(add_namespace_to_spdx_id(snippet.spdx_id, doc_namespace, external_doc_namespaces)) graph.add((snippet_resource, RDF.type, spdx_namespace.Snippet)) - graph.add((snippet_resource, spdx_namespace.snippetFromFile, URIRef(snippet.file_spdx_id))) + graph.add((snippet_resource, spdx_namespace.snippetFromFile, + URIRef(add_namespace_to_spdx_id(snippet.file_spdx_id, doc_namespace, external_doc_namespaces)))) add_range_to_graph(graph, snippet_resource, snippet.byte_range, snippet.file_spdx_id) add_range_to_graph(graph, snippet_resource, snippet.line_range, snippet.file_spdx_id) add_literal_or_no_assertion_or_none(graph, snippet_resource, spdx_namespace.licenseConcluded, diff --git a/src/spdx/writer/rdf/writer_utils.py b/src/spdx/writer/rdf/writer_utils.py index 1f3c20c18..cec03c1d8 100644 --- a/src/spdx/writer/rdf/writer_utils.py +++ b/src/spdx/writer/rdf/writer_utils.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from datetime import datetime -from typing import Union, Any, Optional +from typing import Any, Optional, Dict from rdflib import Namespace, Graph, Literal from rdflib.term import Node @@ -17,6 +17,7 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone +from spdx.validation.spdx_id_validators import is_valid_internal_spdx_id spdx_namespace = Namespace("http://spdx.org/rdf/terms#") @@ -32,6 +33,7 @@ def add_literal_value(graph: Graph, parent: Node, predicate: Node, value: Any): element_triple = (parent, predicate, Literal(str(element))) graph.add(element_triple) + def add_literal_or_no_assertion_or_none(graph: Graph, parent: Node, predicate: Node, value: Any): if not value: return @@ -40,15 +42,28 @@ def add_literal_or_no_assertion_or_none(graph: Graph, parent: Node, predicate: N return add_literal_or_no_assertion(graph, parent, predicate, value) + def add_literal_or_no_assertion(graph: Graph, parent: Node, predicate: Node, value: Any): if not value: return if isinstance(value, SpdxNoAssertion): graph.add((parent, predicate, spdx_namespace.noassertion)) return - add_literal_value(graph,parent, predicate, value) + add_literal_value(graph, parent, predicate, value) + def add_datetime_to_graph(graph: Graph, parent: Node, predicate: Node, value: Optional[datetime]): if not value: return graph.add((parent, predicate, Literal(datetime_to_iso_string(value)))) + + +def add_namespace_to_spdx_id(spdx_id: str, doc_namespace: str, external_doc_namespaces: Dict[str, str]) -> str: + if ":" in spdx_id: + external_doc_ref_id = spdx_id.split(":")[0] + return f"{external_doc_namespaces[external_doc_ref_id]}#{spdx_id.split(':')[1]}" + + if is_valid_internal_spdx_id(spdx_id): + return f"{doc_namespace}#{spdx_id}" + + return spdx_id diff --git a/tests/spdx/writer/rdf/test_annotation_writer.py b/tests/spdx/writer/rdf/test_annotation_writer.py index b8bdc7654..d55a22950 100644 --- a/tests/spdx/writer/rdf/test_annotation_writer.py +++ b/tests/spdx/writer/rdf/test_annotation_writer.py @@ -22,7 +22,7 @@ def test_add_annotation_info_to_graph(): graph = Graph() annotation = annotation_fixture() - add_annotation_info_to_graph(annotation, graph, "anyURI") + add_annotation_info_to_graph(annotation, graph, "anyURI", {}) assert (None, None, spdx_namespace.Annotation) in graph assert (None, spdx_namespace.annotationType, spdx_namespace.annotationType_review) in graph diff --git a/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py b/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py index 95a3fc3a6..02ca40a6c 100644 --- a/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py +++ b/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py @@ -19,7 +19,7 @@ def test_add_extracted_licensing_info_to_graph(): graph = Graph() extracted_licensing_info = extracted_licensing_info_fixture() - add_extracted_licensing_info_to_graph(extracted_licensing_info, graph, doc_node=URIRef("anyURI")) + add_extracted_licensing_info_to_graph(extracted_licensing_info, graph, URIRef("anyURI"), "anyURI") assert (URIRef("anyURI"), spdx_namespace.hasExtractedLicensingInfo, None) in graph assert (None, None, spdx_namespace.ExtractedLicensingInfo) in graph diff --git a/tests/spdx/writer/rdf/test_file_writer.py b/tests/spdx/writer/rdf/test_file_writer.py index 5e07c427d..3729aa304 100644 --- a/tests/spdx/writer/rdf/test_file_writer.py +++ b/tests/spdx/writer/rdf/test_file_writer.py @@ -19,7 +19,7 @@ def test_add_file_information_to_graph(): graph = Graph() file = file_fixture() - add_file_information_to_graph(file, graph, "anyURI") + add_file_information_to_graph(file, graph, "anyURI", {}) assert (URIRef("anyURI#SPDXRef-File"), RDF.type, spdx_namespace.File) in graph assert (None, spdx_namespace.fileName, Literal("./fileName.py")) in graph diff --git a/tests/spdx/writer/rdf/test_package_writer.py b/tests/spdx/writer/rdf/test_package_writer.py index eb69bf246..f254d8609 100644 --- a/tests/spdx/writer/rdf/test_package_writer.py +++ b/tests/spdx/writer/rdf/test_package_writer.py @@ -23,7 +23,7 @@ def test_add_package_information_to_graph(): graph = Graph() package = package_fixture() - add_package_information_to_graph(package, graph, "anyURI") + add_package_information_to_graph(package, graph, "anyURI", {}) assert (URIRef("anyURI#SPDXRef-Package"), RDF.type, spdx_namespace.Package) in graph assert (None, spdx_namespace.name, Literal("packageName")) in graph @@ -52,6 +52,7 @@ def test_add_package_information_to_graph(): assert (None, spdx_namespace.builtDate, Literal(datetime_to_iso_string(datetime(2022, 12, 2)))) in graph assert (None, spdx_namespace.validUntilDate, Literal(datetime_to_iso_string(datetime(2022, 12, 3)))) in graph + def test_add_package_verification_code_to_graph(): graph = Graph() verification_code = package_verification_code_fixture() @@ -59,7 +60,8 @@ def test_add_package_verification_code_to_graph(): add_package_verification_code_to_graph(verification_code, graph, URIRef("anyURI")) assert (None, None, spdx_namespace.PackageVerificationCode) in graph - assert (None, spdx_namespace.packageVerificationCodeValue, Literal("85ed0817af83a24ad8da68c2b5094de69833983c")) in graph + assert (None, spdx_namespace.packageVerificationCodeValue, + Literal("85ed0817af83a24ad8da68c2b5094de69833983c")) in graph assert (None, spdx_namespace.packageVerificationCodeExcludedFile, Literal("./exclude.py")) in graph @@ -71,6 +73,6 @@ def test_external_package_ref_to_graph(): assert (None, None, spdx_namespace.ExternalRef) in graph assert (None, spdx_namespace.referenceCategory, spdx_namespace.referenceCategory_packageManager) in graph - assert (None, spdx_namespace.referenceType, Literal("maven-central") ) in graph + assert (None, spdx_namespace.referenceType, Literal("maven-central")) in graph assert (None, spdx_namespace.referenceLocator, Literal("org.apache.tomcat:tomcat:9.0.0.M4")) in graph assert (None, RDFS.comment, Literal("externalPackageRefComment")) in graph diff --git a/tests/spdx/writer/rdf/test_relationship_writer.py b/tests/spdx/writer/rdf/test_relationship_writer.py index 61e786102..34a2173d7 100644 --- a/tests/spdx/writer/rdf/test_relationship_writer.py +++ b/tests/spdx/writer/rdf/test_relationship_writer.py @@ -18,7 +18,7 @@ def test_add_relationship_info_to_graph(): relationship = relationship_fixture() graph = Graph() - add_relationship_info_to_graph(relationship, graph, "anyURI") + add_relationship_info_to_graph(relationship, graph, "anyURI", {}) assert (None, spdx_namespace.relationshipType, spdx_namespace.relationshipType_describes) in graph assert (None, spdx_namespace.relatedSpdxElement, None) in graph diff --git a/tests/spdx/writer/rdf/test_snippet_writer.py b/tests/spdx/writer/rdf/test_snippet_writer.py index e38f31d72..2cb69700b 100644 --- a/tests/spdx/writer/rdf/test_snippet_writer.py +++ b/tests/spdx/writer/rdf/test_snippet_writer.py @@ -19,7 +19,7 @@ def test_add_snippet_information_to_graph(): graph = Graph() snippet = snippet_fixture() - add_snippet_information_to_graph(snippet, graph, "anyURI") + add_snippet_information_to_graph(snippet, graph, "anyURI", {}) assert (URIRef("anyURI#SPDXRef-Snippet"), RDF.type, spdx_namespace.Snippet) in graph assert (None, spdx_namespace.snippetFromFile, URIRef(snippet.file_spdx_id)) in graph @@ -30,5 +30,3 @@ def test_add_snippet_information_to_graph(): assert (None, spdx_namespace.name, Literal("snippetName")) in graph assert (None, spdx_namespace.attributionText, Literal("snippetAttributionText")) in graph assert (None, RDFS.comment, Literal("snippetComment")) in graph - - From 8854204b5d9ae27b9e04e261cec672d4a6d817e3 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 26 Jan 2023 12:23:15 +0100 Subject: [PATCH 190/362] [issue-407] add rdf writer to CLI Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/rdf_writer.py | 12 +++++++++++- src/spdx/writer/write_anything.py | 10 ++++++---- tests/spdx/writer/rdf/test_rdf_writer.py | 2 +- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/spdx/writer/rdf/rdf_writer.py b/src/spdx/writer/rdf/rdf_writer.py index 6bfc44128..2dcc3611d 100644 --- a/src/spdx/writer/rdf/rdf_writer.py +++ b/src/spdx/writer/rdf/rdf_writer.py @@ -8,10 +8,14 @@ # 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 typing import List + from rdflib import Graph, DOAP from rdflib.compare import to_isomorphic from spdx.model.document import Document +from spdx.validation.document_validator import validate_full_spdx_document +from spdx.validation.validation_message import ValidationMessage from spdx.writer.rdf.annotation_writer import add_annotation_info_to_graph from spdx.writer.rdf.creation_info_writer import add_creation_info_to_graph from spdx.writer.rdf.extracted_licensing_info_writer import add_extracted_licensing_info_to_graph @@ -22,7 +26,13 @@ from spdx.writer.rdf.writer_utils import spdx_namespace -def write_document_to_file(document: Document, file_name: str): +def write_document_to_file(document: Document, file_name: str, validate: bool): + if validate: + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, + document.creation_info.spdx_version) + if validation_messages: + raise ValueError(f"Document is not valid. The following errors were detected: {validation_messages}") + graph = Graph() doc_namespace = document.creation_info.document_namespace external_doc_namespace_mapping = {external_doc_ref.document_ref_id: external_doc_ref.document_uri for diff --git a/src/spdx/writer/write_anything.py b/src/spdx/writer/write_anything.py index 43f786639..14506a007 100644 --- a/src/spdx/writer/write_anything.py +++ b/src/spdx/writer/write_anything.py @@ -8,6 +8,8 @@ # 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 spdx.writer.rdf import rdf_writer + from spdx.formats import file_name_to_format, FileFormat from spdx.model.document import Document from spdx.writer.json import json_writer @@ -19,12 +21,12 @@ 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, validate=False) + json_writer.write_document(document, file_name, validate) elif output_format == FileFormat.YAML: - yaml_writer.write_document_to_file(document, file_name, validate=False) + yaml_writer.write_document_to_file(document, file_name, validate) elif output_format == FileFormat.XML: - xml_writer.write_document_to_file(document, file_name, validate=False) + xml_writer.write_document_to_file(document, file_name, validate) elif output_format == FileFormat.TAG_VALUE: tagvalue_writer.write_document_to_file(document, file_name) elif output_format == FileFormat.RDF_XML: - raise NotImplementedError("Currently, the rdf writer is not implemented") + rdf_writer.write_document_to_file(document, file_name, validate) diff --git a/tests/spdx/writer/rdf/test_rdf_writer.py b/tests/spdx/writer/rdf/test_rdf_writer.py index ee817e0c7..f6bf29ace 100644 --- a/tests/spdx/writer/rdf/test_rdf_writer.py +++ b/tests/spdx/writer/rdf/test_rdf_writer.py @@ -26,4 +26,4 @@ def temporary_file_path() -> str: def test_write_document_to_file(temporary_file_path: str): document: Document = document_fixture() - write_document_to_file(document, temporary_file_path) + write_document_to_file(document, temporary_file_path, False) From cd31eab1b1190e7f86e1caec792e2d0994141894 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 26 Jan 2023 12:44:24 +0100 Subject: [PATCH 191/362] [issue-407] fix writing of reference_type in ExternalPackageRef Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/package_writer.py | 16 ++++++++++------ tests/spdx/writer/rdf/test_package_writer.py | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/spdx/writer/rdf/package_writer.py b/src/spdx/writer/rdf/package_writer.py index 8710fa263..336e0ae7a 100644 --- a/src/spdx/writer/rdf/package_writer.py +++ b/src/spdx/writer/rdf/package_writer.py @@ -10,12 +10,13 @@ # limitations under the License. from typing import Dict -from rdflib import Graph, URIRef, RDF, Literal, XSD, BNode, DOAP, RDFS, Namespace +from rdflib import Graph, URIRef, RDF, Literal, XSD, BNode, DOAP, RDFS from spdx.writer.casing_tools import snake_case_to_camel_case from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph -from spdx.model.package import Package, PackageVerificationCode, ExternalPackageRef +from spdx.model.package import Package, PackageVerificationCode, ExternalPackageRef, \ + CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value, add_literal_or_no_assertion_or_none, \ add_datetime_to_graph, add_namespace_to_spdx_id @@ -84,10 +85,13 @@ def add_external_package_ref_to_graph(graph: Graph, external_package_ref: Extern graph.add((external_package_ref_node, RDF.type, spdx_namespace.ExternalRef)) graph.add((external_package_ref_node, spdx_namespace.referenceCategory, spdx_namespace[f"referenceCategory_{snake_case_to_camel_case(external_package_ref.category.name)}"])) - # the referenceType should either be f"http://spdx.org/rdf/references/{location}" for listed locations - # or f"{doc_namespace}#type" for unlisted locations, as a first attempt we simply write a Literal - # TODO: open issue - graph.add((external_package_ref_node, spdx_namespace.referenceType, Literal(external_package_ref.reference_type))) + + if external_package_ref.reference_type in CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES[external_package_ref.category]: + graph.add((external_package_ref_node, spdx_namespace.referenceType, + URIRef(f"http://spdx.org/rdf/references/{external_package_ref.reference_type}"))) + else: + graph.add((external_package_ref_node, spdx_namespace.referenceType, + URIRef(f"{doc_namespace}#{external_package_ref.reference_type}"))) graph.add((external_package_ref_node, spdx_namespace.referenceLocator, Literal(external_package_ref.locator))) if external_package_ref.comment: graph.add((external_package_ref_node, RDFS.comment, Literal(external_package_ref.comment))) diff --git a/tests/spdx/writer/rdf/test_package_writer.py b/tests/spdx/writer/rdf/test_package_writer.py index f254d8609..f40e2a16c 100644 --- a/tests/spdx/writer/rdf/test_package_writer.py +++ b/tests/spdx/writer/rdf/test_package_writer.py @@ -73,6 +73,6 @@ def test_external_package_ref_to_graph(): assert (None, None, spdx_namespace.ExternalRef) in graph assert (None, spdx_namespace.referenceCategory, spdx_namespace.referenceCategory_packageManager) in graph - assert (None, spdx_namespace.referenceType, Literal("maven-central")) in graph + assert (None, spdx_namespace.referenceType, URIRef("http://spdx.org/rdf/references/maven-central")) in graph assert (None, spdx_namespace.referenceLocator, Literal("org.apache.tomcat:tomcat:9.0.0.M4")) in graph assert (None, RDFS.comment, Literal("externalPackageRefComment")) in graph From a0acb4f66ce6ae9be1e7439db62963f86813e2d5 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 26 Jan 2023 14:17:27 +0100 Subject: [PATCH 192/362] [issue-407, refactor] rename Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/annotation_writer.py | 4 ++-- src/spdx/writer/rdf/file_writer.py | 4 ++-- src/spdx/writer/rdf/package_writer.py | 4 ++-- src/spdx/writer/rdf/rdf_writer.py | 17 ++++++++--------- src/spdx/writer/rdf/relationship_writer.py | 6 +++--- src/spdx/writer/rdf/snippet_writer.py | 6 +++--- 6 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/spdx/writer/rdf/annotation_writer.py b/src/spdx/writer/rdf/annotation_writer.py index 3268eeed5..ec67d5504 100644 --- a/src/spdx/writer/rdf/annotation_writer.py +++ b/src/spdx/writer/rdf/annotation_writer.py @@ -19,8 +19,8 @@ def add_annotation_info_to_graph(annotation: Annotation, graph: Graph, doc_namespace: str, - external_doc_namespaces: Dict[str, str]): - annotation_resource = URIRef(add_namespace_to_spdx_id(annotation.spdx_id, doc_namespace, external_doc_namespaces)) + external_doc_ref_to_namespace: Dict[str, str]): + annotation_resource = URIRef(add_namespace_to_spdx_id(annotation.spdx_id, doc_namespace, external_doc_ref_to_namespace)) annotation_node = BNode() graph.add((annotation_node, RDF.type, spdx_namespace.Annotation)) graph.add((annotation_node, spdx_namespace.annotationType, diff --git a/src/spdx/writer/rdf/file_writer.py b/src/spdx/writer/rdf/file_writer.py index 5cfaa55a0..5804d43be 100644 --- a/src/spdx/writer/rdf/file_writer.py +++ b/src/spdx/writer/rdf/file_writer.py @@ -20,8 +20,8 @@ def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str, - external_doc_namespaces: Dict[str, str]): - file_resource = URIRef(add_namespace_to_spdx_id(file.spdx_id, doc_namespace, external_doc_namespaces)) + external_doc_ref_to_namespace: Dict[str, str]): + file_resource = URIRef(add_namespace_to_spdx_id(file.spdx_id, doc_namespace, external_doc_ref_to_namespace)) graph.add((file_resource, RDF.type, spdx_namespace.File)) graph.add((file_resource, spdx_namespace.fileName, Literal(file.name))) for file_type in file.file_type: diff --git a/src/spdx/writer/rdf/package_writer.py b/src/spdx/writer/rdf/package_writer.py index 336e0ae7a..8fe9efdf5 100644 --- a/src/spdx/writer/rdf/package_writer.py +++ b/src/spdx/writer/rdf/package_writer.py @@ -22,8 +22,8 @@ def add_package_information_to_graph(package: Package, graph: Graph, doc_namespace: str, - external_doc_namespaces: Dict[str, str]): - package_resource = URIRef(add_namespace_to_spdx_id(package.spdx_id, doc_namespace, external_doc_namespaces)) + external_doc_ref_to_namespace: Dict[str, str]): + package_resource = URIRef(add_namespace_to_spdx_id(package.spdx_id, doc_namespace, external_doc_ref_to_namespace)) graph.add((package_resource, RDF.type, spdx_namespace.Package)) graph.add((package_resource, spdx_namespace.name, Literal(package.name))) diff --git a/src/spdx/writer/rdf/rdf_writer.py b/src/spdx/writer/rdf/rdf_writer.py index 2dcc3611d..a12e504be 100644 --- a/src/spdx/writer/rdf/rdf_writer.py +++ b/src/spdx/writer/rdf/rdf_writer.py @@ -8,7 +8,7 @@ # 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 typing import List +from typing import Dict, List from rdflib import Graph, DOAP from rdflib.compare import to_isomorphic @@ -35,24 +35,23 @@ def write_document_to_file(document: Document, file_name: str, validate: bool): graph = Graph() doc_namespace = document.creation_info.document_namespace - external_doc_namespace_mapping = {external_doc_ref.document_ref_id: external_doc_ref.document_uri for - external_doc_ref - in document.creation_info.external_document_refs} + external_doc_ref_to_namespace: Dict[str, str] = {external_doc_ref.document_ref_id: external_doc_ref.document_uri for + external_doc_ref in document.creation_info.external_document_refs} doc_node = add_creation_info_to_graph(document.creation_info, graph) for annotation in document.annotations: - add_annotation_info_to_graph(annotation, graph, doc_namespace, external_doc_namespace_mapping) + add_annotation_info_to_graph(annotation, graph, doc_namespace, external_doc_ref_to_namespace) for file in document.files: - add_file_information_to_graph(file, graph, doc_namespace, external_doc_namespace_mapping) + add_file_information_to_graph(file, graph, doc_namespace, external_doc_ref_to_namespace) for package in document.packages: - add_package_information_to_graph(package, graph, doc_namespace, external_doc_namespace_mapping) + add_package_information_to_graph(package, graph, doc_namespace, external_doc_ref_to_namespace) for relationship in document.relationships: - add_relationship_info_to_graph(relationship, graph, doc_namespace, external_doc_namespace_mapping) + add_relationship_info_to_graph(relationship, graph, doc_namespace, external_doc_ref_to_namespace) for snippet in document.snippets: - add_snippet_information_to_graph(snippet, graph, doc_namespace, external_doc_namespace_mapping) + add_snippet_information_to_graph(snippet, graph, doc_namespace, external_doc_ref_to_namespace) for extracted_licensing_info in document.extracted_licensing_info: add_extracted_licensing_info_to_graph(extracted_licensing_info, graph, doc_node, doc_namespace) diff --git a/src/spdx/writer/rdf/relationship_writer.py b/src/spdx/writer/rdf/relationship_writer.py index a3a1be0ad..66cd43c0f 100644 --- a/src/spdx/writer/rdf/relationship_writer.py +++ b/src/spdx/writer/rdf/relationship_writer.py @@ -20,7 +20,7 @@ def add_relationship_info_to_graph(relationship: Relationship, graph: Graph, doc_namespace: str, - external_doc_namespaces: Dict[str, str]): + external_doc_ref_to_namespace: Dict[str, str]): relationship_node = BNode() graph.add((relationship_node, RDF.type, spdx_namespace.Relationship)) graph.add((relationship_node, spdx_namespace.relationshipType, @@ -32,8 +32,8 @@ def add_relationship_info_to_graph(relationship: Relationship, graph: Graph, doc else: graph.add((relationship_node, spdx_namespace.relatedSpdxElement, URIRef(add_namespace_to_spdx_id(relationship.related_spdx_element_id, doc_namespace, - external_doc_namespaces)))) + external_doc_ref_to_namespace)))) relationship_resource = URIRef( - add_namespace_to_spdx_id(relationship.spdx_element_id, doc_namespace, external_doc_namespaces)) + add_namespace_to_spdx_id(relationship.spdx_element_id, doc_namespace, external_doc_ref_to_namespace)) graph.add((relationship_resource, spdx_namespace.relationship, relationship_node)) diff --git a/src/spdx/writer/rdf/snippet_writer.py b/src/spdx/writer/rdf/snippet_writer.py index ceb38be97..67443be85 100644 --- a/src/spdx/writer/rdf/snippet_writer.py +++ b/src/spdx/writer/rdf/snippet_writer.py @@ -18,12 +18,12 @@ def add_snippet_information_to_graph(snippet: Snippet, graph: Graph, doc_namespace: str, - external_doc_namespaces: Dict[str, str]): - snippet_resource = URIRef(add_namespace_to_spdx_id(snippet.spdx_id, doc_namespace, external_doc_namespaces)) + external_doc_ref_to_namespace: Dict[str, str]): + snippet_resource = URIRef(add_namespace_to_spdx_id(snippet.spdx_id, doc_namespace, external_doc_ref_to_namespace)) graph.add((snippet_resource, RDF.type, spdx_namespace.Snippet)) graph.add((snippet_resource, spdx_namespace.snippetFromFile, - URIRef(add_namespace_to_spdx_id(snippet.file_spdx_id, doc_namespace, external_doc_namespaces)))) + URIRef(add_namespace_to_spdx_id(snippet.file_spdx_id, doc_namespace, external_doc_ref_to_namespace)))) add_range_to_graph(graph, snippet_resource, snippet.byte_range, snippet.file_spdx_id) add_range_to_graph(graph, snippet_resource, snippet.line_range, snippet.file_spdx_id) add_literal_or_no_assertion_or_none(graph, snippet_resource, spdx_namespace.licenseConcluded, From 9e349f316947e7915479e7cb5cc2bca08d86eac7 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 26 Jan 2023 14:45:47 +0100 Subject: [PATCH 193/362] [issue-407] add ranges writer Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/rdf_writer.py | 3 +- src/spdx/writer/rdf/snippet_writer.py | 32 +++++++++++++++----- src/spdx/writer/rdf/writer_utils.py | 1 + tests/spdx/writer/rdf/test_snippet_writer.py | 20 ++++++++++-- 4 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/spdx/writer/rdf/rdf_writer.py b/src/spdx/writer/rdf/rdf_writer.py index a12e504be..b5c3cda08 100644 --- a/src/spdx/writer/rdf/rdf_writer.py +++ b/src/spdx/writer/rdf/rdf_writer.py @@ -23,7 +23,7 @@ from spdx.writer.rdf.package_writer import add_package_information_to_graph from spdx.writer.rdf.relationship_writer import add_relationship_info_to_graph from spdx.writer.rdf.snippet_writer import add_snippet_information_to_graph -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import spdx_namespace, pointer_namespace def write_document_to_file(document: Document, file_name: str, validate: bool): @@ -59,4 +59,5 @@ def write_document_to_file(document: Document, file_name: str, validate: bool): graph = to_isomorphic(graph) graph.bind("spdx", spdx_namespace) graph.bind("doap", DOAP) + graph.bind("ptr", pointer_namespace) graph.serialize(file_name, "pretty-xml", encoding="UTF-8", max_depth=100) diff --git a/src/spdx/writer/rdf/snippet_writer.py b/src/spdx/writer/rdf/snippet_writer.py index 67443be85..06603f9af 100644 --- a/src/spdx/writer/rdf/snippet_writer.py +++ b/src/spdx/writer/rdf/snippet_writer.py @@ -10,9 +10,9 @@ # limitations under the License. from typing import Tuple, Optional, Dict -from rdflib import Graph, URIRef, RDF, RDFS, Literal +from rdflib import Graph, URIRef, RDF, RDFS, Literal, BNode from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_or_no_assertion_or_none, add_literal_value, \ - add_namespace_to_spdx_id + add_namespace_to_spdx_id, pointer_namespace from spdx.model.snippet import Snippet @@ -22,10 +22,14 @@ def add_snippet_information_to_graph(snippet: Snippet, graph: Graph, doc_namespa snippet_resource = URIRef(add_namespace_to_spdx_id(snippet.spdx_id, doc_namespace, external_doc_ref_to_namespace)) graph.add((snippet_resource, RDF.type, spdx_namespace.Snippet)) + snippet_from_file_ref = URIRef( + add_namespace_to_spdx_id(snippet.file_spdx_id, doc_namespace, external_doc_ref_to_namespace)) graph.add((snippet_resource, spdx_namespace.snippetFromFile, - URIRef(add_namespace_to_spdx_id(snippet.file_spdx_id, doc_namespace, external_doc_ref_to_namespace)))) - add_range_to_graph(graph, snippet_resource, snippet.byte_range, snippet.file_spdx_id) - add_range_to_graph(graph, snippet_resource, snippet.line_range, snippet.file_spdx_id) + snippet_from_file_ref)) + add_range_to_graph(graph, snippet_resource, snippet.byte_range, snippet_from_file_ref, + pointer_namespace.ByteOffsetPointer) + add_range_to_graph(graph, snippet_resource, snippet.line_range, snippet_from_file_ref, + pointer_namespace.LineCharPointer) add_literal_or_no_assertion_or_none(graph, snippet_resource, spdx_namespace.licenseConcluded, snippet.license_concluded) add_literal_or_no_assertion_or_none(graph, snippet_resource, spdx_namespace.licenseInfoInSnippet, @@ -39,6 +43,18 @@ def add_snippet_information_to_graph(snippet: Snippet, graph: Graph, doc_namespa def add_range_to_graph(graph: Graph, snippet_resource: URIRef, range_information: Optional[Tuple[int, int]], - file_spdx_id: str): - # TODO: implement writer for ranges (https://github.com/spdx/tools-python/issues/274) - pass + snippet_from_file_ref: URIRef, pointer_class: URIRef): + start_end_pointer = BNode() + graph.add((start_end_pointer, RDF.type, pointer_namespace.StartEndPointer)) + for (predicate, value) in [(pointer_namespace.startPointer, range_information[0]), + (pointer_namespace.endPointer, range_information[1])]: + pointer_node = BNode() + graph.add((pointer_node, RDF.type, pointer_class)) + graph.add((start_end_pointer, predicate, pointer_node)) + graph.add((pointer_node, pointer_namespace.reference, snippet_from_file_ref)) + if pointer_class == pointer_namespace.ByteOffsetPointer: + graph.add((pointer_node, pointer_namespace.offset, Literal(str(value)))) + else: + graph.add((pointer_node, pointer_namespace.lineNumber, Literal(str(value)))) + + graph.add((snippet_resource, spdx_namespace.range, start_end_pointer)) diff --git a/src/spdx/writer/rdf/writer_utils.py b/src/spdx/writer/rdf/writer_utils.py index cec03c1d8..fa9c67368 100644 --- a/src/spdx/writer/rdf/writer_utils.py +++ b/src/spdx/writer/rdf/writer_utils.py @@ -20,6 +20,7 @@ from spdx.validation.spdx_id_validators import is_valid_internal_spdx_id spdx_namespace = Namespace("http://spdx.org/rdf/terms#") +pointer_namespace = Namespace("http://www.w3.org/2009/pointers#") def add_literal_value(graph: Graph, parent: Node, predicate: Node, value: Any): diff --git a/tests/spdx/writer/rdf/test_snippet_writer.py b/tests/spdx/writer/rdf/test_snippet_writer.py index 2cb69700b..9858c90ea 100644 --- a/tests/spdx/writer/rdf/test_snippet_writer.py +++ b/tests/spdx/writer/rdf/test_snippet_writer.py @@ -9,9 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. from rdflib import Graph, URIRef, RDF, Literal, RDFS -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import spdx_namespace, pointer_namespace -from spdx.writer.rdf.snippet_writer import add_snippet_information_to_graph +from spdx.writer.rdf.snippet_writer import add_snippet_information_to_graph, add_range_to_graph from tests.spdx.fixtures import snippet_fixture @@ -22,7 +22,7 @@ def test_add_snippet_information_to_graph(): add_snippet_information_to_graph(snippet, graph, "anyURI", {}) assert (URIRef("anyURI#SPDXRef-Snippet"), RDF.type, spdx_namespace.Snippet) in graph - assert (None, spdx_namespace.snippetFromFile, URIRef(snippet.file_spdx_id)) in graph + assert (None, spdx_namespace.snippetFromFile, URIRef(f"anyURI#{snippet.file_spdx_id}")) in graph assert (None, spdx_namespace.licenseConcluded, Literal("snippetLicenseConcluded")) in graph assert (None, spdx_namespace.licenseInfoInSnippet, Literal("licenseInfoInSnippet")) in graph assert (None, spdx_namespace.licenseComments, Literal("snippetLicenseComment")) in graph @@ -30,3 +30,17 @@ def test_add_snippet_information_to_graph(): assert (None, spdx_namespace.name, Literal("snippetName")) in graph assert (None, spdx_namespace.attributionText, Literal("snippetAttributionText")) in graph assert (None, RDFS.comment, Literal("snippetComment")) in graph + + +def test_add_ranges_to_graph(): + graph = Graph() + byte_range = (5, 190) + + add_range_to_graph(graph, URIRef("anyUR"), byte_range, URIRef("anyURI#SPDXRef-File"), pointer_namespace.ByteOffsetPointer) + + assert (None, spdx_namespace.range, None) in graph + assert (None, pointer_namespace.startPointer, None) in graph + assert (None, pointer_namespace.endPointer, None) in graph + assert (None, pointer_namespace.reference, URIRef("anyURI#SPDXRef-File")) in graph + assert (None, pointer_namespace.offset, Literal(str(5))) in graph + assert (None, pointer_namespace.offset, Literal(str(190))) in graph From dbf7aba8b33e4193daa913eee6a82d111f72b6ab Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 26 Jan 2023 16:24:08 +0100 Subject: [PATCH 194/362] [issue-407] fix Signed-off-by: Meret Behrens --- src/spdx/model/package.py | 3 ++- src/spdx/writer/rdf/package_writer.py | 4 ++-- tests/spdx/writer/rdf/test_package_writer.py | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/spdx/model/package.py b/src/spdx/model/package.py index 50bd802b1..7f4e32296 100644 --- a/src/spdx/model/package.py +++ b/src/spdx/model/package.py @@ -57,7 +57,8 @@ class ExternalPackageRefCategory(Enum): CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES: Dict[ExternalPackageRefCategory, List[str]] = { ExternalPackageRefCategory.SECURITY : ["cpe22Type", "cpe23Type", "advisory", "fix", "url", "swid"], ExternalPackageRefCategory.PACKAGE_MANAGER : ["maven-central", "npm", "nuget", "bower", "purl"], - ExternalPackageRefCategory.PERSISTENT_ID : ["swh", "gitoid"] + ExternalPackageRefCategory.PERSISTENT_ID : ["swh", "gitoid"], + ExternalPackageRefCategory.OTHER: [] } diff --git a/src/spdx/writer/rdf/package_writer.py b/src/spdx/writer/rdf/package_writer.py index 8fe9efdf5..7bc7bff64 100644 --- a/src/spdx/writer/rdf/package_writer.py +++ b/src/spdx/writer/rdf/package_writer.py @@ -57,7 +57,7 @@ def add_package_information_to_graph(package: Package, graph: Graph, doc_namespa add_literal_value(graph, package_resource, spdx_namespace.attributionText, attribution_text) if package.primary_package_purpose: graph.add((package_resource, spdx_namespace.primaryPackagePurpose, - spdx_namespace[f"packagePurpose_{snake_case_to_camel_case(package.primary_package_purpose.name)}"])) + spdx_namespace[f"purpose_{snake_case_to_camel_case(package.primary_package_purpose.name)}"])) add_datetime_to_graph(graph, package_resource, spdx_namespace.releaseDate, package.release_date) add_datetime_to_graph(graph, package_resource, spdx_namespace.builtDate, package.built_date) @@ -91,7 +91,7 @@ def add_external_package_ref_to_graph(graph: Graph, external_package_ref: Extern URIRef(f"http://spdx.org/rdf/references/{external_package_ref.reference_type}"))) else: graph.add((external_package_ref_node, spdx_namespace.referenceType, - URIRef(f"{doc_namespace}#{external_package_ref.reference_type}"))) + URIRef(external_package_ref.reference_type))) graph.add((external_package_ref_node, spdx_namespace.referenceLocator, Literal(external_package_ref.locator))) if external_package_ref.comment: graph.add((external_package_ref_node, RDFS.comment, Literal(external_package_ref.comment))) diff --git a/tests/spdx/writer/rdf/test_package_writer.py b/tests/spdx/writer/rdf/test_package_writer.py index f40e2a16c..7112c33cf 100644 --- a/tests/spdx/writer/rdf/test_package_writer.py +++ b/tests/spdx/writer/rdf/test_package_writer.py @@ -46,8 +46,8 @@ def test_add_package_information_to_graph(): assert (None, spdx_namespace.description, Literal("packageDescription")) in graph assert (None, RDFS.comment, Literal("packageComment")) in graph assert (URIRef("anyURI#SPDXRef-Package"), spdx_namespace.externalRef, None) in graph - assert (None, spdx_namespace.attributionText, Literal("packageAttributionText")) - assert (None, spdx_namespace.primaryPackagePurpose, spdx_namespace.primaryPackagePurpose_source) + assert (None, spdx_namespace.attributionText, Literal("packageAttributionText")) in graph + assert (None, spdx_namespace.primaryPackagePurpose, spdx_namespace.purpose_source) in graph assert (None, spdx_namespace.releaseDate, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph assert (None, spdx_namespace.builtDate, Literal(datetime_to_iso_string(datetime(2022, 12, 2)))) in graph assert (None, spdx_namespace.validUntilDate, Literal(datetime_to_iso_string(datetime(2022, 12, 3)))) in graph From 6cb2dfd44d89ae511fdd13096bec4728ff4bcc65 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 26 Jan 2023 17:06:33 +0100 Subject: [PATCH 195/362] [issue-407] add rdflib as optional dependency Signed-off-by: Meret Behrens --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index fd2a6a867..beda649b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,8 @@ dependencies = ["click", "pyyaml", "xmltodict", "rdflib", "typeguard", "uritools dynamic = ["version"] [project.optional-dependencies] -test = ["pytest"] +test = ["pytest", "rdflib"] +rdf = ["rdflib"] [project.scripts] pyspdxtools = "spdx.clitools.pyspdxtools:main" From fd4f5f90f41eb88ab83f1084068eac14c4c01d9d Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 26 Jan 2023 17:21:57 +0100 Subject: [PATCH 196/362] [issue-407] fix pipeline Signed-off-by: Meret Behrens --- .github/workflows/install_and_test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/install_and_test.yml b/.github/workflows/install_and_test.yml index 77078c5f6..de867e1cb 100644 --- a/.github/workflows/install_and_test.yml +++ b/.github/workflows/install_and_test.yml @@ -23,6 +23,7 @@ jobs: python -m build -nwx . python -m pip install --upgrade ./dist/*.whl python -m pip install pytest + python -m pip install rdflib shell: bash - name: Run tests run: pytest From e2925303ac5a4889916d7be78e41c36e4450b68c Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 27 Jan 2023 12:27:41 +0100 Subject: [PATCH 197/362] [issue-407] fix writing of data license Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/creation_info_writer.py | 2 +- tests/spdx/writer/rdf/test_creation_info_writer.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/spdx/writer/rdf/creation_info_writer.py b/src/spdx/writer/rdf/creation_info_writer.py index 99b640211..97f1386d0 100644 --- a/src/spdx/writer/rdf/creation_info_writer.py +++ b/src/spdx/writer/rdf/creation_info_writer.py @@ -20,7 +20,7 @@ def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): doc_node = URIRef(f"{creation_info.document_namespace}#{creation_info.spdx_id}") graph.add((doc_node, RDF.type, spdx_namespace.SpdxDocument)) graph.add((doc_node, spdx_namespace.specVersion, Literal(creation_info.spdx_version))) - graph.add((doc_node, spdx_namespace.dataLicense, Literal(creation_info.data_license))) + graph.add((doc_node, spdx_namespace.dataLicense, URIRef(f"http://spdx.org/licenses/{creation_info.data_license}"))) graph.add((doc_node, spdx_namespace.name, Literal(creation_info.name))) add_literal_value(graph, doc_node, RDFS.comment, creation_info.document_comment) diff --git a/tests/spdx/writer/rdf/test_creation_info_writer.py b/tests/spdx/writer/rdf/test_creation_info_writer.py index 0a14169c5..84e69499f 100644 --- a/tests/spdx/writer/rdf/test_creation_info_writer.py +++ b/tests/spdx/writer/rdf/test_creation_info_writer.py @@ -26,9 +26,10 @@ def test_add_creation_info_to_graph(): assert (None, None, spdx_namespace.SpdxDocument) in graph assert (URIRef(f"{creation_info.document_namespace}#{creation_info.spdx_id}"), None, None) in graph - assert (None, spdx_namespace.creationInfo, None) in graph + assert (None, spdx_namespace.dataLicense, URIRef("https://spdx.org/licenses/CC0-1.0")) assert (None, spdx_namespace.name, Literal("documentName")) in graph assert (None, spdx_namespace.specVersion, Literal("SPDX-2.3")) in graph + assert (None, spdx_namespace.creationInfo, None) in graph assert (None, None, spdx_namespace.CreationInfo) in graph assert (None, spdx_namespace.created, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph From 3ce2f9299d86dcb38e91739334614f66eec7b1f1 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 27 Jan 2023 12:28:04 +0100 Subject: [PATCH 198/362] [issue-407] fix tests after rebase Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/writer_utils.py | 6 +++--- tests/spdx/writer/rdf/test_file_writer.py | 5 +++-- tests/spdx/writer/rdf/test_package_writer.py | 7 ++++--- tests/spdx/writer/rdf/test_snippet_writer.py | 5 +++-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/spdx/writer/rdf/writer_utils.py b/src/spdx/writer/rdf/writer_utils.py index fa9c67368..100eca8a4 100644 --- a/src/spdx/writer/rdf/writer_utils.py +++ b/src/spdx/writer/rdf/writer_utils.py @@ -24,7 +24,7 @@ def add_literal_value(graph: Graph, parent: Node, predicate: Node, value: Any): - if not value: + if value is None: return if not isinstance(value, list): graph.add((parent, predicate, Literal(str(value)))) @@ -36,7 +36,7 @@ def add_literal_value(graph: Graph, parent: Node, predicate: Node, value: Any): def add_literal_or_no_assertion_or_none(graph: Graph, parent: Node, predicate: Node, value: Any): - if not value: + if value is None: return if isinstance(value, SpdxNone): graph.add((parent, predicate, spdx_namespace.none)) @@ -45,7 +45,7 @@ def add_literal_or_no_assertion_or_none(graph: Graph, parent: Node, predicate: N def add_literal_or_no_assertion(graph: Graph, parent: Node, predicate: Node, value: Any): - if not value: + if value is None: return if isinstance(value, SpdxNoAssertion): graph.add((parent, predicate, spdx_namespace.noassertion)) diff --git a/tests/spdx/writer/rdf/test_file_writer.py b/tests/spdx/writer/rdf/test_file_writer.py index 3729aa304..2a1880050 100644 --- a/tests/spdx/writer/rdf/test_file_writer.py +++ b/tests/spdx/writer/rdf/test_file_writer.py @@ -25,8 +25,9 @@ def test_add_file_information_to_graph(): assert (None, spdx_namespace.fileName, Literal("./fileName.py")) in graph assert (None, spdx_namespace.fileType, spdx_namespace.fileType_text) in graph assert (None, spdx_namespace.licenseComments, Literal("licenseComment")) in graph - assert (None, spdx_namespace.licenseConcluded, Literal("licenseConcludedExpression")) in graph - assert (None, spdx_namespace.licenseInfoInFile, Literal("licenseInfoInFileExpression")) in graph + assert (None, spdx_namespace.licenseConcluded, Literal("MIT AND GPL-2.0")) in graph + assert (None, spdx_namespace.licenseInfoInFile, Literal("MIT")) in graph + assert (None, spdx_namespace.licenseInfoInFile, Literal("GPL-2.0")) in graph assert (None, spdx_namespace.copyrightText, Literal("copyrightText")) in graph assert (None, RDFS.comment, Literal("fileComment")) in graph assert (None, spdx_namespace.noticeText, Literal("fileNotice")) in graph diff --git a/tests/spdx/writer/rdf/test_package_writer.py b/tests/spdx/writer/rdf/test_package_writer.py index 7112c33cf..4b0406278 100644 --- a/tests/spdx/writer/rdf/test_package_writer.py +++ b/tests/spdx/writer/rdf/test_package_writer.py @@ -37,9 +37,10 @@ def test_add_package_information_to_graph(): assert (URIRef("anyURI#SPDXRef-Package"), spdx_namespace.checksum, None) in graph assert (None, DOAP.homepage, Literal("https://homepage.com")) in graph assert (None, spdx_namespace.sourceInfo, Literal("sourceInfo")) in graph - assert (None, spdx_namespace.licenseConcluded, Literal("packageLicenseConcluded")) in graph - assert (None, spdx_namespace.licenseInfoFromFiles, Literal("licenseInfoFromFile")) in graph - assert (None, spdx_namespace.licenseDeclared, Literal("packageLicenseDeclared")) in graph + assert (None, spdx_namespace.licenseConcluded, Literal("MIT AND GPL-2.0")) in graph + assert (None, spdx_namespace.licenseInfoFromFiles, Literal("MIT")) in graph + assert (None, spdx_namespace.licenseInfoFromFiles, Literal("GPL-2.0")) in graph + assert (None, spdx_namespace.licenseDeclared, Literal("MIT AND GPL-2.0")) in graph assert (None, spdx_namespace.licenseComments, Literal("packageLicenseComment")) in graph assert (None, spdx_namespace.copyrightText, Literal("packageCopyrightText")) in graph assert (None, spdx_namespace.summary, Literal("packageSummary")) in graph diff --git a/tests/spdx/writer/rdf/test_snippet_writer.py b/tests/spdx/writer/rdf/test_snippet_writer.py index 9858c90ea..4a7188656 100644 --- a/tests/spdx/writer/rdf/test_snippet_writer.py +++ b/tests/spdx/writer/rdf/test_snippet_writer.py @@ -23,8 +23,9 @@ def test_add_snippet_information_to_graph(): assert (URIRef("anyURI#SPDXRef-Snippet"), RDF.type, spdx_namespace.Snippet) in graph assert (None, spdx_namespace.snippetFromFile, URIRef(f"anyURI#{snippet.file_spdx_id}")) in graph - assert (None, spdx_namespace.licenseConcluded, Literal("snippetLicenseConcluded")) in graph - assert (None, spdx_namespace.licenseInfoInSnippet, Literal("licenseInfoInSnippet")) in graph + assert (None, spdx_namespace.licenseConcluded, Literal("MIT AND GPL-2.0")) in graph + assert (None, spdx_namespace.licenseInfoInSnippet, Literal("MIT")) in graph + assert (None, spdx_namespace.licenseInfoInSnippet, Literal("GPL-2.0")) in graph assert (None, spdx_namespace.licenseComments, Literal("snippetLicenseComment")) in graph assert (None, spdx_namespace.copyrightText, Literal("licenseCopyrightText")) in graph assert (None, spdx_namespace.name, Literal("snippetName")) in graph From 7f6f293042d87add5e587fd2ca3a603b162a0c46 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 30 Jan 2023 14:17:46 +0100 Subject: [PATCH 199/362] [issue-407] add license expression writer Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/file_writer.py | 9 +- .../writer/rdf/license_expression_writer.py | 86 +++++++++++++++++++ src/spdx/writer/rdf/package_writer.py | 15 ++-- src/spdx/writer/rdf/snippet_writer.py | 10 ++- tests/spdx/fixtures.py | 18 ++-- .../json/expected_results/expected.json | 14 +-- tests/spdx/writer/rdf/test_file_writer.py | 5 +- .../rdf/test_license_expression_writer.py | 61 +++++++++++++ tests/spdx/writer/rdf/test_package_writer.py | 8 +- tests/spdx/writer/rdf/test_snippet_writer.py | 5 +- .../writer/tagvalue/test_package_writer.py | 6 +- 11 files changed, 194 insertions(+), 43 deletions(-) create mode 100644 src/spdx/writer/rdf/license_expression_writer.py create mode 100644 tests/spdx/writer/rdf/test_license_expression_writer.py diff --git a/src/spdx/writer/rdf/file_writer.py b/src/spdx/writer/rdf/file_writer.py index 5804d43be..547f5d5a2 100644 --- a/src/spdx/writer/rdf/file_writer.py +++ b/src/spdx/writer/rdf/file_writer.py @@ -15,6 +15,7 @@ from spdx.model.file import File from spdx.writer.casing_tools import snake_case_to_camel_case from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph +from spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value, add_literal_or_no_assertion_or_none, \ add_namespace_to_spdx_id @@ -31,10 +32,10 @@ def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str, for checksum in file.checksums: add_checksum_information_to_graph(checksum, graph, file_resource) - # as long as we don't have a proper handling of the licenses we simply write literals here - add_literal_or_no_assertion_or_none(graph, file_resource, spdx_namespace.licenseConcluded, file.license_concluded) - add_literal_or_no_assertion_or_none(graph, file_resource, spdx_namespace.licenseInfoInFile, - file.license_info_in_file) + add_license_expression_or_none_or_no_assertion(graph, file_resource, spdx_namespace.licenseConcluded, + file.license_concluded, doc_namespace) + add_license_expression_or_none_or_no_assertion(graph, file_resource, spdx_namespace.licenseInfoInFile, + file.license_info_in_file, doc_namespace) add_literal_value(graph, file_resource, spdx_namespace.licenseComments, file.license_comment) add_literal_value(graph, file_resource, spdx_namespace.copyrightText, file.copyright_text) diff --git a/src/spdx/writer/rdf/license_expression_writer.py b/src/spdx/writer/rdf/license_expression_writer.py new file mode 100644 index 000000000..fe6da0020 --- /dev/null +++ b/src/spdx/writer/rdf/license_expression_writer.py @@ -0,0 +1,86 @@ +# Copyright (c) 2023 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 typing import Union, List + +from boolean import Expression +from rdflib import Graph, URIRef, BNode, RDF +from license_expression import AND, OR, LicenseWithExceptionSymbol, LicenseSymbol, get_spdx_licensing, ExpressionInfo, \ + LicenseExpression +from rdflib.term import Node, Literal + +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone + +from spdx.writer.rdf.writer_utils import spdx_namespace + + +def add_license_expression_to_graph(graph: Graph, license_expression: Expression, parent_resource: Node, + doc_namespace: str, predicate: Node): + if isinstance(license_expression, AND): + member_node = BNode() + graph.add((member_node, RDF.type, spdx_namespace.ConjunctiveLicenseSet)) + graph.add((parent_resource, predicate, member_node)) + for arg in license_expression.args: + add_license_expression_to_graph(graph, arg, member_node, doc_namespace, spdx_namespace.member) + if isinstance(license_expression, OR): + member_node = BNode() + graph.add((member_node, RDF.type, spdx_namespace.DisjunctiveLicenseSet)) + graph.add((parent_resource, predicate, member_node)) + for arg in license_expression.args: + add_license_expression_to_graph(graph, arg, member_node, doc_namespace, spdx_namespace.member) + if isinstance(license_expression, LicenseWithExceptionSymbol): + member_node = BNode() + graph.add((member_node, RDF.type, spdx_namespace.WithExceptionOperator)) + graph.add((parent_resource, predicate, member_node)) + + add_license_expression_to_graph(graph, license_expression.license_symbol, member_node, doc_namespace, + spdx_namespace.member) + add_license_exception_to_graph(graph, license_expression.exception_symbol, member_node) + + if isinstance(license_expression, LicenseSymbol): + if check_if_license_or_exception_is_on_spdx_licensing_list(license_expression): + graph.add( + (parent_resource, predicate, URIRef(f"http://spdx.org/licenses/{license_expression}"))) + else: + # assuming that the license expression is a LicenseRef to an instance of ExtractedLicensingInfo + graph.add((parent_resource, predicate, URIRef(f"{doc_namespace}#{license_expression}"))) + + +def add_license_exception_to_graph(graph: Graph, license_exception: LicenseSymbol, member_node: Node): + if check_if_license_or_exception_is_on_spdx_licensing_list(license_exception): + exception_node = URIRef(f"http://spdx.org/licenses/{license_exception}") + graph.add((member_node, spdx_namespace.licenseException, exception_node)) + else: + exception_node = BNode() + graph.add((exception_node, spdx_namespace.licenseExceptionId, Literal(license_exception))) + graph.add((member_node, spdx_namespace.licenseException, exception_node)) + + graph.add((exception_node, RDF.type, spdx_namespace.LicenseException)) + + +def check_if_license_or_exception_is_on_spdx_licensing_list(license_symbol: LicenseSymbol) -> bool: + symbol_info: ExpressionInfo = get_spdx_licensing().validate(license_symbol) + return not symbol_info.errors + + +def add_license_expression_or_none_or_no_assertion(graph: Graph, parent: Node, predicate: Node, value: Union[ + List[LicenseExpression], LicenseExpression, SpdxNoAssertion, SpdxNone], doc_namespace: str): + if isinstance(value, SpdxNoAssertion): + graph.add((parent, predicate, spdx_namespace.noassertion)) + return + if isinstance(value, SpdxNone): + graph.add((parent, predicate, spdx_namespace.none)) + return + if isinstance(value, list): + for license_expression in value: + add_license_expression_to_graph(graph, license_expression, parent, doc_namespace, predicate) + if isinstance(value, LicenseExpression): + add_license_expression_to_graph(graph, value, parent, doc_namespace, predicate) diff --git a/src/spdx/writer/rdf/package_writer.py b/src/spdx/writer/rdf/package_writer.py index 7bc7bff64..a93b54787 100644 --- a/src/spdx/writer/rdf/package_writer.py +++ b/src/spdx/writer/rdf/package_writer.py @@ -11,6 +11,8 @@ from typing import Dict from rdflib import Graph, URIRef, RDF, Literal, XSD, BNode, DOAP, RDFS +from spdx.writer.rdf.license_expression_writer import add_license_expression_to_graph, \ + add_license_expression_or_none_or_no_assertion from spdx.writer.casing_tools import snake_case_to_camel_case from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph @@ -40,12 +42,13 @@ def add_package_information_to_graph(package: Package, graph: Graph, doc_namespa add_literal_value(graph, package_resource, DOAP.homepage, package.homepage) add_literal_value(graph, package_resource, spdx_namespace.sourceInfo, package.source_info) - add_literal_or_no_assertion_or_none(graph, package_resource, spdx_namespace.licenseConcluded, - package.license_concluded) - add_literal_or_no_assertion_or_none(graph, package_resource, spdx_namespace.licenseInfoFromFiles, - package.license_info_from_files) - add_literal_or_no_assertion_or_none(graph, package_resource, spdx_namespace.licenseDeclared, - package.license_declared) + add_license_expression_or_none_or_no_assertion(graph, package_resource, spdx_namespace.licenseConcluded, + package.license_concluded, + doc_namespace) + add_license_expression_or_none_or_no_assertion(graph, package_resource, spdx_namespace.licenseInfoFromFiles, + package.license_info_from_files, doc_namespace) + add_license_expression_or_none_or_no_assertion(graph, package_resource, spdx_namespace.licenseDeclared, + package.license_declared, doc_namespace) add_literal_value(graph, package_resource, spdx_namespace.licenseComments, package.license_comment) add_literal_value(graph, package_resource, spdx_namespace.copyrightText, package.copyright_text) add_literal_value(graph, package_resource, spdx_namespace.summary, package.summary) diff --git a/src/spdx/writer/rdf/snippet_writer.py b/src/spdx/writer/rdf/snippet_writer.py index 06603f9af..87f20492f 100644 --- a/src/spdx/writer/rdf/snippet_writer.py +++ b/src/spdx/writer/rdf/snippet_writer.py @@ -11,6 +11,8 @@ from typing import Tuple, Optional, Dict from rdflib import Graph, URIRef, RDF, RDFS, Literal, BNode + +from spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_or_no_assertion_or_none, add_literal_value, \ add_namespace_to_spdx_id, pointer_namespace @@ -30,10 +32,10 @@ def add_snippet_information_to_graph(snippet: Snippet, graph: Graph, doc_namespa pointer_namespace.ByteOffsetPointer) add_range_to_graph(graph, snippet_resource, snippet.line_range, snippet_from_file_ref, pointer_namespace.LineCharPointer) - add_literal_or_no_assertion_or_none(graph, snippet_resource, spdx_namespace.licenseConcluded, - snippet.license_concluded) - add_literal_or_no_assertion_or_none(graph, snippet_resource, spdx_namespace.licenseInfoInSnippet, - snippet.license_info_in_snippet) + add_license_expression_or_none_or_no_assertion(graph, snippet_resource, spdx_namespace.licenseConcluded, + snippet.license_concluded, doc_namespace) + add_license_expression_or_none_or_no_assertion(graph, snippet_resource, spdx_namespace.licenseInfoInSnippet, + snippet.license_info_in_snippet, doc_namespace) add_literal_value(graph, snippet_resource, spdx_namespace.licenseComments, snippet.license_comment) add_literal_value(graph, snippet_resource, spdx_namespace.copyrightText, snippet.copyright_text) add_literal_value(graph, snippet_resource, RDFS.comment, snippet.comment) diff --git a/tests/spdx/fixtures.py b/tests/spdx/fixtures.py index 094347db7..9c99f17a9 100644 --- a/tests/spdx/fixtures.py +++ b/tests/spdx/fixtures.py @@ -10,7 +10,7 @@ # limitations under the License. from datetime import datetime -from license_expression import Licensing +from license_expression import get_spdx_licensing from spdx.model.actor import Actor, ActorType from spdx.model.annotation import Annotation, AnnotationType @@ -56,13 +56,13 @@ def creation_info_fixture(spdx_version="SPDX-2.3", spdx_id="SPDXRef-DOCUMENT", n def file_fixture(name="./fileName.py", spdx_id="SPDXRef-File", checksums=None, file_type=None, - license_concluded=Licensing().parse("MIT and GPL-2.0"), license_info_in_file=None, + license_concluded=get_spdx_licensing().parse("MIT and GPL-2.0"), license_info_in_file=None, license_comment="licenseComment", copyright_text="copyrightText", comment="fileComment", notice="fileNotice", contributors=None, attribution_texts=None) -> File: checksums = [checksum_fixture()] if checksums is None else checksums file_type = [FileType.TEXT] if file_type is None else file_type - license_info_in_file = [Licensing().parse("MIT"), - Licensing().parse("GPL-2.0")] if license_info_in_file is None else license_info_in_file + license_info_in_file = [get_spdx_licensing().parse("MIT"), + get_spdx_licensing().parse("GPL-2.0")] if license_info_in_file is None else license_info_in_file contributors = ["fileContributor"] if contributors is None else contributors attribution_texts = ["fileAttributionText"] if attribution_texts is None else attribution_texts return File(name=name, spdx_id=spdx_id, checksums=checksums, file_type=file_type, @@ -76,15 +76,15 @@ def package_fixture(spdx_id="SPDXRef-Package", name="packageName", download_loca supplier=actor_fixture(name="supplierName"), originator=actor_fixture(name="originatorName"), files_analyzed=True, verification_code=package_verification_code_fixture(), checksums=None, homepage="https://homepage.com", source_info="sourceInfo", - license_concluded=Licensing().parse("MIT and GPL-2.0"), license_info_from_files=None, - license_declared=Licensing().parse("MIT and GPL-2.0"), + license_concluded=get_spdx_licensing().parse("MIT and GPL-2.0"), license_info_from_files=None, + license_declared=get_spdx_licensing().parse("MIT and GPL-2.0"), license_comment="packageLicenseComment", copyright_text="packageCopyrightText", summary="packageSummary", description="packageDescription", comment="packageComment", external_references=None, attribution_texts=None, primary_package_purpose=PackagePurpose.SOURCE, release_date=datetime(2022, 12, 1), built_date=datetime(2022, 12, 2), valid_until_date=datetime(2022, 12, 3)) -> Package: checksums = [checksum_fixture()] if checksums is None else checksums - license_info_from_files = [Licensing().parse("MIT"), Licensing().parse( + license_info_from_files = [get_spdx_licensing().parse("MIT"), get_spdx_licensing().parse( "GPL-2.0")] if license_info_from_files is None else license_info_from_files external_references = [external_package_ref_fixture()] if external_references is None else external_references attribution_texts = ["packageAttributionText"] if attribution_texts is None else attribution_texts @@ -111,11 +111,11 @@ def external_package_ref_fixture(category=ExternalPackageRefCategory.PACKAGE_MAN def snippet_fixture(spdx_id="SPDXRef-Snippet", file_spdx_id="SPDXRef-File", byte_range=(1, 2), - line_range=(3, 4), license_concluded=Licensing().parse("MIT and GPL-2.0"), + line_range=(3, 4), license_concluded=get_spdx_licensing().parse("MIT and GPL-2.0"), license_info_in_snippet=None, license_comment="snippetLicenseComment", copyright_text="licenseCopyrightText", comment="snippetComment", name="snippetName", attribution_texts=None) -> Snippet: - license_info_in_snippet = [Licensing().parse("MIT"), Licensing().parse( + license_info_in_snippet = [get_spdx_licensing().parse("MIT"), get_spdx_licensing().parse( "GPL-2.0")] if license_info_in_snippet is None else license_info_in_snippet attribution_texts = ["snippetAttributionText"] if attribution_texts is None else attribution_texts return Snippet(spdx_id=spdx_id, file_spdx_id=file_spdx_id, byte_range=byte_range, line_range=line_range, diff --git a/tests/spdx/writer/json/expected_results/expected.json b/tests/spdx/writer/json/expected_results/expected.json index 9e9a9c45a..c441e917d 100644 --- a/tests/spdx/writer/json/expected_results/expected.json +++ b/tests/spdx/writer/json/expected_results/expected.json @@ -54,8 +54,8 @@ "TEXT" ], "licenseComments": "licenseComment", - "licenseConcluded": "MIT AND GPL-2.0", - "licenseInfoInFiles": ["MIT", "GPL-2.0"], + "licenseConcluded": "MIT AND GPL-2.0-only", + "licenseInfoInFiles": ["MIT", "GPL-2.0-only"], "noticeText": "fileNotice" } ], @@ -99,9 +99,9 @@ "filesAnalyzed": true, "homepage": "https://homepage.com", "licenseComments": "packageLicenseComment", - "licenseConcluded": "MIT AND GPL-2.0", - "licenseDeclared": "MIT AND GPL-2.0", - "licenseInfoFromFiles": ["MIT", "GPL-2.0"], + "licenseConcluded": "MIT AND GPL-2.0-only", + "licenseDeclared": "MIT AND GPL-2.0-only", + "licenseInfoFromFiles": ["MIT", "GPL-2.0-only"], "name": "packageName", "originator": "Person: originatorName (some@mail.com)", "packageFileName": "./packageFileName", @@ -137,8 +137,8 @@ "comment": "snippetComment", "copyrightText": "licenseCopyrightText", "licenseComments": "snippetLicenseComment", - "licenseConcluded": "MIT AND GPL-2.0", - "licenseInfoInSnippets": ["MIT", "GPL-2.0"], + "licenseConcluded": "MIT AND GPL-2.0-only", + "licenseInfoInSnippets": ["MIT", "GPL-2.0-only"], "name": "snippetName", "ranges": [ { diff --git a/tests/spdx/writer/rdf/test_file_writer.py b/tests/spdx/writer/rdf/test_file_writer.py index 2a1880050..ff89cd6e5 100644 --- a/tests/spdx/writer/rdf/test_file_writer.py +++ b/tests/spdx/writer/rdf/test_file_writer.py @@ -25,9 +25,8 @@ def test_add_file_information_to_graph(): assert (None, spdx_namespace.fileName, Literal("./fileName.py")) in graph assert (None, spdx_namespace.fileType, spdx_namespace.fileType_text) in graph assert (None, spdx_namespace.licenseComments, Literal("licenseComment")) in graph - assert (None, spdx_namespace.licenseConcluded, Literal("MIT AND GPL-2.0")) in graph - assert (None, spdx_namespace.licenseInfoInFile, Literal("MIT")) in graph - assert (None, spdx_namespace.licenseInfoInFile, Literal("GPL-2.0")) in graph + assert (None, spdx_namespace.licenseConcluded, None) in graph + assert (None, spdx_namespace.licenseInfoInFile, None) in graph assert (None, spdx_namespace.copyrightText, Literal("copyrightText")) in graph assert (None, RDFS.comment, Literal("fileComment")) in graph assert (None, spdx_namespace.noticeText, Literal("fileNotice")) in graph diff --git a/tests/spdx/writer/rdf/test_license_expression_writer.py b/tests/spdx/writer/rdf/test_license_expression_writer.py new file mode 100644 index 000000000..f9d1b31f5 --- /dev/null +++ b/tests/spdx/writer/rdf/test_license_expression_writer.py @@ -0,0 +1,61 @@ +# Copyright (c) 2023 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. +import pytest +from license_expression import get_spdx_licensing +from rdflib import Graph, URIRef, RDF, Literal +from spdx.writer.rdf.writer_utils import spdx_namespace + +from spdx.writer.rdf.license_expression_writer import add_license_expression_to_graph + + +def test_add_conjunctive_license_set_to_graph(): + graph = Graph() + license_expression = get_spdx_licensing().parse("MIT AND GPL-2.0") + + add_license_expression_to_graph(graph, license_expression, URIRef("anyURI"), "https://namespace", + spdx_namespace.licenseConcluded) + + assert (None, RDF.type, spdx_namespace.ConjunctiveLicenseSet) in graph + assert (None, spdx_namespace.member, URIRef("http://spdx.org/licenses/MIT")) in graph + assert (None, spdx_namespace.member, URIRef("http://spdx.org/licenses/GPL-2.0-only")) in graph + + +def test_add_disjunctive_license_set_to_graph(): + graph = Graph() + license_expression = get_spdx_licensing().parse("MIT OR GPL-2.0") + + add_license_expression_to_graph(graph, license_expression, URIRef("anyURI"), "https://namespace", + spdx_namespace.licenseConcluded) + + assert (None, RDF.type, spdx_namespace.DisjunctiveLicenseSet) in graph + assert (None, spdx_namespace.member, URIRef("http://spdx.org/licenses/MIT")) in graph + assert (None, spdx_namespace.member, URIRef("http://spdx.org/licenses/GPL-2.0-only")) in graph + + +@pytest.mark.parametrize("license_with_exception,expected_triple", [("MIT WITH openvpn-openssl-exception", ( + URIRef("http://spdx.org/licenses/openvpn-openssl-exception"), RDF.type, spdx_namespace.LicenseException)), + ("MIT WITH unknown-exception", ( + None, + spdx_namespace.licenseExceptionId, + Literal("unknown-exception")))]) +def test_license_exception_to_graph(license_with_exception, expected_triple): + graph = Graph() + license_expression = get_spdx_licensing().parse(license_with_exception) + + add_license_expression_to_graph(graph, license_expression, URIRef("anyURI"), "https://namespace", + spdx_namespace.licenseConcluded) + + graph.serialize("rdf_with_exception.rdf.xml", "pretty-xml", max_depth=100) + + assert (None, RDF.type, spdx_namespace.WithExceptionOperator) in graph + assert (None, spdx_namespace.member, URIRef("http://spdx.org/licenses/MIT")) in graph + assert (None, spdx_namespace.licenseException, None) in graph + assert expected_triple in graph diff --git a/tests/spdx/writer/rdf/test_package_writer.py b/tests/spdx/writer/rdf/test_package_writer.py index 4b0406278..c89c651f0 100644 --- a/tests/spdx/writer/rdf/test_package_writer.py +++ b/tests/spdx/writer/rdf/test_package_writer.py @@ -25,6 +25,7 @@ def test_add_package_information_to_graph(): add_package_information_to_graph(package, graph, "anyURI", {}) + graph.serialize("package.rdf.xml", "pretty-xml", max_depth =100) assert (URIRef("anyURI#SPDXRef-Package"), RDF.type, spdx_namespace.Package) in graph assert (None, spdx_namespace.name, Literal("packageName")) in graph assert (None, spdx_namespace.versionInfo, Literal("12.2")) in graph @@ -37,10 +38,9 @@ def test_add_package_information_to_graph(): assert (URIRef("anyURI#SPDXRef-Package"), spdx_namespace.checksum, None) in graph assert (None, DOAP.homepage, Literal("https://homepage.com")) in graph assert (None, spdx_namespace.sourceInfo, Literal("sourceInfo")) in graph - assert (None, spdx_namespace.licenseConcluded, Literal("MIT AND GPL-2.0")) in graph - assert (None, spdx_namespace.licenseInfoFromFiles, Literal("MIT")) in graph - assert (None, spdx_namespace.licenseInfoFromFiles, Literal("GPL-2.0")) in graph - assert (None, spdx_namespace.licenseDeclared, Literal("MIT AND GPL-2.0")) in graph + assert (None, spdx_namespace.licenseConcluded, None) in graph + assert (None, spdx_namespace.licenseInfoFromFiles, None) in graph + assert (None, spdx_namespace.licenseDeclared, None) in graph assert (None, spdx_namespace.licenseComments, Literal("packageLicenseComment")) in graph assert (None, spdx_namespace.copyrightText, Literal("packageCopyrightText")) in graph assert (None, spdx_namespace.summary, Literal("packageSummary")) in graph diff --git a/tests/spdx/writer/rdf/test_snippet_writer.py b/tests/spdx/writer/rdf/test_snippet_writer.py index 4a7188656..3be73e8ed 100644 --- a/tests/spdx/writer/rdf/test_snippet_writer.py +++ b/tests/spdx/writer/rdf/test_snippet_writer.py @@ -23,9 +23,8 @@ def test_add_snippet_information_to_graph(): assert (URIRef("anyURI#SPDXRef-Snippet"), RDF.type, spdx_namespace.Snippet) in graph assert (None, spdx_namespace.snippetFromFile, URIRef(f"anyURI#{snippet.file_spdx_id}")) in graph - assert (None, spdx_namespace.licenseConcluded, Literal("MIT AND GPL-2.0")) in graph - assert (None, spdx_namespace.licenseInfoInSnippet, Literal("MIT")) in graph - assert (None, spdx_namespace.licenseInfoInSnippet, Literal("GPL-2.0")) in graph + assert (None, spdx_namespace.licenseConcluded, None) in graph + assert (None, spdx_namespace.licenseInfoInSnippet, None) in graph assert (None, spdx_namespace.licenseComments, Literal("snippetLicenseComment")) in graph assert (None, spdx_namespace.copyrightText, Literal("licenseCopyrightText")) in graph assert (None, spdx_namespace.name, Literal("snippetName")) in graph diff --git a/tests/spdx/writer/tagvalue/test_package_writer.py b/tests/spdx/writer/tagvalue/test_package_writer.py index c7d9ec0c0..feb2608f3 100644 --- a/tests/spdx/writer/tagvalue/test_package_writer.py +++ b/tests/spdx/writer/tagvalue/test_package_writer.py @@ -38,10 +38,10 @@ def test_package_writer(): call('PackageChecksum: SHA1: 71c4025dd9897b364f3ebbb42c484ff43d00791c\n'), call('PackageHomePage: https://homepage.com\n'), call('PackageSourceInfo: sourceInfo\n'), - call('PackageLicenseConcluded: MIT AND GPL-2.0\n'), + call('PackageLicenseConcluded: MIT AND GPL-2.0-only\n'), call('PackageLicenseInfoFromFiles: MIT\n'), - call('PackageLicenseInfoFromFiles: GPL-2.0\n'), - call('PackageLicenseDeclared: MIT AND GPL-2.0\n'), + call('PackageLicenseInfoFromFiles: GPL-2.0-only\n'), + call('PackageLicenseDeclared: MIT AND GPL-2.0-only\n'), call('PackageLicenseComments: packageLicenseComment\n'), call('PackageCopyrightText: packageCopyrightText\n'), call('PackageSummary: packageSummary\n'), From eec0a392e2f173bafcfa492d40d7f80ddbf1afbd Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 30 Jan 2023 15:20:04 +0100 Subject: [PATCH 200/362] [issue-407] delete unused imports Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/file_writer.py | 3 +-- src/spdx/writer/rdf/package_writer.py | 5 ++--- src/spdx/writer/rdf/snippet_writer.py | 9 ++++----- tests/spdx/jsonschema/test_file_converter.py | 2 +- tests/spdx/jsonschema/test_package_converter.py | 2 +- tests/spdx/jsonschema/test_snippet_converter.py | 2 +- tests/spdx/parser/jsonlikedict/test_file_parser.py | 5 +++-- tests/spdx/parser/jsonlikedict/test_package_parser.py | 2 +- tests/spdx/parser/jsonlikedict/test_snippet_parser.py | 2 +- .../spdx/validation/test_license_expression_validator.py | 2 +- 10 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/spdx/writer/rdf/file_writer.py b/src/spdx/writer/rdf/file_writer.py index 547f5d5a2..d8b822e7e 100644 --- a/src/spdx/writer/rdf/file_writer.py +++ b/src/spdx/writer/rdf/file_writer.py @@ -16,8 +16,7 @@ from spdx.writer.casing_tools import snake_case_to_camel_case from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph from spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion -from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value, add_literal_or_no_assertion_or_none, \ - add_namespace_to_spdx_id +from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value, add_namespace_to_spdx_id def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str, diff --git a/src/spdx/writer/rdf/package_writer.py b/src/spdx/writer/rdf/package_writer.py index a93b54787..7af8e3878 100644 --- a/src/spdx/writer/rdf/package_writer.py +++ b/src/spdx/writer/rdf/package_writer.py @@ -11,8 +11,7 @@ from typing import Dict from rdflib import Graph, URIRef, RDF, Literal, XSD, BNode, DOAP, RDFS -from spdx.writer.rdf.license_expression_writer import add_license_expression_to_graph, \ - add_license_expression_or_none_or_no_assertion +from spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion from spdx.writer.casing_tools import snake_case_to_camel_case from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph @@ -46,7 +45,7 @@ def add_package_information_to_graph(package: Package, graph: Graph, doc_namespa package.license_concluded, doc_namespace) add_license_expression_or_none_or_no_assertion(graph, package_resource, spdx_namespace.licenseInfoFromFiles, - package.license_info_from_files, doc_namespace) + package.license_info_from_files, doc_namespace) add_license_expression_or_none_or_no_assertion(graph, package_resource, spdx_namespace.licenseDeclared, package.license_declared, doc_namespace) add_literal_value(graph, package_resource, spdx_namespace.licenseComments, package.license_comment) diff --git a/src/spdx/writer/rdf/snippet_writer.py b/src/spdx/writer/rdf/snippet_writer.py index 87f20492f..dc061cedc 100644 --- a/src/spdx/writer/rdf/snippet_writer.py +++ b/src/spdx/writer/rdf/snippet_writer.py @@ -13,8 +13,7 @@ from rdflib import Graph, URIRef, RDF, RDFS, Literal, BNode from spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion -from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_or_no_assertion_or_none, add_literal_value, \ - add_namespace_to_spdx_id, pointer_namespace +from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value, add_namespace_to_spdx_id, pointer_namespace from spdx.model.snippet import Snippet @@ -33,9 +32,9 @@ def add_snippet_information_to_graph(snippet: Snippet, graph: Graph, doc_namespa add_range_to_graph(graph, snippet_resource, snippet.line_range, snippet_from_file_ref, pointer_namespace.LineCharPointer) add_license_expression_or_none_or_no_assertion(graph, snippet_resource, spdx_namespace.licenseConcluded, - snippet.license_concluded, doc_namespace) + snippet.license_concluded, doc_namespace) add_license_expression_or_none_or_no_assertion(graph, snippet_resource, spdx_namespace.licenseInfoInSnippet, - snippet.license_info_in_snippet, doc_namespace) + snippet.license_info_in_snippet, doc_namespace) add_literal_value(graph, snippet_resource, spdx_namespace.licenseComments, snippet.license_comment) add_literal_value(graph, snippet_resource, spdx_namespace.copyrightText, snippet.copyright_text) add_literal_value(graph, snippet_resource, RDFS.comment, snippet.comment) @@ -49,7 +48,7 @@ def add_range_to_graph(graph: Graph, snippet_resource: URIRef, range_information start_end_pointer = BNode() graph.add((start_end_pointer, RDF.type, pointer_namespace.StartEndPointer)) for (predicate, value) in [(pointer_namespace.startPointer, range_information[0]), - (pointer_namespace.endPointer, range_information[1])]: + (pointer_namespace.endPointer, range_information[1])]: pointer_node = BNode() graph.add((pointer_node, RDF.type, pointer_class)) graph.add((start_end_pointer, predicate, pointer_node)) diff --git a/tests/spdx/jsonschema/test_file_converter.py b/tests/spdx/jsonschema/test_file_converter.py index b169a9d44..d1a08282c 100644 --- a/tests/spdx/jsonschema/test_file_converter.py +++ b/tests/spdx/jsonschema/test_file_converter.py @@ -23,7 +23,7 @@ from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.model.document import Document from spdx.model.file import File, FileType -from license_expression import LicenseExpression, Licensing +from license_expression import Licensing from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING from spdx.model.spdx_none import SpdxNone, SPDX_NONE_STRING from tests.spdx.fixtures import creation_info_fixture, file_fixture, annotation_fixture, document_fixture diff --git a/tests/spdx/jsonschema/test_package_converter.py b/tests/spdx/jsonschema/test_package_converter.py index fae81f6db..eb88da3eb 100644 --- a/tests/spdx/jsonschema/test_package_converter.py +++ b/tests/spdx/jsonschema/test_package_converter.py @@ -22,7 +22,7 @@ from spdx.model.annotation import Annotation, AnnotationType from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.model.document import Document -from license_expression import LicenseExpression, Licensing +from license_expression import Licensing from spdx.model.package import Package, PackageVerificationCode, PackagePurpose from spdx.model.relationship import RelationshipType from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING diff --git a/tests/spdx/jsonschema/test_snippet_converter.py b/tests/spdx/jsonschema/test_snippet_converter.py index 082cd1a90..9a12b34f4 100644 --- a/tests/spdx/jsonschema/test_snippet_converter.py +++ b/tests/spdx/jsonschema/test_snippet_converter.py @@ -21,7 +21,7 @@ from spdx.model.actor import Actor, ActorType from spdx.model.annotation import Annotation, AnnotationType from spdx.model.document import Document -from license_expression import LicenseExpression, Licensing +from license_expression import Licensing from spdx.model.snippet import Snippet from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING from spdx.model.spdx_none import SpdxNone, SPDX_NONE_STRING diff --git a/tests/spdx/parser/jsonlikedict/test_file_parser.py b/tests/spdx/parser/jsonlikedict/test_file_parser.py index 3050dcaec..a4faa3ae8 100644 --- a/tests/spdx/parser/jsonlikedict/test_file_parser.py +++ b/tests/spdx/parser/jsonlikedict/test_file_parser.py @@ -14,7 +14,7 @@ from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.model.file import FileType -from license_expression import LicenseExpression, Licensing +from license_expression import Licensing from spdx.parser.error import SPDXParsingError from spdx.parser.jsonlikedict.dict_parsing_functions import parse_list_of_elements from spdx.parser.jsonlikedict.file_parser import FileParser @@ -126,4 +126,5 @@ def test_parse_invalid_file_types(): with pytest.raises(SPDXParsingError) as err: file_parser.parse_file_types(file_types_list) - TestCase().assertCountEqual(err.value.get_messages(), ["Error while parsing FileType: ['Invalid FileType: APPLICAON']"]) + TestCase().assertCountEqual(err.value.get_messages(), + ["Error while parsing FileType: ['Invalid FileType: APPLICAON']"]) diff --git a/tests/spdx/parser/jsonlikedict/test_package_parser.py b/tests/spdx/parser/jsonlikedict/test_package_parser.py index fa9408dd7..a9813f2fc 100644 --- a/tests/spdx/parser/jsonlikedict/test_package_parser.py +++ b/tests/spdx/parser/jsonlikedict/test_package_parser.py @@ -15,7 +15,7 @@ from spdx.model.actor import Actor, ActorType from spdx.model.checksum import Checksum, ChecksumAlgorithm -from license_expression import LicenseExpression, Licensing +from license_expression import Licensing from spdx.model.package import PackageVerificationCode, ExternalPackageRef, ExternalPackageRefCategory, PackagePurpose from spdx.parser.error import SPDXParsingError from spdx.parser.jsonlikedict.dict_parsing_functions import parse_list_of_elements diff --git a/tests/spdx/parser/jsonlikedict/test_snippet_parser.py b/tests/spdx/parser/jsonlikedict/test_snippet_parser.py index 9bbe71a26..2663b6e61 100644 --- a/tests/spdx/parser/jsonlikedict/test_snippet_parser.py +++ b/tests/spdx/parser/jsonlikedict/test_snippet_parser.py @@ -12,7 +12,7 @@ import pytest -from license_expression import LicenseExpression, Licensing +from license_expression import Licensing from spdx.parser.error import SPDXParsingError from spdx.parser.jsonlikedict.snippet_parser import SnippetParser diff --git a/tests/spdx/validation/test_license_expression_validator.py b/tests/spdx/validation/test_license_expression_validator.py index 6167fd50a..458a98acc 100644 --- a/tests/spdx/validation/test_license_expression_validator.py +++ b/tests/spdx/validation/test_license_expression_validator.py @@ -11,7 +11,7 @@ from typing import List -from license_expression import LicenseExpression, Licensing +from license_expression import Licensing from spdx.validation.license_expression_validator import validate_license_expression from spdx.validation.validation_message import ValidationMessage From 6afd3c6fe848a9c35a0e5c5a215410da2b21b629 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 30 Jan 2023 15:25:45 +0100 Subject: [PATCH 201/362] [issue-407] refactor Signed-off-by: Meret Behrens --- .../writer/rdf/license_expression_writer.py | 66 +++++++++---------- .../rdf/test_license_expression_writer.py | 12 ++-- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/spdx/writer/rdf/license_expression_writer.py b/src/spdx/writer/rdf/license_expression_writer.py index fe6da0020..b8c9988f6 100644 --- a/src/spdx/writer/rdf/license_expression_writer.py +++ b/src/spdx/writer/rdf/license_expression_writer.py @@ -22,40 +22,60 @@ from spdx.writer.rdf.writer_utils import spdx_namespace -def add_license_expression_to_graph(graph: Graph, license_expression: Expression, parent_resource: Node, - doc_namespace: str, predicate: Node): +def add_license_expression_or_none_or_no_assertion(graph: Graph, parent: Node, predicate: Node, value: Union[ + List[LicenseExpression], LicenseExpression, SpdxNoAssertion, SpdxNone], doc_namespace: str): + if isinstance(value, SpdxNoAssertion): + graph.add((parent, predicate, spdx_namespace.noassertion)) + return + if isinstance(value, SpdxNone): + graph.add((parent, predicate, spdx_namespace.none)) + return + if isinstance(value, list): + for license_expression in value: + add_license_expression_to_graph(graph, parent, predicate, license_expression, doc_namespace) + if isinstance(value, LicenseExpression): + add_license_expression_to_graph(graph, parent, predicate, value, doc_namespace) + + +def add_license_expression_to_graph(graph: Graph, subject: Node, predicate: Node, + license_expression: Expression, doc_namespace: str): if isinstance(license_expression, AND): member_node = BNode() graph.add((member_node, RDF.type, spdx_namespace.ConjunctiveLicenseSet)) - graph.add((parent_resource, predicate, member_node)) + graph.add((subject, predicate, member_node)) for arg in license_expression.args: - add_license_expression_to_graph(graph, arg, member_node, doc_namespace, spdx_namespace.member) + add_license_expression_to_graph(graph, member_node, spdx_namespace.member, arg, doc_namespace) if isinstance(license_expression, OR): member_node = BNode() graph.add((member_node, RDF.type, spdx_namespace.DisjunctiveLicenseSet)) - graph.add((parent_resource, predicate, member_node)) + graph.add((subject, predicate, member_node)) for arg in license_expression.args: - add_license_expression_to_graph(graph, arg, member_node, doc_namespace, spdx_namespace.member) + add_license_expression_to_graph(graph, member_node, spdx_namespace.member, arg, doc_namespace) if isinstance(license_expression, LicenseWithExceptionSymbol): member_node = BNode() graph.add((member_node, RDF.type, spdx_namespace.WithExceptionOperator)) - graph.add((parent_resource, predicate, member_node)) + graph.add((subject, predicate, member_node)) - add_license_expression_to_graph(graph, license_expression.license_symbol, member_node, doc_namespace, - spdx_namespace.member) + add_license_expression_to_graph(graph, member_node, spdx_namespace.member, license_expression.license_symbol, + doc_namespace) add_license_exception_to_graph(graph, license_expression.exception_symbol, member_node) if isinstance(license_expression, LicenseSymbol): - if check_if_license_or_exception_is_on_spdx_licensing_list(license_expression): + if license_or_exception_is_on_spdx_licensing_list(license_expression): graph.add( - (parent_resource, predicate, URIRef(f"http://spdx.org/licenses/{license_expression}"))) + (subject, predicate, URIRef(f"http://spdx.org/licenses/{license_expression}"))) else: # assuming that the license expression is a LicenseRef to an instance of ExtractedLicensingInfo - graph.add((parent_resource, predicate, URIRef(f"{doc_namespace}#{license_expression}"))) + graph.add((subject, predicate, URIRef(f"{doc_namespace}#{license_expression}"))) + + +def license_or_exception_is_on_spdx_licensing_list(license_symbol: LicenseSymbol) -> bool: + symbol_info: ExpressionInfo = get_spdx_licensing().validate(license_symbol) + return not symbol_info.errors def add_license_exception_to_graph(graph: Graph, license_exception: LicenseSymbol, member_node: Node): - if check_if_license_or_exception_is_on_spdx_licensing_list(license_exception): + if license_or_exception_is_on_spdx_licensing_list(license_exception): exception_node = URIRef(f"http://spdx.org/licenses/{license_exception}") graph.add((member_node, spdx_namespace.licenseException, exception_node)) else: @@ -64,23 +84,3 @@ def add_license_exception_to_graph(graph: Graph, license_exception: LicenseSymbo graph.add((member_node, spdx_namespace.licenseException, exception_node)) graph.add((exception_node, RDF.type, spdx_namespace.LicenseException)) - - -def check_if_license_or_exception_is_on_spdx_licensing_list(license_symbol: LicenseSymbol) -> bool: - symbol_info: ExpressionInfo = get_spdx_licensing().validate(license_symbol) - return not symbol_info.errors - - -def add_license_expression_or_none_or_no_assertion(graph: Graph, parent: Node, predicate: Node, value: Union[ - List[LicenseExpression], LicenseExpression, SpdxNoAssertion, SpdxNone], doc_namespace: str): - if isinstance(value, SpdxNoAssertion): - graph.add((parent, predicate, spdx_namespace.noassertion)) - return - if isinstance(value, SpdxNone): - graph.add((parent, predicate, spdx_namespace.none)) - return - if isinstance(value, list): - for license_expression in value: - add_license_expression_to_graph(graph, license_expression, parent, doc_namespace, predicate) - if isinstance(value, LicenseExpression): - add_license_expression_to_graph(graph, value, parent, doc_namespace, predicate) diff --git a/tests/spdx/writer/rdf/test_license_expression_writer.py b/tests/spdx/writer/rdf/test_license_expression_writer.py index f9d1b31f5..54da757bc 100644 --- a/tests/spdx/writer/rdf/test_license_expression_writer.py +++ b/tests/spdx/writer/rdf/test_license_expression_writer.py @@ -20,8 +20,8 @@ def test_add_conjunctive_license_set_to_graph(): graph = Graph() license_expression = get_spdx_licensing().parse("MIT AND GPL-2.0") - add_license_expression_to_graph(graph, license_expression, URIRef("anyURI"), "https://namespace", - spdx_namespace.licenseConcluded) + add_license_expression_to_graph(graph, URIRef("anyURI"), spdx_namespace.licenseConcluded, license_expression, + "https://namespace") assert (None, RDF.type, spdx_namespace.ConjunctiveLicenseSet) in graph assert (None, spdx_namespace.member, URIRef("http://spdx.org/licenses/MIT")) in graph @@ -32,8 +32,8 @@ def test_add_disjunctive_license_set_to_graph(): graph = Graph() license_expression = get_spdx_licensing().parse("MIT OR GPL-2.0") - add_license_expression_to_graph(graph, license_expression, URIRef("anyURI"), "https://namespace", - spdx_namespace.licenseConcluded) + add_license_expression_to_graph(graph, URIRef("anyURI"), spdx_namespace.licenseConcluded, license_expression, + "https://namespace") assert (None, RDF.type, spdx_namespace.DisjunctiveLicenseSet) in graph assert (None, spdx_namespace.member, URIRef("http://spdx.org/licenses/MIT")) in graph @@ -50,8 +50,8 @@ def test_license_exception_to_graph(license_with_exception, expected_triple): graph = Graph() license_expression = get_spdx_licensing().parse(license_with_exception) - add_license_expression_to_graph(graph, license_expression, URIRef("anyURI"), "https://namespace", - spdx_namespace.licenseConcluded) + add_license_expression_to_graph(graph, URIRef("anyURI"), spdx_namespace.licenseConcluded, license_expression, + "https://namespace") graph.serialize("rdf_with_exception.rdf.xml", "pretty-xml", max_depth=100) From 96617b9fde75a0a90b30132bcf4227d98dd39695 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 30 Jan 2023 16:54:37 +0100 Subject: [PATCH 202/362] [issue-407] delete debug output from development Signed-off-by: Meret Behrens --- tests/spdx/writer/rdf/test_license_expression_writer.py | 2 -- tests/spdx/writer/rdf/test_package_writer.py | 1 - 2 files changed, 3 deletions(-) diff --git a/tests/spdx/writer/rdf/test_license_expression_writer.py b/tests/spdx/writer/rdf/test_license_expression_writer.py index 54da757bc..3f119373c 100644 --- a/tests/spdx/writer/rdf/test_license_expression_writer.py +++ b/tests/spdx/writer/rdf/test_license_expression_writer.py @@ -53,8 +53,6 @@ def test_license_exception_to_graph(license_with_exception, expected_triple): add_license_expression_to_graph(graph, URIRef("anyURI"), spdx_namespace.licenseConcluded, license_expression, "https://namespace") - graph.serialize("rdf_with_exception.rdf.xml", "pretty-xml", max_depth=100) - assert (None, RDF.type, spdx_namespace.WithExceptionOperator) in graph assert (None, spdx_namespace.member, URIRef("http://spdx.org/licenses/MIT")) in graph assert (None, spdx_namespace.licenseException, None) in graph diff --git a/tests/spdx/writer/rdf/test_package_writer.py b/tests/spdx/writer/rdf/test_package_writer.py index c89c651f0..cb922e960 100644 --- a/tests/spdx/writer/rdf/test_package_writer.py +++ b/tests/spdx/writer/rdf/test_package_writer.py @@ -25,7 +25,6 @@ def test_add_package_information_to_graph(): add_package_information_to_graph(package, graph, "anyURI", {}) - graph.serialize("package.rdf.xml", "pretty-xml", max_depth =100) assert (URIRef("anyURI#SPDXRef-Package"), RDF.type, spdx_namespace.Package) in graph assert (None, spdx_namespace.name, Literal("packageName")) in graph assert (None, spdx_namespace.versionInfo, Literal("12.2")) in graph From c5ad3852bf4c181a6059dde1802165c735fb3afc Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 31 Jan 2023 11:31:26 +0100 Subject: [PATCH 203/362] [issue-407, review] make global constants uppercase Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/annotation_writer.py | 14 ++-- src/spdx/writer/rdf/checksum_writer.py | 12 ++-- src/spdx/writer/rdf/creation_info_writer.py | 20 +++--- .../rdf/external_document_ref_writer.py | 8 +-- .../rdf/extracted_licensing_info_writer.py | 12 ++-- src/spdx/writer/rdf/file_writer.py | 24 +++---- .../writer/rdf/license_expression_writer.py | 26 +++---- src/spdx/writer/rdf/package_writer.py | 68 +++++++++---------- src/spdx/writer/rdf/rdf_writer.py | 6 +- src/spdx/writer/rdf/relationship_writer.py | 16 ++--- src/spdx/writer/rdf/snippet_writer.py | 38 +++++------ src/spdx/writer/rdf/writer_utils.py | 8 +-- .../spdx/writer/rdf/test_annotation_writer.py | 10 +-- tests/spdx/writer/rdf/test_checksum_writer.py | 42 ++++++------ .../writer/rdf/test_creation_info_writer.py | 20 +++--- .../rdf/test_external_document_ref_writer.py | 12 ++-- .../test_extracted_licensing_info_writer.py | 12 ++-- tests/spdx/writer/rdf/test_file_writer.py | 24 +++---- .../rdf/test_license_expression_writer.py | 30 ++++---- tests/spdx/writer/rdf/test_package_writer.py | 64 ++++++++--------- .../writer/rdf/test_relationship_writer.py | 6 +- tests/spdx/writer/rdf/test_snippet_writer.py | 32 ++++----- 22 files changed, 252 insertions(+), 252 deletions(-) diff --git a/src/spdx/writer/rdf/annotation_writer.py b/src/spdx/writer/rdf/annotation_writer.py index ec67d5504..c28add860 100644 --- a/src/spdx/writer/rdf/annotation_writer.py +++ b/src/spdx/writer/rdf/annotation_writer.py @@ -15,19 +15,19 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.model.annotation import Annotation from spdx.writer.casing_tools import snake_case_to_camel_case -from spdx.writer.rdf.writer_utils import spdx_namespace, add_namespace_to_spdx_id +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_namespace_to_spdx_id def add_annotation_info_to_graph(annotation: Annotation, graph: Graph, doc_namespace: str, external_doc_ref_to_namespace: Dict[str, str]): annotation_resource = URIRef(add_namespace_to_spdx_id(annotation.spdx_id, doc_namespace, external_doc_ref_to_namespace)) annotation_node = BNode() - graph.add((annotation_node, RDF.type, spdx_namespace.Annotation)) - graph.add((annotation_node, spdx_namespace.annotationType, - spdx_namespace[f"annotationType_{snake_case_to_camel_case(annotation.annotation_type.name)}"])) - graph.add((annotation_node, spdx_namespace.annotator, Literal(annotation.annotator.to_serialized_string()))) + graph.add((annotation_node, RDF.type, SPDX_NAMESPACE.Annotation)) + graph.add((annotation_node, SPDX_NAMESPACE.annotationType, + SPDX_NAMESPACE[f"annotationType_{snake_case_to_camel_case(annotation.annotation_type.name)}"])) + graph.add((annotation_node, SPDX_NAMESPACE.annotator, Literal(annotation.annotator.to_serialized_string()))) graph.add( - (annotation_node, spdx_namespace.annotationDate, Literal(datetime_to_iso_string(annotation.annotation_date)))) + (annotation_node, SPDX_NAMESPACE.annotationDate, Literal(datetime_to_iso_string(annotation.annotation_date)))) graph.add((annotation_node, RDFS.comment, Literal(annotation.annotation_comment))) - graph.add((annotation_resource, spdx_namespace.annotation, annotation_node)) + graph.add((annotation_resource, SPDX_NAMESPACE.annotation, annotation_node)) diff --git a/src/spdx/writer/rdf/checksum_writer.py b/src/spdx/writer/rdf/checksum_writer.py index 75b4fdd2b..599a82377 100644 --- a/src/spdx/writer/rdf/checksum_writer.py +++ b/src/spdx/writer/rdf/checksum_writer.py @@ -11,16 +11,16 @@ from rdflib import Graph, URIRef, BNode, RDF, Literal from spdx.model.checksum import Checksum, ChecksumAlgorithm -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE def add_checksum_information_to_graph(checksum: Checksum, graph: Graph, parent_node: URIRef): checksum_node = BNode() - graph.add((checksum_node, RDF.type, spdx_namespace.Checksum)) - graph.add((checksum_node, spdx_namespace.algorithm, algorithm_to_rdf_string(checksum.algorithm))) - graph.add((checksum_node, spdx_namespace.checksumValue, Literal(checksum.value))) + graph.add((checksum_node, RDF.type, SPDX_NAMESPACE.Checksum)) + graph.add((checksum_node, SPDX_NAMESPACE.algorithm, algorithm_to_rdf_string(checksum.algorithm))) + graph.add((checksum_node, SPDX_NAMESPACE.checksumValue, Literal(checksum.value))) - graph.add((parent_node, spdx_namespace.checksum, checksum_node)) + graph.add((parent_node, SPDX_NAMESPACE.checksum, checksum_node)) def algorithm_to_rdf_string(algorithm: ChecksumAlgorithm) -> URIRef: if "BLAKE2B" in algorithm.name: @@ -28,4 +28,4 @@ def algorithm_to_rdf_string(algorithm: ChecksumAlgorithm) -> URIRef: else: algorithm_rdf_string = algorithm.name.lower() - return spdx_namespace[f"checksumAlgorithm_{algorithm_rdf_string}"] + return SPDX_NAMESPACE[f"checksumAlgorithm_{algorithm_rdf_string}"] diff --git a/src/spdx/writer/rdf/creation_info_writer.py b/src/spdx/writer/rdf/creation_info_writer.py index 97f1386d0..b20163593 100644 --- a/src/spdx/writer/rdf/creation_info_writer.py +++ b/src/spdx/writer/rdf/creation_info_writer.py @@ -13,29 +13,29 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.model.document import CreationInfo from spdx.writer.rdf.external_document_ref_writer import add_external_document_ref_to_graph -from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_literal_value def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): doc_node = URIRef(f"{creation_info.document_namespace}#{creation_info.spdx_id}") - graph.add((doc_node, RDF.type, spdx_namespace.SpdxDocument)) - graph.add((doc_node, spdx_namespace.specVersion, Literal(creation_info.spdx_version))) - graph.add((doc_node, spdx_namespace.dataLicense, URIRef(f"http://spdx.org/licenses/{creation_info.data_license}"))) - graph.add((doc_node, spdx_namespace.name, Literal(creation_info.name))) + graph.add((doc_node, RDF.type, SPDX_NAMESPACE.SpdxDocument)) + graph.add((doc_node, SPDX_NAMESPACE.specVersion, Literal(creation_info.spdx_version))) + graph.add((doc_node, SPDX_NAMESPACE.dataLicense, URIRef(f"http://spdx.org/licenses/{creation_info.data_license}"))) + graph.add((doc_node, SPDX_NAMESPACE.name, Literal(creation_info.name))) add_literal_value(graph, doc_node, RDFS.comment, creation_info.document_comment) creation_info_node = BNode() - graph.add((creation_info_node, RDF.type, spdx_namespace.CreationInfo)) + graph.add((creation_info_node, RDF.type, SPDX_NAMESPACE.CreationInfo)) - graph.add((creation_info_node, spdx_namespace.created, Literal(datetime_to_iso_string(creation_info.created)))) + graph.add((creation_info_node, SPDX_NAMESPACE.created, Literal(datetime_to_iso_string(creation_info.created)))) for creator in creation_info.creators: - graph.add((creation_info_node, spdx_namespace.creator, Literal(creator.to_serialized_string()))) + graph.add((creation_info_node, SPDX_NAMESPACE.creator, Literal(creator.to_serialized_string()))) - add_literal_value(graph, creation_info_node, spdx_namespace.licenseListVersion, creation_info.license_list_version) + add_literal_value(graph, creation_info_node, SPDX_NAMESPACE.licenseListVersion, creation_info.license_list_version) add_literal_value(graph, creation_info_node, RDFS.comment, creation_info.creator_comment) - graph.add((doc_node, spdx_namespace.creationInfo, creation_info_node)) + graph.add((doc_node, SPDX_NAMESPACE.creationInfo, creation_info_node)) for external_document_ref in creation_info.external_document_refs: add_external_document_ref_to_graph(external_document_ref, graph, doc_node, creation_info.document_namespace) diff --git a/src/spdx/writer/rdf/external_document_ref_writer.py b/src/spdx/writer/rdf/external_document_ref_writer.py index 084e47ffc..299467c26 100644 --- a/src/spdx/writer/rdf/external_document_ref_writer.py +++ b/src/spdx/writer/rdf/external_document_ref_writer.py @@ -12,14 +12,14 @@ from spdx.model.external_document_ref import ExternalDocumentRef from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE def add_external_document_ref_to_graph(external_document_ref: ExternalDocumentRef, graph: Graph, doc_node: URIRef, doc_namespace: str): external_document_ref_resource = URIRef(f"{doc_namespace}#{external_document_ref.document_ref_id}") - graph.add((external_document_ref_resource, RDF.type, spdx_namespace.ExternalDocumentRef)) - graph.add((external_document_ref_resource, spdx_namespace.spdxDocument, URIRef(external_document_ref.document_uri))) + graph.add((external_document_ref_resource, RDF.type, SPDX_NAMESPACE.ExternalDocumentRef)) + graph.add((external_document_ref_resource, SPDX_NAMESPACE.spdxDocument, URIRef(external_document_ref.document_uri))) add_checksum_information_to_graph(external_document_ref.checksum, graph, external_document_ref_resource) - graph.add((doc_node, spdx_namespace.externalDocumentRef, external_document_ref_resource)) + graph.add((doc_node, SPDX_NAMESPACE.externalDocumentRef, external_document_ref_resource)) diff --git a/src/spdx/writer/rdf/extracted_licensing_info_writer.py b/src/spdx/writer/rdf/extracted_licensing_info_writer.py index 3133387bc..951cd4b55 100644 --- a/src/spdx/writer/rdf/extracted_licensing_info_writer.py +++ b/src/spdx/writer/rdf/extracted_licensing_info_writer.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from rdflib import Graph, URIRef, RDF, BNode, RDFS, Literal -from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value, add_literal_or_no_assertion +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_literal_value, add_literal_or_no_assertion from spdx.model.extracted_licensing_info import ExtractedLicensingInfo @@ -18,17 +18,17 @@ def add_extracted_licensing_info_to_graph(extracted_licensing_info: ExtractedLic doc_namespace: str): if extracted_licensing_info.license_id: extracted_licensing_info_resource = URIRef(f"{doc_namespace}#{extracted_licensing_info.license_id}") - graph.add((extracted_licensing_info_resource, RDF.type, spdx_namespace.ExtractedLicensingInfo)) + graph.add((extracted_licensing_info_resource, RDF.type, SPDX_NAMESPACE.ExtractedLicensingInfo)) else: extracted_licensing_info_resource = BNode() - add_literal_value(graph, extracted_licensing_info_resource, spdx_namespace.licenseId, + add_literal_value(graph, extracted_licensing_info_resource, SPDX_NAMESPACE.licenseId, extracted_licensing_info.license_id) - add_literal_value(graph, extracted_licensing_info_resource, spdx_namespace.extractedText, + add_literal_value(graph, extracted_licensing_info_resource, SPDX_NAMESPACE.extractedText, extracted_licensing_info.extracted_text) - add_literal_or_no_assertion(graph, extracted_licensing_info_resource, spdx_namespace.name, + add_literal_or_no_assertion(graph, extracted_licensing_info_resource, SPDX_NAMESPACE.name, extracted_licensing_info.license_name) for cross_reference in extracted_licensing_info.cross_references: graph.add((extracted_licensing_info_resource, RDFS.seeAlso, Literal(cross_reference))) add_literal_value(graph, extracted_licensing_info_resource, RDFS.comment, extracted_licensing_info.comment) - graph.add((doc_node, spdx_namespace.hasExtractedLicensingInfo, extracted_licensing_info_resource)) + graph.add((doc_node, SPDX_NAMESPACE.hasExtractedLicensingInfo, extracted_licensing_info_resource)) diff --git a/src/spdx/writer/rdf/file_writer.py b/src/spdx/writer/rdf/file_writer.py index d8b822e7e..e0d7b5aab 100644 --- a/src/spdx/writer/rdf/file_writer.py +++ b/src/spdx/writer/rdf/file_writer.py @@ -16,31 +16,31 @@ from spdx.writer.casing_tools import snake_case_to_camel_case from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph from spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion -from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value, add_namespace_to_spdx_id +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_literal_value, add_namespace_to_spdx_id def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str, external_doc_ref_to_namespace: Dict[str, str]): file_resource = URIRef(add_namespace_to_spdx_id(file.spdx_id, doc_namespace, external_doc_ref_to_namespace)) - graph.add((file_resource, RDF.type, spdx_namespace.File)) - graph.add((file_resource, spdx_namespace.fileName, Literal(file.name))) + graph.add((file_resource, RDF.type, SPDX_NAMESPACE.File)) + graph.add((file_resource, SPDX_NAMESPACE.fileName, Literal(file.name))) for file_type in file.file_type: - graph.add((file_resource, spdx_namespace.fileType, - spdx_namespace[f"fileType_{snake_case_to_camel_case(file_type.name)}"])) + graph.add((file_resource, SPDX_NAMESPACE.fileType, + SPDX_NAMESPACE[f"fileType_{snake_case_to_camel_case(file_type.name)}"])) for checksum in file.checksums: add_checksum_information_to_graph(checksum, graph, file_resource) - add_license_expression_or_none_or_no_assertion(graph, file_resource, spdx_namespace.licenseConcluded, + add_license_expression_or_none_or_no_assertion(graph, file_resource, SPDX_NAMESPACE.licenseConcluded, file.license_concluded, doc_namespace) - add_license_expression_or_none_or_no_assertion(graph, file_resource, spdx_namespace.licenseInfoInFile, + add_license_expression_or_none_or_no_assertion(graph, file_resource, SPDX_NAMESPACE.licenseInfoInFile, file.license_info_in_file, doc_namespace) - add_literal_value(graph, file_resource, spdx_namespace.licenseComments, file.license_comment) - add_literal_value(graph, file_resource, spdx_namespace.copyrightText, file.copyright_text) + add_literal_value(graph, file_resource, SPDX_NAMESPACE.licenseComments, file.license_comment) + add_literal_value(graph, file_resource, SPDX_NAMESPACE.copyrightText, file.copyright_text) add_literal_value(graph, file_resource, RDFS.comment, file.comment) - add_literal_value(graph, file_resource, spdx_namespace.noticeText, file.notice) + add_literal_value(graph, file_resource, SPDX_NAMESPACE.noticeText, file.notice) for contributor in file.contributors: - graph.add((file_resource, spdx_namespace.fileContributor, Literal(contributor))) + graph.add((file_resource, SPDX_NAMESPACE.fileContributor, Literal(contributor))) for attribution_text in file.attribution_texts: - graph.add((file_resource, spdx_namespace.attributionText, Literal(attribution_text))) + graph.add((file_resource, SPDX_NAMESPACE.attributionText, Literal(attribution_text))) diff --git a/src/spdx/writer/rdf/license_expression_writer.py b/src/spdx/writer/rdf/license_expression_writer.py index b8c9988f6..8aabd8eaf 100644 --- a/src/spdx/writer/rdf/license_expression_writer.py +++ b/src/spdx/writer/rdf/license_expression_writer.py @@ -19,16 +19,16 @@ from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE def add_license_expression_or_none_or_no_assertion(graph: Graph, parent: Node, predicate: Node, value: Union[ List[LicenseExpression], LicenseExpression, SpdxNoAssertion, SpdxNone], doc_namespace: str): if isinstance(value, SpdxNoAssertion): - graph.add((parent, predicate, spdx_namespace.noassertion)) + graph.add((parent, predicate, SPDX_NAMESPACE.noassertion)) return if isinstance(value, SpdxNone): - graph.add((parent, predicate, spdx_namespace.none)) + graph.add((parent, predicate, SPDX_NAMESPACE.none)) return if isinstance(value, list): for license_expression in value: @@ -41,22 +41,22 @@ def add_license_expression_to_graph(graph: Graph, subject: Node, predicate: Node license_expression: Expression, doc_namespace: str): if isinstance(license_expression, AND): member_node = BNode() - graph.add((member_node, RDF.type, spdx_namespace.ConjunctiveLicenseSet)) + graph.add((member_node, RDF.type, SPDX_NAMESPACE.ConjunctiveLicenseSet)) graph.add((subject, predicate, member_node)) for arg in license_expression.args: - add_license_expression_to_graph(graph, member_node, spdx_namespace.member, arg, doc_namespace) + add_license_expression_to_graph(graph, member_node, SPDX_NAMESPACE.member, arg, doc_namespace) if isinstance(license_expression, OR): member_node = BNode() - graph.add((member_node, RDF.type, spdx_namespace.DisjunctiveLicenseSet)) + graph.add((member_node, RDF.type, SPDX_NAMESPACE.DisjunctiveLicenseSet)) graph.add((subject, predicate, member_node)) for arg in license_expression.args: - add_license_expression_to_graph(graph, member_node, spdx_namespace.member, arg, doc_namespace) + add_license_expression_to_graph(graph, member_node, SPDX_NAMESPACE.member, arg, doc_namespace) if isinstance(license_expression, LicenseWithExceptionSymbol): member_node = BNode() - graph.add((member_node, RDF.type, spdx_namespace.WithExceptionOperator)) + graph.add((member_node, RDF.type, SPDX_NAMESPACE.WithExceptionOperator)) graph.add((subject, predicate, member_node)) - add_license_expression_to_graph(graph, member_node, spdx_namespace.member, license_expression.license_symbol, + add_license_expression_to_graph(graph, member_node, SPDX_NAMESPACE.member, license_expression.license_symbol, doc_namespace) add_license_exception_to_graph(graph, license_expression.exception_symbol, member_node) @@ -77,10 +77,10 @@ def license_or_exception_is_on_spdx_licensing_list(license_symbol: LicenseSymbol def add_license_exception_to_graph(graph: Graph, license_exception: LicenseSymbol, member_node: Node): if license_or_exception_is_on_spdx_licensing_list(license_exception): exception_node = URIRef(f"http://spdx.org/licenses/{license_exception}") - graph.add((member_node, spdx_namespace.licenseException, exception_node)) + graph.add((member_node, SPDX_NAMESPACE.licenseException, exception_node)) else: exception_node = BNode() - graph.add((exception_node, spdx_namespace.licenseExceptionId, Literal(license_exception))) - graph.add((member_node, spdx_namespace.licenseException, exception_node)) + graph.add((exception_node, SPDX_NAMESPACE.licenseExceptionId, Literal(license_exception))) + graph.add((member_node, SPDX_NAMESPACE.licenseException, exception_node)) - graph.add((exception_node, RDF.type, spdx_namespace.LicenseException)) + graph.add((exception_node, RDF.type, SPDX_NAMESPACE.LicenseException)) diff --git a/src/spdx/writer/rdf/package_writer.py b/src/spdx/writer/rdf/package_writer.py index 7af8e3878..c93a39622 100644 --- a/src/spdx/writer/rdf/package_writer.py +++ b/src/spdx/writer/rdf/package_writer.py @@ -18,52 +18,52 @@ from spdx.model.package import Package, PackageVerificationCode, ExternalPackageRef, \ CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES -from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value, add_literal_or_no_assertion_or_none, \ +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_literal_value, add_literal_or_no_assertion_or_none, \ add_datetime_to_graph, add_namespace_to_spdx_id def add_package_information_to_graph(package: Package, graph: Graph, doc_namespace: str, external_doc_ref_to_namespace: Dict[str, str]): package_resource = URIRef(add_namespace_to_spdx_id(package.spdx_id, doc_namespace, external_doc_ref_to_namespace)) - graph.add((package_resource, RDF.type, spdx_namespace.Package)) + graph.add((package_resource, RDF.type, SPDX_NAMESPACE.Package)) - graph.add((package_resource, spdx_namespace.name, Literal(package.name))) - add_literal_value(graph, package_resource, spdx_namespace.versionInfo, package.version) - add_literal_value(graph, package_resource, spdx_namespace.packageFileName, package.file_name) - add_literal_value(graph, package_resource, spdx_namespace.supplier, package.supplier) - add_literal_value(graph, package_resource, spdx_namespace.originator, package.originator) - add_literal_or_no_assertion_or_none(graph, package_resource, spdx_namespace.downloadLocation, + graph.add((package_resource, SPDX_NAMESPACE.name, Literal(package.name))) + add_literal_value(graph, package_resource, SPDX_NAMESPACE.versionInfo, package.version) + add_literal_value(graph, package_resource, SPDX_NAMESPACE.packageFileName, package.file_name) + add_literal_value(graph, package_resource, SPDX_NAMESPACE.supplier, package.supplier) + add_literal_value(graph, package_resource, SPDX_NAMESPACE.originator, package.originator) + add_literal_or_no_assertion_or_none(graph, package_resource, SPDX_NAMESPACE.downloadLocation, package.download_location) - graph.add((package_resource, spdx_namespace.filesAnalyzed, Literal(package.files_analyzed, datatype=XSD.boolean))) + graph.add((package_resource, SPDX_NAMESPACE.filesAnalyzed, Literal(package.files_analyzed, datatype=XSD.boolean))) add_package_verification_code_to_graph(package.verification_code, graph, package_resource) for checksum in package.checksums: add_checksum_information_to_graph(checksum, graph, package_resource) add_literal_value(graph, package_resource, DOAP.homepage, package.homepage) - add_literal_value(graph, package_resource, spdx_namespace.sourceInfo, package.source_info) - add_license_expression_or_none_or_no_assertion(graph, package_resource, spdx_namespace.licenseConcluded, + add_literal_value(graph, package_resource, SPDX_NAMESPACE.sourceInfo, package.source_info) + add_license_expression_or_none_or_no_assertion(graph, package_resource, SPDX_NAMESPACE.licenseConcluded, package.license_concluded, doc_namespace) - add_license_expression_or_none_or_no_assertion(graph, package_resource, spdx_namespace.licenseInfoFromFiles, + add_license_expression_or_none_or_no_assertion(graph, package_resource, SPDX_NAMESPACE.licenseInfoFromFiles, package.license_info_from_files, doc_namespace) - add_license_expression_or_none_or_no_assertion(graph, package_resource, spdx_namespace.licenseDeclared, + add_license_expression_or_none_or_no_assertion(graph, package_resource, SPDX_NAMESPACE.licenseDeclared, package.license_declared, doc_namespace) - add_literal_value(graph, package_resource, spdx_namespace.licenseComments, package.license_comment) - add_literal_value(graph, package_resource, spdx_namespace.copyrightText, package.copyright_text) - add_literal_value(graph, package_resource, spdx_namespace.summary, package.summary) - add_literal_value(graph, package_resource, spdx_namespace.description, package.description) + add_literal_value(graph, package_resource, SPDX_NAMESPACE.licenseComments, package.license_comment) + add_literal_value(graph, package_resource, SPDX_NAMESPACE.copyrightText, package.copyright_text) + add_literal_value(graph, package_resource, SPDX_NAMESPACE.summary, package.summary) + add_literal_value(graph, package_resource, SPDX_NAMESPACE.description, package.description) add_literal_value(graph, package_resource, RDFS.comment, package.comment) for external_reference in package.external_references: add_external_package_ref_to_graph(graph, external_reference, package_resource) for attribution_text in package.attribution_texts: - add_literal_value(graph, package_resource, spdx_namespace.attributionText, attribution_text) + add_literal_value(graph, package_resource, SPDX_NAMESPACE.attributionText, attribution_text) if package.primary_package_purpose: - graph.add((package_resource, spdx_namespace.primaryPackagePurpose, - spdx_namespace[f"purpose_{snake_case_to_camel_case(package.primary_package_purpose.name)}"])) + graph.add((package_resource, SPDX_NAMESPACE.primaryPackagePurpose, + SPDX_NAMESPACE[f"purpose_{snake_case_to_camel_case(package.primary_package_purpose.name)}"])) - add_datetime_to_graph(graph, package_resource, spdx_namespace.releaseDate, package.release_date) - add_datetime_to_graph(graph, package_resource, spdx_namespace.builtDate, package.built_date) - add_datetime_to_graph(graph, package_resource, spdx_namespace.validUntilDate, package.valid_until_date) + add_datetime_to_graph(graph, package_resource, SPDX_NAMESPACE.releaseDate, package.release_date) + add_datetime_to_graph(graph, package_resource, SPDX_NAMESPACE.builtDate, package.built_date) + add_datetime_to_graph(graph, package_resource, SPDX_NAMESPACE.validUntilDate, package.valid_until_date) def add_package_verification_code_to_graph(package_verification_code: PackageVerificationCode, graph: Graph, @@ -71,31 +71,31 @@ def add_package_verification_code_to_graph(package_verification_code: PackageVer if not package_verification_code: return package_verification_code_node = BNode() - graph.add((package_verification_code_node, RDF.type, spdx_namespace.PackageVerificationCode)) - graph.add((package_verification_code_node, spdx_namespace.packageVerificationCodeValue, + graph.add((package_verification_code_node, RDF.type, SPDX_NAMESPACE.PackageVerificationCode)) + graph.add((package_verification_code_node, SPDX_NAMESPACE.packageVerificationCodeValue, Literal(package_verification_code.value))) for excluded_file in package_verification_code.excluded_files: - graph.add((package_verification_code_node, spdx_namespace.packageVerificationCodeExcludedFile, + graph.add((package_verification_code_node, SPDX_NAMESPACE.packageVerificationCodeExcludedFile, Literal(excluded_file))) - graph.add((package_resource, spdx_namespace.packageVerificationCode, package_verification_code_node)) + graph.add((package_resource, SPDX_NAMESPACE.packageVerificationCode, package_verification_code_node)) def add_external_package_ref_to_graph(graph: Graph, external_package_ref: ExternalPackageRef, package_resource: URIRef): external_package_ref_node = BNode() - graph.add((external_package_ref_node, RDF.type, spdx_namespace.ExternalRef)) - graph.add((external_package_ref_node, spdx_namespace.referenceCategory, - spdx_namespace[f"referenceCategory_{snake_case_to_camel_case(external_package_ref.category.name)}"])) + graph.add((external_package_ref_node, RDF.type, SPDX_NAMESPACE.ExternalRef)) + graph.add((external_package_ref_node, SPDX_NAMESPACE.referenceCategory, + SPDX_NAMESPACE[f"referenceCategory_{snake_case_to_camel_case(external_package_ref.category.name)}"])) if external_package_ref.reference_type in CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES[external_package_ref.category]: - graph.add((external_package_ref_node, spdx_namespace.referenceType, + graph.add((external_package_ref_node, SPDX_NAMESPACE.referenceType, URIRef(f"http://spdx.org/rdf/references/{external_package_ref.reference_type}"))) else: - graph.add((external_package_ref_node, spdx_namespace.referenceType, + graph.add((external_package_ref_node, SPDX_NAMESPACE.referenceType, URIRef(external_package_ref.reference_type))) - graph.add((external_package_ref_node, spdx_namespace.referenceLocator, Literal(external_package_ref.locator))) + graph.add((external_package_ref_node, SPDX_NAMESPACE.referenceLocator, Literal(external_package_ref.locator))) if external_package_ref.comment: graph.add((external_package_ref_node, RDFS.comment, Literal(external_package_ref.comment))) - graph.add((package_resource, spdx_namespace.externalRef, external_package_ref_node)) + graph.add((package_resource, SPDX_NAMESPACE.externalRef, external_package_ref_node)) diff --git a/src/spdx/writer/rdf/rdf_writer.py b/src/spdx/writer/rdf/rdf_writer.py index b5c3cda08..d37ab7966 100644 --- a/src/spdx/writer/rdf/rdf_writer.py +++ b/src/spdx/writer/rdf/rdf_writer.py @@ -23,7 +23,7 @@ from spdx.writer.rdf.package_writer import add_package_information_to_graph from spdx.writer.rdf.relationship_writer import add_relationship_info_to_graph from spdx.writer.rdf.snippet_writer import add_snippet_information_to_graph -from spdx.writer.rdf.writer_utils import spdx_namespace, pointer_namespace +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, POINTER_NAMESPACE def write_document_to_file(document: Document, file_name: str, validate: bool): @@ -57,7 +57,7 @@ def write_document_to_file(document: Document, file_name: str, validate: bool): add_extracted_licensing_info_to_graph(extracted_licensing_info, graph, doc_node, doc_namespace) graph = to_isomorphic(graph) - graph.bind("spdx", spdx_namespace) + graph.bind("spdx", SPDX_NAMESPACE) graph.bind("doap", DOAP) - graph.bind("ptr", pointer_namespace) + graph.bind("ptr", POINTER_NAMESPACE) graph.serialize(file_name, "pretty-xml", encoding="UTF-8", max_depth=100) diff --git a/src/spdx/writer/rdf/relationship_writer.py b/src/spdx/writer/rdf/relationship_writer.py index 66cd43c0f..98ab6bf8e 100644 --- a/src/spdx/writer/rdf/relationship_writer.py +++ b/src/spdx/writer/rdf/relationship_writer.py @@ -16,24 +16,24 @@ from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone from spdx.writer.casing_tools import snake_case_to_camel_case -from spdx.writer.rdf.writer_utils import spdx_namespace, add_namespace_to_spdx_id +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_namespace_to_spdx_id def add_relationship_info_to_graph(relationship: Relationship, graph: Graph, doc_namespace: str, external_doc_ref_to_namespace: Dict[str, str]): relationship_node = BNode() - graph.add((relationship_node, RDF.type, spdx_namespace.Relationship)) - graph.add((relationship_node, spdx_namespace.relationshipType, - spdx_namespace[f"relationshipType_{snake_case_to_camel_case(relationship.relationship_type.name)}"])) + graph.add((relationship_node, RDF.type, SPDX_NAMESPACE.Relationship)) + graph.add((relationship_node, SPDX_NAMESPACE.relationshipType, + SPDX_NAMESPACE[f"relationshipType_{snake_case_to_camel_case(relationship.relationship_type.name)}"])) if isinstance(relationship.related_spdx_element_id, SpdxNone): - graph.add((relationship_node, spdx_namespace.relatedSpdxElement, spdx_namespace.none)) + graph.add((relationship_node, SPDX_NAMESPACE.relatedSpdxElement, SPDX_NAMESPACE.none)) elif isinstance(relationship.related_spdx_element_id, SpdxNoAssertion): - graph.add((relationship_node, spdx_namespace.relatedSpdxElement, spdx_namespace.noassertion)) + graph.add((relationship_node, SPDX_NAMESPACE.relatedSpdxElement, SPDX_NAMESPACE.noassertion)) else: - graph.add((relationship_node, spdx_namespace.relatedSpdxElement, + graph.add((relationship_node, SPDX_NAMESPACE.relatedSpdxElement, URIRef(add_namespace_to_spdx_id(relationship.related_spdx_element_id, doc_namespace, external_doc_ref_to_namespace)))) relationship_resource = URIRef( add_namespace_to_spdx_id(relationship.spdx_element_id, doc_namespace, external_doc_ref_to_namespace)) - graph.add((relationship_resource, spdx_namespace.relationship, relationship_node)) + graph.add((relationship_resource, SPDX_NAMESPACE.relationship, relationship_node)) diff --git a/src/spdx/writer/rdf/snippet_writer.py b/src/spdx/writer/rdf/snippet_writer.py index dc061cedc..139393b25 100644 --- a/src/spdx/writer/rdf/snippet_writer.py +++ b/src/spdx/writer/rdf/snippet_writer.py @@ -13,7 +13,7 @@ from rdflib import Graph, URIRef, RDF, RDFS, Literal, BNode from spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion -from spdx.writer.rdf.writer_utils import spdx_namespace, add_literal_value, add_namespace_to_spdx_id, pointer_namespace +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_literal_value, add_namespace_to_spdx_id, POINTER_NAMESPACE from spdx.model.snippet import Snippet @@ -21,41 +21,41 @@ def add_snippet_information_to_graph(snippet: Snippet, graph: Graph, doc_namespace: str, external_doc_ref_to_namespace: Dict[str, str]): snippet_resource = URIRef(add_namespace_to_spdx_id(snippet.spdx_id, doc_namespace, external_doc_ref_to_namespace)) - graph.add((snippet_resource, RDF.type, spdx_namespace.Snippet)) + graph.add((snippet_resource, RDF.type, SPDX_NAMESPACE.Snippet)) snippet_from_file_ref = URIRef( add_namespace_to_spdx_id(snippet.file_spdx_id, doc_namespace, external_doc_ref_to_namespace)) - graph.add((snippet_resource, spdx_namespace.snippetFromFile, + graph.add((snippet_resource, SPDX_NAMESPACE.snippetFromFile, snippet_from_file_ref)) add_range_to_graph(graph, snippet_resource, snippet.byte_range, snippet_from_file_ref, - pointer_namespace.ByteOffsetPointer) + POINTER_NAMESPACE.ByteOffsetPointer) add_range_to_graph(graph, snippet_resource, snippet.line_range, snippet_from_file_ref, - pointer_namespace.LineCharPointer) - add_license_expression_or_none_or_no_assertion(graph, snippet_resource, spdx_namespace.licenseConcluded, + POINTER_NAMESPACE.LineCharPointer) + add_license_expression_or_none_or_no_assertion(graph, snippet_resource, SPDX_NAMESPACE.licenseConcluded, snippet.license_concluded, doc_namespace) - add_license_expression_or_none_or_no_assertion(graph, snippet_resource, spdx_namespace.licenseInfoInSnippet, + add_license_expression_or_none_or_no_assertion(graph, snippet_resource, SPDX_NAMESPACE.licenseInfoInSnippet, snippet.license_info_in_snippet, doc_namespace) - add_literal_value(graph, snippet_resource, spdx_namespace.licenseComments, snippet.license_comment) - add_literal_value(graph, snippet_resource, spdx_namespace.copyrightText, snippet.copyright_text) + add_literal_value(graph, snippet_resource, SPDX_NAMESPACE.licenseComments, snippet.license_comment) + add_literal_value(graph, snippet_resource, SPDX_NAMESPACE.copyrightText, snippet.copyright_text) add_literal_value(graph, snippet_resource, RDFS.comment, snippet.comment) - add_literal_value(graph, snippet_resource, spdx_namespace.name, snippet.name) + add_literal_value(graph, snippet_resource, SPDX_NAMESPACE.name, snippet.name) for attribution_text in snippet.attribution_texts: - graph.add((snippet_resource, spdx_namespace.attributionText, Literal(attribution_text))) + graph.add((snippet_resource, SPDX_NAMESPACE.attributionText, Literal(attribution_text))) def add_range_to_graph(graph: Graph, snippet_resource: URIRef, range_information: Optional[Tuple[int, int]], snippet_from_file_ref: URIRef, pointer_class: URIRef): start_end_pointer = BNode() - graph.add((start_end_pointer, RDF.type, pointer_namespace.StartEndPointer)) - for (predicate, value) in [(pointer_namespace.startPointer, range_information[0]), - (pointer_namespace.endPointer, range_information[1])]: + graph.add((start_end_pointer, RDF.type, POINTER_NAMESPACE.StartEndPointer)) + for (predicate, value) in [(POINTER_NAMESPACE.startPointer, range_information[0]), + (POINTER_NAMESPACE.endPointer, range_information[1])]: pointer_node = BNode() graph.add((pointer_node, RDF.type, pointer_class)) graph.add((start_end_pointer, predicate, pointer_node)) - graph.add((pointer_node, pointer_namespace.reference, snippet_from_file_ref)) - if pointer_class == pointer_namespace.ByteOffsetPointer: - graph.add((pointer_node, pointer_namespace.offset, Literal(str(value)))) + graph.add((pointer_node, POINTER_NAMESPACE.reference, snippet_from_file_ref)) + if pointer_class == POINTER_NAMESPACE.ByteOffsetPointer: + graph.add((pointer_node, POINTER_NAMESPACE.offset, Literal(str(value)))) else: - graph.add((pointer_node, pointer_namespace.lineNumber, Literal(str(value)))) + graph.add((pointer_node, POINTER_NAMESPACE.lineNumber, Literal(str(value)))) - graph.add((snippet_resource, spdx_namespace.range, start_end_pointer)) + graph.add((snippet_resource, SPDX_NAMESPACE.range, start_end_pointer)) diff --git a/src/spdx/writer/rdf/writer_utils.py b/src/spdx/writer/rdf/writer_utils.py index 100eca8a4..13ffbf8e9 100644 --- a/src/spdx/writer/rdf/writer_utils.py +++ b/src/spdx/writer/rdf/writer_utils.py @@ -19,8 +19,8 @@ from spdx.model.spdx_none import SpdxNone from spdx.validation.spdx_id_validators import is_valid_internal_spdx_id -spdx_namespace = Namespace("http://spdx.org/rdf/terms#") -pointer_namespace = Namespace("http://www.w3.org/2009/pointers#") +SPDX_NAMESPACE = Namespace("http://spdx.org/rdf/terms#") +POINTER_NAMESPACE = Namespace("http://www.w3.org/2009/pointers#") def add_literal_value(graph: Graph, parent: Node, predicate: Node, value: Any): @@ -39,7 +39,7 @@ def add_literal_or_no_assertion_or_none(graph: Graph, parent: Node, predicate: N if value is None: return if isinstance(value, SpdxNone): - graph.add((parent, predicate, spdx_namespace.none)) + graph.add((parent, predicate, SPDX_NAMESPACE.none)) return add_literal_or_no_assertion(graph, parent, predicate, value) @@ -48,7 +48,7 @@ def add_literal_or_no_assertion(graph: Graph, parent: Node, predicate: Node, val if value is None: return if isinstance(value, SpdxNoAssertion): - graph.add((parent, predicate, spdx_namespace.noassertion)) + graph.add((parent, predicate, SPDX_NAMESPACE.noassertion)) return add_literal_value(graph, parent, predicate, value) diff --git a/tests/spdx/writer/rdf/test_annotation_writer.py b/tests/spdx/writer/rdf/test_annotation_writer.py index d55a22950..a5b885396 100644 --- a/tests/spdx/writer/rdf/test_annotation_writer.py +++ b/tests/spdx/writer/rdf/test_annotation_writer.py @@ -14,7 +14,7 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.writer.rdf.annotation_writer import add_annotation_info_to_graph -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE from tests.spdx.fixtures import annotation_fixture @@ -24,8 +24,8 @@ def test_add_annotation_info_to_graph(): add_annotation_info_to_graph(annotation, graph, "anyURI", {}) - assert (None, None, spdx_namespace.Annotation) in graph - assert (None, spdx_namespace.annotationType, spdx_namespace.annotationType_review) in graph - assert (None, spdx_namespace.annotationDate, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph - assert (None, spdx_namespace.annotator, Literal("Person: annotatorName (some@mail.com)")) in graph + assert (None, None, SPDX_NAMESPACE.Annotation) in graph + assert (None, SPDX_NAMESPACE.annotationType, SPDX_NAMESPACE.annotationType_review) in graph + assert (None, SPDX_NAMESPACE.annotationDate, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph + assert (None, SPDX_NAMESPACE.annotator, Literal("Person: annotatorName (some@mail.com)")) in graph assert (None, RDFS.comment, Literal("annotationComment")) in graph diff --git a/tests/spdx/writer/rdf/test_checksum_writer.py b/tests/spdx/writer/rdf/test_checksum_writer.py index 487b248fd..e6affb205 100644 --- a/tests/spdx/writer/rdf/test_checksum_writer.py +++ b/tests/spdx/writer/rdf/test_checksum_writer.py @@ -13,7 +13,7 @@ from spdx.model.checksum import ChecksumAlgorithm from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph, algorithm_to_rdf_string -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE from tests.spdx.fixtures import checksum_fixture @@ -23,31 +23,31 @@ def test_add_checksum_information_to_graph(): add_checksum_information_to_graph(checksum, graph, URIRef("TestURI")) - assert (None, None, spdx_namespace.Checksum) in graph - assert (None, spdx_namespace.algorithm, spdx_namespace.checksumAlgorithm_sha1) in graph - assert (None, spdx_namespace.checksumValue, Literal("71c4025dd9897b364f3ebbb42c484ff43d00791c")) in graph + assert (None, None, SPDX_NAMESPACE.Checksum) in graph + assert (None, SPDX_NAMESPACE.algorithm, SPDX_NAMESPACE.checksumAlgorithm_sha1) in graph + assert (None, SPDX_NAMESPACE.checksumValue, Literal("71c4025dd9897b364f3ebbb42c484ff43d00791c")) in graph -@pytest.mark.parametrize("algorithm,expected", [(ChecksumAlgorithm.SHA1, spdx_namespace.checksumAlgorithm_sha1), - (ChecksumAlgorithm.SHA224, spdx_namespace.checksumAlgorithm_sha224), - (ChecksumAlgorithm.SHA256, spdx_namespace.checksumAlgorithm_sha256), - (ChecksumAlgorithm.SHA384, spdx_namespace.checksumAlgorithm_sha384), - (ChecksumAlgorithm.SHA512, spdx_namespace.checksumAlgorithm_sha512), - (ChecksumAlgorithm.SHA3_256, spdx_namespace.checksumAlgorithm_sha3_256), - (ChecksumAlgorithm.SHA3_384, spdx_namespace.checksumAlgorithm_sha3_384), - (ChecksumAlgorithm.SHA3_512, spdx_namespace.checksumAlgorithm_sha3_512), +@pytest.mark.parametrize("algorithm,expected", [(ChecksumAlgorithm.SHA1, SPDX_NAMESPACE.checksumAlgorithm_sha1), + (ChecksumAlgorithm.SHA224, SPDX_NAMESPACE.checksumAlgorithm_sha224), + (ChecksumAlgorithm.SHA256, SPDX_NAMESPACE.checksumAlgorithm_sha256), + (ChecksumAlgorithm.SHA384, SPDX_NAMESPACE.checksumAlgorithm_sha384), + (ChecksumAlgorithm.SHA512, SPDX_NAMESPACE.checksumAlgorithm_sha512), + (ChecksumAlgorithm.SHA3_256, SPDX_NAMESPACE.checksumAlgorithm_sha3_256), + (ChecksumAlgorithm.SHA3_384, SPDX_NAMESPACE.checksumAlgorithm_sha3_384), + (ChecksumAlgorithm.SHA3_512, SPDX_NAMESPACE.checksumAlgorithm_sha3_512), (ChecksumAlgorithm.BLAKE2B_256, - spdx_namespace.checksumAlgorithm_blake2b256), + SPDX_NAMESPACE.checksumAlgorithm_blake2b256), (ChecksumAlgorithm.BLAKE2B_384, - spdx_namespace.checksumAlgorithm_blake2b384), + SPDX_NAMESPACE.checksumAlgorithm_blake2b384), (ChecksumAlgorithm.BLAKE2B_512, - spdx_namespace.checksumAlgorithm_blake2b512), - (ChecksumAlgorithm.BLAKE3, spdx_namespace.checksumAlgorithm_blake3), - (ChecksumAlgorithm.MD2, spdx_namespace.checksumAlgorithm_md2), - (ChecksumAlgorithm.MD4, spdx_namespace.checksumAlgorithm_md4), - (ChecksumAlgorithm.MD5, spdx_namespace.checksumAlgorithm_md5), - (ChecksumAlgorithm.MD6, spdx_namespace.checksumAlgorithm_md6), - (ChecksumAlgorithm.ADLER32, spdx_namespace.checksumAlgorithm_adler32) + SPDX_NAMESPACE.checksumAlgorithm_blake2b512), + (ChecksumAlgorithm.BLAKE3, SPDX_NAMESPACE.checksumAlgorithm_blake3), + (ChecksumAlgorithm.MD2, SPDX_NAMESPACE.checksumAlgorithm_md2), + (ChecksumAlgorithm.MD4, SPDX_NAMESPACE.checksumAlgorithm_md4), + (ChecksumAlgorithm.MD5, SPDX_NAMESPACE.checksumAlgorithm_md5), + (ChecksumAlgorithm.MD6, SPDX_NAMESPACE.checksumAlgorithm_md6), + (ChecksumAlgorithm.ADLER32, SPDX_NAMESPACE.checksumAlgorithm_adler32) ]) def test_algorithm_to_rdf_string(algorithm, expected): rdf_element = algorithm_to_rdf_string(algorithm) diff --git a/tests/spdx/writer/rdf/test_creation_info_writer.py b/tests/spdx/writer/rdf/test_creation_info_writer.py index 84e69499f..76fad516c 100644 --- a/tests/spdx/writer/rdf/test_creation_info_writer.py +++ b/tests/spdx/writer/rdf/test_creation_info_writer.py @@ -14,7 +14,7 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.writer.rdf.creation_info_writer import add_creation_info_to_graph -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE from tests.spdx.fixtures import creation_info_fixture @@ -24,15 +24,15 @@ def test_add_creation_info_to_graph(): add_creation_info_to_graph(creation_info, graph) - assert (None, None, spdx_namespace.SpdxDocument) in graph + assert (None, None, SPDX_NAMESPACE.SpdxDocument) in graph assert (URIRef(f"{creation_info.document_namespace}#{creation_info.spdx_id}"), None, None) in graph - assert (None, spdx_namespace.dataLicense, URIRef("https://spdx.org/licenses/CC0-1.0")) - assert (None, spdx_namespace.name, Literal("documentName")) in graph - assert (None, spdx_namespace.specVersion, Literal("SPDX-2.3")) in graph - assert (None, spdx_namespace.creationInfo, None) in graph + assert (None, SPDX_NAMESPACE.dataLicense, URIRef("https://spdx.org/licenses/CC0-1.0")) + assert (None, SPDX_NAMESPACE.name, Literal("documentName")) in graph + assert (None, SPDX_NAMESPACE.specVersion, Literal("SPDX-2.3")) in graph + assert (None, SPDX_NAMESPACE.creationInfo, None) in graph - assert (None, None, spdx_namespace.CreationInfo) in graph - assert (None, spdx_namespace.created, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph + assert (None, None, SPDX_NAMESPACE.CreationInfo) in graph + assert (None, SPDX_NAMESPACE.created, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph assert (None, RDFS.comment, Literal("creatorComment")) in graph - assert (None, spdx_namespace.licenseListVersion, Literal("3.19")) in graph - assert (None, spdx_namespace.creator, Literal("Person: creatorName (some@mail.com)")) in graph + assert (None, SPDX_NAMESPACE.licenseListVersion, Literal("3.19")) in graph + assert (None, SPDX_NAMESPACE.creator, Literal("Person: creatorName (some@mail.com)")) in graph diff --git a/tests/spdx/writer/rdf/test_external_document_ref_writer.py b/tests/spdx/writer/rdf/test_external_document_ref_writer.py index 3722fd545..ce830e86d 100644 --- a/tests/spdx/writer/rdf/test_external_document_ref_writer.py +++ b/tests/spdx/writer/rdf/test_external_document_ref_writer.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from rdflib import Graph, URIRef -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE from spdx.writer.rdf.external_document_ref_writer import add_external_document_ref_to_graph from tests.spdx.fixtures import external_document_ref_fixture @@ -21,11 +21,11 @@ def test_add_external_document_ref_to_graph(): add_external_document_ref_to_graph(external_document_ref, graph, URIRef("anyURI"), "anyURI") - assert (None, spdx_namespace.externalDocumentRef, URIRef("anyURI#DocumentRef-external")) in graph - assert (None, None, spdx_namespace.ExternalDocumentRef) in graph - assert (None, spdx_namespace.checksum, None) in graph - assert (None, None, spdx_namespace.Checksum) in graph - assert (None, spdx_namespace.spdxDocument, URIRef("https://namespace.com")) in graph + assert (None, SPDX_NAMESPACE.externalDocumentRef, URIRef("anyURI#DocumentRef-external")) in graph + assert (None, None, SPDX_NAMESPACE.ExternalDocumentRef) in graph + assert (None, SPDX_NAMESPACE.checksum, None) in graph + assert (None, None, SPDX_NAMESPACE.Checksum) in graph + assert (None, SPDX_NAMESPACE.spdxDocument, URIRef("https://namespace.com")) in graph diff --git a/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py b/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py index 02ca40a6c..eb6a4a78f 100644 --- a/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py +++ b/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from rdflib import Graph, Literal, RDFS, URIRef -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE from spdx.writer.rdf.extracted_licensing_info_writer import add_extracted_licensing_info_to_graph from tests.spdx.fixtures import extracted_licensing_info_fixture @@ -21,10 +21,10 @@ def test_add_extracted_licensing_info_to_graph(): add_extracted_licensing_info_to_graph(extracted_licensing_info, graph, URIRef("anyURI"), "anyURI") - assert (URIRef("anyURI"), spdx_namespace.hasExtractedLicensingInfo, None) in graph - assert (None, None, spdx_namespace.ExtractedLicensingInfo) in graph - assert (None, spdx_namespace.licenseId, Literal("LicenseRef-1")) in graph - assert (None, spdx_namespace.extractedText, Literal("extractedText")) in graph + assert (URIRef("anyURI"), SPDX_NAMESPACE.hasExtractedLicensingInfo, None) in graph + assert (None, None, SPDX_NAMESPACE.ExtractedLicensingInfo) in graph + assert (None, SPDX_NAMESPACE.licenseId, Literal("LicenseRef-1")) in graph + assert (None, SPDX_NAMESPACE.extractedText, Literal("extractedText")) in graph assert (None, RDFS.seeAlso, Literal("https://see.also")) in graph - assert (None, spdx_namespace.name, Literal("licenseName")) in graph + assert (None, SPDX_NAMESPACE.name, Literal("licenseName")) in graph assert (None, RDFS.comment, Literal("licenseComment")) in graph diff --git a/tests/spdx/writer/rdf/test_file_writer.py b/tests/spdx/writer/rdf/test_file_writer.py index ff89cd6e5..4f6cc9dca 100644 --- a/tests/spdx/writer/rdf/test_file_writer.py +++ b/tests/spdx/writer/rdf/test_file_writer.py @@ -11,7 +11,7 @@ from rdflib import Graph, Literal, RDFS, RDF, URIRef from spdx.writer.rdf.file_writer import add_file_information_to_graph -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE from tests.spdx.fixtures import file_fixture @@ -21,15 +21,15 @@ def test_add_file_information_to_graph(): add_file_information_to_graph(file, graph, "anyURI", {}) - assert (URIRef("anyURI#SPDXRef-File"), RDF.type, spdx_namespace.File) in graph - assert (None, spdx_namespace.fileName, Literal("./fileName.py")) in graph - assert (None, spdx_namespace.fileType, spdx_namespace.fileType_text) in graph - assert (None, spdx_namespace.licenseComments, Literal("licenseComment")) in graph - assert (None, spdx_namespace.licenseConcluded, None) in graph - assert (None, spdx_namespace.licenseInfoInFile, None) in graph - assert (None, spdx_namespace.copyrightText, Literal("copyrightText")) in graph + assert (URIRef("anyURI#SPDXRef-File"), RDF.type, SPDX_NAMESPACE.File) in graph + assert (None, SPDX_NAMESPACE.fileName, Literal("./fileName.py")) in graph + assert (None, SPDX_NAMESPACE.fileType, SPDX_NAMESPACE.fileType_text) in graph + assert (None, SPDX_NAMESPACE.licenseComments, Literal("licenseComment")) in graph + assert (None, SPDX_NAMESPACE.licenseConcluded, None) in graph + assert (None, SPDX_NAMESPACE.licenseInfoInFile, None) in graph + assert (None, SPDX_NAMESPACE.copyrightText, Literal("copyrightText")) in graph assert (None, RDFS.comment, Literal("fileComment")) in graph - assert (None, spdx_namespace.noticeText, Literal("fileNotice")) in graph - assert (None, spdx_namespace.fileContributor, Literal("fileContributor")) in graph - assert (None, spdx_namespace.checksum, None) in graph - assert (None, spdx_namespace.attributionText, Literal("fileAttributionText")) in graph + assert (None, SPDX_NAMESPACE.noticeText, Literal("fileNotice")) in graph + assert (None, SPDX_NAMESPACE.fileContributor, Literal("fileContributor")) in graph + assert (None, SPDX_NAMESPACE.checksum, None) in graph + assert (None, SPDX_NAMESPACE.attributionText, Literal("fileAttributionText")) in graph diff --git a/tests/spdx/writer/rdf/test_license_expression_writer.py b/tests/spdx/writer/rdf/test_license_expression_writer.py index 3f119373c..d5b355e58 100644 --- a/tests/spdx/writer/rdf/test_license_expression_writer.py +++ b/tests/spdx/writer/rdf/test_license_expression_writer.py @@ -11,7 +11,7 @@ import pytest from license_expression import get_spdx_licensing from rdflib import Graph, URIRef, RDF, Literal -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE from spdx.writer.rdf.license_expression_writer import add_license_expression_to_graph @@ -20,40 +20,40 @@ def test_add_conjunctive_license_set_to_graph(): graph = Graph() license_expression = get_spdx_licensing().parse("MIT AND GPL-2.0") - add_license_expression_to_graph(graph, URIRef("anyURI"), spdx_namespace.licenseConcluded, license_expression, + add_license_expression_to_graph(graph, URIRef("anyURI"), SPDX_NAMESPACE.licenseConcluded, license_expression, "https://namespace") - assert (None, RDF.type, spdx_namespace.ConjunctiveLicenseSet) in graph - assert (None, spdx_namespace.member, URIRef("http://spdx.org/licenses/MIT")) in graph - assert (None, spdx_namespace.member, URIRef("http://spdx.org/licenses/GPL-2.0-only")) in graph + assert (None, RDF.type, SPDX_NAMESPACE.ConjunctiveLicenseSet) in graph + assert (None, SPDX_NAMESPACE.member, URIRef("http://spdx.org/licenses/MIT")) in graph + assert (None, SPDX_NAMESPACE.member, URIRef("http://spdx.org/licenses/GPL-2.0-only")) in graph def test_add_disjunctive_license_set_to_graph(): graph = Graph() license_expression = get_spdx_licensing().parse("MIT OR GPL-2.0") - add_license_expression_to_graph(graph, URIRef("anyURI"), spdx_namespace.licenseConcluded, license_expression, + add_license_expression_to_graph(graph, URIRef("anyURI"), SPDX_NAMESPACE.licenseConcluded, license_expression, "https://namespace") - assert (None, RDF.type, spdx_namespace.DisjunctiveLicenseSet) in graph - assert (None, spdx_namespace.member, URIRef("http://spdx.org/licenses/MIT")) in graph - assert (None, spdx_namespace.member, URIRef("http://spdx.org/licenses/GPL-2.0-only")) in graph + assert (None, RDF.type, SPDX_NAMESPACE.DisjunctiveLicenseSet) in graph + assert (None, SPDX_NAMESPACE.member, URIRef("http://spdx.org/licenses/MIT")) in graph + assert (None, SPDX_NAMESPACE.member, URIRef("http://spdx.org/licenses/GPL-2.0-only")) in graph @pytest.mark.parametrize("license_with_exception,expected_triple", [("MIT WITH openvpn-openssl-exception", ( - URIRef("http://spdx.org/licenses/openvpn-openssl-exception"), RDF.type, spdx_namespace.LicenseException)), + URIRef("http://spdx.org/licenses/openvpn-openssl-exception"), RDF.type, SPDX_NAMESPACE.LicenseException)), ("MIT WITH unknown-exception", ( None, - spdx_namespace.licenseExceptionId, + SPDX_NAMESPACE.licenseExceptionId, Literal("unknown-exception")))]) def test_license_exception_to_graph(license_with_exception, expected_triple): graph = Graph() license_expression = get_spdx_licensing().parse(license_with_exception) - add_license_expression_to_graph(graph, URIRef("anyURI"), spdx_namespace.licenseConcluded, license_expression, + add_license_expression_to_graph(graph, URIRef("anyURI"), SPDX_NAMESPACE.licenseConcluded, license_expression, "https://namespace") - assert (None, RDF.type, spdx_namespace.WithExceptionOperator) in graph - assert (None, spdx_namespace.member, URIRef("http://spdx.org/licenses/MIT")) in graph - assert (None, spdx_namespace.licenseException, None) in graph + assert (None, RDF.type, SPDX_NAMESPACE.WithExceptionOperator) in graph + assert (None, SPDX_NAMESPACE.member, URIRef("http://spdx.org/licenses/MIT")) in graph + assert (None, SPDX_NAMESPACE.licenseException, None) in graph assert expected_triple in graph diff --git a/tests/spdx/writer/rdf/test_package_writer.py b/tests/spdx/writer/rdf/test_package_writer.py index cb922e960..a2794f8cb 100644 --- a/tests/spdx/writer/rdf/test_package_writer.py +++ b/tests/spdx/writer/rdf/test_package_writer.py @@ -15,7 +15,7 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.writer.rdf.package_writer import add_package_information_to_graph, add_external_package_ref_to_graph, \ add_package_verification_code_to_graph -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE from tests.spdx.fixtures import package_fixture, external_package_ref_fixture, package_verification_code_fixture @@ -25,32 +25,32 @@ def test_add_package_information_to_graph(): add_package_information_to_graph(package, graph, "anyURI", {}) - assert (URIRef("anyURI#SPDXRef-Package"), RDF.type, spdx_namespace.Package) in graph - assert (None, spdx_namespace.name, Literal("packageName")) in graph - assert (None, spdx_namespace.versionInfo, Literal("12.2")) in graph - assert (None, spdx_namespace.packageFileName, Literal("./packageFileName")) in graph - assert (None, spdx_namespace.supplier, Literal("Person: supplierName (some@mail.com)")) in graph - assert (None, spdx_namespace.originator, Literal("Person: originatorName (some@mail.com)")) in graph - assert (None, spdx_namespace.downloadLocation, Literal("https://download.com")) in graph - assert (None, spdx_namespace.filesAnalyzed, Literal("true", datatype=XSD.boolean)) in graph - assert (URIRef("anyURI#SPDXRef-Package"), spdx_namespace.packageVerificationCode, None) in graph - assert (URIRef("anyURI#SPDXRef-Package"), spdx_namespace.checksum, None) in graph + assert (URIRef("anyURI#SPDXRef-Package"), RDF.type, SPDX_NAMESPACE.Package) in graph + assert (None, SPDX_NAMESPACE.name, Literal("packageName")) in graph + assert (None, SPDX_NAMESPACE.versionInfo, Literal("12.2")) in graph + assert (None, SPDX_NAMESPACE.packageFileName, Literal("./packageFileName")) in graph + assert (None, SPDX_NAMESPACE.supplier, Literal("Person: supplierName (some@mail.com)")) in graph + assert (None, SPDX_NAMESPACE.originator, Literal("Person: originatorName (some@mail.com)")) in graph + assert (None, SPDX_NAMESPACE.downloadLocation, Literal("https://download.com")) in graph + assert (None, SPDX_NAMESPACE.filesAnalyzed, Literal("true", datatype=XSD.boolean)) in graph + assert (URIRef("anyURI#SPDXRef-Package"), SPDX_NAMESPACE.packageVerificationCode, None) in graph + assert (URIRef("anyURI#SPDXRef-Package"), SPDX_NAMESPACE.checksum, None) in graph assert (None, DOAP.homepage, Literal("https://homepage.com")) in graph - assert (None, spdx_namespace.sourceInfo, Literal("sourceInfo")) in graph - assert (None, spdx_namespace.licenseConcluded, None) in graph - assert (None, spdx_namespace.licenseInfoFromFiles, None) in graph - assert (None, spdx_namespace.licenseDeclared, None) in graph - assert (None, spdx_namespace.licenseComments, Literal("packageLicenseComment")) in graph - assert (None, spdx_namespace.copyrightText, Literal("packageCopyrightText")) in graph - assert (None, spdx_namespace.summary, Literal("packageSummary")) in graph - assert (None, spdx_namespace.description, Literal("packageDescription")) in graph + assert (None, SPDX_NAMESPACE.sourceInfo, Literal("sourceInfo")) in graph + assert (None, SPDX_NAMESPACE.licenseConcluded, None) in graph + assert (None, SPDX_NAMESPACE.licenseInfoFromFiles, None) in graph + assert (None, SPDX_NAMESPACE.licenseDeclared, None) in graph + assert (None, SPDX_NAMESPACE.licenseComments, Literal("packageLicenseComment")) in graph + assert (None, SPDX_NAMESPACE.copyrightText, Literal("packageCopyrightText")) in graph + assert (None, SPDX_NAMESPACE.summary, Literal("packageSummary")) in graph + assert (None, SPDX_NAMESPACE.description, Literal("packageDescription")) in graph assert (None, RDFS.comment, Literal("packageComment")) in graph - assert (URIRef("anyURI#SPDXRef-Package"), spdx_namespace.externalRef, None) in graph - assert (None, spdx_namespace.attributionText, Literal("packageAttributionText")) in graph - assert (None, spdx_namespace.primaryPackagePurpose, spdx_namespace.purpose_source) in graph - assert (None, spdx_namespace.releaseDate, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph - assert (None, spdx_namespace.builtDate, Literal(datetime_to_iso_string(datetime(2022, 12, 2)))) in graph - assert (None, spdx_namespace.validUntilDate, Literal(datetime_to_iso_string(datetime(2022, 12, 3)))) in graph + assert (URIRef("anyURI#SPDXRef-Package"), SPDX_NAMESPACE.externalRef, None) in graph + assert (None, SPDX_NAMESPACE.attributionText, Literal("packageAttributionText")) in graph + assert (None, SPDX_NAMESPACE.primaryPackagePurpose, SPDX_NAMESPACE.purpose_source) in graph + assert (None, SPDX_NAMESPACE.releaseDate, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph + assert (None, SPDX_NAMESPACE.builtDate, Literal(datetime_to_iso_string(datetime(2022, 12, 2)))) in graph + assert (None, SPDX_NAMESPACE.validUntilDate, Literal(datetime_to_iso_string(datetime(2022, 12, 3)))) in graph def test_add_package_verification_code_to_graph(): @@ -59,10 +59,10 @@ def test_add_package_verification_code_to_graph(): add_package_verification_code_to_graph(verification_code, graph, URIRef("anyURI")) - assert (None, None, spdx_namespace.PackageVerificationCode) in graph - assert (None, spdx_namespace.packageVerificationCodeValue, + assert (None, None, SPDX_NAMESPACE.PackageVerificationCode) in graph + assert (None, SPDX_NAMESPACE.packageVerificationCodeValue, Literal("85ed0817af83a24ad8da68c2b5094de69833983c")) in graph - assert (None, spdx_namespace.packageVerificationCodeExcludedFile, Literal("./exclude.py")) in graph + assert (None, SPDX_NAMESPACE.packageVerificationCodeExcludedFile, Literal("./exclude.py")) in graph def test_external_package_ref_to_graph(): @@ -71,8 +71,8 @@ def test_external_package_ref_to_graph(): add_external_package_ref_to_graph(graph, external_reference, URIRef("anyURI")) - assert (None, None, spdx_namespace.ExternalRef) in graph - assert (None, spdx_namespace.referenceCategory, spdx_namespace.referenceCategory_packageManager) in graph - assert (None, spdx_namespace.referenceType, URIRef("http://spdx.org/rdf/references/maven-central")) in graph - assert (None, spdx_namespace.referenceLocator, Literal("org.apache.tomcat:tomcat:9.0.0.M4")) in graph + assert (None, None, SPDX_NAMESPACE.ExternalRef) in graph + assert (None, SPDX_NAMESPACE.referenceCategory, SPDX_NAMESPACE.referenceCategory_packageManager) in graph + assert (None, SPDX_NAMESPACE.referenceType, URIRef("http://spdx.org/rdf/references/maven-central")) in graph + assert (None, SPDX_NAMESPACE.referenceLocator, Literal("org.apache.tomcat:tomcat:9.0.0.M4")) in graph assert (None, RDFS.comment, Literal("externalPackageRefComment")) in graph diff --git a/tests/spdx/writer/rdf/test_relationship_writer.py b/tests/spdx/writer/rdf/test_relationship_writer.py index 34a2173d7..38534e740 100644 --- a/tests/spdx/writer/rdf/test_relationship_writer.py +++ b/tests/spdx/writer/rdf/test_relationship_writer.py @@ -11,7 +11,7 @@ from rdflib import Graph from spdx.writer.rdf.relationship_writer import add_relationship_info_to_graph -from spdx.writer.rdf.writer_utils import spdx_namespace +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE from tests.spdx.fixtures import relationship_fixture @@ -20,5 +20,5 @@ def test_add_relationship_info_to_graph(): graph = Graph() add_relationship_info_to_graph(relationship, graph, "anyURI", {}) - assert (None, spdx_namespace.relationshipType, spdx_namespace.relationshipType_describes) in graph - assert (None, spdx_namespace.relatedSpdxElement, None) in graph + assert (None, SPDX_NAMESPACE.relationshipType, SPDX_NAMESPACE.relationshipType_describes) in graph + assert (None, SPDX_NAMESPACE.relatedSpdxElement, None) in graph diff --git a/tests/spdx/writer/rdf/test_snippet_writer.py b/tests/spdx/writer/rdf/test_snippet_writer.py index 3be73e8ed..914034d2e 100644 --- a/tests/spdx/writer/rdf/test_snippet_writer.py +++ b/tests/spdx/writer/rdf/test_snippet_writer.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from rdflib import Graph, URIRef, RDF, Literal, RDFS -from spdx.writer.rdf.writer_utils import spdx_namespace, pointer_namespace +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, POINTER_NAMESPACE from spdx.writer.rdf.snippet_writer import add_snippet_information_to_graph, add_range_to_graph from tests.spdx.fixtures import snippet_fixture @@ -21,14 +21,14 @@ def test_add_snippet_information_to_graph(): add_snippet_information_to_graph(snippet, graph, "anyURI", {}) - assert (URIRef("anyURI#SPDXRef-Snippet"), RDF.type, spdx_namespace.Snippet) in graph - assert (None, spdx_namespace.snippetFromFile, URIRef(f"anyURI#{snippet.file_spdx_id}")) in graph - assert (None, spdx_namespace.licenseConcluded, None) in graph - assert (None, spdx_namespace.licenseInfoInSnippet, None) in graph - assert (None, spdx_namespace.licenseComments, Literal("snippetLicenseComment")) in graph - assert (None, spdx_namespace.copyrightText, Literal("licenseCopyrightText")) in graph - assert (None, spdx_namespace.name, Literal("snippetName")) in graph - assert (None, spdx_namespace.attributionText, Literal("snippetAttributionText")) in graph + assert (URIRef("anyURI#SPDXRef-Snippet"), RDF.type, SPDX_NAMESPACE.Snippet) in graph + assert (None, SPDX_NAMESPACE.snippetFromFile, URIRef(f"anyURI#{snippet.file_spdx_id}")) in graph + assert (None, SPDX_NAMESPACE.licenseConcluded, None) in graph + assert (None, SPDX_NAMESPACE.licenseInfoInSnippet, None) in graph + assert (None, SPDX_NAMESPACE.licenseComments, Literal("snippetLicenseComment")) in graph + assert (None, SPDX_NAMESPACE.copyrightText, Literal("licenseCopyrightText")) in graph + assert (None, SPDX_NAMESPACE.name, Literal("snippetName")) in graph + assert (None, SPDX_NAMESPACE.attributionText, Literal("snippetAttributionText")) in graph assert (None, RDFS.comment, Literal("snippetComment")) in graph @@ -36,11 +36,11 @@ def test_add_ranges_to_graph(): graph = Graph() byte_range = (5, 190) - add_range_to_graph(graph, URIRef("anyUR"), byte_range, URIRef("anyURI#SPDXRef-File"), pointer_namespace.ByteOffsetPointer) + add_range_to_graph(graph, URIRef("anyUR"), byte_range, URIRef("anyURI#SPDXRef-File"), POINTER_NAMESPACE.ByteOffsetPointer) - assert (None, spdx_namespace.range, None) in graph - assert (None, pointer_namespace.startPointer, None) in graph - assert (None, pointer_namespace.endPointer, None) in graph - assert (None, pointer_namespace.reference, URIRef("anyURI#SPDXRef-File")) in graph - assert (None, pointer_namespace.offset, Literal(str(5))) in graph - assert (None, pointer_namespace.offset, Literal(str(190))) in graph + assert (None, SPDX_NAMESPACE.range, None) in graph + assert (None, POINTER_NAMESPACE.startPointer, None) in graph + assert (None, POINTER_NAMESPACE.endPointer, None) in graph + assert (None, POINTER_NAMESPACE.reference, URIRef("anyURI#SPDXRef-File")) in graph + assert (None, POINTER_NAMESPACE.offset, Literal(str(5))) in graph + assert (None, POINTER_NAMESPACE.offset, Literal(str(190))) in graph From bc9dde7f0bf032a180d118c11ddfa5c062ebe925 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 31 Jan 2023 11:33:57 +0100 Subject: [PATCH 204/362] [issue-407, review] reformat and use positive logic Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/writer_utils.py | 18 ++++++++---------- .../rdf/test_license_expression_writer.py | 12 ++++++------ tests/spdx/writer/rdf/test_rdf_writer.py | 2 ++ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/spdx/writer/rdf/writer_utils.py b/src/spdx/writer/rdf/writer_utils.py index 13ffbf8e9..3ae1774d7 100644 --- a/src/spdx/writer/rdf/writer_utils.py +++ b/src/spdx/writer/rdf/writer_utils.py @@ -26,13 +26,12 @@ def add_literal_value(graph: Graph, parent: Node, predicate: Node, value: Any): if value is None: return - if not isinstance(value, list): - graph.add((parent, predicate, Literal(str(value)))) - return - - for element in value: - element_triple = (parent, predicate, Literal(str(element))) - graph.add(element_triple) + if isinstance(value, list): + for element in value: + element_triple = (parent, predicate, Literal(str(element))) + graph.add(element_triple) + graph.add((parent, predicate, Literal(str(value)))) + return def add_literal_or_no_assertion_or_none(graph: Graph, parent: Node, predicate: Node, value: Any): @@ -54,9 +53,8 @@ def add_literal_or_no_assertion(graph: Graph, parent: Node, predicate: Node, val def add_datetime_to_graph(graph: Graph, parent: Node, predicate: Node, value: Optional[datetime]): - if not value: - return - graph.add((parent, predicate, Literal(datetime_to_iso_string(value)))) + if value: + graph.add((parent, predicate, Literal(datetime_to_iso_string(value)))) def add_namespace_to_spdx_id(spdx_id: str, doc_namespace: str, external_doc_namespaces: Dict[str, str]) -> str: diff --git a/tests/spdx/writer/rdf/test_license_expression_writer.py b/tests/spdx/writer/rdf/test_license_expression_writer.py index d5b355e58..6cab80ad3 100644 --- a/tests/spdx/writer/rdf/test_license_expression_writer.py +++ b/tests/spdx/writer/rdf/test_license_expression_writer.py @@ -40,12 +40,12 @@ def test_add_disjunctive_license_set_to_graph(): assert (None, SPDX_NAMESPACE.member, URIRef("http://spdx.org/licenses/GPL-2.0-only")) in graph -@pytest.mark.parametrize("license_with_exception,expected_triple", [("MIT WITH openvpn-openssl-exception", ( - URIRef("http://spdx.org/licenses/openvpn-openssl-exception"), RDF.type, SPDX_NAMESPACE.LicenseException)), - ("MIT WITH unknown-exception", ( - None, - SPDX_NAMESPACE.licenseExceptionId, - Literal("unknown-exception")))]) +@pytest.mark.parametrize("license_with_exception," + "expected_triple", [("MIT WITH openvpn-openssl-exception", + (URIRef("http://spdx.org/licenses/openvpn-openssl-exception"), RDF.type, + SPDX_NAMESPACE.LicenseException)), + ("MIT WITH unknown-exception", + (None, SPDX_NAMESPACE.licenseExceptionId, Literal("unknown-exception")))]) def test_license_exception_to_graph(license_with_exception, expected_triple): graph = Graph() license_expression = get_spdx_licensing().parse(license_with_exception) diff --git a/tests/spdx/writer/rdf/test_rdf_writer.py b/tests/spdx/writer/rdf/test_rdf_writer.py index f6bf29ace..38e4f5871 100644 --- a/tests/spdx/writer/rdf/test_rdf_writer.py +++ b/tests/spdx/writer/rdf/test_rdf_writer.py @@ -23,6 +23,8 @@ def temporary_file_path() -> str: temporary_file_path = "temp_test_rdf_writer_output.rdf.xml" yield temporary_file_path os.remove(temporary_file_path) + + def test_write_document_to_file(temporary_file_path: str): document: Document = document_fixture() From f4a13d365c3fb47dca205a55893af8712cd5a713 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 31 Jan 2023 12:31:28 +0100 Subject: [PATCH 205/362] [issue-407, review] also test line_range and set datatype for offset and lineNumber to integer Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/snippet_writer.py | 4 ++-- tests/spdx/writer/rdf/test_snippet_writer.py | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/spdx/writer/rdf/snippet_writer.py b/src/spdx/writer/rdf/snippet_writer.py index 139393b25..8de6fb1e8 100644 --- a/src/spdx/writer/rdf/snippet_writer.py +++ b/src/spdx/writer/rdf/snippet_writer.py @@ -54,8 +54,8 @@ def add_range_to_graph(graph: Graph, snippet_resource: URIRef, range_information graph.add((start_end_pointer, predicate, pointer_node)) graph.add((pointer_node, POINTER_NAMESPACE.reference, snippet_from_file_ref)) if pointer_class == POINTER_NAMESPACE.ByteOffsetPointer: - graph.add((pointer_node, POINTER_NAMESPACE.offset, Literal(str(value)))) + graph.add((pointer_node, POINTER_NAMESPACE.offset, Literal(value))) else: - graph.add((pointer_node, POINTER_NAMESPACE.lineNumber, Literal(str(value)))) + graph.add((pointer_node, POINTER_NAMESPACE.lineNumber, Literal(value))) graph.add((snippet_resource, SPDX_NAMESPACE.range, start_end_pointer)) diff --git a/tests/spdx/writer/rdf/test_snippet_writer.py b/tests/spdx/writer/rdf/test_snippet_writer.py index 914034d2e..efce79598 100644 --- a/tests/spdx/writer/rdf/test_snippet_writer.py +++ b/tests/spdx/writer/rdf/test_snippet_writer.py @@ -8,6 +8,7 @@ # 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 pytest from rdflib import Graph, URIRef, RDF, Literal, RDFS from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, POINTER_NAMESPACE @@ -32,15 +33,17 @@ def test_add_snippet_information_to_graph(): assert (None, RDFS.comment, Literal("snippetComment")) in graph -def test_add_ranges_to_graph(): +@pytest.mark.parametrize("range,pointer,predicate", + [((5, 190), POINTER_NAMESPACE.ByteOffsetPointer, POINTER_NAMESPACE.offset), + ((1, 3), POINTER_NAMESPACE.LineCharPointer, POINTER_NAMESPACE.lineNumber)]) +def test_add_ranges_to_graph(range, pointer, predicate): graph = Graph() - byte_range = (5, 190) - - add_range_to_graph(graph, URIRef("anyUR"), byte_range, URIRef("anyURI#SPDXRef-File"), POINTER_NAMESPACE.ByteOffsetPointer) + add_range_to_graph(graph, URIRef("anyUR"), range, URIRef("anyURI#SPDXRef-File"), + pointer) assert (None, SPDX_NAMESPACE.range, None) in graph assert (None, POINTER_NAMESPACE.startPointer, None) in graph assert (None, POINTER_NAMESPACE.endPointer, None) in graph assert (None, POINTER_NAMESPACE.reference, URIRef("anyURI#SPDXRef-File")) in graph - assert (None, POINTER_NAMESPACE.offset, Literal(str(5))) in graph - assert (None, POINTER_NAMESPACE.offset, Literal(str(190))) in graph + assert (None, predicate, Literal(range[0])) in graph + assert (None, predicate, Literal(range[1])) in graph From 02ba6b232c980b6f2a6cbd8be4245b8b1335e649 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 31 Jan 2023 12:35:59 +0100 Subject: [PATCH 206/362] [issue-407, review] include all parameters in test Signed-off-by: Meret Behrens --- tests/spdx/writer/rdf/test_annotation_writer.py | 3 ++- tests/spdx/writer/rdf/test_checksum_writer.py | 1 + tests/spdx/writer/rdf/test_external_document_ref_writer.py | 2 +- tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py | 2 +- tests/spdx/writer/rdf/test_license_expression_writer.py | 3 +++ tests/spdx/writer/rdf/test_relationship_writer.py | 3 ++- 6 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/spdx/writer/rdf/test_annotation_writer.py b/tests/spdx/writer/rdf/test_annotation_writer.py index a5b885396..0d17bd01b 100644 --- a/tests/spdx/writer/rdf/test_annotation_writer.py +++ b/tests/spdx/writer/rdf/test_annotation_writer.py @@ -10,7 +10,7 @@ # limitations under the License. from datetime import datetime -from rdflib import Graph, Literal, RDFS +from rdflib import Graph, Literal, RDFS, URIRef from spdx.datetime_conversions import datetime_to_iso_string from spdx.writer.rdf.annotation_writer import add_annotation_info_to_graph @@ -24,6 +24,7 @@ def test_add_annotation_info_to_graph(): add_annotation_info_to_graph(annotation, graph, "anyURI", {}) + assert (URIRef("anyURI#SPDXRef-File"), SPDX_NAMESPACE.annotation, None) in graph assert (None, None, SPDX_NAMESPACE.Annotation) in graph assert (None, SPDX_NAMESPACE.annotationType, SPDX_NAMESPACE.annotationType_review) in graph assert (None, SPDX_NAMESPACE.annotationDate, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph diff --git a/tests/spdx/writer/rdf/test_checksum_writer.py b/tests/spdx/writer/rdf/test_checksum_writer.py index e6affb205..e6b53ac74 100644 --- a/tests/spdx/writer/rdf/test_checksum_writer.py +++ b/tests/spdx/writer/rdf/test_checksum_writer.py @@ -23,6 +23,7 @@ def test_add_checksum_information_to_graph(): add_checksum_information_to_graph(checksum, graph, URIRef("TestURI")) + assert (URIRef("TestURI"), SPDX_NAMESPACE.checksum, None) in graph assert (None, None, SPDX_NAMESPACE.Checksum) in graph assert (None, SPDX_NAMESPACE.algorithm, SPDX_NAMESPACE.checksumAlgorithm_sha1) in graph assert (None, SPDX_NAMESPACE.checksumValue, Literal("71c4025dd9897b364f3ebbb42c484ff43d00791c")) in graph diff --git a/tests/spdx/writer/rdf/test_external_document_ref_writer.py b/tests/spdx/writer/rdf/test_external_document_ref_writer.py index ce830e86d..2760f63a8 100644 --- a/tests/spdx/writer/rdf/test_external_document_ref_writer.py +++ b/tests/spdx/writer/rdf/test_external_document_ref_writer.py @@ -21,7 +21,7 @@ def test_add_external_document_ref_to_graph(): add_external_document_ref_to_graph(external_document_ref, graph, URIRef("anyURI"), "anyURI") - assert (None, SPDX_NAMESPACE.externalDocumentRef, URIRef("anyURI#DocumentRef-external")) in graph + assert (URIRef("anyURI"), SPDX_NAMESPACE.externalDocumentRef, URIRef("anyURI#DocumentRef-external")) in graph assert (None, None, SPDX_NAMESPACE.ExternalDocumentRef) in graph assert (None, SPDX_NAMESPACE.checksum, None) in graph assert (None, None, SPDX_NAMESPACE.Checksum) in graph diff --git a/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py b/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py index eb6a4a78f..dcb2d7d92 100644 --- a/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py +++ b/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py @@ -22,7 +22,7 @@ def test_add_extracted_licensing_info_to_graph(): add_extracted_licensing_info_to_graph(extracted_licensing_info, graph, URIRef("anyURI"), "anyURI") assert (URIRef("anyURI"), SPDX_NAMESPACE.hasExtractedLicensingInfo, None) in graph - assert (None, None, SPDX_NAMESPACE.ExtractedLicensingInfo) in graph + assert (URIRef("anyURI#LicenseRef-1"), None, SPDX_NAMESPACE.ExtractedLicensingInfo) in graph assert (None, SPDX_NAMESPACE.licenseId, Literal("LicenseRef-1")) in graph assert (None, SPDX_NAMESPACE.extractedText, Literal("extractedText")) in graph assert (None, RDFS.seeAlso, Literal("https://see.also")) in graph diff --git a/tests/spdx/writer/rdf/test_license_expression_writer.py b/tests/spdx/writer/rdf/test_license_expression_writer.py index 6cab80ad3..224132784 100644 --- a/tests/spdx/writer/rdf/test_license_expression_writer.py +++ b/tests/spdx/writer/rdf/test_license_expression_writer.py @@ -23,6 +23,7 @@ def test_add_conjunctive_license_set_to_graph(): add_license_expression_to_graph(graph, URIRef("anyURI"), SPDX_NAMESPACE.licenseConcluded, license_expression, "https://namespace") + assert (URIRef("anyURI"), SPDX_NAMESPACE.licenseConcluded, None) in graph assert (None, RDF.type, SPDX_NAMESPACE.ConjunctiveLicenseSet) in graph assert (None, SPDX_NAMESPACE.member, URIRef("http://spdx.org/licenses/MIT")) in graph assert (None, SPDX_NAMESPACE.member, URIRef("http://spdx.org/licenses/GPL-2.0-only")) in graph @@ -35,6 +36,7 @@ def test_add_disjunctive_license_set_to_graph(): add_license_expression_to_graph(graph, URIRef("anyURI"), SPDX_NAMESPACE.licenseConcluded, license_expression, "https://namespace") + assert (URIRef("anyURI"), SPDX_NAMESPACE.licenseConcluded, None) in graph assert (None, RDF.type, SPDX_NAMESPACE.DisjunctiveLicenseSet) in graph assert (None, SPDX_NAMESPACE.member, URIRef("http://spdx.org/licenses/MIT")) in graph assert (None, SPDX_NAMESPACE.member, URIRef("http://spdx.org/licenses/GPL-2.0-only")) in graph @@ -53,6 +55,7 @@ def test_license_exception_to_graph(license_with_exception, expected_triple): add_license_expression_to_graph(graph, URIRef("anyURI"), SPDX_NAMESPACE.licenseConcluded, license_expression, "https://namespace") + assert (URIRef("anyURI"), SPDX_NAMESPACE.licenseConcluded, None) in graph assert (None, RDF.type, SPDX_NAMESPACE.WithExceptionOperator) in graph assert (None, SPDX_NAMESPACE.member, URIRef("http://spdx.org/licenses/MIT")) in graph assert (None, SPDX_NAMESPACE.licenseException, None) in graph diff --git a/tests/spdx/writer/rdf/test_relationship_writer.py b/tests/spdx/writer/rdf/test_relationship_writer.py index 38534e740..95d4f59c6 100644 --- a/tests/spdx/writer/rdf/test_relationship_writer.py +++ b/tests/spdx/writer/rdf/test_relationship_writer.py @@ -8,7 +8,7 @@ # 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 rdflib import Graph +from rdflib import Graph, URIRef from spdx.writer.rdf.relationship_writer import add_relationship_info_to_graph from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE @@ -20,5 +20,6 @@ def test_add_relationship_info_to_graph(): graph = Graph() add_relationship_info_to_graph(relationship, graph, "anyURI", {}) + assert(URIRef("anyURI#SPDXRef-DOCUMENT"), SPDX_NAMESPACE.relationship, None) in graph assert (None, SPDX_NAMESPACE.relationshipType, SPDX_NAMESPACE.relationshipType_describes) in graph assert (None, SPDX_NAMESPACE.relatedSpdxElement, None) in graph From d692fc69fb4166c1b632fe6a7d0d7701fe920e60 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 31 Jan 2023 12:47:06 +0100 Subject: [PATCH 207/362] [issue-407, review] rename parameter for clarification Signed-off-by: Meret Behrens --- tests/spdx/writer/rdf/test_annotation_writer.py | 4 ++-- tests/spdx/writer/rdf/test_checksum_writer.py | 4 ++-- .../rdf/test_external_document_ref_writer.py | 4 ++-- .../rdf/test_extracted_licensing_info_writer.py | 6 +++--- tests/spdx/writer/rdf/test_file_writer.py | 4 ++-- .../writer/rdf/test_license_expression_writer.py | 12 ++++++------ tests/spdx/writer/rdf/test_package_writer.py | 14 +++++++------- tests/spdx/writer/rdf/test_relationship_writer.py | 4 ++-- tests/spdx/writer/rdf/test_snippet_writer.py | 10 +++++----- 9 files changed, 31 insertions(+), 31 deletions(-) diff --git a/tests/spdx/writer/rdf/test_annotation_writer.py b/tests/spdx/writer/rdf/test_annotation_writer.py index 0d17bd01b..5c90f7779 100644 --- a/tests/spdx/writer/rdf/test_annotation_writer.py +++ b/tests/spdx/writer/rdf/test_annotation_writer.py @@ -22,9 +22,9 @@ def test_add_annotation_info_to_graph(): graph = Graph() annotation = annotation_fixture() - add_annotation_info_to_graph(annotation, graph, "anyURI", {}) + add_annotation_info_to_graph(annotation, graph, "docNamespace", {}) - assert (URIRef("anyURI#SPDXRef-File"), SPDX_NAMESPACE.annotation, None) in graph + assert (URIRef("docNamespace#SPDXRef-File"), SPDX_NAMESPACE.annotation, None) in graph assert (None, None, SPDX_NAMESPACE.Annotation) in graph assert (None, SPDX_NAMESPACE.annotationType, SPDX_NAMESPACE.annotationType_review) in graph assert (None, SPDX_NAMESPACE.annotationDate, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph diff --git a/tests/spdx/writer/rdf/test_checksum_writer.py b/tests/spdx/writer/rdf/test_checksum_writer.py index e6b53ac74..b86c60c14 100644 --- a/tests/spdx/writer/rdf/test_checksum_writer.py +++ b/tests/spdx/writer/rdf/test_checksum_writer.py @@ -21,9 +21,9 @@ def test_add_checksum_information_to_graph(): graph = Graph() checksum = checksum_fixture() - add_checksum_information_to_graph(checksum, graph, URIRef("TestURI")) + add_checksum_information_to_graph(checksum, graph, URIRef("parentNode")) - assert (URIRef("TestURI"), SPDX_NAMESPACE.checksum, None) in graph + assert (URIRef("parentNode"), SPDX_NAMESPACE.checksum, None) in graph assert (None, None, SPDX_NAMESPACE.Checksum) in graph assert (None, SPDX_NAMESPACE.algorithm, SPDX_NAMESPACE.checksumAlgorithm_sha1) in graph assert (None, SPDX_NAMESPACE.checksumValue, Literal("71c4025dd9897b364f3ebbb42c484ff43d00791c")) in graph diff --git a/tests/spdx/writer/rdf/test_external_document_ref_writer.py b/tests/spdx/writer/rdf/test_external_document_ref_writer.py index 2760f63a8..943e69e43 100644 --- a/tests/spdx/writer/rdf/test_external_document_ref_writer.py +++ b/tests/spdx/writer/rdf/test_external_document_ref_writer.py @@ -19,9 +19,9 @@ def test_add_external_document_ref_to_graph(): graph = Graph() external_document_ref = external_document_ref_fixture() - add_external_document_ref_to_graph(external_document_ref, graph, URIRef("anyURI"), "anyURI") + add_external_document_ref_to_graph(external_document_ref, graph, URIRef("docNode"), "docNamespace") - assert (URIRef("anyURI"), SPDX_NAMESPACE.externalDocumentRef, URIRef("anyURI#DocumentRef-external")) in graph + assert (URIRef("docNode"), SPDX_NAMESPACE.externalDocumentRef, URIRef("docNamespace#DocumentRef-external")) in graph assert (None, None, SPDX_NAMESPACE.ExternalDocumentRef) in graph assert (None, SPDX_NAMESPACE.checksum, None) in graph assert (None, None, SPDX_NAMESPACE.Checksum) in graph diff --git a/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py b/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py index dcb2d7d92..561f69cf1 100644 --- a/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py +++ b/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py @@ -19,10 +19,10 @@ def test_add_extracted_licensing_info_to_graph(): graph = Graph() extracted_licensing_info = extracted_licensing_info_fixture() - add_extracted_licensing_info_to_graph(extracted_licensing_info, graph, URIRef("anyURI"), "anyURI") + add_extracted_licensing_info_to_graph(extracted_licensing_info, graph, URIRef("docNode"), "docNamespace") - assert (URIRef("anyURI"), SPDX_NAMESPACE.hasExtractedLicensingInfo, None) in graph - assert (URIRef("anyURI#LicenseRef-1"), None, SPDX_NAMESPACE.ExtractedLicensingInfo) in graph + assert (URIRef("docNode"), SPDX_NAMESPACE.hasExtractedLicensingInfo, None) in graph + assert (URIRef("docNamespace#LicenseRef-1"), None, SPDX_NAMESPACE.ExtractedLicensingInfo) in graph assert (None, SPDX_NAMESPACE.licenseId, Literal("LicenseRef-1")) in graph assert (None, SPDX_NAMESPACE.extractedText, Literal("extractedText")) in graph assert (None, RDFS.seeAlso, Literal("https://see.also")) in graph diff --git a/tests/spdx/writer/rdf/test_file_writer.py b/tests/spdx/writer/rdf/test_file_writer.py index 4f6cc9dca..cb5732caa 100644 --- a/tests/spdx/writer/rdf/test_file_writer.py +++ b/tests/spdx/writer/rdf/test_file_writer.py @@ -19,9 +19,9 @@ def test_add_file_information_to_graph(): graph = Graph() file = file_fixture() - add_file_information_to_graph(file, graph, "anyURI", {}) + add_file_information_to_graph(file, graph, "docNamespace", {}) - assert (URIRef("anyURI#SPDXRef-File"), RDF.type, SPDX_NAMESPACE.File) in graph + assert (URIRef("docNamespace#SPDXRef-File"), RDF.type, SPDX_NAMESPACE.File) in graph assert (None, SPDX_NAMESPACE.fileName, Literal("./fileName.py")) in graph assert (None, SPDX_NAMESPACE.fileType, SPDX_NAMESPACE.fileType_text) in graph assert (None, SPDX_NAMESPACE.licenseComments, Literal("licenseComment")) in graph diff --git a/tests/spdx/writer/rdf/test_license_expression_writer.py b/tests/spdx/writer/rdf/test_license_expression_writer.py index 224132784..432e01b2c 100644 --- a/tests/spdx/writer/rdf/test_license_expression_writer.py +++ b/tests/spdx/writer/rdf/test_license_expression_writer.py @@ -20,10 +20,10 @@ def test_add_conjunctive_license_set_to_graph(): graph = Graph() license_expression = get_spdx_licensing().parse("MIT AND GPL-2.0") - add_license_expression_to_graph(graph, URIRef("anyURI"), SPDX_NAMESPACE.licenseConcluded, license_expression, + add_license_expression_to_graph(graph, URIRef("parentNode"), SPDX_NAMESPACE.licenseConcluded, license_expression, "https://namespace") - assert (URIRef("anyURI"), SPDX_NAMESPACE.licenseConcluded, None) in graph + assert (URIRef("parentNode"), SPDX_NAMESPACE.licenseConcluded, None) in graph assert (None, RDF.type, SPDX_NAMESPACE.ConjunctiveLicenseSet) in graph assert (None, SPDX_NAMESPACE.member, URIRef("http://spdx.org/licenses/MIT")) in graph assert (None, SPDX_NAMESPACE.member, URIRef("http://spdx.org/licenses/GPL-2.0-only")) in graph @@ -33,10 +33,10 @@ def test_add_disjunctive_license_set_to_graph(): graph = Graph() license_expression = get_spdx_licensing().parse("MIT OR GPL-2.0") - add_license_expression_to_graph(graph, URIRef("anyURI"), SPDX_NAMESPACE.licenseConcluded, license_expression, + add_license_expression_to_graph(graph, URIRef("parentNode"), SPDX_NAMESPACE.licenseConcluded, license_expression, "https://namespace") - assert (URIRef("anyURI"), SPDX_NAMESPACE.licenseConcluded, None) in graph + assert (URIRef("parentNode"), SPDX_NAMESPACE.licenseConcluded, None) in graph assert (None, RDF.type, SPDX_NAMESPACE.DisjunctiveLicenseSet) in graph assert (None, SPDX_NAMESPACE.member, URIRef("http://spdx.org/licenses/MIT")) in graph assert (None, SPDX_NAMESPACE.member, URIRef("http://spdx.org/licenses/GPL-2.0-only")) in graph @@ -52,10 +52,10 @@ def test_license_exception_to_graph(license_with_exception, expected_triple): graph = Graph() license_expression = get_spdx_licensing().parse(license_with_exception) - add_license_expression_to_graph(graph, URIRef("anyURI"), SPDX_NAMESPACE.licenseConcluded, license_expression, + add_license_expression_to_graph(graph, URIRef("parentNode"), SPDX_NAMESPACE.licenseConcluded, license_expression, "https://namespace") - assert (URIRef("anyURI"), SPDX_NAMESPACE.licenseConcluded, None) in graph + assert (URIRef("parentNode"), SPDX_NAMESPACE.licenseConcluded, None) in graph assert (None, RDF.type, SPDX_NAMESPACE.WithExceptionOperator) in graph assert (None, SPDX_NAMESPACE.member, URIRef("http://spdx.org/licenses/MIT")) in graph assert (None, SPDX_NAMESPACE.licenseException, None) in graph diff --git a/tests/spdx/writer/rdf/test_package_writer.py b/tests/spdx/writer/rdf/test_package_writer.py index a2794f8cb..fab30771a 100644 --- a/tests/spdx/writer/rdf/test_package_writer.py +++ b/tests/spdx/writer/rdf/test_package_writer.py @@ -23,9 +23,9 @@ def test_add_package_information_to_graph(): graph = Graph() package = package_fixture() - add_package_information_to_graph(package, graph, "anyURI", {}) + add_package_information_to_graph(package, graph, "docNamespace", {}) - assert (URIRef("anyURI#SPDXRef-Package"), RDF.type, SPDX_NAMESPACE.Package) in graph + assert (URIRef("docNamespace#SPDXRef-Package"), RDF.type, SPDX_NAMESPACE.Package) in graph assert (None, SPDX_NAMESPACE.name, Literal("packageName")) in graph assert (None, SPDX_NAMESPACE.versionInfo, Literal("12.2")) in graph assert (None, SPDX_NAMESPACE.packageFileName, Literal("./packageFileName")) in graph @@ -33,8 +33,8 @@ def test_add_package_information_to_graph(): assert (None, SPDX_NAMESPACE.originator, Literal("Person: originatorName (some@mail.com)")) in graph assert (None, SPDX_NAMESPACE.downloadLocation, Literal("https://download.com")) in graph assert (None, SPDX_NAMESPACE.filesAnalyzed, Literal("true", datatype=XSD.boolean)) in graph - assert (URIRef("anyURI#SPDXRef-Package"), SPDX_NAMESPACE.packageVerificationCode, None) in graph - assert (URIRef("anyURI#SPDXRef-Package"), SPDX_NAMESPACE.checksum, None) in graph + assert (URIRef("docNamespace#SPDXRef-Package"), SPDX_NAMESPACE.packageVerificationCode, None) in graph + assert (URIRef("docNamespace#SPDXRef-Package"), SPDX_NAMESPACE.checksum, None) in graph assert (None, DOAP.homepage, Literal("https://homepage.com")) in graph assert (None, SPDX_NAMESPACE.sourceInfo, Literal("sourceInfo")) in graph assert (None, SPDX_NAMESPACE.licenseConcluded, None) in graph @@ -45,7 +45,7 @@ def test_add_package_information_to_graph(): assert (None, SPDX_NAMESPACE.summary, Literal("packageSummary")) in graph assert (None, SPDX_NAMESPACE.description, Literal("packageDescription")) in graph assert (None, RDFS.comment, Literal("packageComment")) in graph - assert (URIRef("anyURI#SPDXRef-Package"), SPDX_NAMESPACE.externalRef, None) in graph + assert (URIRef("docNamespace#SPDXRef-Package"), SPDX_NAMESPACE.externalRef, None) in graph assert (None, SPDX_NAMESPACE.attributionText, Literal("packageAttributionText")) in graph assert (None, SPDX_NAMESPACE.primaryPackagePurpose, SPDX_NAMESPACE.purpose_source) in graph assert (None, SPDX_NAMESPACE.releaseDate, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph @@ -57,7 +57,7 @@ def test_add_package_verification_code_to_graph(): graph = Graph() verification_code = package_verification_code_fixture() - add_package_verification_code_to_graph(verification_code, graph, URIRef("anyURI")) + add_package_verification_code_to_graph(verification_code, graph, URIRef("docNamespace")) assert (None, None, SPDX_NAMESPACE.PackageVerificationCode) in graph assert (None, SPDX_NAMESPACE.packageVerificationCodeValue, @@ -69,7 +69,7 @@ def test_external_package_ref_to_graph(): graph = Graph() external_reference = external_package_ref_fixture() - add_external_package_ref_to_graph(graph, external_reference, URIRef("anyURI")) + add_external_package_ref_to_graph(graph, external_reference, URIRef("docNamespace")) assert (None, None, SPDX_NAMESPACE.ExternalRef) in graph assert (None, SPDX_NAMESPACE.referenceCategory, SPDX_NAMESPACE.referenceCategory_packageManager) in graph diff --git a/tests/spdx/writer/rdf/test_relationship_writer.py b/tests/spdx/writer/rdf/test_relationship_writer.py index 95d4f59c6..4c2bcb1d7 100644 --- a/tests/spdx/writer/rdf/test_relationship_writer.py +++ b/tests/spdx/writer/rdf/test_relationship_writer.py @@ -18,8 +18,8 @@ def test_add_relationship_info_to_graph(): relationship = relationship_fixture() graph = Graph() - add_relationship_info_to_graph(relationship, graph, "anyURI", {}) + add_relationship_info_to_graph(relationship, graph, "docNamespace", {}) - assert(URIRef("anyURI#SPDXRef-DOCUMENT"), SPDX_NAMESPACE.relationship, None) in graph + assert(URIRef("docNamespace#SPDXRef-DOCUMENT"), SPDX_NAMESPACE.relationship, None) in graph assert (None, SPDX_NAMESPACE.relationshipType, SPDX_NAMESPACE.relationshipType_describes) in graph assert (None, SPDX_NAMESPACE.relatedSpdxElement, None) in graph diff --git a/tests/spdx/writer/rdf/test_snippet_writer.py b/tests/spdx/writer/rdf/test_snippet_writer.py index efce79598..4be2fbf6c 100644 --- a/tests/spdx/writer/rdf/test_snippet_writer.py +++ b/tests/spdx/writer/rdf/test_snippet_writer.py @@ -20,10 +20,10 @@ def test_add_snippet_information_to_graph(): graph = Graph() snippet = snippet_fixture() - add_snippet_information_to_graph(snippet, graph, "anyURI", {}) + add_snippet_information_to_graph(snippet, graph, "docNamespace", {}) - assert (URIRef("anyURI#SPDXRef-Snippet"), RDF.type, SPDX_NAMESPACE.Snippet) in graph - assert (None, SPDX_NAMESPACE.snippetFromFile, URIRef(f"anyURI#{snippet.file_spdx_id}")) in graph + assert (URIRef("docNamespace#SPDXRef-Snippet"), RDF.type, SPDX_NAMESPACE.Snippet) in graph + assert (None, SPDX_NAMESPACE.snippetFromFile, URIRef(f"docNamespace#{snippet.file_spdx_id}")) in graph assert (None, SPDX_NAMESPACE.licenseConcluded, None) in graph assert (None, SPDX_NAMESPACE.licenseInfoInSnippet, None) in graph assert (None, SPDX_NAMESPACE.licenseComments, Literal("snippetLicenseComment")) in graph @@ -38,12 +38,12 @@ def test_add_snippet_information_to_graph(): ((1, 3), POINTER_NAMESPACE.LineCharPointer, POINTER_NAMESPACE.lineNumber)]) def test_add_ranges_to_graph(range, pointer, predicate): graph = Graph() - add_range_to_graph(graph, URIRef("anyUR"), range, URIRef("anyURI#SPDXRef-File"), + add_range_to_graph(graph, URIRef("anyUR"), range, URIRef("docNamespace#SPDXRef-File"), pointer) assert (None, SPDX_NAMESPACE.range, None) in graph assert (None, POINTER_NAMESPACE.startPointer, None) in graph assert (None, POINTER_NAMESPACE.endPointer, None) in graph - assert (None, POINTER_NAMESPACE.reference, URIRef("anyURI#SPDXRef-File")) in graph + assert (None, POINTER_NAMESPACE.reference, URIRef("docNamespace#SPDXRef-File")) in graph assert (None, predicate, Literal(range[0])) in graph assert (None, predicate, Literal(range[1])) in graph From 7fa9cfae7f77c67d89cfa12ca3b643a143b5ae35 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 31 Jan 2023 13:52:47 +0100 Subject: [PATCH 208/362] [issue-407, review] add tests for helper function and add error handling Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/writer_utils.py | 5 +++++ tests/spdx/writer/rdf/test_writer_utils.py | 25 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 tests/spdx/writer/rdf/test_writer_utils.py diff --git a/src/spdx/writer/rdf/writer_utils.py b/src/spdx/writer/rdf/writer_utils.py index 3ae1774d7..7250df376 100644 --- a/src/spdx/writer/rdf/writer_utils.py +++ b/src/spdx/writer/rdf/writer_utils.py @@ -8,6 +8,7 @@ # 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 datetime import datetime from typing import Any, Optional, Dict @@ -60,6 +61,10 @@ def add_datetime_to_graph(graph: Graph, parent: Node, predicate: Node, value: Op def add_namespace_to_spdx_id(spdx_id: str, doc_namespace: str, external_doc_namespaces: Dict[str, str]) -> str: if ":" in spdx_id: external_doc_ref_id = spdx_id.split(":")[0] + if external_doc_ref_id not in external_doc_namespaces.keys(): + print(f"No namespace for external document reference with id {external_doc_ref_id} provided.", + file=sys.stderr) + return spdx_id return f"{external_doc_namespaces[external_doc_ref_id]}#{spdx_id.split(':')[1]}" if is_valid_internal_spdx_id(spdx_id): diff --git a/tests/spdx/writer/rdf/test_writer_utils.py b/tests/spdx/writer/rdf/test_writer_utils.py new file mode 100644 index 000000000..258fa3788 --- /dev/null +++ b/tests/spdx/writer/rdf/test_writer_utils.py @@ -0,0 +1,25 @@ +# Copyright (c) 2023 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. +import pytest + +from spdx.writer.rdf.writer_utils import add_namespace_to_spdx_id + + +@pytest.mark.parametrize("spdx_id,namespace,external_namespaces,expected", + [("SPDXRef-File", "docNamespace", {}, "docNamespace#SPDXRef-File"), + ("externalDoc:SPDXRef-File", "docNamespace", {"externalDoc": "externalNamespace"}, + "externalNamespace#SPDXRef-File"), + ("externalDoc#A-Ref", "", {}, "externalDoc#A-Ref"), + ("externalDoc:A-Ref", "", {}, "externalDoc:A-Ref")]) +def test_add_namespace_to_spdx_id(spdx_id, namespace, expected, external_namespaces): + extended_spdx_id = add_namespace_to_spdx_id(spdx_id, namespace, external_namespaces) + + assert extended_spdx_id == expected From edab59c62b3acdb15ee3c4f3656014f16d682e0f Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 31 Jan 2023 13:55:05 +0100 Subject: [PATCH 209/362] [issue-407, review] rename Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/creation_info_writer.py | 8 +++--- .../rdf/extracted_licensing_info_writer.py | 12 ++++----- src/spdx/writer/rdf/file_writer.py | 10 +++---- src/spdx/writer/rdf/package_writer.py | 26 +++++++++---------- src/spdx/writer/rdf/snippet_writer.py | 10 +++---- src/spdx/writer/rdf/writer_utils.py | 4 +-- 6 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/spdx/writer/rdf/creation_info_writer.py b/src/spdx/writer/rdf/creation_info_writer.py index b20163593..62c378dfe 100644 --- a/src/spdx/writer/rdf/creation_info_writer.py +++ b/src/spdx/writer/rdf/creation_info_writer.py @@ -13,7 +13,7 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.model.document import CreationInfo from spdx.writer.rdf.external_document_ref_writer import add_external_document_ref_to_graph -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_literal_value +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_optional_literal def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): @@ -22,7 +22,7 @@ def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): graph.add((doc_node, SPDX_NAMESPACE.specVersion, Literal(creation_info.spdx_version))) graph.add((doc_node, SPDX_NAMESPACE.dataLicense, URIRef(f"http://spdx.org/licenses/{creation_info.data_license}"))) graph.add((doc_node, SPDX_NAMESPACE.name, Literal(creation_info.name))) - add_literal_value(graph, doc_node, RDFS.comment, creation_info.document_comment) + add_optional_literal(graph, doc_node, RDFS.comment, creation_info.document_comment) creation_info_node = BNode() graph.add((creation_info_node, RDF.type, SPDX_NAMESPACE.CreationInfo)) @@ -32,8 +32,8 @@ def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): for creator in creation_info.creators: graph.add((creation_info_node, SPDX_NAMESPACE.creator, Literal(creator.to_serialized_string()))) - add_literal_value(graph, creation_info_node, SPDX_NAMESPACE.licenseListVersion, creation_info.license_list_version) - add_literal_value(graph, creation_info_node, RDFS.comment, creation_info.creator_comment) + add_optional_literal(graph, creation_info_node, SPDX_NAMESPACE.licenseListVersion, creation_info.license_list_version) + add_optional_literal(graph, creation_info_node, RDFS.comment, creation_info.creator_comment) graph.add((doc_node, SPDX_NAMESPACE.creationInfo, creation_info_node)) diff --git a/src/spdx/writer/rdf/extracted_licensing_info_writer.py b/src/spdx/writer/rdf/extracted_licensing_info_writer.py index 951cd4b55..750ea0d5f 100644 --- a/src/spdx/writer/rdf/extracted_licensing_info_writer.py +++ b/src/spdx/writer/rdf/extracted_licensing_info_writer.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from rdflib import Graph, URIRef, RDF, BNode, RDFS, Literal -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_literal_value, add_literal_or_no_assertion +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_optional_literal, add_literal_or_no_assertion from spdx.model.extracted_licensing_info import ExtractedLicensingInfo @@ -21,14 +21,14 @@ def add_extracted_licensing_info_to_graph(extracted_licensing_info: ExtractedLic graph.add((extracted_licensing_info_resource, RDF.type, SPDX_NAMESPACE.ExtractedLicensingInfo)) else: extracted_licensing_info_resource = BNode() - add_literal_value(graph, extracted_licensing_info_resource, SPDX_NAMESPACE.licenseId, - extracted_licensing_info.license_id) - add_literal_value(graph, extracted_licensing_info_resource, SPDX_NAMESPACE.extractedText, - extracted_licensing_info.extracted_text) + add_optional_literal(graph, extracted_licensing_info_resource, SPDX_NAMESPACE.licenseId, + extracted_licensing_info.license_id) + add_optional_literal(graph, extracted_licensing_info_resource, SPDX_NAMESPACE.extractedText, + extracted_licensing_info.extracted_text) add_literal_or_no_assertion(graph, extracted_licensing_info_resource, SPDX_NAMESPACE.name, extracted_licensing_info.license_name) for cross_reference in extracted_licensing_info.cross_references: graph.add((extracted_licensing_info_resource, RDFS.seeAlso, Literal(cross_reference))) - add_literal_value(graph, extracted_licensing_info_resource, RDFS.comment, extracted_licensing_info.comment) + add_optional_literal(graph, extracted_licensing_info_resource, RDFS.comment, extracted_licensing_info.comment) graph.add((doc_node, SPDX_NAMESPACE.hasExtractedLicensingInfo, extracted_licensing_info_resource)) diff --git a/src/spdx/writer/rdf/file_writer.py b/src/spdx/writer/rdf/file_writer.py index e0d7b5aab..f80891343 100644 --- a/src/spdx/writer/rdf/file_writer.py +++ b/src/spdx/writer/rdf/file_writer.py @@ -16,7 +16,7 @@ from spdx.writer.casing_tools import snake_case_to_camel_case from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph from spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_literal_value, add_namespace_to_spdx_id +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_optional_literal, add_namespace_to_spdx_id def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str, @@ -36,10 +36,10 @@ def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str, add_license_expression_or_none_or_no_assertion(graph, file_resource, SPDX_NAMESPACE.licenseInfoInFile, file.license_info_in_file, doc_namespace) - add_literal_value(graph, file_resource, SPDX_NAMESPACE.licenseComments, file.license_comment) - add_literal_value(graph, file_resource, SPDX_NAMESPACE.copyrightText, file.copyright_text) - add_literal_value(graph, file_resource, RDFS.comment, file.comment) - add_literal_value(graph, file_resource, SPDX_NAMESPACE.noticeText, file.notice) + add_optional_literal(graph, file_resource, SPDX_NAMESPACE.licenseComments, file.license_comment) + add_optional_literal(graph, file_resource, SPDX_NAMESPACE.copyrightText, file.copyright_text) + add_optional_literal(graph, file_resource, RDFS.comment, file.comment) + add_optional_literal(graph, file_resource, SPDX_NAMESPACE.noticeText, file.notice) for contributor in file.contributors: graph.add((file_resource, SPDX_NAMESPACE.fileContributor, Literal(contributor))) for attribution_text in file.attribution_texts: diff --git a/src/spdx/writer/rdf/package_writer.py b/src/spdx/writer/rdf/package_writer.py index c93a39622..d7da996a1 100644 --- a/src/spdx/writer/rdf/package_writer.py +++ b/src/spdx/writer/rdf/package_writer.py @@ -18,7 +18,7 @@ from spdx.model.package import Package, PackageVerificationCode, ExternalPackageRef, \ CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_literal_value, add_literal_or_no_assertion_or_none, \ +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_optional_literal, add_literal_or_no_assertion_or_none, \ add_datetime_to_graph, add_namespace_to_spdx_id @@ -28,10 +28,10 @@ def add_package_information_to_graph(package: Package, graph: Graph, doc_namespa graph.add((package_resource, RDF.type, SPDX_NAMESPACE.Package)) graph.add((package_resource, SPDX_NAMESPACE.name, Literal(package.name))) - add_literal_value(graph, package_resource, SPDX_NAMESPACE.versionInfo, package.version) - add_literal_value(graph, package_resource, SPDX_NAMESPACE.packageFileName, package.file_name) - add_literal_value(graph, package_resource, SPDX_NAMESPACE.supplier, package.supplier) - add_literal_value(graph, package_resource, SPDX_NAMESPACE.originator, package.originator) + add_optional_literal(graph, package_resource, SPDX_NAMESPACE.versionInfo, package.version) + add_optional_literal(graph, package_resource, SPDX_NAMESPACE.packageFileName, package.file_name) + add_optional_literal(graph, package_resource, SPDX_NAMESPACE.supplier, package.supplier) + add_optional_literal(graph, package_resource, SPDX_NAMESPACE.originator, package.originator) add_literal_or_no_assertion_or_none(graph, package_resource, SPDX_NAMESPACE.downloadLocation, package.download_location) graph.add((package_resource, SPDX_NAMESPACE.filesAnalyzed, Literal(package.files_analyzed, datatype=XSD.boolean))) @@ -39,8 +39,8 @@ def add_package_information_to_graph(package: Package, graph: Graph, doc_namespa for checksum in package.checksums: add_checksum_information_to_graph(checksum, graph, package_resource) - add_literal_value(graph, package_resource, DOAP.homepage, package.homepage) - add_literal_value(graph, package_resource, SPDX_NAMESPACE.sourceInfo, package.source_info) + add_optional_literal(graph, package_resource, DOAP.homepage, package.homepage) + add_optional_literal(graph, package_resource, SPDX_NAMESPACE.sourceInfo, package.source_info) add_license_expression_or_none_or_no_assertion(graph, package_resource, SPDX_NAMESPACE.licenseConcluded, package.license_concluded, doc_namespace) @@ -48,15 +48,15 @@ def add_package_information_to_graph(package: Package, graph: Graph, doc_namespa package.license_info_from_files, doc_namespace) add_license_expression_or_none_or_no_assertion(graph, package_resource, SPDX_NAMESPACE.licenseDeclared, package.license_declared, doc_namespace) - add_literal_value(graph, package_resource, SPDX_NAMESPACE.licenseComments, package.license_comment) - add_literal_value(graph, package_resource, SPDX_NAMESPACE.copyrightText, package.copyright_text) - add_literal_value(graph, package_resource, SPDX_NAMESPACE.summary, package.summary) - add_literal_value(graph, package_resource, SPDX_NAMESPACE.description, package.description) - add_literal_value(graph, package_resource, RDFS.comment, package.comment) + add_optional_literal(graph, package_resource, SPDX_NAMESPACE.licenseComments, package.license_comment) + add_optional_literal(graph, package_resource, SPDX_NAMESPACE.copyrightText, package.copyright_text) + add_optional_literal(graph, package_resource, SPDX_NAMESPACE.summary, package.summary) + add_optional_literal(graph, package_resource, SPDX_NAMESPACE.description, package.description) + add_optional_literal(graph, package_resource, RDFS.comment, package.comment) for external_reference in package.external_references: add_external_package_ref_to_graph(graph, external_reference, package_resource) for attribution_text in package.attribution_texts: - add_literal_value(graph, package_resource, SPDX_NAMESPACE.attributionText, attribution_text) + add_optional_literal(graph, package_resource, SPDX_NAMESPACE.attributionText, attribution_text) if package.primary_package_purpose: graph.add((package_resource, SPDX_NAMESPACE.primaryPackagePurpose, SPDX_NAMESPACE[f"purpose_{snake_case_to_camel_case(package.primary_package_purpose.name)}"])) diff --git a/src/spdx/writer/rdf/snippet_writer.py b/src/spdx/writer/rdf/snippet_writer.py index 8de6fb1e8..5a8d0bb9d 100644 --- a/src/spdx/writer/rdf/snippet_writer.py +++ b/src/spdx/writer/rdf/snippet_writer.py @@ -13,7 +13,7 @@ from rdflib import Graph, URIRef, RDF, RDFS, Literal, BNode from spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_literal_value, add_namespace_to_spdx_id, POINTER_NAMESPACE +from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_optional_literal, add_namespace_to_spdx_id, POINTER_NAMESPACE from spdx.model.snippet import Snippet @@ -35,10 +35,10 @@ def add_snippet_information_to_graph(snippet: Snippet, graph: Graph, doc_namespa snippet.license_concluded, doc_namespace) add_license_expression_or_none_or_no_assertion(graph, snippet_resource, SPDX_NAMESPACE.licenseInfoInSnippet, snippet.license_info_in_snippet, doc_namespace) - add_literal_value(graph, snippet_resource, SPDX_NAMESPACE.licenseComments, snippet.license_comment) - add_literal_value(graph, snippet_resource, SPDX_NAMESPACE.copyrightText, snippet.copyright_text) - add_literal_value(graph, snippet_resource, RDFS.comment, snippet.comment) - add_literal_value(graph, snippet_resource, SPDX_NAMESPACE.name, snippet.name) + add_optional_literal(graph, snippet_resource, SPDX_NAMESPACE.licenseComments, snippet.license_comment) + add_optional_literal(graph, snippet_resource, SPDX_NAMESPACE.copyrightText, snippet.copyright_text) + add_optional_literal(graph, snippet_resource, RDFS.comment, snippet.comment) + add_optional_literal(graph, snippet_resource, SPDX_NAMESPACE.name, snippet.name) for attribution_text in snippet.attribution_texts: graph.add((snippet_resource, SPDX_NAMESPACE.attributionText, Literal(attribution_text))) diff --git a/src/spdx/writer/rdf/writer_utils.py b/src/spdx/writer/rdf/writer_utils.py index 7250df376..8b4ae432c 100644 --- a/src/spdx/writer/rdf/writer_utils.py +++ b/src/spdx/writer/rdf/writer_utils.py @@ -24,7 +24,7 @@ POINTER_NAMESPACE = Namespace("http://www.w3.org/2009/pointers#") -def add_literal_value(graph: Graph, parent: Node, predicate: Node, value: Any): +def add_optional_literal(graph: Graph, parent: Node, predicate: Node, value: Any): if value is None: return if isinstance(value, list): @@ -50,7 +50,7 @@ def add_literal_or_no_assertion(graph: Graph, parent: Node, predicate: Node, val if isinstance(value, SpdxNoAssertion): graph.add((parent, predicate, SPDX_NAMESPACE.noassertion)) return - add_literal_value(graph, parent, predicate, value) + add_optional_literal(graph, parent, predicate, value) def add_datetime_to_graph(graph: Graph, parent: Node, predicate: Node, value: Optional[datetime]): From 9e283af097961780103137d99fa123ea156165fd Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 31 Jan 2023 14:07:48 +0100 Subject: [PATCH 210/362] [issue-407, review] refactor: change order of arguments and rename some of them Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/checksum_writer.py | 4 +- src/spdx/writer/rdf/creation_info_writer.py | 7 ++- .../rdf/extracted_licensing_info_writer.py | 14 ++--- src/spdx/writer/rdf/file_writer.py | 16 ++--- .../writer/rdf/license_expression_writer.py | 37 ++++++------ src/spdx/writer/rdf/package_writer.py | 58 +++++++++---------- src/spdx/writer/rdf/snippet_writer.py | 24 ++++---- src/spdx/writer/rdf/writer_utils.py | 12 ++-- .../rdf/test_license_expression_writer.py | 6 +- tests/spdx/writer/rdf/test_package_writer.py | 2 +- tests/spdx/writer/rdf/test_snippet_writer.py | 3 +- 11 files changed, 91 insertions(+), 92 deletions(-) diff --git a/src/spdx/writer/rdf/checksum_writer.py b/src/spdx/writer/rdf/checksum_writer.py index 599a82377..02b53befe 100644 --- a/src/spdx/writer/rdf/checksum_writer.py +++ b/src/spdx/writer/rdf/checksum_writer.py @@ -14,13 +14,13 @@ from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE -def add_checksum_information_to_graph(checksum: Checksum, graph: Graph, parent_node: URIRef): +def add_checksum_information_to_graph(checksum: Checksum, graph: Graph, parent: URIRef): checksum_node = BNode() graph.add((checksum_node, RDF.type, SPDX_NAMESPACE.Checksum)) graph.add((checksum_node, SPDX_NAMESPACE.algorithm, algorithm_to_rdf_string(checksum.algorithm))) graph.add((checksum_node, SPDX_NAMESPACE.checksumValue, Literal(checksum.value))) - graph.add((parent_node, SPDX_NAMESPACE.checksum, checksum_node)) + graph.add((parent, SPDX_NAMESPACE.checksum, checksum_node)) def algorithm_to_rdf_string(algorithm: ChecksumAlgorithm) -> URIRef: if "BLAKE2B" in algorithm.name: diff --git a/src/spdx/writer/rdf/creation_info_writer.py b/src/spdx/writer/rdf/creation_info_writer.py index 62c378dfe..f191fb411 100644 --- a/src/spdx/writer/rdf/creation_info_writer.py +++ b/src/spdx/writer/rdf/creation_info_writer.py @@ -22,7 +22,7 @@ def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): graph.add((doc_node, SPDX_NAMESPACE.specVersion, Literal(creation_info.spdx_version))) graph.add((doc_node, SPDX_NAMESPACE.dataLicense, URIRef(f"http://spdx.org/licenses/{creation_info.data_license}"))) graph.add((doc_node, SPDX_NAMESPACE.name, Literal(creation_info.name))) - add_optional_literal(graph, doc_node, RDFS.comment, creation_info.document_comment) + add_optional_literal(creation_info.document_comment, graph, doc_node, RDFS.comment) creation_info_node = BNode() graph.add((creation_info_node, RDF.type, SPDX_NAMESPACE.CreationInfo)) @@ -32,8 +32,9 @@ def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): for creator in creation_info.creators: graph.add((creation_info_node, SPDX_NAMESPACE.creator, Literal(creator.to_serialized_string()))) - add_optional_literal(graph, creation_info_node, SPDX_NAMESPACE.licenseListVersion, creation_info.license_list_version) - add_optional_literal(graph, creation_info_node, RDFS.comment, creation_info.creator_comment) + add_optional_literal(creation_info.license_list_version, graph, creation_info_node, + SPDX_NAMESPACE.licenseListVersion) + add_optional_literal(creation_info.creator_comment, graph, creation_info_node, RDFS.comment) graph.add((doc_node, SPDX_NAMESPACE.creationInfo, creation_info_node)) diff --git a/src/spdx/writer/rdf/extracted_licensing_info_writer.py b/src/spdx/writer/rdf/extracted_licensing_info_writer.py index 750ea0d5f..36c382e2c 100644 --- a/src/spdx/writer/rdf/extracted_licensing_info_writer.py +++ b/src/spdx/writer/rdf/extracted_licensing_info_writer.py @@ -21,14 +21,14 @@ def add_extracted_licensing_info_to_graph(extracted_licensing_info: ExtractedLic graph.add((extracted_licensing_info_resource, RDF.type, SPDX_NAMESPACE.ExtractedLicensingInfo)) else: extracted_licensing_info_resource = BNode() - add_optional_literal(graph, extracted_licensing_info_resource, SPDX_NAMESPACE.licenseId, - extracted_licensing_info.license_id) - add_optional_literal(graph, extracted_licensing_info_resource, SPDX_NAMESPACE.extractedText, - extracted_licensing_info.extracted_text) - add_literal_or_no_assertion(graph, extracted_licensing_info_resource, SPDX_NAMESPACE.name, - extracted_licensing_info.license_name) + add_optional_literal(extracted_licensing_info.license_id, graph, extracted_licensing_info_resource, + SPDX_NAMESPACE.licenseId) + add_optional_literal(extracted_licensing_info.extracted_text, graph, extracted_licensing_info_resource, + SPDX_NAMESPACE.extractedText) + add_literal_or_no_assertion(extracted_licensing_info.license_name, graph, extracted_licensing_info_resource, + SPDX_NAMESPACE.name) for cross_reference in extracted_licensing_info.cross_references: graph.add((extracted_licensing_info_resource, RDFS.seeAlso, Literal(cross_reference))) - add_optional_literal(graph, extracted_licensing_info_resource, RDFS.comment, extracted_licensing_info.comment) + add_optional_literal(extracted_licensing_info.comment, graph, extracted_licensing_info_resource, RDFS.comment) graph.add((doc_node, SPDX_NAMESPACE.hasExtractedLicensingInfo, extracted_licensing_info_resource)) diff --git a/src/spdx/writer/rdf/file_writer.py b/src/spdx/writer/rdf/file_writer.py index f80891343..a5b7c7c2c 100644 --- a/src/spdx/writer/rdf/file_writer.py +++ b/src/spdx/writer/rdf/file_writer.py @@ -31,15 +31,15 @@ def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str, for checksum in file.checksums: add_checksum_information_to_graph(checksum, graph, file_resource) - add_license_expression_or_none_or_no_assertion(graph, file_resource, SPDX_NAMESPACE.licenseConcluded, - file.license_concluded, doc_namespace) - add_license_expression_or_none_or_no_assertion(graph, file_resource, SPDX_NAMESPACE.licenseInfoInFile, - file.license_info_in_file, doc_namespace) + add_license_expression_or_none_or_no_assertion(file.license_concluded, graph, file_resource, + SPDX_NAMESPACE.licenseConcluded, doc_namespace) + add_license_expression_or_none_or_no_assertion(file.license_info_in_file, graph, file_resource, + SPDX_NAMESPACE.licenseInfoInFile, doc_namespace) - add_optional_literal(graph, file_resource, SPDX_NAMESPACE.licenseComments, file.license_comment) - add_optional_literal(graph, file_resource, SPDX_NAMESPACE.copyrightText, file.copyright_text) - add_optional_literal(graph, file_resource, RDFS.comment, file.comment) - add_optional_literal(graph, file_resource, SPDX_NAMESPACE.noticeText, file.notice) + add_optional_literal(file.license_comment, graph, file_resource, SPDX_NAMESPACE.licenseComments) + add_optional_literal(file.copyright_text, graph, file_resource, SPDX_NAMESPACE.copyrightText) + add_optional_literal(file.comment, graph, file_resource, RDFS.comment) + add_optional_literal(file.notice, graph, file_resource, SPDX_NAMESPACE.noticeText) for contributor in file.contributors: graph.add((file_resource, SPDX_NAMESPACE.fileContributor, Literal(contributor))) for attribution_text in file.attribution_texts: diff --git a/src/spdx/writer/rdf/license_expression_writer.py b/src/spdx/writer/rdf/license_expression_writer.py index 8aabd8eaf..d75e20527 100644 --- a/src/spdx/writer/rdf/license_expression_writer.py +++ b/src/spdx/writer/rdf/license_expression_writer.py @@ -22,8 +22,9 @@ from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE -def add_license_expression_or_none_or_no_assertion(graph: Graph, parent: Node, predicate: Node, value: Union[ - List[LicenseExpression], LicenseExpression, SpdxNoAssertion, SpdxNone], doc_namespace: str): +def add_license_expression_or_none_or_no_assertion(value: Union[ + List[LicenseExpression], LicenseExpression, SpdxNoAssertion, SpdxNone], graph: Graph, parent: Node, predicate: Node, + doc_namespace: str): if isinstance(value, SpdxNoAssertion): graph.add((parent, predicate, SPDX_NAMESPACE.noassertion)) return @@ -32,41 +33,41 @@ def add_license_expression_or_none_or_no_assertion(graph: Graph, parent: Node, p return if isinstance(value, list): for license_expression in value: - add_license_expression_to_graph(graph, parent, predicate, license_expression, doc_namespace) + add_license_expression_to_graph(license_expression, graph, parent, predicate, doc_namespace) if isinstance(value, LicenseExpression): - add_license_expression_to_graph(graph, parent, predicate, value, doc_namespace) + add_license_expression_to_graph(value, graph, parent, predicate, doc_namespace) -def add_license_expression_to_graph(graph: Graph, subject: Node, predicate: Node, - license_expression: Expression, doc_namespace: str): +def add_license_expression_to_graph(license_expression: Expression, graph: Graph, parent: Node, predicate: Node, + doc_namespace: str): if isinstance(license_expression, AND): member_node = BNode() graph.add((member_node, RDF.type, SPDX_NAMESPACE.ConjunctiveLicenseSet)) - graph.add((subject, predicate, member_node)) + graph.add((parent, predicate, member_node)) for arg in license_expression.args: - add_license_expression_to_graph(graph, member_node, SPDX_NAMESPACE.member, arg, doc_namespace) + add_license_expression_to_graph(arg, graph, member_node, SPDX_NAMESPACE.member, doc_namespace) if isinstance(license_expression, OR): member_node = BNode() graph.add((member_node, RDF.type, SPDX_NAMESPACE.DisjunctiveLicenseSet)) - graph.add((subject, predicate, member_node)) + graph.add((parent, predicate, member_node)) for arg in license_expression.args: - add_license_expression_to_graph(graph, member_node, SPDX_NAMESPACE.member, arg, doc_namespace) + add_license_expression_to_graph(arg, graph, member_node, SPDX_NAMESPACE.member, doc_namespace) if isinstance(license_expression, LicenseWithExceptionSymbol): member_node = BNode() graph.add((member_node, RDF.type, SPDX_NAMESPACE.WithExceptionOperator)) - graph.add((subject, predicate, member_node)) + graph.add((parent, predicate, member_node)) - add_license_expression_to_graph(graph, member_node, SPDX_NAMESPACE.member, license_expression.license_symbol, + add_license_expression_to_graph(license_expression.license_symbol, graph, member_node, SPDX_NAMESPACE.member, doc_namespace) - add_license_exception_to_graph(graph, license_expression.exception_symbol, member_node) + add_license_exception_to_graph(license_expression.exception_symbol, graph, member_node) if isinstance(license_expression, LicenseSymbol): if license_or_exception_is_on_spdx_licensing_list(license_expression): graph.add( - (subject, predicate, URIRef(f"http://spdx.org/licenses/{license_expression}"))) + (parent, predicate, URIRef(f"http://spdx.org/licenses/{license_expression}"))) else: # assuming that the license expression is a LicenseRef to an instance of ExtractedLicensingInfo - graph.add((subject, predicate, URIRef(f"{doc_namespace}#{license_expression}"))) + graph.add((parent, predicate, URIRef(f"{doc_namespace}#{license_expression}"))) def license_or_exception_is_on_spdx_licensing_list(license_symbol: LicenseSymbol) -> bool: @@ -74,13 +75,13 @@ def license_or_exception_is_on_spdx_licensing_list(license_symbol: LicenseSymbol return not symbol_info.errors -def add_license_exception_to_graph(graph: Graph, license_exception: LicenseSymbol, member_node: Node): +def add_license_exception_to_graph(license_exception: LicenseSymbol, graph: Graph, parent: Node): if license_or_exception_is_on_spdx_licensing_list(license_exception): exception_node = URIRef(f"http://spdx.org/licenses/{license_exception}") - graph.add((member_node, SPDX_NAMESPACE.licenseException, exception_node)) + graph.add((parent, SPDX_NAMESPACE.licenseException, exception_node)) else: exception_node = BNode() graph.add((exception_node, SPDX_NAMESPACE.licenseExceptionId, Literal(license_exception))) - graph.add((member_node, SPDX_NAMESPACE.licenseException, exception_node)) + graph.add((parent, SPDX_NAMESPACE.licenseException, exception_node)) graph.add((exception_node, RDF.type, SPDX_NAMESPACE.LicenseException)) diff --git a/src/spdx/writer/rdf/package_writer.py b/src/spdx/writer/rdf/package_writer.py index d7da996a1..2bd3707de 100644 --- a/src/spdx/writer/rdf/package_writer.py +++ b/src/spdx/writer/rdf/package_writer.py @@ -28,46 +28,45 @@ def add_package_information_to_graph(package: Package, graph: Graph, doc_namespa graph.add((package_resource, RDF.type, SPDX_NAMESPACE.Package)) graph.add((package_resource, SPDX_NAMESPACE.name, Literal(package.name))) - add_optional_literal(graph, package_resource, SPDX_NAMESPACE.versionInfo, package.version) - add_optional_literal(graph, package_resource, SPDX_NAMESPACE.packageFileName, package.file_name) - add_optional_literal(graph, package_resource, SPDX_NAMESPACE.supplier, package.supplier) - add_optional_literal(graph, package_resource, SPDX_NAMESPACE.originator, package.originator) - add_literal_or_no_assertion_or_none(graph, package_resource, SPDX_NAMESPACE.downloadLocation, - package.download_location) + add_optional_literal(package.version, graph, package_resource, SPDX_NAMESPACE.versionInfo) + add_optional_literal(package.file_name, graph, package_resource, SPDX_NAMESPACE.packageFileName) + add_optional_literal(package.supplier, graph, package_resource, SPDX_NAMESPACE.supplier) + add_optional_literal(package.originator, graph, package_resource, SPDX_NAMESPACE.originator) + add_literal_or_no_assertion_or_none(package.download_location, graph, package_resource, + SPDX_NAMESPACE.downloadLocation) graph.add((package_resource, SPDX_NAMESPACE.filesAnalyzed, Literal(package.files_analyzed, datatype=XSD.boolean))) add_package_verification_code_to_graph(package.verification_code, graph, package_resource) for checksum in package.checksums: add_checksum_information_to_graph(checksum, graph, package_resource) - add_optional_literal(graph, package_resource, DOAP.homepage, package.homepage) - add_optional_literal(graph, package_resource, SPDX_NAMESPACE.sourceInfo, package.source_info) - add_license_expression_or_none_or_no_assertion(graph, package_resource, SPDX_NAMESPACE.licenseConcluded, - package.license_concluded, - doc_namespace) - add_license_expression_or_none_or_no_assertion(graph, package_resource, SPDX_NAMESPACE.licenseInfoFromFiles, - package.license_info_from_files, doc_namespace) - add_license_expression_or_none_or_no_assertion(graph, package_resource, SPDX_NAMESPACE.licenseDeclared, - package.license_declared, doc_namespace) - add_optional_literal(graph, package_resource, SPDX_NAMESPACE.licenseComments, package.license_comment) - add_optional_literal(graph, package_resource, SPDX_NAMESPACE.copyrightText, package.copyright_text) - add_optional_literal(graph, package_resource, SPDX_NAMESPACE.summary, package.summary) - add_optional_literal(graph, package_resource, SPDX_NAMESPACE.description, package.description) - add_optional_literal(graph, package_resource, RDFS.comment, package.comment) + add_optional_literal(package.homepage, graph, package_resource, DOAP.homepage) + add_optional_literal(package.source_info, graph, package_resource, SPDX_NAMESPACE.sourceInfo) + add_license_expression_or_none_or_no_assertion(package.license_concluded, graph, package_resource, + SPDX_NAMESPACE.licenseConcluded, doc_namespace) + add_license_expression_or_none_or_no_assertion(package.license_info_from_files, graph, package_resource, + SPDX_NAMESPACE.licenseInfoFromFiles, doc_namespace) + add_license_expression_or_none_or_no_assertion(package.license_declared, graph, package_resource, + SPDX_NAMESPACE.licenseDeclared, doc_namespace) + add_optional_literal(package.license_comment, graph, package_resource, SPDX_NAMESPACE.licenseComments) + add_optional_literal(package.copyright_text, graph, package_resource, SPDX_NAMESPACE.copyrightText) + add_optional_literal(package.summary, graph, package_resource, SPDX_NAMESPACE.summary) + add_optional_literal(package.description, graph, package_resource, SPDX_NAMESPACE.description) + add_optional_literal(package.comment, graph, package_resource, RDFS.comment) for external_reference in package.external_references: - add_external_package_ref_to_graph(graph, external_reference, package_resource) + add_external_package_ref_to_graph(external_reference, graph, package_resource) for attribution_text in package.attribution_texts: - add_optional_literal(graph, package_resource, SPDX_NAMESPACE.attributionText, attribution_text) + add_optional_literal(attribution_text, graph, package_resource, SPDX_NAMESPACE.attributionText) if package.primary_package_purpose: graph.add((package_resource, SPDX_NAMESPACE.primaryPackagePurpose, SPDX_NAMESPACE[f"purpose_{snake_case_to_camel_case(package.primary_package_purpose.name)}"])) - add_datetime_to_graph(graph, package_resource, SPDX_NAMESPACE.releaseDate, package.release_date) - add_datetime_to_graph(graph, package_resource, SPDX_NAMESPACE.builtDate, package.built_date) - add_datetime_to_graph(graph, package_resource, SPDX_NAMESPACE.validUntilDate, package.valid_until_date) + add_datetime_to_graph(package.release_date, graph, package_resource, SPDX_NAMESPACE.releaseDate) + add_datetime_to_graph(package.built_date, graph, package_resource, SPDX_NAMESPACE.builtDate) + add_datetime_to_graph(package.valid_until_date, graph, package_resource, SPDX_NAMESPACE.validUntilDate) def add_package_verification_code_to_graph(package_verification_code: PackageVerificationCode, graph: Graph, - package_resource: URIRef): + package_node: URIRef): if not package_verification_code: return package_verification_code_node = BNode() @@ -78,11 +77,10 @@ def add_package_verification_code_to_graph(package_verification_code: PackageVer graph.add((package_verification_code_node, SPDX_NAMESPACE.packageVerificationCodeExcludedFile, Literal(excluded_file))) - graph.add((package_resource, SPDX_NAMESPACE.packageVerificationCode, package_verification_code_node)) + graph.add((package_node, SPDX_NAMESPACE.packageVerificationCode, package_verification_code_node)) -def add_external_package_ref_to_graph(graph: Graph, external_package_ref: ExternalPackageRef, - package_resource: URIRef): +def add_external_package_ref_to_graph(external_package_ref: ExternalPackageRef, graph: Graph, package_node: URIRef): external_package_ref_node = BNode() graph.add((external_package_ref_node, RDF.type, SPDX_NAMESPACE.ExternalRef)) graph.add((external_package_ref_node, SPDX_NAMESPACE.referenceCategory, @@ -98,4 +96,4 @@ def add_external_package_ref_to_graph(graph: Graph, external_package_ref: Extern if external_package_ref.comment: graph.add((external_package_ref_node, RDFS.comment, Literal(external_package_ref.comment))) - graph.add((package_resource, SPDX_NAMESPACE.externalRef, external_package_ref_node)) + graph.add((package_node, SPDX_NAMESPACE.externalRef, external_package_ref_node)) diff --git a/src/spdx/writer/rdf/snippet_writer.py b/src/spdx/writer/rdf/snippet_writer.py index 5a8d0bb9d..64831b757 100644 --- a/src/spdx/writer/rdf/snippet_writer.py +++ b/src/spdx/writer/rdf/snippet_writer.py @@ -27,23 +27,23 @@ def add_snippet_information_to_graph(snippet: Snippet, graph: Graph, doc_namespa add_namespace_to_spdx_id(snippet.file_spdx_id, doc_namespace, external_doc_ref_to_namespace)) graph.add((snippet_resource, SPDX_NAMESPACE.snippetFromFile, snippet_from_file_ref)) - add_range_to_graph(graph, snippet_resource, snippet.byte_range, snippet_from_file_ref, + add_range_to_graph(snippet.byte_range, graph, snippet_resource, snippet_from_file_ref, POINTER_NAMESPACE.ByteOffsetPointer) - add_range_to_graph(graph, snippet_resource, snippet.line_range, snippet_from_file_ref, + add_range_to_graph(snippet.line_range, graph, snippet_resource, snippet_from_file_ref, POINTER_NAMESPACE.LineCharPointer) - add_license_expression_or_none_or_no_assertion(graph, snippet_resource, SPDX_NAMESPACE.licenseConcluded, - snippet.license_concluded, doc_namespace) - add_license_expression_or_none_or_no_assertion(graph, snippet_resource, SPDX_NAMESPACE.licenseInfoInSnippet, - snippet.license_info_in_snippet, doc_namespace) - add_optional_literal(graph, snippet_resource, SPDX_NAMESPACE.licenseComments, snippet.license_comment) - add_optional_literal(graph, snippet_resource, SPDX_NAMESPACE.copyrightText, snippet.copyright_text) - add_optional_literal(graph, snippet_resource, RDFS.comment, snippet.comment) - add_optional_literal(graph, snippet_resource, SPDX_NAMESPACE.name, snippet.name) + add_license_expression_or_none_or_no_assertion(snippet.license_concluded, graph, snippet_resource, + SPDX_NAMESPACE.licenseConcluded, doc_namespace) + add_license_expression_or_none_or_no_assertion(snippet.license_info_in_snippet, graph, snippet_resource, + SPDX_NAMESPACE.licenseInfoInSnippet, doc_namespace) + add_optional_literal(snippet.license_comment, graph, snippet_resource, SPDX_NAMESPACE.licenseComments) + add_optional_literal(snippet.copyright_text, graph, snippet_resource, SPDX_NAMESPACE.copyrightText) + add_optional_literal(snippet.comment, graph, snippet_resource, RDFS.comment) + add_optional_literal(snippet.name, graph, snippet_resource, SPDX_NAMESPACE.name) for attribution_text in snippet.attribution_texts: graph.add((snippet_resource, SPDX_NAMESPACE.attributionText, Literal(attribution_text))) -def add_range_to_graph(graph: Graph, snippet_resource: URIRef, range_information: Optional[Tuple[int, int]], +def add_range_to_graph(range_information: Optional[Tuple[int, int]], graph: Graph, snippet_node: URIRef, snippet_from_file_ref: URIRef, pointer_class: URIRef): start_end_pointer = BNode() graph.add((start_end_pointer, RDF.type, POINTER_NAMESPACE.StartEndPointer)) @@ -58,4 +58,4 @@ def add_range_to_graph(graph: Graph, snippet_resource: URIRef, range_information else: graph.add((pointer_node, POINTER_NAMESPACE.lineNumber, Literal(value))) - graph.add((snippet_resource, SPDX_NAMESPACE.range, start_end_pointer)) + graph.add((snippet_node, SPDX_NAMESPACE.range, start_end_pointer)) diff --git a/src/spdx/writer/rdf/writer_utils.py b/src/spdx/writer/rdf/writer_utils.py index 8b4ae432c..a9fbfb36a 100644 --- a/src/spdx/writer/rdf/writer_utils.py +++ b/src/spdx/writer/rdf/writer_utils.py @@ -24,7 +24,7 @@ POINTER_NAMESPACE = Namespace("http://www.w3.org/2009/pointers#") -def add_optional_literal(graph: Graph, parent: Node, predicate: Node, value: Any): +def add_optional_literal(value: Any, graph: Graph, parent: Node, predicate: Node): if value is None: return if isinstance(value, list): @@ -35,25 +35,25 @@ def add_optional_literal(graph: Graph, parent: Node, predicate: Node, value: Any return -def add_literal_or_no_assertion_or_none(graph: Graph, parent: Node, predicate: Node, value: Any): +def add_literal_or_no_assertion_or_none(value: Any, graph: Graph, parent: Node, predicate: Node): if value is None: return if isinstance(value, SpdxNone): graph.add((parent, predicate, SPDX_NAMESPACE.none)) return - add_literal_or_no_assertion(graph, parent, predicate, value) + add_literal_or_no_assertion(value, graph, parent, predicate) -def add_literal_or_no_assertion(graph: Graph, parent: Node, predicate: Node, value: Any): +def add_literal_or_no_assertion(value: Any, graph: Graph, parent: Node, predicate: Node): if value is None: return if isinstance(value, SpdxNoAssertion): graph.add((parent, predicate, SPDX_NAMESPACE.noassertion)) return - add_optional_literal(graph, parent, predicate, value) + add_optional_literal(value, graph, parent, predicate) -def add_datetime_to_graph(graph: Graph, parent: Node, predicate: Node, value: Optional[datetime]): +def add_datetime_to_graph(value: Optional[datetime], graph: Graph, parent: Node, predicate: Node): if value: graph.add((parent, predicate, Literal(datetime_to_iso_string(value)))) diff --git a/tests/spdx/writer/rdf/test_license_expression_writer.py b/tests/spdx/writer/rdf/test_license_expression_writer.py index 432e01b2c..2f1b4f693 100644 --- a/tests/spdx/writer/rdf/test_license_expression_writer.py +++ b/tests/spdx/writer/rdf/test_license_expression_writer.py @@ -20,7 +20,7 @@ def test_add_conjunctive_license_set_to_graph(): graph = Graph() license_expression = get_spdx_licensing().parse("MIT AND GPL-2.0") - add_license_expression_to_graph(graph, URIRef("parentNode"), SPDX_NAMESPACE.licenseConcluded, license_expression, + add_license_expression_to_graph(license_expression, graph, URIRef("parentNode"), SPDX_NAMESPACE.licenseConcluded, "https://namespace") assert (URIRef("parentNode"), SPDX_NAMESPACE.licenseConcluded, None) in graph @@ -33,7 +33,7 @@ def test_add_disjunctive_license_set_to_graph(): graph = Graph() license_expression = get_spdx_licensing().parse("MIT OR GPL-2.0") - add_license_expression_to_graph(graph, URIRef("parentNode"), SPDX_NAMESPACE.licenseConcluded, license_expression, + add_license_expression_to_graph(license_expression, graph, URIRef("parentNode"), SPDX_NAMESPACE.licenseConcluded, "https://namespace") assert (URIRef("parentNode"), SPDX_NAMESPACE.licenseConcluded, None) in graph @@ -52,7 +52,7 @@ def test_license_exception_to_graph(license_with_exception, expected_triple): graph = Graph() license_expression = get_spdx_licensing().parse(license_with_exception) - add_license_expression_to_graph(graph, URIRef("parentNode"), SPDX_NAMESPACE.licenseConcluded, license_expression, + add_license_expression_to_graph(license_expression, graph, URIRef("parentNode"), SPDX_NAMESPACE.licenseConcluded, "https://namespace") assert (URIRef("parentNode"), SPDX_NAMESPACE.licenseConcluded, None) in graph diff --git a/tests/spdx/writer/rdf/test_package_writer.py b/tests/spdx/writer/rdf/test_package_writer.py index fab30771a..3a3172f35 100644 --- a/tests/spdx/writer/rdf/test_package_writer.py +++ b/tests/spdx/writer/rdf/test_package_writer.py @@ -69,7 +69,7 @@ def test_external_package_ref_to_graph(): graph = Graph() external_reference = external_package_ref_fixture() - add_external_package_ref_to_graph(graph, external_reference, URIRef("docNamespace")) + add_external_package_ref_to_graph(external_reference, graph, URIRef("docNamespace")) assert (None, None, SPDX_NAMESPACE.ExternalRef) in graph assert (None, SPDX_NAMESPACE.referenceCategory, SPDX_NAMESPACE.referenceCategory_packageManager) in graph diff --git a/tests/spdx/writer/rdf/test_snippet_writer.py b/tests/spdx/writer/rdf/test_snippet_writer.py index 4be2fbf6c..a17ed9051 100644 --- a/tests/spdx/writer/rdf/test_snippet_writer.py +++ b/tests/spdx/writer/rdf/test_snippet_writer.py @@ -38,8 +38,7 @@ def test_add_snippet_information_to_graph(): ((1, 3), POINTER_NAMESPACE.LineCharPointer, POINTER_NAMESPACE.lineNumber)]) def test_add_ranges_to_graph(range, pointer, predicate): graph = Graph() - add_range_to_graph(graph, URIRef("anyUR"), range, URIRef("docNamespace#SPDXRef-File"), - pointer) + add_range_to_graph(range, graph, URIRef("anyUR"), URIRef("docNamespace#SPDXRef-File"), pointer) assert (None, SPDX_NAMESPACE.range, None) in graph assert (None, POINTER_NAMESPACE.startPointer, None) in graph From 48d8746a56562792c1e60298fdf9e5cf600ae5d2 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 31 Jan 2023 14:21:45 +0100 Subject: [PATCH 211/362] [issue-407, review] make comment in pyproject.toml more comprehensible Signed-off-by: Meret Behrens --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index beda649b7..97a5f851a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,8 +41,10 @@ include-package-data = true [tool.setuptools.packages.find] where = ["src"] +# the default git describe resolves to the tag `python3.6` because the current release tag is not on main +# by adding "v" the command resolves to the alpha release of 0.7.0 which leads to the desired name spdx-tools-0.7.0 [tool.setuptools_scm] -git_describe_command = ["git", "describe", "--dirty", "--tags", "--long", "--match", "v[0-9]*"] # `python3.6` tag falsely matches to the default one, clearly a bug in setuptools_scm +git_describe_command = ["git", "describe", "--dirty", "--tags", "--long", "--match", "v[0-9]*"] [tool.aliases] release = "clean --all sdist --formats=gztar bdist_wheel" From af2bcb0628dd1a87d6c2d1cd396a8bdbb8c8af2b Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 31 Jan 2023 14:25:47 +0100 Subject: [PATCH 212/362] [issue-407, review] rename methods Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/annotation_writer.py | 4 ++-- src/spdx/writer/rdf/checksum_writer.py | 2 +- .../rdf/external_document_ref_writer.py | 4 ++-- src/spdx/writer/rdf/file_writer.py | 8 ++++---- src/spdx/writer/rdf/package_writer.py | 8 ++++---- src/spdx/writer/rdf/rdf_writer.py | 20 +++++++++---------- src/spdx/writer/rdf/relationship_writer.py | 4 ++-- src/spdx/writer/rdf/snippet_writer.py | 4 ++-- .../spdx/writer/rdf/test_annotation_writer.py | 6 +++--- tests/spdx/writer/rdf/test_checksum_writer.py | 6 +++--- tests/spdx/writer/rdf/test_file_writer.py | 6 +++--- tests/spdx/writer/rdf/test_package_writer.py | 6 +++--- .../writer/rdf/test_relationship_writer.py | 6 +++--- tests/spdx/writer/rdf/test_snippet_writer.py | 6 +++--- 14 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/spdx/writer/rdf/annotation_writer.py b/src/spdx/writer/rdf/annotation_writer.py index c28add860..207b3102d 100644 --- a/src/spdx/writer/rdf/annotation_writer.py +++ b/src/spdx/writer/rdf/annotation_writer.py @@ -18,8 +18,8 @@ from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_namespace_to_spdx_id -def add_annotation_info_to_graph(annotation: Annotation, graph: Graph, doc_namespace: str, - external_doc_ref_to_namespace: Dict[str, str]): +def add_annotation_to_graph(annotation: Annotation, graph: Graph, doc_namespace: str, + external_doc_ref_to_namespace: Dict[str, str]): annotation_resource = URIRef(add_namespace_to_spdx_id(annotation.spdx_id, doc_namespace, external_doc_ref_to_namespace)) annotation_node = BNode() graph.add((annotation_node, RDF.type, SPDX_NAMESPACE.Annotation)) diff --git a/src/spdx/writer/rdf/checksum_writer.py b/src/spdx/writer/rdf/checksum_writer.py index 02b53befe..6a5b760a3 100644 --- a/src/spdx/writer/rdf/checksum_writer.py +++ b/src/spdx/writer/rdf/checksum_writer.py @@ -14,7 +14,7 @@ from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE -def add_checksum_information_to_graph(checksum: Checksum, graph: Graph, parent: URIRef): +def add_checksum_to_graph(checksum: Checksum, graph: Graph, parent: URIRef): checksum_node = BNode() graph.add((checksum_node, RDF.type, SPDX_NAMESPACE.Checksum)) graph.add((checksum_node, SPDX_NAMESPACE.algorithm, algorithm_to_rdf_string(checksum.algorithm))) diff --git a/src/spdx/writer/rdf/external_document_ref_writer.py b/src/spdx/writer/rdf/external_document_ref_writer.py index 299467c26..cf4ae82d9 100644 --- a/src/spdx/writer/rdf/external_document_ref_writer.py +++ b/src/spdx/writer/rdf/external_document_ref_writer.py @@ -11,7 +11,7 @@ from rdflib import Graph, URIRef, RDF from spdx.model.external_document_ref import ExternalDocumentRef -from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph +from spdx.writer.rdf.checksum_writer import add_checksum_to_graph from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE @@ -20,6 +20,6 @@ def add_external_document_ref_to_graph(external_document_ref: ExternalDocumentRe external_document_ref_resource = URIRef(f"{doc_namespace}#{external_document_ref.document_ref_id}") graph.add((external_document_ref_resource, RDF.type, SPDX_NAMESPACE.ExternalDocumentRef)) graph.add((external_document_ref_resource, SPDX_NAMESPACE.spdxDocument, URIRef(external_document_ref.document_uri))) - add_checksum_information_to_graph(external_document_ref.checksum, graph, external_document_ref_resource) + add_checksum_to_graph(external_document_ref.checksum, graph, external_document_ref_resource) graph.add((doc_node, SPDX_NAMESPACE.externalDocumentRef, external_document_ref_resource)) diff --git a/src/spdx/writer/rdf/file_writer.py b/src/spdx/writer/rdf/file_writer.py index a5b7c7c2c..5c025865f 100644 --- a/src/spdx/writer/rdf/file_writer.py +++ b/src/spdx/writer/rdf/file_writer.py @@ -14,13 +14,13 @@ from spdx.model.file import File from spdx.writer.casing_tools import snake_case_to_camel_case -from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph +from spdx.writer.rdf.checksum_writer import add_checksum_to_graph from spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_optional_literal, add_namespace_to_spdx_id -def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str, - external_doc_ref_to_namespace: Dict[str, str]): +def add_file_to_graph(file: File, graph: Graph, doc_namespace: str, + external_doc_ref_to_namespace: Dict[str, str]): file_resource = URIRef(add_namespace_to_spdx_id(file.spdx_id, doc_namespace, external_doc_ref_to_namespace)) graph.add((file_resource, RDF.type, SPDX_NAMESPACE.File)) graph.add((file_resource, SPDX_NAMESPACE.fileName, Literal(file.name))) @@ -29,7 +29,7 @@ def add_file_information_to_graph(file: File, graph: Graph, doc_namespace: str, SPDX_NAMESPACE[f"fileType_{snake_case_to_camel_case(file_type.name)}"])) for checksum in file.checksums: - add_checksum_information_to_graph(checksum, graph, file_resource) + add_checksum_to_graph(checksum, graph, file_resource) add_license_expression_or_none_or_no_assertion(file.license_concluded, graph, file_resource, SPDX_NAMESPACE.licenseConcluded, doc_namespace) diff --git a/src/spdx/writer/rdf/package_writer.py b/src/spdx/writer/rdf/package_writer.py index 2bd3707de..b2b78e776 100644 --- a/src/spdx/writer/rdf/package_writer.py +++ b/src/spdx/writer/rdf/package_writer.py @@ -14,7 +14,7 @@ from spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion from spdx.writer.casing_tools import snake_case_to_camel_case -from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph +from spdx.writer.rdf.checksum_writer import add_checksum_to_graph from spdx.model.package import Package, PackageVerificationCode, ExternalPackageRef, \ CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES @@ -22,8 +22,8 @@ add_datetime_to_graph, add_namespace_to_spdx_id -def add_package_information_to_graph(package: Package, graph: Graph, doc_namespace: str, - external_doc_ref_to_namespace: Dict[str, str]): +def add_package_to_graph(package: Package, graph: Graph, doc_namespace: str, + external_doc_ref_to_namespace: Dict[str, str]): package_resource = URIRef(add_namespace_to_spdx_id(package.spdx_id, doc_namespace, external_doc_ref_to_namespace)) graph.add((package_resource, RDF.type, SPDX_NAMESPACE.Package)) @@ -37,7 +37,7 @@ def add_package_information_to_graph(package: Package, graph: Graph, doc_namespa graph.add((package_resource, SPDX_NAMESPACE.filesAnalyzed, Literal(package.files_analyzed, datatype=XSD.boolean))) add_package_verification_code_to_graph(package.verification_code, graph, package_resource) for checksum in package.checksums: - add_checksum_information_to_graph(checksum, graph, package_resource) + add_checksum_to_graph(checksum, graph, package_resource) add_optional_literal(package.homepage, graph, package_resource, DOAP.homepage) add_optional_literal(package.source_info, graph, package_resource, SPDX_NAMESPACE.sourceInfo) diff --git a/src/spdx/writer/rdf/rdf_writer.py b/src/spdx/writer/rdf/rdf_writer.py index d37ab7966..767517df6 100644 --- a/src/spdx/writer/rdf/rdf_writer.py +++ b/src/spdx/writer/rdf/rdf_writer.py @@ -16,13 +16,13 @@ from spdx.model.document import Document from spdx.validation.document_validator import validate_full_spdx_document from spdx.validation.validation_message import ValidationMessage -from spdx.writer.rdf.annotation_writer import add_annotation_info_to_graph +from spdx.writer.rdf.annotation_writer import add_annotation_to_graph from spdx.writer.rdf.creation_info_writer import add_creation_info_to_graph from spdx.writer.rdf.extracted_licensing_info_writer import add_extracted_licensing_info_to_graph -from spdx.writer.rdf.file_writer import add_file_information_to_graph -from spdx.writer.rdf.package_writer import add_package_information_to_graph -from spdx.writer.rdf.relationship_writer import add_relationship_info_to_graph -from spdx.writer.rdf.snippet_writer import add_snippet_information_to_graph +from spdx.writer.rdf.file_writer import add_file_to_graph +from spdx.writer.rdf.package_writer import add_package_to_graph +from spdx.writer.rdf.relationship_writer import add_relationship_to_graph +from spdx.writer.rdf.snippet_writer import add_snippet_to_graph from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, POINTER_NAMESPACE @@ -39,19 +39,19 @@ def write_document_to_file(document: Document, file_name: str, validate: bool): external_doc_ref in document.creation_info.external_document_refs} doc_node = add_creation_info_to_graph(document.creation_info, graph) for annotation in document.annotations: - add_annotation_info_to_graph(annotation, graph, doc_namespace, external_doc_ref_to_namespace) + add_annotation_to_graph(annotation, graph, doc_namespace, external_doc_ref_to_namespace) for file in document.files: - add_file_information_to_graph(file, graph, doc_namespace, external_doc_ref_to_namespace) + add_file_to_graph(file, graph, doc_namespace, external_doc_ref_to_namespace) for package in document.packages: - add_package_information_to_graph(package, graph, doc_namespace, external_doc_ref_to_namespace) + add_package_to_graph(package, graph, doc_namespace, external_doc_ref_to_namespace) for relationship in document.relationships: - add_relationship_info_to_graph(relationship, graph, doc_namespace, external_doc_ref_to_namespace) + add_relationship_to_graph(relationship, graph, doc_namespace, external_doc_ref_to_namespace) for snippet in document.snippets: - add_snippet_information_to_graph(snippet, graph, doc_namespace, external_doc_ref_to_namespace) + add_snippet_to_graph(snippet, graph, doc_namespace, external_doc_ref_to_namespace) for extracted_licensing_info in document.extracted_licensing_info: add_extracted_licensing_info_to_graph(extracted_licensing_info, graph, doc_node, doc_namespace) diff --git a/src/spdx/writer/rdf/relationship_writer.py b/src/spdx/writer/rdf/relationship_writer.py index 98ab6bf8e..e57eca0b0 100644 --- a/src/spdx/writer/rdf/relationship_writer.py +++ b/src/spdx/writer/rdf/relationship_writer.py @@ -19,8 +19,8 @@ from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_namespace_to_spdx_id -def add_relationship_info_to_graph(relationship: Relationship, graph: Graph, doc_namespace: str, - external_doc_ref_to_namespace: Dict[str, str]): +def add_relationship_to_graph(relationship: Relationship, graph: Graph, doc_namespace: str, + external_doc_ref_to_namespace: Dict[str, str]): relationship_node = BNode() graph.add((relationship_node, RDF.type, SPDX_NAMESPACE.Relationship)) graph.add((relationship_node, SPDX_NAMESPACE.relationshipType, diff --git a/src/spdx/writer/rdf/snippet_writer.py b/src/spdx/writer/rdf/snippet_writer.py index 64831b757..cda25f67d 100644 --- a/src/spdx/writer/rdf/snippet_writer.py +++ b/src/spdx/writer/rdf/snippet_writer.py @@ -18,8 +18,8 @@ from spdx.model.snippet import Snippet -def add_snippet_information_to_graph(snippet: Snippet, graph: Graph, doc_namespace: str, - external_doc_ref_to_namespace: Dict[str, str]): +def add_snippet_to_graph(snippet: Snippet, graph: Graph, doc_namespace: str, + external_doc_ref_to_namespace: Dict[str, str]): snippet_resource = URIRef(add_namespace_to_spdx_id(snippet.spdx_id, doc_namespace, external_doc_ref_to_namespace)) graph.add((snippet_resource, RDF.type, SPDX_NAMESPACE.Snippet)) diff --git a/tests/spdx/writer/rdf/test_annotation_writer.py b/tests/spdx/writer/rdf/test_annotation_writer.py index 5c90f7779..99792e74e 100644 --- a/tests/spdx/writer/rdf/test_annotation_writer.py +++ b/tests/spdx/writer/rdf/test_annotation_writer.py @@ -13,16 +13,16 @@ from rdflib import Graph, Literal, RDFS, URIRef from spdx.datetime_conversions import datetime_to_iso_string -from spdx.writer.rdf.annotation_writer import add_annotation_info_to_graph +from spdx.writer.rdf.annotation_writer import add_annotation_to_graph from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE from tests.spdx.fixtures import annotation_fixture -def test_add_annotation_info_to_graph(): +def test_add_annotation_to_graph(): graph = Graph() annotation = annotation_fixture() - add_annotation_info_to_graph(annotation, graph, "docNamespace", {}) + add_annotation_to_graph(annotation, graph, "docNamespace", {}) assert (URIRef("docNamespace#SPDXRef-File"), SPDX_NAMESPACE.annotation, None) in graph assert (None, None, SPDX_NAMESPACE.Annotation) in graph diff --git a/tests/spdx/writer/rdf/test_checksum_writer.py b/tests/spdx/writer/rdf/test_checksum_writer.py index b86c60c14..b13e98def 100644 --- a/tests/spdx/writer/rdf/test_checksum_writer.py +++ b/tests/spdx/writer/rdf/test_checksum_writer.py @@ -12,16 +12,16 @@ from rdflib import Graph, URIRef, Literal from spdx.model.checksum import ChecksumAlgorithm -from spdx.writer.rdf.checksum_writer import add_checksum_information_to_graph, algorithm_to_rdf_string +from spdx.writer.rdf.checksum_writer import add_checksum_to_graph, algorithm_to_rdf_string from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE from tests.spdx.fixtures import checksum_fixture -def test_add_checksum_information_to_graph(): +def test_add_checksum_to_graph(): graph = Graph() checksum = checksum_fixture() - add_checksum_information_to_graph(checksum, graph, URIRef("parentNode")) + add_checksum_to_graph(checksum, graph, URIRef("parentNode")) assert (URIRef("parentNode"), SPDX_NAMESPACE.checksum, None) in graph assert (None, None, SPDX_NAMESPACE.Checksum) in graph diff --git a/tests/spdx/writer/rdf/test_file_writer.py b/tests/spdx/writer/rdf/test_file_writer.py index cb5732caa..9dcbc37c5 100644 --- a/tests/spdx/writer/rdf/test_file_writer.py +++ b/tests/spdx/writer/rdf/test_file_writer.py @@ -10,16 +10,16 @@ # limitations under the License. from rdflib import Graph, Literal, RDFS, RDF, URIRef -from spdx.writer.rdf.file_writer import add_file_information_to_graph +from spdx.writer.rdf.file_writer import add_file_to_graph from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE from tests.spdx.fixtures import file_fixture -def test_add_file_information_to_graph(): +def test_add_file_to_graph(): graph = Graph() file = file_fixture() - add_file_information_to_graph(file, graph, "docNamespace", {}) + add_file_to_graph(file, graph, "docNamespace", {}) assert (URIRef("docNamespace#SPDXRef-File"), RDF.type, SPDX_NAMESPACE.File) in graph assert (None, SPDX_NAMESPACE.fileName, Literal("./fileName.py")) in graph diff --git a/tests/spdx/writer/rdf/test_package_writer.py b/tests/spdx/writer/rdf/test_package_writer.py index 3a3172f35..d09fef44a 100644 --- a/tests/spdx/writer/rdf/test_package_writer.py +++ b/tests/spdx/writer/rdf/test_package_writer.py @@ -13,17 +13,17 @@ from rdflib import Graph, URIRef, RDF, Literal, XSD, RDFS, DOAP from spdx.datetime_conversions import datetime_to_iso_string -from spdx.writer.rdf.package_writer import add_package_information_to_graph, add_external_package_ref_to_graph, \ +from spdx.writer.rdf.package_writer import add_package_to_graph, add_external_package_ref_to_graph, \ add_package_verification_code_to_graph from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE from tests.spdx.fixtures import package_fixture, external_package_ref_fixture, package_verification_code_fixture -def test_add_package_information_to_graph(): +def test_add_package_to_graph(): graph = Graph() package = package_fixture() - add_package_information_to_graph(package, graph, "docNamespace", {}) + add_package_to_graph(package, graph, "docNamespace", {}) assert (URIRef("docNamespace#SPDXRef-Package"), RDF.type, SPDX_NAMESPACE.Package) in graph assert (None, SPDX_NAMESPACE.name, Literal("packageName")) in graph diff --git a/tests/spdx/writer/rdf/test_relationship_writer.py b/tests/spdx/writer/rdf/test_relationship_writer.py index 4c2bcb1d7..2711786c8 100644 --- a/tests/spdx/writer/rdf/test_relationship_writer.py +++ b/tests/spdx/writer/rdf/test_relationship_writer.py @@ -10,15 +10,15 @@ # limitations under the License. from rdflib import Graph, URIRef -from spdx.writer.rdf.relationship_writer import add_relationship_info_to_graph +from spdx.writer.rdf.relationship_writer import add_relationship_to_graph from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE from tests.spdx.fixtures import relationship_fixture -def test_add_relationship_info_to_graph(): +def test_add_relationship_to_graph(): relationship = relationship_fixture() graph = Graph() - add_relationship_info_to_graph(relationship, graph, "docNamespace", {}) + add_relationship_to_graph(relationship, graph, "docNamespace", {}) assert(URIRef("docNamespace#SPDXRef-DOCUMENT"), SPDX_NAMESPACE.relationship, None) in graph assert (None, SPDX_NAMESPACE.relationshipType, SPDX_NAMESPACE.relationshipType_describes) in graph diff --git a/tests/spdx/writer/rdf/test_snippet_writer.py b/tests/spdx/writer/rdf/test_snippet_writer.py index a17ed9051..200b667c6 100644 --- a/tests/spdx/writer/rdf/test_snippet_writer.py +++ b/tests/spdx/writer/rdf/test_snippet_writer.py @@ -12,15 +12,15 @@ from rdflib import Graph, URIRef, RDF, Literal, RDFS from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, POINTER_NAMESPACE -from spdx.writer.rdf.snippet_writer import add_snippet_information_to_graph, add_range_to_graph +from spdx.writer.rdf.snippet_writer import add_snippet_to_graph, add_range_to_graph from tests.spdx.fixtures import snippet_fixture -def test_add_snippet_information_to_graph(): +def test_add_snippet_to_graph(): graph = Graph() snippet = snippet_fixture() - add_snippet_information_to_graph(snippet, graph, "docNamespace", {}) + add_snippet_to_graph(snippet, graph, "docNamespace", {}) assert (URIRef("docNamespace#SPDXRef-Snippet"), RDF.type, SPDX_NAMESPACE.Snippet) in graph assert (None, SPDX_NAMESPACE.snippetFromFile, URIRef(f"docNamespace#{snippet.file_spdx_id}")) in graph From c36bf8dbfefe94d2b913ed3cafe4d7a3be3f6162 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 31 Jan 2023 14:51:43 +0100 Subject: [PATCH 213/362] [issue-407, review] use fixture values in assert statements Signed-off-by: Meret Behrens --- .../spdx/writer/rdf/test_annotation_writer.py | 6 +-- tests/spdx/writer/rdf/test_checksum_writer.py | 2 +- .../writer/rdf/test_creation_info_writer.py | 16 ++++---- .../rdf/test_external_document_ref_writer.py | 2 +- .../test_extracted_licensing_info_writer.py | 10 ++--- tests/spdx/writer/rdf/test_file_writer.py | 14 +++---- tests/spdx/writer/rdf/test_package_writer.py | 38 +++++++++---------- .../writer/rdf/test_relationship_writer.py | 2 +- tests/spdx/writer/rdf/test_snippet_writer.py | 14 +++---- 9 files changed, 50 insertions(+), 54 deletions(-) diff --git a/tests/spdx/writer/rdf/test_annotation_writer.py b/tests/spdx/writer/rdf/test_annotation_writer.py index 99792e74e..7a496dbbc 100644 --- a/tests/spdx/writer/rdf/test_annotation_writer.py +++ b/tests/spdx/writer/rdf/test_annotation_writer.py @@ -27,6 +27,6 @@ def test_add_annotation_to_graph(): assert (URIRef("docNamespace#SPDXRef-File"), SPDX_NAMESPACE.annotation, None) in graph assert (None, None, SPDX_NAMESPACE.Annotation) in graph assert (None, SPDX_NAMESPACE.annotationType, SPDX_NAMESPACE.annotationType_review) in graph - assert (None, SPDX_NAMESPACE.annotationDate, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph - assert (None, SPDX_NAMESPACE.annotator, Literal("Person: annotatorName (some@mail.com)")) in graph - assert (None, RDFS.comment, Literal("annotationComment")) in graph + assert (None, SPDX_NAMESPACE.annotationDate, Literal(datetime_to_iso_string(annotation.annotation_date))) in graph + assert (None, SPDX_NAMESPACE.annotator, Literal(annotation.annotator.to_serialized_string())) in graph + assert (None, RDFS.comment, Literal(annotation.annotation_comment)) in graph diff --git a/tests/spdx/writer/rdf/test_checksum_writer.py b/tests/spdx/writer/rdf/test_checksum_writer.py index b13e98def..ab86896f2 100644 --- a/tests/spdx/writer/rdf/test_checksum_writer.py +++ b/tests/spdx/writer/rdf/test_checksum_writer.py @@ -26,7 +26,7 @@ def test_add_checksum_to_graph(): assert (URIRef("parentNode"), SPDX_NAMESPACE.checksum, None) in graph assert (None, None, SPDX_NAMESPACE.Checksum) in graph assert (None, SPDX_NAMESPACE.algorithm, SPDX_NAMESPACE.checksumAlgorithm_sha1) in graph - assert (None, SPDX_NAMESPACE.checksumValue, Literal("71c4025dd9897b364f3ebbb42c484ff43d00791c")) in graph + assert (None, SPDX_NAMESPACE.checksumValue, Literal(checksum.value)) in graph @pytest.mark.parametrize("algorithm,expected", [(ChecksumAlgorithm.SHA1, SPDX_NAMESPACE.checksumAlgorithm_sha1), diff --git a/tests/spdx/writer/rdf/test_creation_info_writer.py b/tests/spdx/writer/rdf/test_creation_info_writer.py index 76fad516c..327fa4749 100644 --- a/tests/spdx/writer/rdf/test_creation_info_writer.py +++ b/tests/spdx/writer/rdf/test_creation_info_writer.py @@ -8,8 +8,6 @@ # 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 datetime import datetime - from rdflib import Graph, Literal, RDFS, URIRef from spdx.datetime_conversions import datetime_to_iso_string @@ -26,13 +24,13 @@ def test_add_creation_info_to_graph(): assert (None, None, SPDX_NAMESPACE.SpdxDocument) in graph assert (URIRef(f"{creation_info.document_namespace}#{creation_info.spdx_id}"), None, None) in graph - assert (None, SPDX_NAMESPACE.dataLicense, URIRef("https://spdx.org/licenses/CC0-1.0")) - assert (None, SPDX_NAMESPACE.name, Literal("documentName")) in graph - assert (None, SPDX_NAMESPACE.specVersion, Literal("SPDX-2.3")) in graph + assert (None, SPDX_NAMESPACE.dataLicense, URIRef(f"https://spdx.org/licenses/{creation_info.data_license}")) + assert (None, SPDX_NAMESPACE.name, Literal(creation_info.name)) in graph + assert (None, SPDX_NAMESPACE.specVersion, Literal(creation_info.spdx_version)) in graph assert (None, SPDX_NAMESPACE.creationInfo, None) in graph assert (None, None, SPDX_NAMESPACE.CreationInfo) in graph - assert (None, SPDX_NAMESPACE.created, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph - assert (None, RDFS.comment, Literal("creatorComment")) in graph - assert (None, SPDX_NAMESPACE.licenseListVersion, Literal("3.19")) in graph - assert (None, SPDX_NAMESPACE.creator, Literal("Person: creatorName (some@mail.com)")) in graph + assert (None, SPDX_NAMESPACE.created, Literal(datetime_to_iso_string(creation_info.created))) in graph + assert (None, RDFS.comment, Literal(creation_info.creator_comment)) in graph + assert (None, SPDX_NAMESPACE.licenseListVersion, Literal(creation_info.license_list_version)) in graph + assert (None, SPDX_NAMESPACE.creator, Literal(creation_info.creators[0].to_serialized_string())) in graph diff --git a/tests/spdx/writer/rdf/test_external_document_ref_writer.py b/tests/spdx/writer/rdf/test_external_document_ref_writer.py index 943e69e43..ed13f7d82 100644 --- a/tests/spdx/writer/rdf/test_external_document_ref_writer.py +++ b/tests/spdx/writer/rdf/test_external_document_ref_writer.py @@ -25,7 +25,7 @@ def test_add_external_document_ref_to_graph(): assert (None, None, SPDX_NAMESPACE.ExternalDocumentRef) in graph assert (None, SPDX_NAMESPACE.checksum, None) in graph assert (None, None, SPDX_NAMESPACE.Checksum) in graph - assert (None, SPDX_NAMESPACE.spdxDocument, URIRef("https://namespace.com")) in graph + assert (None, SPDX_NAMESPACE.spdxDocument, URIRef(external_document_ref.document_uri)) in graph diff --git a/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py b/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py index 561f69cf1..1d8312e31 100644 --- a/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py +++ b/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py @@ -23,8 +23,8 @@ def test_add_extracted_licensing_info_to_graph(): assert (URIRef("docNode"), SPDX_NAMESPACE.hasExtractedLicensingInfo, None) in graph assert (URIRef("docNamespace#LicenseRef-1"), None, SPDX_NAMESPACE.ExtractedLicensingInfo) in graph - assert (None, SPDX_NAMESPACE.licenseId, Literal("LicenseRef-1")) in graph - assert (None, SPDX_NAMESPACE.extractedText, Literal("extractedText")) in graph - assert (None, RDFS.seeAlso, Literal("https://see.also")) in graph - assert (None, SPDX_NAMESPACE.name, Literal("licenseName")) in graph - assert (None, RDFS.comment, Literal("licenseComment")) in graph + assert (None, SPDX_NAMESPACE.licenseId, Literal(extracted_licensing_info.license_id)) in graph + assert (None, SPDX_NAMESPACE.extractedText, Literal(extracted_licensing_info.extracted_text)) in graph + assert (None, RDFS.seeAlso, Literal(extracted_licensing_info.cross_references[0])) in graph + assert (None, SPDX_NAMESPACE.name, Literal(extracted_licensing_info.license_name)) in graph + assert (None, RDFS.comment, Literal(extracted_licensing_info.comment)) in graph diff --git a/tests/spdx/writer/rdf/test_file_writer.py b/tests/spdx/writer/rdf/test_file_writer.py index 9dcbc37c5..006cab91b 100644 --- a/tests/spdx/writer/rdf/test_file_writer.py +++ b/tests/spdx/writer/rdf/test_file_writer.py @@ -22,14 +22,14 @@ def test_add_file_to_graph(): add_file_to_graph(file, graph, "docNamespace", {}) assert (URIRef("docNamespace#SPDXRef-File"), RDF.type, SPDX_NAMESPACE.File) in graph - assert (None, SPDX_NAMESPACE.fileName, Literal("./fileName.py")) in graph + assert (None, SPDX_NAMESPACE.fileName, Literal(file.name)) in graph assert (None, SPDX_NAMESPACE.fileType, SPDX_NAMESPACE.fileType_text) in graph - assert (None, SPDX_NAMESPACE.licenseComments, Literal("licenseComment")) in graph + assert (None, SPDX_NAMESPACE.licenseComments, Literal(file.license_comment)) in graph assert (None, SPDX_NAMESPACE.licenseConcluded, None) in graph assert (None, SPDX_NAMESPACE.licenseInfoInFile, None) in graph - assert (None, SPDX_NAMESPACE.copyrightText, Literal("copyrightText")) in graph - assert (None, RDFS.comment, Literal("fileComment")) in graph - assert (None, SPDX_NAMESPACE.noticeText, Literal("fileNotice")) in graph - assert (None, SPDX_NAMESPACE.fileContributor, Literal("fileContributor")) in graph + assert (None, SPDX_NAMESPACE.copyrightText, Literal(file.copyright_text)) in graph + assert (None, RDFS.comment, Literal(file.comment)) in graph + assert (None, SPDX_NAMESPACE.noticeText, Literal(file.notice)) in graph + assert (None, SPDX_NAMESPACE.fileContributor, Literal(file.contributors[0])) in graph assert (None, SPDX_NAMESPACE.checksum, None) in graph - assert (None, SPDX_NAMESPACE.attributionText, Literal("fileAttributionText")) in graph + assert (None, SPDX_NAMESPACE.attributionText, Literal(file.attribution_texts[0])) in graph diff --git a/tests/spdx/writer/rdf/test_package_writer.py b/tests/spdx/writer/rdf/test_package_writer.py index d09fef44a..52b4c66d8 100644 --- a/tests/spdx/writer/rdf/test_package_writer.py +++ b/tests/spdx/writer/rdf/test_package_writer.py @@ -8,8 +8,6 @@ # 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 datetime import datetime - from rdflib import Graph, URIRef, RDF, Literal, XSD, RDFS, DOAP from spdx.datetime_conversions import datetime_to_iso_string @@ -26,31 +24,31 @@ def test_add_package_to_graph(): add_package_to_graph(package, graph, "docNamespace", {}) assert (URIRef("docNamespace#SPDXRef-Package"), RDF.type, SPDX_NAMESPACE.Package) in graph - assert (None, SPDX_NAMESPACE.name, Literal("packageName")) in graph - assert (None, SPDX_NAMESPACE.versionInfo, Literal("12.2")) in graph - assert (None, SPDX_NAMESPACE.packageFileName, Literal("./packageFileName")) in graph - assert (None, SPDX_NAMESPACE.supplier, Literal("Person: supplierName (some@mail.com)")) in graph - assert (None, SPDX_NAMESPACE.originator, Literal("Person: originatorName (some@mail.com)")) in graph - assert (None, SPDX_NAMESPACE.downloadLocation, Literal("https://download.com")) in graph - assert (None, SPDX_NAMESPACE.filesAnalyzed, Literal("true", datatype=XSD.boolean)) in graph + assert (None, SPDX_NAMESPACE.name, Literal(package.name)) in graph + assert (None, SPDX_NAMESPACE.versionInfo, Literal(package.version)) in graph + assert (None, SPDX_NAMESPACE.packageFileName, Literal(package.file_name)) in graph + assert (None, SPDX_NAMESPACE.supplier, Literal(package.supplier.to_serialized_string())) in graph + assert (None, SPDX_NAMESPACE.originator, Literal(package.originator.to_serialized_string())) in graph + assert (None, SPDX_NAMESPACE.downloadLocation, Literal(package.download_location)) in graph + assert (None, SPDX_NAMESPACE.filesAnalyzed, Literal(package.files_analyzed, datatype=XSD.boolean)) in graph assert (URIRef("docNamespace#SPDXRef-Package"), SPDX_NAMESPACE.packageVerificationCode, None) in graph assert (URIRef("docNamespace#SPDXRef-Package"), SPDX_NAMESPACE.checksum, None) in graph - assert (None, DOAP.homepage, Literal("https://homepage.com")) in graph - assert (None, SPDX_NAMESPACE.sourceInfo, Literal("sourceInfo")) in graph + assert (None, DOAP.homepage, Literal(package.homepage)) in graph + assert (None, SPDX_NAMESPACE.sourceInfo, Literal(package.source_info)) in graph assert (None, SPDX_NAMESPACE.licenseConcluded, None) in graph assert (None, SPDX_NAMESPACE.licenseInfoFromFiles, None) in graph assert (None, SPDX_NAMESPACE.licenseDeclared, None) in graph - assert (None, SPDX_NAMESPACE.licenseComments, Literal("packageLicenseComment")) in graph - assert (None, SPDX_NAMESPACE.copyrightText, Literal("packageCopyrightText")) in graph - assert (None, SPDX_NAMESPACE.summary, Literal("packageSummary")) in graph - assert (None, SPDX_NAMESPACE.description, Literal("packageDescription")) in graph - assert (None, RDFS.comment, Literal("packageComment")) in graph + assert (None, SPDX_NAMESPACE.licenseComments, Literal(package.license_comment)) in graph + assert (None, SPDX_NAMESPACE.copyrightText, Literal(package.copyright_text)) in graph + assert (None, SPDX_NAMESPACE.summary, Literal(package.summary)) in graph + assert (None, SPDX_NAMESPACE.description, Literal(package.description)) in graph + assert (None, RDFS.comment, Literal(package.comment)) in graph assert (URIRef("docNamespace#SPDXRef-Package"), SPDX_NAMESPACE.externalRef, None) in graph - assert (None, SPDX_NAMESPACE.attributionText, Literal("packageAttributionText")) in graph + assert (None, SPDX_NAMESPACE.attributionText, Literal(package.attribution_texts[0])) in graph assert (None, SPDX_NAMESPACE.primaryPackagePurpose, SPDX_NAMESPACE.purpose_source) in graph - assert (None, SPDX_NAMESPACE.releaseDate, Literal(datetime_to_iso_string(datetime(2022, 12, 1)))) in graph - assert (None, SPDX_NAMESPACE.builtDate, Literal(datetime_to_iso_string(datetime(2022, 12, 2)))) in graph - assert (None, SPDX_NAMESPACE.validUntilDate, Literal(datetime_to_iso_string(datetime(2022, 12, 3)))) in graph + assert (None, SPDX_NAMESPACE.releaseDate, Literal(datetime_to_iso_string(package.release_date))) in graph + assert (None, SPDX_NAMESPACE.builtDate, Literal(datetime_to_iso_string(package.built_date))) in graph + assert (None, SPDX_NAMESPACE.validUntilDate, Literal(datetime_to_iso_string(package.valid_until_date))) in graph def test_add_package_verification_code_to_graph(): diff --git a/tests/spdx/writer/rdf/test_relationship_writer.py b/tests/spdx/writer/rdf/test_relationship_writer.py index 2711786c8..451a2f97c 100644 --- a/tests/spdx/writer/rdf/test_relationship_writer.py +++ b/tests/spdx/writer/rdf/test_relationship_writer.py @@ -22,4 +22,4 @@ def test_add_relationship_to_graph(): assert(URIRef("docNamespace#SPDXRef-DOCUMENT"), SPDX_NAMESPACE.relationship, None) in graph assert (None, SPDX_NAMESPACE.relationshipType, SPDX_NAMESPACE.relationshipType_describes) in graph - assert (None, SPDX_NAMESPACE.relatedSpdxElement, None) in graph + assert (None, SPDX_NAMESPACE.relatedSpdxElement, URIRef("docNamespace#SPDXRef-File")) in graph diff --git a/tests/spdx/writer/rdf/test_snippet_writer.py b/tests/spdx/writer/rdf/test_snippet_writer.py index 200b667c6..eb35b8ca7 100644 --- a/tests/spdx/writer/rdf/test_snippet_writer.py +++ b/tests/spdx/writer/rdf/test_snippet_writer.py @@ -26,11 +26,11 @@ def test_add_snippet_to_graph(): assert (None, SPDX_NAMESPACE.snippetFromFile, URIRef(f"docNamespace#{snippet.file_spdx_id}")) in graph assert (None, SPDX_NAMESPACE.licenseConcluded, None) in graph assert (None, SPDX_NAMESPACE.licenseInfoInSnippet, None) in graph - assert (None, SPDX_NAMESPACE.licenseComments, Literal("snippetLicenseComment")) in graph - assert (None, SPDX_NAMESPACE.copyrightText, Literal("licenseCopyrightText")) in graph - assert (None, SPDX_NAMESPACE.name, Literal("snippetName")) in graph - assert (None, SPDX_NAMESPACE.attributionText, Literal("snippetAttributionText")) in graph - assert (None, RDFS.comment, Literal("snippetComment")) in graph + assert (None, SPDX_NAMESPACE.licenseComments, Literal(snippet.license_comment)) in graph + assert (None, SPDX_NAMESPACE.copyrightText, Literal(snippet.copyright_text)) in graph + assert (None, SPDX_NAMESPACE.name, Literal(snippet.name)) in graph + assert (None, SPDX_NAMESPACE.attributionText, Literal(snippet.attribution_texts[0])) in graph + assert (None, RDFS.comment, Literal(snippet.comment)) in graph @pytest.mark.parametrize("range,pointer,predicate", @@ -38,9 +38,9 @@ def test_add_snippet_to_graph(): ((1, 3), POINTER_NAMESPACE.LineCharPointer, POINTER_NAMESPACE.lineNumber)]) def test_add_ranges_to_graph(range, pointer, predicate): graph = Graph() - add_range_to_graph(range, graph, URIRef("anyUR"), URIRef("docNamespace#SPDXRef-File"), pointer) + add_range_to_graph(range, graph, URIRef("snippetNode"), URIRef("docNamespace#SPDXRef-File"), pointer) - assert (None, SPDX_NAMESPACE.range, None) in graph + assert (URIRef("snippetNode"), SPDX_NAMESPACE.range, None) in graph assert (None, POINTER_NAMESPACE.startPointer, None) in graph assert (None, POINTER_NAMESPACE.endPointer, None) in graph assert (None, POINTER_NAMESPACE.reference, URIRef("docNamespace#SPDXRef-File")) in graph From bceddaef36a89a599bf41240248fa477ddf4fc76 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 31 Jan 2023 17:26:34 +0100 Subject: [PATCH 214/362] [issue-407, refactor] introduce module "rdfschema" to use common spdx namespaces in rdf writer and parser Signed-off-by: Meret Behrens --- src/spdx/rdfschema/__init__.py | 0 src/spdx/rdfschema/namespace.py | 14 ++++++++++++++ src/spdx/writer/rdf/annotation_writer.py | 3 ++- src/spdx/writer/rdf/checksum_writer.py | 2 +- src/spdx/writer/rdf/creation_info_writer.py | 3 ++- .../writer/rdf/external_document_ref_writer.py | 2 +- .../writer/rdf/extracted_licensing_info_writer.py | 3 ++- src/spdx/writer/rdf/file_writer.py | 3 ++- src/spdx/writer/rdf/license_expression_writer.py | 2 +- src/spdx/writer/rdf/package_writer.py | 3 ++- src/spdx/writer/rdf/rdf_writer.py | 2 +- src/spdx/writer/rdf/relationship_writer.py | 3 ++- src/spdx/writer/rdf/snippet_writer.py | 3 ++- src/spdx/writer/rdf/writer_utils.py | 6 ++---- tests/spdx/writer/rdf/test_annotation_writer.py | 2 +- tests/spdx/writer/rdf/test_checksum_writer.py | 2 +- tests/spdx/writer/rdf/test_creation_info_writer.py | 2 +- .../rdf/test_external_document_ref_writer.py | 2 +- .../rdf/test_extracted_licensing_info_writer.py | 2 +- tests/spdx/writer/rdf/test_file_writer.py | 2 +- .../writer/rdf/test_license_expression_writer.py | 2 +- tests/spdx/writer/rdf/test_package_writer.py | 2 +- tests/spdx/writer/rdf/test_relationship_writer.py | 2 +- tests/spdx/writer/rdf/test_snippet_writer.py | 2 +- 24 files changed, 44 insertions(+), 25 deletions(-) create mode 100644 src/spdx/rdfschema/__init__.py create mode 100644 src/spdx/rdfschema/namespace.py diff --git a/src/spdx/rdfschema/__init__.py b/src/spdx/rdfschema/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/spdx/rdfschema/namespace.py b/src/spdx/rdfschema/namespace.py new file mode 100644 index 000000000..45b4d938f --- /dev/null +++ b/src/spdx/rdfschema/namespace.py @@ -0,0 +1,14 @@ +# Copyright (c) 2023 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 rdflib import Namespace + +SPDX_NAMESPACE = Namespace("http://spdx.org/rdf/terms#") +POINTER_NAMESPACE = Namespace("http://www.w3.org/2009/pointers#") diff --git a/src/spdx/writer/rdf/annotation_writer.py b/src/spdx/writer/rdf/annotation_writer.py index 207b3102d..56b272329 100644 --- a/src/spdx/writer/rdf/annotation_writer.py +++ b/src/spdx/writer/rdf/annotation_writer.py @@ -15,7 +15,8 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.model.annotation import Annotation from spdx.writer.casing_tools import snake_case_to_camel_case -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_namespace_to_spdx_id +from spdx.writer.rdf.writer_utils import add_namespace_to_spdx_id +from spdx.rdfschema.namespace import SPDX_NAMESPACE def add_annotation_to_graph(annotation: Annotation, graph: Graph, doc_namespace: str, diff --git a/src/spdx/writer/rdf/checksum_writer.py b/src/spdx/writer/rdf/checksum_writer.py index 6a5b760a3..b9b2fe84f 100644 --- a/src/spdx/writer/rdf/checksum_writer.py +++ b/src/spdx/writer/rdf/checksum_writer.py @@ -11,7 +11,7 @@ from rdflib import Graph, URIRef, BNode, RDF, Literal from spdx.model.checksum import Checksum, ChecksumAlgorithm -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE def add_checksum_to_graph(checksum: Checksum, graph: Graph, parent: URIRef): diff --git a/src/spdx/writer/rdf/creation_info_writer.py b/src/spdx/writer/rdf/creation_info_writer.py index f191fb411..d1aa8a0e4 100644 --- a/src/spdx/writer/rdf/creation_info_writer.py +++ b/src/spdx/writer/rdf/creation_info_writer.py @@ -13,7 +13,8 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.model.document import CreationInfo from spdx.writer.rdf.external_document_ref_writer import add_external_document_ref_to_graph -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_optional_literal +from spdx.writer.rdf.writer_utils import add_optional_literal +from spdx.rdfschema.namespace import SPDX_NAMESPACE def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): diff --git a/src/spdx/writer/rdf/external_document_ref_writer.py b/src/spdx/writer/rdf/external_document_ref_writer.py index cf4ae82d9..7521bd338 100644 --- a/src/spdx/writer/rdf/external_document_ref_writer.py +++ b/src/spdx/writer/rdf/external_document_ref_writer.py @@ -12,7 +12,7 @@ from spdx.model.external_document_ref import ExternalDocumentRef from spdx.writer.rdf.checksum_writer import add_checksum_to_graph -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE def add_external_document_ref_to_graph(external_document_ref: ExternalDocumentRef, graph: Graph, doc_node: URIRef, diff --git a/src/spdx/writer/rdf/extracted_licensing_info_writer.py b/src/spdx/writer/rdf/extracted_licensing_info_writer.py index 36c382e2c..4d0f4b674 100644 --- a/src/spdx/writer/rdf/extracted_licensing_info_writer.py +++ b/src/spdx/writer/rdf/extracted_licensing_info_writer.py @@ -9,7 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. from rdflib import Graph, URIRef, RDF, BNode, RDFS, Literal -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_optional_literal, add_literal_or_no_assertion +from spdx.writer.rdf.writer_utils import add_optional_literal, add_literal_or_no_assertion +from spdx.rdfschema.namespace import SPDX_NAMESPACE from spdx.model.extracted_licensing_info import ExtractedLicensingInfo diff --git a/src/spdx/writer/rdf/file_writer.py b/src/spdx/writer/rdf/file_writer.py index 5c025865f..76d6209f4 100644 --- a/src/spdx/writer/rdf/file_writer.py +++ b/src/spdx/writer/rdf/file_writer.py @@ -16,7 +16,8 @@ from spdx.writer.casing_tools import snake_case_to_camel_case from spdx.writer.rdf.checksum_writer import add_checksum_to_graph from spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_optional_literal, add_namespace_to_spdx_id +from spdx.writer.rdf.writer_utils import add_optional_literal, add_namespace_to_spdx_id +from spdx.rdfschema.namespace import SPDX_NAMESPACE def add_file_to_graph(file: File, graph: Graph, doc_namespace: str, diff --git a/src/spdx/writer/rdf/license_expression_writer.py b/src/spdx/writer/rdf/license_expression_writer.py index d75e20527..8364ee0b8 100644 --- a/src/spdx/writer/rdf/license_expression_writer.py +++ b/src/spdx/writer/rdf/license_expression_writer.py @@ -19,7 +19,7 @@ from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE def add_license_expression_or_none_or_no_assertion(value: Union[ diff --git a/src/spdx/writer/rdf/package_writer.py b/src/spdx/writer/rdf/package_writer.py index b2b78e776..d0fcad9f8 100644 --- a/src/spdx/writer/rdf/package_writer.py +++ b/src/spdx/writer/rdf/package_writer.py @@ -18,8 +18,9 @@ from spdx.model.package import Package, PackageVerificationCode, ExternalPackageRef, \ CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_optional_literal, add_literal_or_no_assertion_or_none, \ +from spdx.writer.rdf.writer_utils import add_optional_literal, add_literal_or_no_assertion_or_none, \ add_datetime_to_graph, add_namespace_to_spdx_id +from spdx.rdfschema.namespace import SPDX_NAMESPACE def add_package_to_graph(package: Package, graph: Graph, doc_namespace: str, diff --git a/src/spdx/writer/rdf/rdf_writer.py b/src/spdx/writer/rdf/rdf_writer.py index 767517df6..9251f57a8 100644 --- a/src/spdx/writer/rdf/rdf_writer.py +++ b/src/spdx/writer/rdf/rdf_writer.py @@ -23,7 +23,7 @@ from spdx.writer.rdf.package_writer import add_package_to_graph from spdx.writer.rdf.relationship_writer import add_relationship_to_graph from spdx.writer.rdf.snippet_writer import add_snippet_to_graph -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, POINTER_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE, POINTER_NAMESPACE def write_document_to_file(document: Document, file_name: str, validate: bool): diff --git a/src/spdx/writer/rdf/relationship_writer.py b/src/spdx/writer/rdf/relationship_writer.py index e57eca0b0..fcef9648f 100644 --- a/src/spdx/writer/rdf/relationship_writer.py +++ b/src/spdx/writer/rdf/relationship_writer.py @@ -16,7 +16,8 @@ from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone from spdx.writer.casing_tools import snake_case_to_camel_case -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_namespace_to_spdx_id +from spdx.writer.rdf.writer_utils import add_namespace_to_spdx_id +from spdx.rdfschema.namespace import SPDX_NAMESPACE def add_relationship_to_graph(relationship: Relationship, graph: Graph, doc_namespace: str, diff --git a/src/spdx/writer/rdf/snippet_writer.py b/src/spdx/writer/rdf/snippet_writer.py index cda25f67d..b7ab6ce2a 100644 --- a/src/spdx/writer/rdf/snippet_writer.py +++ b/src/spdx/writer/rdf/snippet_writer.py @@ -13,7 +13,8 @@ from rdflib import Graph, URIRef, RDF, RDFS, Literal, BNode from spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, add_optional_literal, add_namespace_to_spdx_id, POINTER_NAMESPACE +from spdx.writer.rdf.writer_utils import add_optional_literal, add_namespace_to_spdx_id +from spdx.rdfschema.namespace import SPDX_NAMESPACE, POINTER_NAMESPACE from spdx.model.snippet import Snippet diff --git a/src/spdx/writer/rdf/writer_utils.py b/src/spdx/writer/rdf/writer_utils.py index a9fbfb36a..e09f2b0e0 100644 --- a/src/spdx/writer/rdf/writer_utils.py +++ b/src/spdx/writer/rdf/writer_utils.py @@ -12,17 +12,15 @@ from datetime import datetime from typing import Any, Optional, Dict -from rdflib import Namespace, Graph, Literal +from rdflib import Graph, Literal from rdflib.term import Node from spdx.datetime_conversions import datetime_to_iso_string from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone +from spdx.rdfschema.namespace import SPDX_NAMESPACE from spdx.validation.spdx_id_validators import is_valid_internal_spdx_id -SPDX_NAMESPACE = Namespace("http://spdx.org/rdf/terms#") -POINTER_NAMESPACE = Namespace("http://www.w3.org/2009/pointers#") - def add_optional_literal(value: Any, graph: Graph, parent: Node, predicate: Node): if value is None: diff --git a/tests/spdx/writer/rdf/test_annotation_writer.py b/tests/spdx/writer/rdf/test_annotation_writer.py index 7a496dbbc..45b6ea9d1 100644 --- a/tests/spdx/writer/rdf/test_annotation_writer.py +++ b/tests/spdx/writer/rdf/test_annotation_writer.py @@ -14,7 +14,7 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.writer.rdf.annotation_writer import add_annotation_to_graph -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE from tests.spdx.fixtures import annotation_fixture diff --git a/tests/spdx/writer/rdf/test_checksum_writer.py b/tests/spdx/writer/rdf/test_checksum_writer.py index ab86896f2..81e8a5902 100644 --- a/tests/spdx/writer/rdf/test_checksum_writer.py +++ b/tests/spdx/writer/rdf/test_checksum_writer.py @@ -13,7 +13,7 @@ from spdx.model.checksum import ChecksumAlgorithm from spdx.writer.rdf.checksum_writer import add_checksum_to_graph, algorithm_to_rdf_string -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE from tests.spdx.fixtures import checksum_fixture diff --git a/tests/spdx/writer/rdf/test_creation_info_writer.py b/tests/spdx/writer/rdf/test_creation_info_writer.py index 327fa4749..3a9b78323 100644 --- a/tests/spdx/writer/rdf/test_creation_info_writer.py +++ b/tests/spdx/writer/rdf/test_creation_info_writer.py @@ -12,7 +12,7 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.writer.rdf.creation_info_writer import add_creation_info_to_graph -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE from tests.spdx.fixtures import creation_info_fixture diff --git a/tests/spdx/writer/rdf/test_external_document_ref_writer.py b/tests/spdx/writer/rdf/test_external_document_ref_writer.py index ed13f7d82..2ab488435 100644 --- a/tests/spdx/writer/rdf/test_external_document_ref_writer.py +++ b/tests/spdx/writer/rdf/test_external_document_ref_writer.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from rdflib import Graph, URIRef -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE from spdx.writer.rdf.external_document_ref_writer import add_external_document_ref_to_graph from tests.spdx.fixtures import external_document_ref_fixture diff --git a/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py b/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py index 1d8312e31..2ef7a5094 100644 --- a/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py +++ b/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from rdflib import Graph, Literal, RDFS, URIRef -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE from spdx.writer.rdf.extracted_licensing_info_writer import add_extracted_licensing_info_to_graph from tests.spdx.fixtures import extracted_licensing_info_fixture diff --git a/tests/spdx/writer/rdf/test_file_writer.py b/tests/spdx/writer/rdf/test_file_writer.py index 006cab91b..0fb67a884 100644 --- a/tests/spdx/writer/rdf/test_file_writer.py +++ b/tests/spdx/writer/rdf/test_file_writer.py @@ -11,7 +11,7 @@ from rdflib import Graph, Literal, RDFS, RDF, URIRef from spdx.writer.rdf.file_writer import add_file_to_graph -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE from tests.spdx.fixtures import file_fixture diff --git a/tests/spdx/writer/rdf/test_license_expression_writer.py b/tests/spdx/writer/rdf/test_license_expression_writer.py index 2f1b4f693..8f7d9ff5c 100644 --- a/tests/spdx/writer/rdf/test_license_expression_writer.py +++ b/tests/spdx/writer/rdf/test_license_expression_writer.py @@ -11,7 +11,7 @@ import pytest from license_expression import get_spdx_licensing from rdflib import Graph, URIRef, RDF, Literal -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE from spdx.writer.rdf.license_expression_writer import add_license_expression_to_graph diff --git a/tests/spdx/writer/rdf/test_package_writer.py b/tests/spdx/writer/rdf/test_package_writer.py index 52b4c66d8..81cabe921 100644 --- a/tests/spdx/writer/rdf/test_package_writer.py +++ b/tests/spdx/writer/rdf/test_package_writer.py @@ -13,7 +13,7 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.writer.rdf.package_writer import add_package_to_graph, add_external_package_ref_to_graph, \ add_package_verification_code_to_graph -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE from tests.spdx.fixtures import package_fixture, external_package_ref_fixture, package_verification_code_fixture diff --git a/tests/spdx/writer/rdf/test_relationship_writer.py b/tests/spdx/writer/rdf/test_relationship_writer.py index 451a2f97c..998da1db8 100644 --- a/tests/spdx/writer/rdf/test_relationship_writer.py +++ b/tests/spdx/writer/rdf/test_relationship_writer.py @@ -11,7 +11,7 @@ from rdflib import Graph, URIRef from spdx.writer.rdf.relationship_writer import add_relationship_to_graph -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE from tests.spdx.fixtures import relationship_fixture diff --git a/tests/spdx/writer/rdf/test_snippet_writer.py b/tests/spdx/writer/rdf/test_snippet_writer.py index eb35b8ca7..03591c2ff 100644 --- a/tests/spdx/writer/rdf/test_snippet_writer.py +++ b/tests/spdx/writer/rdf/test_snippet_writer.py @@ -10,7 +10,7 @@ # limitations under the License. import pytest from rdflib import Graph, URIRef, RDF, Literal, RDFS -from spdx.writer.rdf.writer_utils import SPDX_NAMESPACE, POINTER_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE, POINTER_NAMESPACE from spdx.writer.rdf.snippet_writer import add_snippet_to_graph, add_range_to_graph from tests.spdx.fixtures import snippet_fixture From 5d05e4f4b0f2a616a50290d652916ac83df1867e Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 31 Jan 2023 17:41:56 +0100 Subject: [PATCH 215/362] [issue-407] add license and reference namespace Signed-off-by: Meret Behrens --- src/spdx/rdfschema/namespace.py | 2 ++ src/spdx/writer/rdf/creation_info_writer.py | 4 ++-- src/spdx/writer/rdf/license_expression_writer.py | 6 +++--- src/spdx/writer/rdf/package_writer.py | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/spdx/rdfschema/namespace.py b/src/spdx/rdfschema/namespace.py index 45b4d938f..b29f55ad5 100644 --- a/src/spdx/rdfschema/namespace.py +++ b/src/spdx/rdfschema/namespace.py @@ -12,3 +12,5 @@ SPDX_NAMESPACE = Namespace("http://spdx.org/rdf/terms#") POINTER_NAMESPACE = Namespace("http://www.w3.org/2009/pointers#") +REFERENCE_NAMESPACE = Namespace("http://spdx.org/rdf/references/") +LICENSE_NAMESPACE = Namespace("http://spdx.org/licenses/") diff --git a/src/spdx/writer/rdf/creation_info_writer.py b/src/spdx/writer/rdf/creation_info_writer.py index d1aa8a0e4..1fe1330c5 100644 --- a/src/spdx/writer/rdf/creation_info_writer.py +++ b/src/spdx/writer/rdf/creation_info_writer.py @@ -14,14 +14,14 @@ from spdx.model.document import CreationInfo from spdx.writer.rdf.external_document_ref_writer import add_external_document_ref_to_graph from spdx.writer.rdf.writer_utils import add_optional_literal -from spdx.rdfschema.namespace import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE, LICENSE_NAMESPACE def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph): doc_node = URIRef(f"{creation_info.document_namespace}#{creation_info.spdx_id}") graph.add((doc_node, RDF.type, SPDX_NAMESPACE.SpdxDocument)) graph.add((doc_node, SPDX_NAMESPACE.specVersion, Literal(creation_info.spdx_version))) - graph.add((doc_node, SPDX_NAMESPACE.dataLicense, URIRef(f"http://spdx.org/licenses/{creation_info.data_license}"))) + graph.add((doc_node, SPDX_NAMESPACE.dataLicense, LICENSE_NAMESPACE[creation_info.data_license])) graph.add((doc_node, SPDX_NAMESPACE.name, Literal(creation_info.name))) add_optional_literal(creation_info.document_comment, graph, doc_node, RDFS.comment) diff --git a/src/spdx/writer/rdf/license_expression_writer.py b/src/spdx/writer/rdf/license_expression_writer.py index 8364ee0b8..aa21ba740 100644 --- a/src/spdx/writer/rdf/license_expression_writer.py +++ b/src/spdx/writer/rdf/license_expression_writer.py @@ -19,7 +19,7 @@ from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone -from spdx.rdfschema.namespace import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE, LICENSE_NAMESPACE def add_license_expression_or_none_or_no_assertion(value: Union[ @@ -64,7 +64,7 @@ def add_license_expression_to_graph(license_expression: Expression, graph: Graph if isinstance(license_expression, LicenseSymbol): if license_or_exception_is_on_spdx_licensing_list(license_expression): graph.add( - (parent, predicate, URIRef(f"http://spdx.org/licenses/{license_expression}"))) + (parent, predicate, LICENSE_NAMESPACE[str(license_expression)])) else: # assuming that the license expression is a LicenseRef to an instance of ExtractedLicensingInfo graph.add((parent, predicate, URIRef(f"{doc_namespace}#{license_expression}"))) @@ -77,7 +77,7 @@ def license_or_exception_is_on_spdx_licensing_list(license_symbol: LicenseSymbol def add_license_exception_to_graph(license_exception: LicenseSymbol, graph: Graph, parent: Node): if license_or_exception_is_on_spdx_licensing_list(license_exception): - exception_node = URIRef(f"http://spdx.org/licenses/{license_exception}") + exception_node = LICENSE_NAMESPACE[str(license_exception)] graph.add((parent, SPDX_NAMESPACE.licenseException, exception_node)) else: exception_node = BNode() diff --git a/src/spdx/writer/rdf/package_writer.py b/src/spdx/writer/rdf/package_writer.py index d0fcad9f8..37f963f91 100644 --- a/src/spdx/writer/rdf/package_writer.py +++ b/src/spdx/writer/rdf/package_writer.py @@ -20,7 +20,7 @@ CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES from spdx.writer.rdf.writer_utils import add_optional_literal, add_literal_or_no_assertion_or_none, \ add_datetime_to_graph, add_namespace_to_spdx_id -from spdx.rdfschema.namespace import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE, REFERENCE_NAMESPACE def add_package_to_graph(package: Package, graph: Graph, doc_namespace: str, @@ -89,7 +89,7 @@ def add_external_package_ref_to_graph(external_package_ref: ExternalPackageRef, if external_package_ref.reference_type in CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES[external_package_ref.category]: graph.add((external_package_ref_node, SPDX_NAMESPACE.referenceType, - URIRef(f"http://spdx.org/rdf/references/{external_package_ref.reference_type}"))) + REFERENCE_NAMESPACE[external_package_ref.reference_type])) else: graph.add((external_package_ref_node, SPDX_NAMESPACE.referenceType, URIRef(external_package_ref.reference_type))) From 57d9db29617a9cc510f888eaee54cb90ae1ebf0e Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Feb 2023 08:42:38 +0100 Subject: [PATCH 216/362] [issue-407, review] inline triple Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/writer_utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/spdx/writer/rdf/writer_utils.py b/src/spdx/writer/rdf/writer_utils.py index e09f2b0e0..772f80f6e 100644 --- a/src/spdx/writer/rdf/writer_utils.py +++ b/src/spdx/writer/rdf/writer_utils.py @@ -27,10 +27,9 @@ def add_optional_literal(value: Any, graph: Graph, parent: Node, predicate: Node return if isinstance(value, list): for element in value: - element_triple = (parent, predicate, Literal(str(element))) - graph.add(element_triple) + graph.add((parent, predicate, Literal(str(element)))) + return graph.add((parent, predicate, Literal(str(value)))) - return def add_literal_or_no_assertion_or_none(value: Any, graph: Graph, parent: Node, predicate: Node): From e25467fb6125911bfa08de4d7ccfb885092a6612 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Feb 2023 08:53:12 +0100 Subject: [PATCH 217/362] [issue-407, review] precise tests by using RDF.type as predicate and not only None as a wildcard Signed-off-by: Meret Behrens --- tests/spdx/writer/rdf/test_annotation_writer.py | 6 ++---- tests/spdx/writer/rdf/test_checksum_writer.py | 4 ++-- tests/spdx/writer/rdf/test_creation_info_writer.py | 6 +++--- tests/spdx/writer/rdf/test_external_document_ref_writer.py | 6 +++--- .../spdx/writer/rdf/test_extracted_licensing_info_writer.py | 4 ++-- tests/spdx/writer/rdf/test_package_writer.py | 4 ++-- 6 files changed, 14 insertions(+), 16 deletions(-) diff --git a/tests/spdx/writer/rdf/test_annotation_writer.py b/tests/spdx/writer/rdf/test_annotation_writer.py index 45b6ea9d1..435599b85 100644 --- a/tests/spdx/writer/rdf/test_annotation_writer.py +++ b/tests/spdx/writer/rdf/test_annotation_writer.py @@ -8,9 +8,7 @@ # 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 datetime import datetime - -from rdflib import Graph, Literal, RDFS, URIRef +from rdflib import Graph, Literal, RDFS, URIRef, RDF from spdx.datetime_conversions import datetime_to_iso_string from spdx.writer.rdf.annotation_writer import add_annotation_to_graph @@ -25,7 +23,7 @@ def test_add_annotation_to_graph(): add_annotation_to_graph(annotation, graph, "docNamespace", {}) assert (URIRef("docNamespace#SPDXRef-File"), SPDX_NAMESPACE.annotation, None) in graph - assert (None, None, SPDX_NAMESPACE.Annotation) in graph + assert (None, RDF.type, SPDX_NAMESPACE.Annotation) in graph assert (None, SPDX_NAMESPACE.annotationType, SPDX_NAMESPACE.annotationType_review) in graph assert (None, SPDX_NAMESPACE.annotationDate, Literal(datetime_to_iso_string(annotation.annotation_date))) in graph assert (None, SPDX_NAMESPACE.annotator, Literal(annotation.annotator.to_serialized_string())) in graph diff --git a/tests/spdx/writer/rdf/test_checksum_writer.py b/tests/spdx/writer/rdf/test_checksum_writer.py index 81e8a5902..535418dff 100644 --- a/tests/spdx/writer/rdf/test_checksum_writer.py +++ b/tests/spdx/writer/rdf/test_checksum_writer.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import pytest -from rdflib import Graph, URIRef, Literal +from rdflib import Graph, URIRef, Literal, RDF from spdx.model.checksum import ChecksumAlgorithm from spdx.writer.rdf.checksum_writer import add_checksum_to_graph, algorithm_to_rdf_string @@ -24,7 +24,7 @@ def test_add_checksum_to_graph(): add_checksum_to_graph(checksum, graph, URIRef("parentNode")) assert (URIRef("parentNode"), SPDX_NAMESPACE.checksum, None) in graph - assert (None, None, SPDX_NAMESPACE.Checksum) in graph + assert (None, RDF.type, SPDX_NAMESPACE.Checksum) in graph assert (None, SPDX_NAMESPACE.algorithm, SPDX_NAMESPACE.checksumAlgorithm_sha1) in graph assert (None, SPDX_NAMESPACE.checksumValue, Literal(checksum.value)) in graph diff --git a/tests/spdx/writer/rdf/test_creation_info_writer.py b/tests/spdx/writer/rdf/test_creation_info_writer.py index 3a9b78323..b7592555c 100644 --- a/tests/spdx/writer/rdf/test_creation_info_writer.py +++ b/tests/spdx/writer/rdf/test_creation_info_writer.py @@ -8,7 +8,7 @@ # 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 rdflib import Graph, Literal, RDFS, URIRef +from rdflib import Graph, Literal, RDFS, URIRef, RDF from spdx.datetime_conversions import datetime_to_iso_string from spdx.writer.rdf.creation_info_writer import add_creation_info_to_graph @@ -22,14 +22,14 @@ def test_add_creation_info_to_graph(): add_creation_info_to_graph(creation_info, graph) - assert (None, None, SPDX_NAMESPACE.SpdxDocument) in graph + assert (None, RDF.type, SPDX_NAMESPACE.SpdxDocument) in graph assert (URIRef(f"{creation_info.document_namespace}#{creation_info.spdx_id}"), None, None) in graph assert (None, SPDX_NAMESPACE.dataLicense, URIRef(f"https://spdx.org/licenses/{creation_info.data_license}")) assert (None, SPDX_NAMESPACE.name, Literal(creation_info.name)) in graph assert (None, SPDX_NAMESPACE.specVersion, Literal(creation_info.spdx_version)) in graph assert (None, SPDX_NAMESPACE.creationInfo, None) in graph - assert (None, None, SPDX_NAMESPACE.CreationInfo) in graph + assert (None, RDF.type, SPDX_NAMESPACE.CreationInfo) in graph assert (None, SPDX_NAMESPACE.created, Literal(datetime_to_iso_string(creation_info.created))) in graph assert (None, RDFS.comment, Literal(creation_info.creator_comment)) in graph assert (None, SPDX_NAMESPACE.licenseListVersion, Literal(creation_info.license_list_version)) in graph diff --git a/tests/spdx/writer/rdf/test_external_document_ref_writer.py b/tests/spdx/writer/rdf/test_external_document_ref_writer.py index 2ab488435..a8c206df1 100644 --- a/tests/spdx/writer/rdf/test_external_document_ref_writer.py +++ b/tests/spdx/writer/rdf/test_external_document_ref_writer.py @@ -8,7 +8,7 @@ # 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 rdflib import Graph, URIRef +from rdflib import Graph, URIRef, RDF from spdx.rdfschema.namespace import SPDX_NAMESPACE from spdx.writer.rdf.external_document_ref_writer import add_external_document_ref_to_graph @@ -22,9 +22,9 @@ def test_add_external_document_ref_to_graph(): add_external_document_ref_to_graph(external_document_ref, graph, URIRef("docNode"), "docNamespace") assert (URIRef("docNode"), SPDX_NAMESPACE.externalDocumentRef, URIRef("docNamespace#DocumentRef-external")) in graph - assert (None, None, SPDX_NAMESPACE.ExternalDocumentRef) in graph + assert (None, RDF.type, SPDX_NAMESPACE.ExternalDocumentRef) in graph assert (None, SPDX_NAMESPACE.checksum, None) in graph - assert (None, None, SPDX_NAMESPACE.Checksum) in graph + assert (None, RDF.type, SPDX_NAMESPACE.Checksum) in graph assert (None, SPDX_NAMESPACE.spdxDocument, URIRef(external_document_ref.document_uri)) in graph diff --git a/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py b/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py index 2ef7a5094..55669dae5 100644 --- a/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py +++ b/tests/spdx/writer/rdf/test_extracted_licensing_info_writer.py @@ -8,7 +8,7 @@ # 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 rdflib import Graph, Literal, RDFS, URIRef +from rdflib import Graph, Literal, RDFS, URIRef, RDF from spdx.rdfschema.namespace import SPDX_NAMESPACE from spdx.writer.rdf.extracted_licensing_info_writer import add_extracted_licensing_info_to_graph @@ -22,7 +22,7 @@ def test_add_extracted_licensing_info_to_graph(): add_extracted_licensing_info_to_graph(extracted_licensing_info, graph, URIRef("docNode"), "docNamespace") assert (URIRef("docNode"), SPDX_NAMESPACE.hasExtractedLicensingInfo, None) in graph - assert (URIRef("docNamespace#LicenseRef-1"), None, SPDX_NAMESPACE.ExtractedLicensingInfo) in graph + assert (URIRef("docNamespace#LicenseRef-1"), RDF.type, SPDX_NAMESPACE.ExtractedLicensingInfo) in graph assert (None, SPDX_NAMESPACE.licenseId, Literal(extracted_licensing_info.license_id)) in graph assert (None, SPDX_NAMESPACE.extractedText, Literal(extracted_licensing_info.extracted_text)) in graph assert (None, RDFS.seeAlso, Literal(extracted_licensing_info.cross_references[0])) in graph diff --git a/tests/spdx/writer/rdf/test_package_writer.py b/tests/spdx/writer/rdf/test_package_writer.py index 81cabe921..7d2bd20df 100644 --- a/tests/spdx/writer/rdf/test_package_writer.py +++ b/tests/spdx/writer/rdf/test_package_writer.py @@ -57,7 +57,7 @@ def test_add_package_verification_code_to_graph(): add_package_verification_code_to_graph(verification_code, graph, URIRef("docNamespace")) - assert (None, None, SPDX_NAMESPACE.PackageVerificationCode) in graph + assert (None, RDF.type, SPDX_NAMESPACE.PackageVerificationCode) in graph assert (None, SPDX_NAMESPACE.packageVerificationCodeValue, Literal("85ed0817af83a24ad8da68c2b5094de69833983c")) in graph assert (None, SPDX_NAMESPACE.packageVerificationCodeExcludedFile, Literal("./exclude.py")) in graph @@ -69,7 +69,7 @@ def test_external_package_ref_to_graph(): add_external_package_ref_to_graph(external_reference, graph, URIRef("docNamespace")) - assert (None, None, SPDX_NAMESPACE.ExternalRef) in graph + assert (None, RDF.type, SPDX_NAMESPACE.ExternalRef) in graph assert (None, SPDX_NAMESPACE.referenceCategory, SPDX_NAMESPACE.referenceCategory_packageManager) in graph assert (None, SPDX_NAMESPACE.referenceType, URIRef("http://spdx.org/rdf/references/maven-central")) in graph assert (None, SPDX_NAMESPACE.referenceLocator, Literal("org.apache.tomcat:tomcat:9.0.0.M4")) in graph From bb4a265113ea6fd87b88e6f3c2319e48f9048296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 1 Feb 2023 10:36:35 +0100 Subject: [PATCH 218/362] [issue-378] implement invalidation of duplicated SPDX Ids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/validation/document_validator.py | 13 +++++++++++++ .../spdx/validation/test_document_validator.py | 17 ++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/spdx/validation/document_validator.py b/src/spdx/validation/document_validator.py index a3fa00758..fe6422a21 100644 --- a/src/spdx/validation/document_validator.py +++ b/src/spdx/validation/document_validator.py @@ -21,6 +21,7 @@ from spdx.validation.package_validator import validate_packages from spdx.validation.relationship_validator import validate_relationships from spdx.validation.snippet_validator import validate_snippets +from spdx.validation.spdx_id_validators import get_list_of_all_spdx_ids from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType @@ -74,4 +75,16 @@ def validate_full_spdx_document(document: Document, spdx_version: str = None) -> ValidationContext(spdx_id=document_id, element_type=SpdxElementType.DOCUMENT))) + all_spdx_ids: List[str] = get_list_of_all_spdx_ids(document) + auxiliary_set = set() + duplicated_spdx_ids = set( + spdx_id for spdx_id in all_spdx_ids if spdx_id in auxiliary_set or auxiliary_set.add(spdx_id)) + + if duplicated_spdx_ids: + validation_messages.append( + ValidationMessage( + f"every spdx_id must be unique within the document, but found the following duplicates: {sorted(duplicated_spdx_ids)}", + context) + ) + return validation_messages diff --git a/tests/spdx/validation/test_document_validator.py b/tests/spdx/validation/test_document_validator.py index c3b805587..944f3077e 100644 --- a/tests/spdx/validation/test_document_validator.py +++ b/tests/spdx/validation/test_document_validator.py @@ -16,7 +16,7 @@ from spdx.model.document import Document, CreationInfo from spdx.validation.document_validator import validate_full_spdx_document from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.spdx.fixtures import document_fixture, creation_info_fixture +from tests.spdx.fixtures import document_fixture, creation_info_fixture, file_fixture, package_fixture, snippet_fixture def test_valid_document(): @@ -56,3 +56,18 @@ def test_spdx_version_handling(creation_info: CreationInfo, version_input: str, assert validation_messages == expected # TODO: https://github.com/spdx/tools-python/issues/375 + + +def test_duplicated_spdx_ids(): + document = document_fixture( + files=[file_fixture(spdx_id="SPDXRef-File"), file_fixture(spdx_id="SPDXRef-2"), file_fixture(spdx_id="SPDXRef-3")], + packages=[package_fixture(spdx_id="SPDXRef-2"), package_fixture(spdx_id="SPDXRef-DOCUMENT")], + snippets=[snippet_fixture(spdx_id="SPDXRef-2"), snippet_fixture(spdx_id="SPDXRef-3")]) + + context = ValidationContext(spdx_id=document.creation_info.spdx_id, element_type=SpdxElementType.DOCUMENT) + + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document) + + assert validation_messages == [ValidationMessage( + "every spdx_id must be unique within the document, but found the following duplicates: ['SPDXRef-2', 'SPDXRef-3', 'SPDXRef-DOCUMENT']", + context)] From 0c8c30dac50eb857d268feae47abab0170b9db06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 1 Feb 2023 11:21:31 +0100 Subject: [PATCH 219/362] [issue-376] add tests for spdx_id_validators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit also fix some strings Signed-off-by: Armin Tänzer --- src/spdx/validation/spdx_id_validators.py | 9 +- .../validation/test_annotation_validator.py | 2 +- .../validation/test_relationship_validator.py | 4 +- .../validation/test_spdx_id_validators.py | 112 +++++++++++++++++- 4 files changed, 118 insertions(+), 9 deletions(-) diff --git a/src/spdx/validation/spdx_id_validators.py b/src/spdx/validation/spdx_id_validators.py index dfe25f416..cdf8f0c3a 100644 --- a/src/spdx/validation/spdx_id_validators.py +++ b/src/spdx/validation/spdx_id_validators.py @@ -53,8 +53,7 @@ def validate_spdx_id(spdx_id: str, document: Document, check_document: bool = Fa str]: """ Test that the given spdx_id (and a potential DocumentRef to an external document) is valid and, if it is a reference, actually exists in the document. Optionally checks files or the whole document - for the existence of the spdx_id (i.e. if it is used as a reference). Returns a list of validation messages, - and the external document ref part and id part of the provided spdx_id. """ + for the existence of the spdx_id (i.e. if it is used as a reference). Returns a list of validation messages. """ validation_messages: List[str] = [] split_id: List[str] = spdx_id.split(":") @@ -74,7 +73,7 @@ def validate_spdx_id(spdx_id: str, document: Document, check_document: bool = Fa f'the internal SPDX id part of spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: {split_id[1]}') if not is_external_doc_ref_present_in_document(split_id[0], document): validation_messages.append( - f"did not find the external document reference {split_id[0]} in the SPDX document") + f'did not find the external document reference "{split_id[0]}" in the SPDX document') return validation_messages @@ -85,10 +84,10 @@ def validate_spdx_id(spdx_id: str, document: Document, check_document: bool = Fa if check_document: if not is_spdx_id_present_in_document(spdx_id, document): - validation_messages.append(f"did not find the referenced spdx_id {spdx_id} in the SPDX document") + validation_messages.append(f'did not find the referenced spdx_id "{spdx_id}" in the SPDX document') if check_files: if not is_spdx_id_present_in_files(spdx_id, document.files): - validation_messages.append(f"did not find the referenced spdx_id {spdx_id} in the SPDX document's files") + validation_messages.append(f'did not find the referenced spdx_id "{spdx_id}" in the SPDX document\'s files') return validation_messages diff --git a/tests/spdx/validation/test_annotation_validator.py b/tests/spdx/validation/test_annotation_validator.py index b37a6860d..608de5bb8 100644 --- a/tests/spdx/validation/test_annotation_validator.py +++ b/tests/spdx/validation/test_annotation_validator.py @@ -28,7 +28,7 @@ def test_valid_annotation(): @pytest.mark.parametrize("annotation_id, file_id, expected_message", [("SPDXRef-File", "SPDXRef-hiddenFile", - "did not find the referenced spdx_id SPDXRef-File in the SPDX document") + 'did not find the referenced spdx_id "SPDXRef-File" in the SPDX document') ]) def test_invalid_annotation(annotation_id, file_id, expected_message): annotation: Annotation = annotation_fixture(spdx_id=annotation_id) diff --git a/tests/spdx/validation/test_relationship_validator.py b/tests/spdx/validation/test_relationship_validator.py index c93c2e24c..89ea0883d 100644 --- a/tests/spdx/validation/test_relationship_validator.py +++ b/tests/spdx/validation/test_relationship_validator.py @@ -33,9 +33,9 @@ def test_valid_relationship(related_spdx_element): @pytest.mark.parametrize("spdx_element_id, related_spdx_element_id, expected_message", [("SPDXRef-unknownFile", "SPDXRef-File", - 'did not find the referenced spdx_id SPDXRef-unknownFile in the SPDX document'), + 'did not find the referenced spdx_id "SPDXRef-unknownFile" in the SPDX document'), ("SPDXRef-File", "SPDXRef-unknownFile", - 'did not find the referenced spdx_id SPDXRef-unknownFile in the SPDX document'), + 'did not find the referenced spdx_id "SPDXRef-unknownFile" in the SPDX document'), ]) def test_unknown_spdx_id(spdx_element_id, related_spdx_element_id, expected_message): relationship: Relationship = relationship_fixture(spdx_element_id=spdx_element_id, diff --git a/tests/spdx/validation/test_spdx_id_validators.py b/tests/spdx/validation/test_spdx_id_validators.py index 4c001697f..ee8536cba 100644 --- a/tests/spdx/validation/test_spdx_id_validators.py +++ b/tests/spdx/validation/test_spdx_id_validators.py @@ -8,5 +8,115 @@ # 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 spdx.validation.spdx_id_validators import is_valid_internal_spdx_id, is_valid_external_doc_ref_id, \ + get_list_of_all_spdx_ids, is_spdx_id_present_in_document, is_external_doc_ref_present_in_document, validate_spdx_id +from tests.spdx.fixtures import document_fixture, file_fixture, package_fixture, snippet_fixture, creation_info_fixture, \ + external_document_ref_fixture + +DOCUMENT = document_fixture(files=[file_fixture(spdx_id="SPDXRef-File1"), + file_fixture(spdx_id="SPDXRef-File2")], + packages=[package_fixture(spdx_id="SPDXRef-Package1"), + package_fixture(spdx_id="SPDXRef-Package2")], + snippets=[snippet_fixture(spdx_id="SPDXRef-Snippet1"), + snippet_fixture(spdx_id="SPDXRef-Snippet2")], + creation_info=creation_info_fixture( + external_document_refs=[external_document_ref_fixture(document_ref_id="DocumentRef-external"), + external_document_ref_fixture(document_ref_id="DocumentRef-1.2-ext")])) + + +@pytest.mark.parametrize("spdx_id", ["SPDXRef-DOCUMENT", "SPDXRef-File1", "SPDXRef-1.3-3.7"]) +def test_valid_internal_spdx_ids(spdx_id): + assert is_valid_internal_spdx_id(spdx_id) + + +@pytest.mark.parametrize("spdx_id", + ["spdxId", "spdxRef-DOCUMENT", "SPDXRef.File", "SPDXRef#Snippet", "SPDXRef-1.3_3.7"]) +def test_invalid_internal_spdx_ids(spdx_id): + assert not is_valid_internal_spdx_id(spdx_id) + + +@pytest.mark.parametrize("doc_ref_id", ["DocumentRef-external", "DocumentRef-...+", "DocumentRef-v0.4.2-alpha"]) +def test_valid_external_doc_ref_ids(doc_ref_id): + assert is_valid_external_doc_ref_id(doc_ref_id) + + +@pytest.mark.parametrize("doc_ref_id", + ["external-ref", "Documentref-external", "DocumentRef-...#", "DocumentRef-v0_4_2-alpha"]) +def test_invalid_external_doc_ref_ids(doc_ref_id): + assert not is_valid_external_doc_ref_id(doc_ref_id) + + +def test_is_spdx_id_present_in_document(): + assert is_spdx_id_present_in_document("SPDXRef-File1", DOCUMENT) + assert is_spdx_id_present_in_document("SPDXRef-Package2", DOCUMENT) + assert is_spdx_id_present_in_document("SPDXRef-Snippet1", DOCUMENT) + assert is_spdx_id_present_in_document("SPDXRef-DOCUMENT", DOCUMENT) + assert not is_spdx_id_present_in_document("SPDXRef-file2", DOCUMENT) + + +def test_is_external_doc_ref_present_in_document(): + assert is_external_doc_ref_present_in_document("DocumentRef-1.2-ext", DOCUMENT) + assert not is_external_doc_ref_present_in_document("DocumentRef-External1", DOCUMENT) + +def test_list_of_all_spdx_ids(): + TestCase().assertCountEqual(get_list_of_all_spdx_ids(DOCUMENT), + ["SPDXRef-DOCUMENT", "SPDXRef-File1", "SPDXRef-File2", "SPDXRef-Package1", + "SPDXRef-Package2", "SPDXRef-Snippet1", "SPDXRef-Snippet2"]) + + +@pytest.mark.parametrize("spdx_id", + ["DocumentRef-external:SPDXRef-File", "SPDXRef-Package"]) +def test_valid_spdx_id(spdx_id): + validation_messages = validate_spdx_id(spdx_id, DOCUMENT) + + assert validation_messages == [] + + +@pytest.mark.parametrize("spdx_id, expected_messages", + [("DocumentRef-external:extern:SPDXRef-File", + [f"spdx_id must not contain more than one colon in order to separate the external document reference id from the internal SPDX id, but is: DocumentRef-external:extern:SPDXRef-File"]), + ("DocumentRef external:SPDXRef-File", + ['the external document reference part of spdx_id must only contain letters, numbers, ".", "-" and "+" and must begin with "DocumentRef-", but is: DocumentRef external', + 'did not find the external document reference "DocumentRef external" in the SPDX document']), + ("DocRef-ext:SPDXRef-File_2", + ['the external document reference part of spdx_id must only contain letters, numbers, ".", "-" and "+" and must begin with "DocumentRef-", but is: DocRef-ext', + 'the internal SPDX id part of spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: SPDXRef-File_2', + 'did not find the external document reference "DocRef-ext" in the SPDX document']), + ("DocumentRef-external:SPDXRef-File_2", + ['the internal SPDX id part of spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: SPDXRef-File_2']), + ("SPDXRef-42+", + ['spdx_id must only contain letters, numbers, "." and "-" and must begin with "SPDXRef-", but is: SPDXRef-42+']) + ]) +def test_invalid_spdx_id(spdx_id, expected_messages): + validation_messages = validate_spdx_id(spdx_id, DOCUMENT) + + TestCase().assertCountEqual(validation_messages, expected_messages) + + +@pytest.mark.parametrize("spdx_id", + ["DocumentRef-external:SPDXRef-File", "SPDXRef-DOCUMENT", "SPDXRef-File1", "SPDXRef-Package1", "SPDXRef-Snippet1"]) +def test_valid_spdx_id_with_check_document(spdx_id): + validation_messages = validate_spdx_id(spdx_id, DOCUMENT, check_document=True) + assert validation_messages == [] + + +def test_invalid_spdx_id_with_check_document(): + validation_messages = validate_spdx_id("SPDXRef-Filet", DOCUMENT, check_document=True) + assert validation_messages == ['did not find the referenced spdx_id "SPDXRef-Filet" in the SPDX document'] + + +@pytest.mark.parametrize("spdx_id", + ["DocumentRef-external:SPDXRef-File", "SPDXRef-File1"]) +def test_valid_spdx_id_with_check_files(spdx_id): + validation_messages = validate_spdx_id(spdx_id, DOCUMENT, check_files=True) + assert validation_messages == [] + + +def test_invalid_spdx_id_with_check_files(): + validation_messages = validate_spdx_id("SPDXRef-Package1", DOCUMENT, check_files=True) + assert validation_messages == ['did not find the referenced spdx_id "SPDXRef-Package1" in the SPDX document\'s files'] -# TODO: https://github.com/spdx/tools-python/issues/376 From f5abfe3b922807fdfc362d9440f7b7bf4410cf04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 1 Feb 2023 14:36:15 +0100 Subject: [PATCH 220/362] [issue-386] add tests for package_verification_code_validator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- .../package_verification_code_validator.py | 7 ++-- ...est_package_verification_code_validator.py | 38 +++++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 tests/spdx/validation/test_package_verification_code_validator.py diff --git a/src/spdx/validation/package_verification_code_validator.py b/src/spdx/validation/package_verification_code_validator.py index 2b86520cc..de6abbec2 100644 --- a/src/spdx/validation/package_verification_code_validator.py +++ b/src/spdx/validation/package_verification_code_validator.py @@ -16,24 +16,23 @@ from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -# TODO: make test for this (https://github.com/spdx/tools-python/issues/386) def validate_verification_code(verification_code: PackageVerificationCode, parent_id: str) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.PACKAGE_VERIFICATION_CODE, full_element=verification_code) for file in verification_code.excluded_files: - if not file.startswith("./"): + if file.startswith("/"): validation_messages.append( ValidationMessage( - f'file name must be a relative path to the file, starting with "./", but is: {file}', context) + f'file name must not be an absolute path starting with "/", but is: {file}', context) ) value: str = verification_code.value if not re.match("^[0-9a-f]{40}$", value): validation_messages.append( ValidationMessage( - f"value of verification_code must consist of 40 hexadecimal digits, but is: {value} (length: {len(value)} digits)", + f"value of verification_code must consist of 40 lowercase hexadecimal digits, but is: {value} (length: {len(value)} digits)", context) ) diff --git a/tests/spdx/validation/test_package_verification_code_validator.py b/tests/spdx/validation/test_package_verification_code_validator.py new file mode 100644 index 000000000..10ff096c9 --- /dev/null +++ b/tests/spdx/validation/test_package_verification_code_validator.py @@ -0,0 +1,38 @@ +# 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. + +import pytest + +from spdx.model.package import PackageVerificationCode +from spdx.validation.package_verification_code_validator import validate_verification_code +from spdx.validation.validation_message import ValidationContext, SpdxElementType, ValidationMessage + + +def test_valid_package_verification_code(): + code = PackageVerificationCode("71c4025dd9897b364f3ebbb42c484ff43d00791c", ["./excluded_file", "another.file"]) + validation_messages = validate_verification_code(code, "SPDXRef-Package") + + assert validation_messages == [] + + +@pytest.mark.parametrize("code, expected_message", + [(PackageVerificationCode("71c4025dd9897b364f3ebbb42c484ff43d00791cab", []), + "value of verification_code must consist of 40 lowercase hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791cab (length: 42 digits)"), + (PackageVerificationCode("71c4025dd9897b364f3ebbb42c484ff43d00791c", ["/invalid/excluded/file"]), + 'file name must not be an absolute path starting with "/", but is: /invalid/excluded/file') + ]) +def test_invalid_package_verification_code(code, expected_message): + parent_id = "SPDXRef-Package" + context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.PACKAGE_VERIFICATION_CODE, + full_element=code) + validation_messages = validate_verification_code(code, parent_id) + + assert validation_messages == [ValidationMessage(expected_message, context)] From a0141ede44a0a1fd55fbe18ea78141ef638a2690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 1 Feb 2023 15:08:56 +0100 Subject: [PATCH 221/362] [issue-386] test "no contains relationship when files_analyzed=False" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/validation/package_validator.py | 25 +++++++---------- .../spdx/validation/test_package_validator.py | 27 +++++++++++++++++-- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/spdx/validation/package_validator.py b/src/spdx/validation/package_validator.py index 5131d39e4..f73f42eff 100644 --- a/src/spdx/validation/package_validator.py +++ b/src/spdx/validation/package_validator.py @@ -13,7 +13,8 @@ from spdx.model.document import Document from spdx.model.package import Package -from spdx.model.relationship import RelationshipType +from spdx.model.relationship import RelationshipType, Relationship +from spdx.model.relationship_filters import filter_by_type_and_origin, filter_by_type_and_target from spdx.validation.checksum_validator import validate_checksums from spdx.validation.external_package_ref_validator import validate_external_package_refs from spdx.validation.license_expression_validator import validate_license_expression, validate_license_expressions @@ -43,23 +44,18 @@ def validate_package_within_document(package: Package, document: Document) -> Li for message in validate_spdx_id(package.spdx_id, document): validation_messages.append(ValidationMessage(message, context)) - # TODO: make test for this (https://github.com/spdx/tools-python/issues/386) if not package.files_analyzed: - package_contains_relationships = [relationship for relationship in document.relationships if - relationship.relationship_type == RelationshipType.CONTAINS and relationship.spdx_element_id == package.spdx_id] - if package_contains_relationships: - validation_messages.append( - ValidationMessage( - f"package must contain no elements if files_analyzed is False, but found {package_contains_relationships}", - context) - ) + package_contains_relationships = filter_by_type_and_origin(document.relationships, RelationshipType.CONTAINS, + package.spdx_id) + contained_in_package_relationships = filter_by_type_and_target(document.relationships, + RelationshipType.CONTAINED_BY, package.spdx_id) + + combined_relationships: List[Relationship] = package_contains_relationships + contained_in_package_relationships - contained_in_package_relationships = [relationship for relationship in document.relationships if - relationship.relationship_type == RelationshipType.CONTAINED_BY and relationship.related_spdx_element_id == package.spdx_id] - if contained_in_package_relationships: + if combined_relationships: validation_messages.append( ValidationMessage( - f"package must contain no elements if files_analyzed is False, but found {contained_in_package_relationships}", + f"package must contain no elements if files_analyzed is False, but found {combined_relationships}", context) ) @@ -83,7 +79,6 @@ def validate_package(package: Package, context: Optional[ValidationContext] = No for message in validate_url(homepage): validation_messages.append(ValidationMessage("homepage " + message, context)) - # TODO: is verification_code required if files_analyzed=True? (https://github.com/spdx/tools-python/issues/386) verification_code = package.verification_code if verification_code: if not package.files_analyzed: diff --git a/tests/spdx/validation/test_package_validator.py b/tests/spdx/validation/test_package_validator.py index 3a4133872..fa7923982 100644 --- a/tests/spdx/validation/test_package_validator.py +++ b/tests/spdx/validation/test_package_validator.py @@ -14,11 +14,12 @@ import pytest from license_expression import Licensing +from spdx.model.relationship import Relationship, RelationshipType from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone from spdx.validation.package_validator import validate_package_within_document from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -from tests.spdx.fixtures import package_fixture, package_verification_code_fixture, document_fixture +from tests.spdx.fixtures import package_fixture, package_verification_code_fixture, document_fixture, file_fixture def test_valid_package(): @@ -42,7 +43,7 @@ def test_valid_package(): license_info_from_files=[Licensing().parse("some_license")], verification_code=None), "license_info_from_files must be None if files_analyzed is False, but is: [LicenseSymbol('some_license', " - "is_exception=False)]") + "is_exception=False)]") ]) def test_invalid_package(package_input, expected_message): validation_messages: List[ValidationMessage] = validate_package_within_document(package_input, @@ -54,3 +55,25 @@ def test_invalid_package(package_input, expected_message): full_element=package_input)) assert validation_messages == [expected] + + +@pytest.mark.parametrize("relationships", + [[Relationship("SPDXRef-Package", RelationshipType.CONTAINS, "SPDXRef-File1")], + [Relationship("SPDXRef-Package", RelationshipType.CONTAINS, "DocumentRef-external:SPDXRef-File")], + [Relationship("SPDXRef-File2", RelationshipType.CONTAINED_BY, "SPDXRef-Package")], + [Relationship("DocumentRef-external:SPDXRef-File", RelationshipType.CONTAINED_BY, "SPDXRef-Package")], + [Relationship("SPDXRef-Package", RelationshipType.CONTAINS, "SPDXRef-File2"), + Relationship("SPDXRef-File1", RelationshipType.CONTAINED_BY, "SPDXRef-Package")]]) +def test_invalid_package_with_contains(relationships): + document = document_fixture(relationships=relationships, + files=[file_fixture(spdx_id="SPDXRef-File1"), file_fixture(spdx_id="SPDXRef-File2")]) + package = package_fixture(files_analyzed=False, verification_code=None, license_info_from_files=[]) + context = ValidationContext(spdx_id=package.spdx_id, parent_id=document.creation_info.spdx_id, + element_type=SpdxElementType.PACKAGE, + full_element=package) + + validation_messages: List[ValidationMessage] = validate_package_within_document(package, document) + + assert validation_messages == [ + ValidationMessage(f"package must contain no elements if files_analyzed is False, but found {relationships}", + context)] From a47533c64432fd12bb24ad8537e6d3e24ef53f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 1 Feb 2023 16:25:39 +0100 Subject: [PATCH 222/362] [issue-386] test uppercase checksums/verification codes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/validation/checksum_validator.py | 2 +- .../validation/test_checksum_validator.py | 36 ++++++++++--------- ...est_package_verification_code_validator.py | 2 ++ 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/spdx/validation/checksum_validator.py b/src/spdx/validation/checksum_validator.py index b5f3f8078..b684ec772 100644 --- a/src/spdx/validation/checksum_validator.py +++ b/src/spdx/validation/checksum_validator.py @@ -60,7 +60,7 @@ def validate_checksum(checksum: Checksum, parent_id: str) -> List[ValidationMess length = algorithm_length[algorithm] validation_messages.append( ValidationMessage( - f"value of {algorithm} must consist of {length} hexadecimal digits, but is: {checksum.value} (length: {len(checksum.value)} digits)", + f"value of {algorithm} must consist of {length} lowercase hexadecimal digits, but is: {checksum.value} (length: {len(checksum.value)} digits)", context) ) diff --git a/tests/spdx/validation/test_checksum_validator.py b/tests/spdx/validation/test_checksum_validator.py index 99a127db5..7fd5f56d2 100644 --- a/tests/spdx/validation/test_checksum_validator.py +++ b/tests/spdx/validation/test_checksum_validator.py @@ -58,40 +58,42 @@ def test_valid_checksum(checksum): @pytest.mark.parametrize("checksum, expected_message", [(Checksum(ChecksumAlgorithm.SHA1, "af1eec2a1b18886c3f3cc244349d91d8"), - "value of ChecksumAlgorithm.SHA1 must consist of 40 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), + "value of ChecksumAlgorithm.SHA1 must consist of 40 lowercase hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.SHA224, "af1eec2a1b18886c3f3cc244349d91d8"), - "value of ChecksumAlgorithm.SHA224 must consist of 56 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), + "value of ChecksumAlgorithm.SHA224 must consist of 56 lowercase hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.SHA256, "af1eec2a1b18886c3f3cc244349d91d8"), - "value of ChecksumAlgorithm.SHA256 must consist of 64 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), + "value of ChecksumAlgorithm.SHA256 must consist of 64 lowercase hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.SHA384, "af1eec2a1b18886c3f3cc244349d91d8"), - "value of ChecksumAlgorithm.SHA384 must consist of 96 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), + "value of ChecksumAlgorithm.SHA384 must consist of 96 lowercase hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.SHA512, "af1eec2a1b18886c3f3cc244349d91d8"), - "value of ChecksumAlgorithm.SHA512 must consist of 128 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), + "value of ChecksumAlgorithm.SHA512 must consist of 128 lowercase hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.SHA3_256, "af1eec2a1b18886c3f3cc244349d91d8"), - "value of ChecksumAlgorithm.SHA3_256 must consist of 64 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), + "value of ChecksumAlgorithm.SHA3_256 must consist of 64 lowercase hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.SHA3_384, "af1eec2a1b18886c3f3cc244349d91d8"), - "value of ChecksumAlgorithm.SHA3_384 must consist of 96 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), + "value of ChecksumAlgorithm.SHA3_384 must consist of 96 lowercase hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.SHA3_512, "af1eec2a1b18886c3f3cc244349d91d8"), - "value of ChecksumAlgorithm.SHA3_512 must consist of 128 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), + "value of ChecksumAlgorithm.SHA3_512 must consist of 128 lowercase hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.BLAKE2B_256, "af1eec2a1b18886c3f3cc244349d91d8"), - "value of ChecksumAlgorithm.BLAKE2B_256 must consist of 64 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), + "value of ChecksumAlgorithm.BLAKE2B_256 must consist of 64 lowercase hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.BLAKE2B_384, "af1eec2a1b18886c3f3cc244349d91d8"), - "value of ChecksumAlgorithm.BLAKE2B_384 must consist of 96 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), + "value of ChecksumAlgorithm.BLAKE2B_384 must consist of 96 lowercase hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.BLAKE2B_512, "af1eec2a1b18886c3f3cc244349d91d8"), - "value of ChecksumAlgorithm.BLAKE2B_512 must consist of 128 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), + "value of ChecksumAlgorithm.BLAKE2B_512 must consist of 128 lowercase hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.BLAKE3, "af1eec2a1b18886c3f3cc244349d91d8"), - "value of ChecksumAlgorithm.BLAKE3 must consist of at least 256 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), + "value of ChecksumAlgorithm.BLAKE3 must consist of at least 256 lowercase hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), (Checksum(ChecksumAlgorithm.MD2, "71c4025dd9897b364f3ebbb42c484ff43d00791c"), - "value of ChecksumAlgorithm.MD2 must consist of 32 hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791c (length: 40 digits)"), + "value of ChecksumAlgorithm.MD2 must consist of 32 lowercase hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791c (length: 40 digits)"), (Checksum(ChecksumAlgorithm.MD4, "71c4025dd9897b364f3ebbb42c484ff43d00791c"), - "value of ChecksumAlgorithm.MD4 must consist of 32 hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791c (length: 40 digits)"), + "value of ChecksumAlgorithm.MD4 must consist of 32 lowercase hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791c (length: 40 digits)"), (Checksum(ChecksumAlgorithm.MD5, "71c4025dd9897b364f3ebbb42c484ff43d00791c"), - "value of ChecksumAlgorithm.MD5 must consist of 32 hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791c (length: 40 digits)"), + "value of ChecksumAlgorithm.MD5 must consist of 32 lowercase hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791c (length: 40 digits)"), (Checksum(ChecksumAlgorithm.MD6, "a872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafed5"), - "value of ChecksumAlgorithm.MD6 must consist of between 0 and 512 hexadecimal digits, but is: a872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafed5 (length: 513 digits)"), + "value of ChecksumAlgorithm.MD6 must consist of between 0 and 512 lowercase hexadecimal digits, but is: a872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafed5 (length: 513 digits)"), (Checksum(ChecksumAlgorithm.ADLER32, "af1eec2a1b18886c3f3cc244349d91d8"), - "value of ChecksumAlgorithm.ADLER32 must consist of 8 hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), + "value of ChecksumAlgorithm.ADLER32 must consist of 8 lowercase hexadecimal digits, but is: af1eec2a1b18886c3f3cc244349d91d8 (length: 32 digits)"), + (Checksum(ChecksumAlgorithm.SHA1, "CE9F343C4BA371746FD7EAD9B59031AE34D8AFC4"), + "value of ChecksumAlgorithm.SHA1 must consist of 40 lowercase hexadecimal digits, but is: CE9F343C4BA371746FD7EAD9B59031AE34D8AFC4 (length: 40 digits)"), ]) def test_invalid_checksum(checksum, expected_message): parent_id = "parent_id" diff --git a/tests/spdx/validation/test_package_verification_code_validator.py b/tests/spdx/validation/test_package_verification_code_validator.py index 10ff096c9..5ed3ae4ce 100644 --- a/tests/spdx/validation/test_package_verification_code_validator.py +++ b/tests/spdx/validation/test_package_verification_code_validator.py @@ -26,6 +26,8 @@ def test_valid_package_verification_code(): @pytest.mark.parametrize("code, expected_message", [(PackageVerificationCode("71c4025dd9897b364f3ebbb42c484ff43d00791cab", []), "value of verification_code must consist of 40 lowercase hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791cab (length: 42 digits)"), + (PackageVerificationCode("CE9F343C4BA371746FD7EAD9B59031AE34D8AFC4", []), + "value of verification_code must consist of 40 lowercase hexadecimal digits, but is: CE9F343C4BA371746FD7EAD9B59031AE34D8AFC4 (length: 40 digits)"), (PackageVerificationCode("71c4025dd9897b364f3ebbb42c484ff43d00791c", ["/invalid/excluded/file"]), 'file name must not be an absolute path starting with "/", but is: /invalid/excluded/file') ]) From 788e8d4aec940a3c2b2f16bd731564ee1a8da5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 1 Feb 2023 15:57:55 +0100 Subject: [PATCH 223/362] [issue-375] test "document describes at least one element" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- .../validation/test_document_validator.py | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/spdx/validation/test_document_validator.py b/tests/spdx/validation/test_document_validator.py index 944f3077e..0ee0aff9d 100644 --- a/tests/spdx/validation/test_document_validator.py +++ b/tests/spdx/validation/test_document_validator.py @@ -14,6 +14,7 @@ import pytest from spdx.model.document import Document, CreationInfo +from spdx.model.relationship import Relationship, RelationshipType from spdx.validation.document_validator import validate_full_spdx_document from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.spdx.fixtures import document_fixture, creation_info_fixture, file_fixture, package_fixture, snippet_fixture @@ -55,7 +56,25 @@ def test_spdx_version_handling(creation_info: CreationInfo, version_input: str, assert validation_messages == expected - # TODO: https://github.com/spdx/tools-python/issues/375 + +@pytest.mark.parametrize("relationships", + [[Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-File")], + [Relationship("SPDXRef-File", RelationshipType.DESCRIBED_BY, "SPDXRef-DOCUMENT")]]) +def test_document_describes_at_least_one_element(relationships): + document = document_fixture(relationships=relationships) + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document) + + assert validation_messages == [] + + +def test_document_does_not_describe_an_element(): + document = document_fixture(relationships=[Relationship("SPDXRef-Package", RelationshipType.DESCRIBES, "SPDXRef-File")]) + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document) + + assert validation_messages == [ValidationMessage( + 'there must be at least one relationship "SPDXRef-DOCUMENT DESCRIBES ..." or "... DESCRIBED_BY SPDXRef-DOCUMENT"', + ValidationContext(spdx_id="SPDXRef-DOCUMENT", element_type=SpdxElementType.DOCUMENT) + )] def test_duplicated_spdx_ids(): From 5538af962cc4482ff7bde1b9eeb92272f9b7396f Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 8 Feb 2023 11:03:28 +0100 Subject: [PATCH 224/362] [issue-466] bug fix and add test check against RelationshipType to let package information precede contained file information Signed-off-by: Meret Behrens --- .../tagvalue_writer_helper_functions.py | 18 +++++----- .../test_tagvalue_writer_helper_functions.py | 33 +++++++++++++++++++ 2 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 tests/spdx/writer/tagvalue/test_tagvalue_writer_helper_functions.py diff --git a/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py b/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py index 4bf7c71c3..846871982 100644 --- a/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py +++ b/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py @@ -14,7 +14,7 @@ from spdx.model.file import File from license_expression import LicenseExpression from spdx.model.package import Package -from spdx.model.relationship import Relationship +from spdx.model.relationship import Relationship, RelationshipType from spdx.model.snippet import Snippet from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone @@ -81,19 +81,19 @@ def scan_relationships(relationships: List[Relationship], packages: List[Package files_by_spdx_id = {file.spdx_id: file for file in files} packages_spdx_ids = [package.spdx_id for package in packages] for relationship in relationships: - if relationship.relationship_type == "CONTAINS" and \ + if relationship.relationship_type == RelationshipType.CONTAINS and \ relationship.spdx_element_id in packages_spdx_ids and \ - relationship.related_spdx_element in files_by_spdx_id.keys(): + relationship.related_spdx_element_id in files_by_spdx_id.keys(): contained_files_by_package_id.setdefault(relationship.spdx_element_id, []).append( - files_by_spdx_id[relationship.related_spdx_element]) - if relationship.has_comment: + files_by_spdx_id[relationship.related_spdx_element_id]) + if relationship.comment: relationships_to_write.append(relationship) - elif relationship.relationship_type == "CONTAINED_BY" and \ - relationship.related_spdx_element in packages_spdx_ids and \ + elif relationship.relationship_type == RelationshipType.CONTAINED_BY and \ + relationship.related_spdx_element_id in packages_spdx_ids and \ relationship.spdx_element_id in files_by_spdx_id: - contained_files_by_package_id.setdefault(relationship.related_spdx_element, []).append( + contained_files_by_package_id.setdefault(relationship.related_spdx_element_id, []).append( files_by_spdx_id[relationship.spdx_element_id]) - if relationship.has_comment: + if relationship.comment: relationships_to_write.append(relationship) else: relationships_to_write.append(relationship) diff --git a/tests/spdx/writer/tagvalue/test_tagvalue_writer_helper_functions.py b/tests/spdx/writer/tagvalue/test_tagvalue_writer_helper_functions.py new file mode 100644 index 000000000..300a3fd33 --- /dev/null +++ b/tests/spdx/writer/tagvalue/test_tagvalue_writer_helper_functions.py @@ -0,0 +1,33 @@ +# Copyright (c) 2023 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 spdx.model.relationship import RelationshipType +from spdx.writer.tagvalue.tagvalue_writer_helper_functions import scan_relationships +from tests.spdx.fixtures import package_fixture, file_fixture, relationship_fixture + + +def test_scan_relationships(): + first_package_spdx_id = "SPDXRef-Package1" + second_package_spdx_id = "SPDXRef-Package2" + packages = [package_fixture(spdx_id=first_package_spdx_id), package_fixture(spdx_id=second_package_spdx_id)] + file_spdx_id = "SPDXRef-File" + files = [file_fixture(spdx_id=file_spdx_id)] + relationships = [ + relationship_fixture(spdx_element_id=first_package_spdx_id, relationship_type=RelationshipType.CONTAINS, + related_spdx_element_id=file_spdx_id, comment=None), + relationship_fixture(spdx_element_id=second_package_spdx_id, relationship_type=RelationshipType.CONTAINS, + related_spdx_element_id=file_spdx_id, comment=None) + ] + + relationships_to_write, contained_files_by_package_id = scan_relationships(relationships, packages, files) + + assert relationships_to_write == [] + assert contained_files_by_package_id == {first_package_spdx_id: files, + second_package_spdx_id: files} From 01dfeaf0190e29f54fd3ed04c667726e52db9473 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 8 Feb 2023 13:52:15 +0100 Subject: [PATCH 225/362] [issue-407] write comments for relationships Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/relationship_writer.py | 4 ++-- tests/spdx/writer/rdf/test_relationship_writer.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/spdx/writer/rdf/relationship_writer.py b/src/spdx/writer/rdf/relationship_writer.py index fcef9648f..5f7ab274a 100644 --- a/src/spdx/writer/rdf/relationship_writer.py +++ b/src/spdx/writer/rdf/relationship_writer.py @@ -10,7 +10,7 @@ # limitations under the License. from typing import Dict -from rdflib import Graph, BNode, RDF, URIRef +from rdflib import Graph, BNode, RDF, URIRef, RDFS, Literal from spdx.model.relationship import Relationship from spdx.model.spdx_no_assertion import SpdxNoAssertion @@ -34,7 +34,7 @@ def add_relationship_to_graph(relationship: Relationship, graph: Graph, doc_name graph.add((relationship_node, SPDX_NAMESPACE.relatedSpdxElement, URIRef(add_namespace_to_spdx_id(relationship.related_spdx_element_id, doc_namespace, external_doc_ref_to_namespace)))) - + graph.add((relationship_node, RDFS.comment, Literal(relationship.comment))) relationship_resource = URIRef( add_namespace_to_spdx_id(relationship.spdx_element_id, doc_namespace, external_doc_ref_to_namespace)) graph.add((relationship_resource, SPDX_NAMESPACE.relationship, relationship_node)) diff --git a/tests/spdx/writer/rdf/test_relationship_writer.py b/tests/spdx/writer/rdf/test_relationship_writer.py index 998da1db8..fd028d618 100644 --- a/tests/spdx/writer/rdf/test_relationship_writer.py +++ b/tests/spdx/writer/rdf/test_relationship_writer.py @@ -8,7 +8,7 @@ # 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 rdflib import Graph, URIRef +from rdflib import Graph, URIRef, RDFS, Literal from spdx.writer.rdf.relationship_writer import add_relationship_to_graph from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -23,3 +23,4 @@ def test_add_relationship_to_graph(): assert(URIRef("docNamespace#SPDXRef-DOCUMENT"), SPDX_NAMESPACE.relationship, None) in graph assert (None, SPDX_NAMESPACE.relationshipType, SPDX_NAMESPACE.relationshipType_describes) in graph assert (None, SPDX_NAMESPACE.relatedSpdxElement, URIRef("docNamespace#SPDXRef-File")) in graph + assert (None, RDFS.comment, Literal(relationship.comment)) in graph From d0925d167ba419e4e46024c50fc709cd8fac33cc Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 15 Feb 2023 09:44:34 +0100 Subject: [PATCH 226/362] fix helper method The return value of err.args[0] is a string, so we need to use append to add this string to the list of messages in the logger. Signed-off-by: Meret Behrens --- src/spdx/parser/jsonlikedict/dict_parsing_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spdx/parser/jsonlikedict/dict_parsing_functions.py b/src/spdx/parser/jsonlikedict/dict_parsing_functions.py index 6414fee2a..fc8e68179 100644 --- a/src/spdx/parser/jsonlikedict/dict_parsing_functions.py +++ b/src/spdx/parser/jsonlikedict/dict_parsing_functions.py @@ -43,7 +43,7 @@ def parse_field_or_log_error(logger: Logger, field: Any, parsing_method: Callabl except SPDXParsingError as err: logger.extend(err.get_messages()) except (TypeError, ValueError) as err: - logger.extend(err.args[0]) + logger.append(err.args[0]) return default From 11d2c99f13f02e159b94e9bf3c2d40e91b4ed720 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 14 Feb 2023 14:10:12 +0100 Subject: [PATCH 227/362] [issue-360] delete examples for specVersion 2.1 which didn't define a schema for json/yaml/xml Signed-off-by: Meret Behrens --- tests/spdx/data/formats/SPDXJsonExample.json | 216 --------------- tests/spdx/data/formats/SPDXXmlExample.xml | 270 ------------------- tests/spdx/data/formats/SPDXYamlExample.yaml | 247 ----------------- tests/spdx/parser/json/test_json_parser.py | 10 - 4 files changed, 743 deletions(-) delete mode 100644 tests/spdx/data/formats/SPDXJsonExample.json delete mode 100644 tests/spdx/data/formats/SPDXXmlExample.xml delete mode 100644 tests/spdx/data/formats/SPDXYamlExample.yaml diff --git a/tests/spdx/data/formats/SPDXJsonExample.json b/tests/spdx/data/formats/SPDXJsonExample.json deleted file mode 100644 index e87a4ea70..000000000 --- a/tests/spdx/data/formats/SPDXJsonExample.json +++ /dev/null @@ -1,216 +0,0 @@ -{ - "comment": "This is a sample spreadsheet", - "name": "Sample_Document-V2.1", - "documentDescribes": [ - "SPDXRef-Package" - ], - "packages": [ - { - "SPDXID": "SPDXRef-Package", - "originator": "Organization: SPDX", - "hasFiles": [ - "SPDXRef-File1", - "SPDXRef-File2" - ], - "licenseInfoFromFiles": [ - "Apache-1.0", - "LicenseRef-3", - "MPL-1.1", - "LicenseRef-2", - "LicenseRef-4", - "Apache-2.0", - "LicenseRef-1" - ], - "name": "SPDX Translator", - "packageFileName": "spdxtranslator-1.0.zip", - "licenseComments": "The declared license information can be found in the NOTICE file at the root of the archive file", - "summary": "SPDX Translator utility", - "sourceInfo": "Version 1.0 of the SPDX Translator application", - "copyrightText": " Copyright 2010, 2011 Source Auditor Inc.", - "packageVerificationCode": { - "packageVerificationCodeValue": "4e3211c67a2d28fced849ee1bb76e7391b93feba", - "packageVerificationCodeExcludedFiles": [ - "SpdxTranslatorSpdx.rdf", - "SpdxTranslatorSpdx.txt" - ] - }, - "licenseConcluded": "(Apache-1.0 AND LicenseRef-2 AND MPL-1.1 AND LicenseRef-3 AND LicenseRef-4 AND Apache-2.0 AND LicenseRef-1)", - "supplier": "Organization: Linux Foundation", - "attributionTexts": [ - "The GNU C Library is free software. See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed. License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually." - ], - "checksums": [ - { - "checksumValue": "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", - "algorithm": "SHA1" - } - ], - "versionInfo": "Version 0.9.2", - "licenseDeclared": "(LicenseRef-4 AND LicenseRef-3 AND Apache-2.0 AND LicenseRef-2 AND MPL-1.1 AND LicenseRef-1)", - "downloadLocation": "http://www.spdx.org/tools", - "description": "This utility translates and SPDX RDF XML document to a spreadsheet, translates a spreadsheet to an SPDX RDF XML document and translates an SPDX RDFa document to an SPDX RDF XML document.", - "primaryPackagePurpose": "OPERATING-SYSTEM", - "builtDate": "2020-01-01T12:00:00Z", - "releaseDate": "2021-01-01T12:00:00Z", - "validUntilDate": "2022-01-01T12:00:00Z" - } - ], - "files": [ - { - "comment": "This file belongs to Jena", - "licenseInfoInFiles": [ - "LicenseRef-1" - ], - "fileName": "Jenna-2.6.3/jena-2.6.3-sources.jar", - "copyrightText": "(c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP", - "artifactOf": [ - { - "name": "Jena", - "homePage": "http://www.openjena.org/", - "projectUri": "http://subversion.apache.org/doap.rdf" - } - ], - "licenseConcluded": "LicenseRef-1", - "licenseComments": "This license is used by Jena", - "checksums": [ - { - "checksumValue": "3ab4e1c67a2d28fced849ee1bb76e7391b93f125", - "algorithm": "SHA1" - }, - { - "checksumValue": "3ab4e1c67a2d28fced849ee1bb76e7391b93f1250000000000000000", - "algorithm": "SHA256" - } - ], - "fileTypes": [ - "ARCHIVE", - "OTHER" - ], - "SPDXID": "SPDXRef-File1" - }, - { - "licenseInfoInFiles": [ - "Apache-2.0" - ], - "fileName": "src/org/spdx/parser/DOAPProject.java", - "copyrightText": "Copyright 2010, 2011 Source Auditor Inc.", - "licenseConcluded": "Apache-2.0", - "checksums": [ - { - "checksumValue": "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", - "algorithm": "SHA1" - } - ], - "fileTypes": [ - "SOURCE", - "TEXT" - ], - "SPDXID": "SPDXRef-File2" - } - ], - "creationInfo": { - "comment": "This is an example of an SPDX spreadsheet format", - "creators": [ - "Tool: SourceAuditor-V1.2", - "Person: Gary O'Neall", - "Organization: Source Auditor Inc." - ], - "licenseListVersion": "3.6", - "created": "2010-02-03T00:00:00Z" - }, - "externalDocumentRefs": [ - { - "checksum": { - "checksumValue": "d6a770ba38583ed4bb4525bd96e50461655d2759", - "algorithm": "SHA1" - }, - "spdxDocument": "https://spdx.org/spdxdocs/spdx-tools-v2.1-3F2504E0-4F89-41D3-9A0C-0305E82C3301", - "externalDocumentId": "DocumentRef-spdx-tool-2.1" - } - ], - "documentNamespace": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301", - "annotations": [ - { - "comment": "This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses", - "annotationType": "REVIEW", - "annotationDate": "2012-06-13T00:00:00Z", - "annotator": "Person: Jim Reviewer" - } - ], - "dataLicense": "CC0-1.0", - "reviewers": [ - { - "comment": "This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses", - "reviewer": "Person: Joe Reviewer", - "reviewDate": "2010-02-10T00:00:00Z" - }, - { - "comment": "Another example reviewer.", - "reviewer": "Person: Suzanne Reviewer", - "reviewDate": "2011-03-13T00:00:00Z" - } - ], - "hasExtractedLicensingInfos": [ - { - "extractedText": "This package includes the GRDDL parser developed by Hewlett Packard under the following license:\n\u00a9 Copyright 2007 Hewlett-Packard Development Company, LP\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\nRedistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\nThe name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.\nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ", - "licenseId": "LicenseRef-2" - }, - { - "extractedText": "The CyberNeko Software License, Version 1.0\n\n\n(C) Copyright 2002-2005, Andy Clark. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n1. Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n3. The end-user documentation included with the redistribution,\n if any, must include the following acknowledgment:\n \"This product includes software developed by Andy Clark.\"\n Alternately, this acknowledgment may appear in the software itself,\n if and wherever such third-party acknowledgments normally appear.\n\n4. The names \"CyberNeko\" and \"NekoHTML\" must not be used to endorse\n or promote products derived from this software without prior\n written permission. For written permission, please contact\n andyc@cyberneko.net.\n\n5. Products derived from this software may not be called \"CyberNeko\",\n nor may \"CyberNeko\" appear in their name, without prior written\n permission of the author.\n\nTHIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED\nWARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\nOF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS\nBE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,\nOR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT\nOF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\nBUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\nWHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\nOR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\nEVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.", - "comment": "This is tye CyperNeko License", - "licenseId": "LicenseRef-3", - "name": "CyberNeko License", - "seeAlsos": [ - "http://justasample.url.com", - "http://people.apache.org/~andyc/neko/LICENSE" - ] - }, - { - "extractedText": "/*\n * (c) Copyright 2009 University of Bristol\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * 3. The name of the author may not be used to endorse or promote products\n * derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */ ", - "licenseId": "LicenseRef-4" - }, - { - "extractedText": "/*\n * (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * 3. The name of the author may not be used to endorse or promote products\n * derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */", - "licenseId": "LicenseRef-1" - } - ], - "spdxVersion": "SPDX-2.1", - "SPDXID": "SPDXRef-DOCUMENT", - "snippets": [ - { - "comment": "This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0-or-later.", - "name": "from linux kernel", - "copyrightText": "Copyright 2008-2010 John Smith", - "licenseConcluded": "Apache-2.0", - "licenseInfoInSnippets": [ - "Apache-2.0", - "GPL-2.0-only" - ], - "licenseComments": "The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz.", - "SPDXID": "SPDXRef-Snippet", - "snippetFromFile": "SPDXRef-DoapSource", - "ranges": [ - { - "endPointer": { - "offset": 420, - "reference": "SPDXRef-DoapSource" - }, - "startPointer": { - "offset": 310, - "reference": "SPDXRef-DoapSource" - } - }, - { - "endPointer": { - "lineNumber": 23, - "reference": "SPDXRef-DoapSource" - }, - "startPointer": { - "lineNumber": 5, - "reference": "SPDXRef-DoapSource" - } - } - ] - } - ] -} diff --git a/tests/spdx/data/formats/SPDXXmlExample.xml b/tests/spdx/data/formats/SPDXXmlExample.xml deleted file mode 100644 index d65321351..000000000 --- a/tests/spdx/data/formats/SPDXXmlExample.xml +++ /dev/null @@ -1,270 +0,0 @@ - - - This is a sample spreadsheet - Sample_Document-V2.1 - SPDXRef-Package - - SPDXRef-Package - Organization: SPDX - The GNU C Library is free software. See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed. License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually. - LicenseRef-3 - LicenseRef-1 - Apache-1.0 - LicenseRef-4 - Apache-2.0 - LicenseRef-2 - MPL-1.1 - SPDX Translator - SPDXRef-File1 - SPDXRef-File2 - spdxtranslator-1.0.zip - The declared license information can be found in the NOTICE file at the root of the archive file - SPDX Translator utility - Version 1.0 of the SPDX Translator application - Copyright 2010, 2011 Source Auditor Inc. - - 4e3211c67a2d28fced849ee1bb76e7391b93feba - SpdxTranslatorSpdx.rdf - SpdxTranslatorSpdx.txt - - (LicenseRef-1 AND MPL-1.1 AND LicenseRef-2 AND LicenseRef-3 AND Apache-2.0 AND LicenseRef-4 AND Apache-1.0) - Organization: Linux Foundation - - 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 - SHA1 - - Version 0.9.2 - (LicenseRef-3 AND LicenseRef-2 AND Apache-2.0 AND MPL-1.1 AND LicenseRef-1 AND LicenseRef-4) - http://www.spdx.org/tools - This utility translates and SPDX RDF XML document to a spreadsheet, translates a spreadsheet to an SPDX RDF XML document and translates an SPDX RDFa document to an SPDX RDF XML document. - OPERATING-SYSTEM - 2020-01-01T12:00:00Z - 2021-01-01T12:00:00Z - 2022-01-01T12:00:00Z - - - This is an example of an SPDX spreadsheet format - Tool: SourceAuditor-V1.2 - Person: Gary O'Neall - Organization: Source Auditor Inc. - 3.6 - 2010-02-03T00:00:00Z - - - - d6a770ba38583ed4bb4525bd96e50461655d2759 - SHA1 - - https://spdx.org/spdxdocs/spdx-tools-v2.1-3F2504E0-4F89-41D3-9A0C-0305E82C3301 - DocumentRef-spdx-tool-2.1 - - https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 - - This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses - REVIEW - 2012-06-13T00:00:00Z - Person: Jim Reviewer - - CC0-1.0 - - This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses - Person: Joe Reviewer - 2010-02-10T00:00:00Z - - - Another example reviewer. - Person: Suzanne Reviewer - 2011-03-13T00:00:00Z - - - /* - * (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - LicenseRef-1 - - - This package includes the GRDDL parser developed by Hewlett Packard under the following license: -© Copyright 2007 Hewlett-Packard Development Company, LP - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - LicenseRef-2 - - - The CyberNeko Software License, Version 1.0 - - -(C) Copyright 2002-2005, Andy Clark. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - -3. The end-user documentation included with the redistribution, - if any, must include the following acknowledgment: - "This product includes software developed by Andy Clark." - Alternately, this acknowledgment may appear in the software itself, - if and wherever such third-party acknowledgments normally appear. - -4. The names "CyberNeko" and "NekoHTML" must not be used to endorse - or promote products derived from this software without prior - written permission. For written permission, please contact - andyc@cyberneko.net. - -5. Products derived from this software may not be called "CyberNeko", - nor may "CyberNeko" appear in their name, without prior written - permission of the author. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - This is tye CyperNeko License - LicenseRef-3 - CyberNeko License - http://justasample.url.com - http://people.apache.org/~andyc/neko/LICENSE - - - /* - * (c) Copyright 2009 University of Bristol - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - LicenseRef-4 - - SPDX-2.1 - SPDXRef-DOCUMENT - - Apache-2.0 - src/org/spdx/parser/DOAPProject.java - Copyright 2010, 2011 Source Auditor Inc. - Apache-2.0 - - - 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 - SHA1 - - SOURCE - TEXT - SPDXRef-File2 - - - This file belongs to Jena - LicenseRef-1 - Jenna-2.6.3/jena-2.6.3-sources.jar - (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP - - Jena - http://www.openjena.org/ - http://subversion.apache.org/doap.rdf - - LicenseRef-1 - This license is used by Jena - - 3ab4e1c67a2d28fced849ee1bb76e7391b93f125 - SHA1 - - - 3ab4e1c67a2d28fced849ee1bb76e7391b93f1250000000000000000 - SHA256 - - ARCHIVE - OTHER - SPDXRef-File1 - - - This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0-or-later. - from linux kernel - Copyright 2008-2010 John Smith - Apache-2.0 - Apache-2.0 - GPL-2.0-only - The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz. - SPDXRef-Snippet - SPDXRef-DoapSource - - - 420 - SPDXRef-DoapSource - - - 310 - SPDXRef-DoapSource - - - - - 23 - SPDXRef-DoapSource - - - 5 - SPDXRef-DoapSource - - - - diff --git a/tests/spdx/data/formats/SPDXYamlExample.yaml b/tests/spdx/data/formats/SPDXYamlExample.yaml deleted file mode 100644 index 854fdb7a7..000000000 --- a/tests/spdx/data/formats/SPDXYamlExample.yaml +++ /dev/null @@ -1,247 +0,0 @@ -Document: - annotations: - - annotationDate: '2012-06-13T00:00:00Z' - annotationType: REVIEW - annotator: 'Person: Jim Reviewer' - comment: This is just an example. Some of the non-standard licenses look like - they are actually BSD 3 clause licenses - comment: This is a sample spreadsheet - creationInfo: - comment: This is an example of an SPDX spreadsheet format - created: '2010-02-03T00:00:00Z' - creators: - - 'Tool: SourceAuditor-V1.2' - - 'Organization: Source Auditor Inc.' - - 'Person: Gary O''Neall' - licenseListVersion: '3.6' - dataLicense: CC0-1.0 - documentDescribes: - - SPDXRef-Package - packages: - - SPDXID: SPDXRef-Package - checksums: - - algorithm: SHA1 - checksumValue: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 - copyrightText: ' Copyright 2010, 2011 Source Auditor Inc.' - description: This utility translates and SPDX RDF XML document to a spreadsheet, - translates a spreadsheet to an SPDX RDF XML document and translates an SPDX - RDFa document to an SPDX RDF XML document. - downloadLocation: http://www.spdx.org/tools - attributionTexts: - - "The GNU C Library is free software. See the file COPYING.LIB for copying conditions,\ - \ and LICENSES for notices about a few contributions that require these additional\ - \ notices to be distributed. License copyright years may be listed using range\ - \ notation, e.g., 1996-2015, indicating that every year in the range, inclusive,\ - \ is a copyrightable year that would otherwise be listed individually." - licenseComments: The declared license information can be found in the NOTICE - file at the root of the archive file - licenseConcluded: (LicenseRef-3 AND LicenseRef-1 AND MPL-1.1 AND Apache-2.0 - AND LicenseRef-2 AND Apache-1.0 AND LicenseRef-4) - licenseDeclared: (MPL-1.1 AND LicenseRef-4 AND LicenseRef-2 AND LicenseRef-1 - AND Apache-2.0 AND LicenseRef-3) - licenseInfoFromFiles: - - Apache-2.0 - - MPL-1.1 - - LicenseRef-3 - - LicenseRef-1 - - LicenseRef-4 - - Apache-1.0 - - LicenseRef-2 - name: SPDX Translator - originator: 'Organization: SPDX' - packageFileName: spdxtranslator-1.0.zip - hasFiles: - - SPDXRef-File1 - - SPDXRef-File2 - packageVerificationCode: - packageVerificationCodeExcludedFiles: - - SpdxTranslatorSpdx.txt - - SpdxTranslatorSpdx.rdf - packageVerificationCodeValue: 4e3211c67a2d28fced849ee1bb76e7391b93feba - sourceInfo: Version 1.0 of the SPDX Translator application - summary: SPDX Translator utility - supplier: 'Organization: Linux Foundation' - versionInfo: Version 0.9.2 - primaryPackagePurpose: OPERATING-SYSTEM - builtDate: '2020-01-01T12:00:00Z' - releaseDate: '2021-01-01T12:00:00Z' - validUntilDate: '2022-01-01T12:00:00Z' - externalDocumentRefs: - - checksum: - algorithm: SHA1 - checksumValue: d6a770ba38583ed4bb4525bd96e50461655d2759 - externalDocumentId: DocumentRef-spdx-tool-2.1 - spdxDocument: https://spdx.org/spdxdocs/spdx-tools-v2.1-3F2504E0-4F89-41D3-9A0C-0305E82C3301 - hasExtractedLicensingInfos: - - comment: This is tye CyperNeko License - extractedText: "The CyberNeko Software License, Version 1.0\n\n\n(C) Copyright\ - \ 2002-2005, Andy Clark. All rights reserved.\n\nRedistribution and use in\ - \ source and binary forms, with or without\nmodification, are permitted provided\ - \ that the following conditions\nare met:\n\n1. Redistributions of source code\ - \ must retain the above copyright\n notice, this list of conditions and the\ - \ following disclaimer.\n\n2. Redistributions in binary form must reproduce\ - \ the above copyright\n notice, this list of conditions and the following\ - \ disclaimer in\n the documentation and/or other materials provided with the\n\ - \ distribution.\n\n3. The end-user documentation included with the redistribution,\n\ - \ if any, must include the following acknowledgment:\n \"This product\ - \ includes software developed by Andy Clark.\"\n Alternately, this acknowledgment\ - \ may appear in the software itself,\n if and wherever such third-party acknowledgments\ - \ normally appear.\n\n4. The names \"CyberNeko\" and \"NekoHTML\" must not be\ - \ used to endorse\n or promote products derived from this software without\ - \ prior\n written permission. For written permission, please contact\n \ - \ andyc@cyberneko.net.\n\n5. Products derived from this software may not be\ - \ called \"CyberNeko\",\n nor may \"CyberNeko\" appear in their name, without\ - \ prior written\n permission of the author.\n\nTHIS SOFTWARE IS PROVIDED ``AS\ - \ IS'' AND ANY EXPRESSED OR IMPLIED\nWARRANTIES, INCLUDING, BUT NOT LIMITED\ - \ TO, THE IMPLIED WARRANTIES\nOF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\ - \ PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS\n\ - BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,\nOR CONSEQUENTIAL\ - \ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT\nOF SUBSTITUTE GOODS\ - \ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\nBUSINESS INTERRUPTION) HOWEVER\ - \ CAUSED AND ON ANY THEORY OF LIABILITY,\nWHETHER IN CONTRACT, STRICT LIABILITY,\ - \ OR TORT (INCLUDING NEGLIGENCE\nOR OTHERWISE) ARISING IN ANY WAY OUT OF THE\ - \ USE OF THIS SOFTWARE,\nEVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." - licenseId: LicenseRef-3 - name: CyberNeko License - seeAlsos: - - http://justasample.url.com - - http://people.apache.org/~andyc/neko/LICENSE - - extractedText: "/*\n * (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006,\ - \ 2007, 2008, 2009 Hewlett-Packard Development Company, LP\n * All rights reserved.\n\ - \ *\n * Redistribution and use in source and binary forms, with or without\n\ - \ * modification, are permitted provided that the following conditions\n * are\ - \ met:\n * 1. Redistributions of source code must retain the above copyright\n\ - \ * notice, this list of conditions and the following disclaimer.\n * 2.\ - \ Redistributions in binary form must reproduce the above copyright\n * notice,\ - \ this list of conditions and the following disclaimer in the\n * documentation\ - \ and/or other materials provided with the distribution.\n * 3. The name of\ - \ the author may not be used to endorse or promote products\n * derived from\ - \ this software without specific prior written permission.\n *\n * THIS SOFTWARE\ - \ IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES,\ - \ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY\ - \ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL\ - \ THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY,\ - \ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF\ - \ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS\ - \ INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN\ - \ CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE)\ - \ ARISING IN ANY WAY OUT OF THE USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF\ - \ THE POSSIBILITY OF SUCH DAMAGE.\n */" - licenseId: LicenseRef-1 - - extractedText: "This package includes the GRDDL parser developed by Hewlett Packard\ - \ under the following license:\n\xA9 Copyright 2007 Hewlett-Packard Development\ - \ Company, LP\n\nRedistribution and use in source and binary forms, with or\ - \ without modification, are permitted provided that the following conditions\ - \ are met:\n\nRedistributions of source code must retain the above copyright\ - \ notice, this list of conditions and the following disclaimer.\nRedistributions\ - \ in binary form must reproduce the above copyright notice, this list of conditions\ - \ and the following disclaimer in the documentation and/or other materials provided\ - \ with the distribution.\nThe name of the author may not be used to endorse\ - \ or promote products derived from this software without specific prior written\ - \ permission.\nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS\ - \ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\ - \ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN\ - \ NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\ - \ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\ - \ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\ - \ OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER\ - \ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\ - \ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\ - \ POSSIBILITY OF SUCH DAMAGE. " - licenseId: LicenseRef-2 - - extractedText: "/*\n * (c) Copyright 2009 University of Bristol\n * All rights\ - \ reserved.\n *\n * Redistribution and use in source and binary forms, with\ - \ or without\n * modification, are permitted provided that the following conditions\n\ - \ * are met:\n * 1. Redistributions of source code must retain the above copyright\n\ - \ * notice, this list of conditions and the following disclaimer.\n * 2.\ - \ Redistributions in binary form must reproduce the above copyright\n * notice,\ - \ this list of conditions and the following disclaimer in the\n * documentation\ - \ and/or other materials provided with the distribution.\n * 3. The name of\ - \ the author may not be used to endorse or promote products\n * derived from\ - \ this software without specific prior written permission.\n *\n * THIS SOFTWARE\ - \ IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES,\ - \ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY\ - \ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL\ - \ THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY,\ - \ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF\ - \ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS\ - \ INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN\ - \ CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE)\ - \ ARISING IN ANY WAY OUT OF THE USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF\ - \ THE POSSIBILITY OF SUCH DAMAGE.\n */ " - licenseId: LicenseRef-4 - SPDXID: SPDXRef-DOCUMENT - name: Sample_Document-V2.1 - documentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 - reviewers: - - comment: Another example reviewer. - reviewDate: '2011-03-13T00:00:00Z' - reviewer: 'Person: Suzanne Reviewer' - - comment: This is just an example. Some of the non-standard licenses look like - they are actually BSD 3 clause licenses - reviewDate: '2010-02-10T00:00:00Z' - reviewer: 'Person: Joe Reviewer' - snippets: - - comment: This snippet was identified as significant and highlighted in this Apache-2.0 - file, when a commercial scanner identified it as being derived from file foo.c - in package xyz which is licensed under GPL-2.0-or-later. - copyrightText: Copyright 2008-2010 John Smith - snippetFromFile: SPDXRef-DoapSource - SPDXID: SPDXRef-Snippet - licenseComments: The concluded license was taken from package xyz, from which - the snippet was copied into the current file. The concluded license information - was found in the COPYING.txt file in package xyz. - licenseConcluded: Apache-2.0 - licenseInfoInSnippets: - - Apache-2.0 - - GPL-2.0-only - name: from linux kernel - ranges: - - endPointer: - offset: 420 - reference: "SPDXRef-DoapSource" - startPointer: - offset: 310 - reference: "SPDXRef-DoapSource" - - endPointer: - lineNumber: 23 - reference: "SPDXRef-DoapSource" - startPointer: - lineNumber: 5 - reference: "SPDXRef-DoapSource" - spdxVersion: SPDX-2.1 - files: - - SPDXID: SPDXRef-File1 - checksums: - - algorithm: SHA1 - checksumValue: 3ab4e1c67a2d28fced849ee1bb76e7391b93f125 - - algorithm: SHA256 - checksumValue: 3ab4e1c67a2d28fced849ee1bb76e7391b93f1250000000000000000 - comment: This file belongs to Jena - copyrightText: (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, - 2008, 2009 Hewlett-Packard Development Company, LP - artifactOf: - - name: "Jena" - homePage: "http://www.openjena.org/" - projectUri: "http://subversion.apache.org/doap.rdf" - fileTypes: - - ARCHIVE - - OTHER - licenseComments: This license is used by Jena - licenseConcluded: LicenseRef-1 - licenseInfoInFiles: - - LicenseRef-1 - fileName: Jenna-2.6.3/jena-2.6.3-sources.jar - - - SPDXID: SPDXRef-File2 - checksums: - - algorithm: SHA1 - checksumValue: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 - copyrightText: Copyright 2010, 2011 Source Auditor Inc. - fileTypes: - - SOURCE - - TEXT - licenseConcluded: Apache-2.0 - licenseInfoInFiles: - - Apache-2.0 - fileName: src/org/spdx/parser/DOAPProject.java diff --git a/tests/spdx/parser/json/test_json_parser.py b/tests/spdx/parser/json/test_json_parser.py index 6939db9f8..d547332f0 100644 --- a/tests/spdx/parser/json/test_json_parser.py +++ b/tests/spdx/parser/json/test_json_parser.py @@ -45,13 +45,3 @@ def test_parse_json_with_2_2_example(): assert len(doc.relationships) == 11 assert len(doc.extracted_licensing_info) == 5 -def test_parse_json_with_2_1_example(): - doc = json_parser.parse_from_file(os.path.join(os.path.dirname(__file__), - "../../data/formats/SPDXJsonExample.json")) - assert type(doc) == Document - assert len(doc.annotations) == 1 - assert len(doc.files) == 2 - assert len(doc.packages) == 1 - assert len(doc.snippets) == 1 - assert len(doc.relationships) == 3 - assert len(doc.extracted_licensing_info) == 4 From fa0c21502e92d72ab0f3beec4a184bf9ccfe05ae Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 14 Feb 2023 15:56:20 +0100 Subject: [PATCH 228/362] [issue-360] fix SBOM example Signed-off-by: Meret Behrens --- tests/spdx/data/formats/SPDXSBOMExample.spdx.yml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tests/spdx/data/formats/SPDXSBOMExample.spdx.yml b/tests/spdx/data/formats/SPDXSBOMExample.spdx.yml index 5655dae55..d26a7c8d3 100644 --- a/tests/spdx/data/formats/SPDXSBOMExample.spdx.yml +++ b/tests/spdx/data/formats/SPDXSBOMExample.spdx.yml @@ -16,9 +16,6 @@ documentDescribes: packages: - SPDXID: "SPDXRef-Package-xyz" summary: "Awesome product created by Example Inc." - checksums: - - algorithm: SHA1 - checksumValue: SOME-SHA1 copyrightText: "copyright 2004-2020 Example Inc. All Rights Reserved." downloadLocation: "git+ssh://gitlab.example.com:3389/products/xyz.git@b2c358080011af6a366d2512a25a379fbe7b1f78" filesAnalyzed: false @@ -31,9 +28,6 @@ packages: description: "A command line tool and library for transferring data with URL syntax, supporting \ HTTP, HTTPS, FTP, FTPS, GOPHER, TFTP, SCP, SFTP, SMB, TELNET, DICT, LDAP, LDAPS, MQTT, FILE, \ IMAP, SMTP, POP3, RTSP and RTMP. libcurl offers a myriad of powerful features." - checksums: - - algorithm: SHA1 - checksumValue: SOME-SHA1 copyrightText: "Copyright (c) 1996 - 2020, Daniel Stenberg, , and many contributors, see the THANKS file." downloadLocation: "https://github.com/curl/curl/releases/download/curl-7_70_0/curl-7.70.0.tar.gz" @@ -47,9 +41,6 @@ packages: - SPDXID: "SPDXRef-Package-openssl" description: "OpenSSL is a robust, commercial-grade, full-featured Open Source Toolkit for the Transport Layer Security (TLS) protocol formerly known as the Secure Sockets Layer (SSL) protocol. The protocol implementation is based on a full-strength general purpose cryptographic library, which can also be used stand-alone." copyrightText: "copyright 2004-2020 The OpenSSL Project Authors. All Rights Reserved." - checksums: - - algorithm: SHA1 - checksumValue: SOME-SHA1 downloadLocation: "git+ssh://github.com/openssl/openssl.git@e2e09d9fba1187f8d6aafaa34d4172f56f1ffb72" filesAnalyzed: false homepage: "https://www.openssl.org/" @@ -64,4 +55,4 @@ relationships: relationshipType: "CONTAINS" - spdxElementId: "SPDXRef-Package-xyz" relatedSpdxElement: "SPDXRef-Package-openssl" - relationshipType: "CONTAINS" \ No newline at end of file + relationshipType: "CONTAINS" From 20b80dcc35eb4cd354f46c082b140b3c98bd0c48 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 10 Feb 2023 12:52:45 +0100 Subject: [PATCH 229/362] [issue-407] add document namespace to unknown types in external package references Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/package_writer.py | 7 +++--- tests/spdx/writer/rdf/test_package_writer.py | 26 ++++++++++++++------ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/spdx/writer/rdf/package_writer.py b/src/spdx/writer/rdf/package_writer.py index 37f963f91..602c6992b 100644 --- a/src/spdx/writer/rdf/package_writer.py +++ b/src/spdx/writer/rdf/package_writer.py @@ -54,7 +54,7 @@ def add_package_to_graph(package: Package, graph: Graph, doc_namespace: str, add_optional_literal(package.description, graph, package_resource, SPDX_NAMESPACE.description) add_optional_literal(package.comment, graph, package_resource, RDFS.comment) for external_reference in package.external_references: - add_external_package_ref_to_graph(external_reference, graph, package_resource) + add_external_package_ref_to_graph(external_reference, graph, package_resource, doc_namespace) for attribution_text in package.attribution_texts: add_optional_literal(attribution_text, graph, package_resource, SPDX_NAMESPACE.attributionText) if package.primary_package_purpose: @@ -81,7 +81,8 @@ def add_package_verification_code_to_graph(package_verification_code: PackageVer graph.add((package_node, SPDX_NAMESPACE.packageVerificationCode, package_verification_code_node)) -def add_external_package_ref_to_graph(external_package_ref: ExternalPackageRef, graph: Graph, package_node: URIRef): +def add_external_package_ref_to_graph(external_package_ref: ExternalPackageRef, graph: Graph, package_node: URIRef, + doc_namespace: str): external_package_ref_node = BNode() graph.add((external_package_ref_node, RDF.type, SPDX_NAMESPACE.ExternalRef)) graph.add((external_package_ref_node, SPDX_NAMESPACE.referenceCategory, @@ -92,7 +93,7 @@ def add_external_package_ref_to_graph(external_package_ref: ExternalPackageRef, REFERENCE_NAMESPACE[external_package_ref.reference_type])) else: graph.add((external_package_ref_node, SPDX_NAMESPACE.referenceType, - URIRef(external_package_ref.reference_type))) + URIRef(f"{doc_namespace}#{external_package_ref.reference_type}"))) graph.add((external_package_ref_node, SPDX_NAMESPACE.referenceLocator, Literal(external_package_ref.locator))) if external_package_ref.comment: graph.add((external_package_ref_node, RDFS.comment, Literal(external_package_ref.comment))) diff --git a/tests/spdx/writer/rdf/test_package_writer.py b/tests/spdx/writer/rdf/test_package_writer.py index 7d2bd20df..abec88493 100644 --- a/tests/spdx/writer/rdf/test_package_writer.py +++ b/tests/spdx/writer/rdf/test_package_writer.py @@ -8,7 +8,9 @@ # 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 pytest from rdflib import Graph, URIRef, RDF, Literal, XSD, RDFS, DOAP +from spdx.model.package import ExternalPackageRefCategory from spdx.datetime_conversions import datetime_to_iso_string from spdx.writer.rdf.package_writer import add_package_to_graph, add_external_package_ref_to_graph, \ @@ -63,14 +65,22 @@ def test_add_package_verification_code_to_graph(): assert (None, SPDX_NAMESPACE.packageVerificationCodeExcludedFile, Literal("./exclude.py")) in graph -def test_external_package_ref_to_graph(): +@pytest.mark.parametrize("external_reference,ref_type,category", + [(external_package_ref_fixture(), URIRef("http://spdx.org/rdf/references/maven-central"), + SPDX_NAMESPACE.referenceCategory_packageManager), + (external_package_ref_fixture(locator="acmecorp/acmenator/4.1.3-alpha", + category=ExternalPackageRefCategory.OTHER, + reference_type="LocationRef-acmeforge", + comment="This is the external ref for Acme"), + URIRef("https://some.namespace#LocationRef-acmeforge"), + SPDX_NAMESPACE.referenceCategory_other)]) +def test_external_package_ref_to_graph(external_reference, ref_type, category): graph = Graph() - external_reference = external_package_ref_fixture() - - add_external_package_ref_to_graph(external_reference, graph, URIRef("docNamespace")) + doc_namespace = "https://some.namespace" + add_external_package_ref_to_graph(external_reference, graph, URIRef("docNamespace"), doc_namespace) assert (None, RDF.type, SPDX_NAMESPACE.ExternalRef) in graph - assert (None, SPDX_NAMESPACE.referenceCategory, SPDX_NAMESPACE.referenceCategory_packageManager) in graph - assert (None, SPDX_NAMESPACE.referenceType, URIRef("http://spdx.org/rdf/references/maven-central")) in graph - assert (None, SPDX_NAMESPACE.referenceLocator, Literal("org.apache.tomcat:tomcat:9.0.0.M4")) in graph - assert (None, RDFS.comment, Literal("externalPackageRefComment")) in graph + assert (None, SPDX_NAMESPACE.referenceCategory, category) in graph + assert (None, SPDX_NAMESPACE.referenceType, ref_type) in graph + assert (None, SPDX_NAMESPACE.referenceLocator, Literal(external_reference.locator)) in graph + assert (None, RDFS.comment, Literal(external_reference.comment)) in graph From 3e966031386ec9f6576334ab8e622b9872438ae2 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Feb 2023 14:15:07 +0100 Subject: [PATCH 230/362] delete rdflib as optional dependency as it is required Signed-off-by: Meret Behrens --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 97a5f851a..282f56d38 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,8 +28,7 @@ dependencies = ["click", "pyyaml", "xmltodict", "rdflib", "typeguard", "uritools dynamic = ["version"] [project.optional-dependencies] -test = ["pytest", "rdflib"] -rdf = ["rdflib"] +test = ["pytest"] [project.scripts] pyspdxtools = "spdx.clitools.pyspdxtools:main" From 096c77d3b0476251a1fe25c6eade5f298a42d269 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Feb 2023 10:47:55 +0100 Subject: [PATCH 231/362] delete installation from rdflib as the package is a required dependency Signed-off-by: Meret Behrens --- .github/workflows/install_and_test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/install_and_test.yml b/.github/workflows/install_and_test.yml index de867e1cb..77078c5f6 100644 --- a/.github/workflows/install_and_test.yml +++ b/.github/workflows/install_and_test.yml @@ -23,7 +23,6 @@ jobs: python -m build -nwx . python -m pip install --upgrade ./dist/*.whl python -m pip install pytest - python -m pip install rdflib shell: bash - name: Run tests run: pytest From bba54853531108084676b72d17120907daa86247 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Feb 2023 14:11:04 +0100 Subject: [PATCH 232/362] move helper methods to also use it for the rdf parser Signed-off-by: Meret Behrens --- src/spdx/parser/jsonlikedict/actor_parser.py | 2 +- .../parser/jsonlikedict/annotation_parser.py | 4 +-- .../parser/jsonlikedict/checksum_parser.py | 4 +-- .../jsonlikedict/creation_info_parser.py | 3 +- .../jsonlikedict/dict_parsing_functions.py | 18 +---------- .../extracted_licensing_info_parser.py | 3 +- src/spdx/parser/jsonlikedict/file_parser.py | 4 +-- .../jsonlikedict/json_like_dict_parser.py | 4 +-- .../jsonlikedict/license_expression_parser.py | 4 +-- .../parser/jsonlikedict/package_parser.py | 4 +-- .../jsonlikedict/relationship_parser.py | 5 ++- .../parser/jsonlikedict/snippet_parser.py | 3 +- src/spdx/parser/parsing_functions.py | 31 +++++++++++++++++++ 13 files changed, 53 insertions(+), 36 deletions(-) create mode 100644 src/spdx/parser/parsing_functions.py diff --git a/src/spdx/parser/jsonlikedict/actor_parser.py b/src/spdx/parser/jsonlikedict/actor_parser.py index 9126782a4..28c5ad960 100644 --- a/src/spdx/parser/jsonlikedict/actor_parser.py +++ b/src/spdx/parser/jsonlikedict/actor_parser.py @@ -13,7 +13,7 @@ from spdx.model.actor import Actor, ActorType from spdx.parser.error import SPDXParsingError -from spdx.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error +from spdx.parser.parsing_functions import construct_or_raise_parsing_error class ActorParser: diff --git a/src/spdx/parser/jsonlikedict/annotation_parser.py b/src/spdx/parser/jsonlikedict/annotation_parser.py index bdfd5e3df..564ec79ad 100644 --- a/src/spdx/parser/jsonlikedict/annotation_parser.py +++ b/src/spdx/parser/jsonlikedict/annotation_parser.py @@ -15,8 +15,8 @@ from spdx.model.annotation import Annotation, AnnotationType from spdx.parser.error import SPDXParsingError from spdx.parser.jsonlikedict.actor_parser import ActorParser -from spdx.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error, \ - parse_field_or_log_error, append_parsed_field_or_log_error, raise_parsing_error_if_logger_has_messages +from spdx.parser.jsonlikedict.dict_parsing_functions import parse_field_or_log_error, append_parsed_field_or_log_error +from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.datetime_conversions import datetime_from_str from spdx.parser.logger import Logger diff --git a/src/spdx/parser/jsonlikedict/checksum_parser.py b/src/spdx/parser/jsonlikedict/checksum_parser.py index bd03b57a6..5bdb574a5 100644 --- a/src/spdx/parser/jsonlikedict/checksum_parser.py +++ b/src/spdx/parser/jsonlikedict/checksum_parser.py @@ -11,8 +11,8 @@ from typing import Dict, Optional from spdx.model.checksum import Checksum, ChecksumAlgorithm -from spdx.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, \ - construct_or_raise_parsing_error +from spdx.parser.jsonlikedict.dict_parsing_functions import json_str_to_enum_name +from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.logger import Logger diff --git a/src/spdx/parser/jsonlikedict/creation_info_parser.py b/src/spdx/parser/jsonlikedict/creation_info_parser.py index c628bdc89..81725a116 100644 --- a/src/spdx/parser/jsonlikedict/creation_info_parser.py +++ b/src/spdx/parser/jsonlikedict/creation_info_parser.py @@ -20,8 +20,9 @@ from spdx.parser.jsonlikedict.actor_parser import ActorParser from spdx.parser.jsonlikedict.checksum_parser import ChecksumParser from spdx.parser.jsonlikedict.dict_parsing_functions import append_parsed_field_or_log_error, \ - raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error, parse_field_or_log_error, \ + parse_field_or_log_error, \ parse_field_or_no_assertion +from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.datetime_conversions import datetime_from_str from spdx.parser.logger import Logger diff --git a/src/spdx/parser/jsonlikedict/dict_parsing_functions.py b/src/spdx/parser/jsonlikedict/dict_parsing_functions.py index fc8e68179..3986cbfc0 100644 --- a/src/spdx/parser/jsonlikedict/dict_parsing_functions.py +++ b/src/spdx/parser/jsonlikedict/dict_parsing_functions.py @@ -12,9 +12,9 @@ from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone -from common.typing.constructor_type_errors import ConstructorTypeErrors from spdx.parser.error import SPDXParsingError from spdx.parser.logger import Logger +from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages def json_str_to_enum_name(json_str: str) -> str: @@ -23,14 +23,6 @@ def json_str_to_enum_name(json_str: str) -> str: return json_str.replace("-", "_").upper() -def construct_or_raise_parsing_error(object_to_construct: Any, args_for_construction: Dict) -> Any: - try: - constructed_object = object_to_construct(**args_for_construction) - except ConstructorTypeErrors as err: - raise SPDXParsingError([f"Error while constructing {object_to_construct.__name__}: {err.get_messages()}"]) - return constructed_object - - def parse_field_or_log_error(logger: Logger, field: Any, parsing_method: Callable = lambda x: x, default: Any = None, field_is_list: bool = False) -> Any: if not field: @@ -59,14 +51,6 @@ def append_parsed_field_or_log_error(logger: Logger, list_to_append_to: List[Any return list_to_append_to -def raise_parsing_error_if_logger_has_messages(logger: Logger, parsed_object_name: str = None): - if logger.has_messages(): - if parsed_object_name: - raise SPDXParsingError([f"Error while parsing {parsed_object_name}: {logger.get_messages()}"]) - else: - raise SPDXParsingError(logger.get_messages()) - - def parse_field_or_no_assertion_or_none(field: Optional[str], method_for_field: Callable = lambda x: x) -> Any: if field == SpdxNoAssertion().__str__(): return SpdxNoAssertion() diff --git a/src/spdx/parser/jsonlikedict/extracted_licensing_info_parser.py b/src/spdx/parser/jsonlikedict/extracted_licensing_info_parser.py index 0d70ccaba..f1bad9657 100644 --- a/src/spdx/parser/jsonlikedict/extracted_licensing_info_parser.py +++ b/src/spdx/parser/jsonlikedict/extracted_licensing_info_parser.py @@ -12,7 +12,8 @@ from spdx.model.extracted_licensing_info import ExtractedLicensingInfo from spdx.model.spdx_no_assertion import SpdxNoAssertion -from spdx.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error, parse_field_or_no_assertion +from spdx.parser.jsonlikedict.dict_parsing_functions import parse_field_or_no_assertion +from spdx.parser.parsing_functions import construct_or_raise_parsing_error from spdx.parser.logger import Logger diff --git a/src/spdx/parser/jsonlikedict/file_parser.py b/src/spdx/parser/jsonlikedict/file_parser.py index 156fae676..f7cb62030 100644 --- a/src/spdx/parser/jsonlikedict/file_parser.py +++ b/src/spdx/parser/jsonlikedict/file_parser.py @@ -16,9 +16,9 @@ from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone from spdx.parser.jsonlikedict.checksum_parser import ChecksumParser -from spdx.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ - construct_or_raise_parsing_error, parse_field_or_log_error, \ +from spdx.parser.jsonlikedict.dict_parsing_functions import parse_field_or_log_error, \ parse_field_or_no_assertion_or_none +from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser from spdx.parser.logger import Logger diff --git a/src/spdx/parser/jsonlikedict/json_like_dict_parser.py b/src/spdx/parser/jsonlikedict/json_like_dict_parser.py index 70334ff7f..a4ecf49a7 100644 --- a/src/spdx/parser/jsonlikedict/json_like_dict_parser.py +++ b/src/spdx/parser/jsonlikedict/json_like_dict_parser.py @@ -14,8 +14,8 @@ from spdx.parser.error import SPDXParsingError from spdx.parser.jsonlikedict.annotation_parser import AnnotationParser from spdx.parser.jsonlikedict.creation_info_parser import CreationInfoParser -from spdx.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ - construct_or_raise_parsing_error, parse_list_of_elements +from spdx.parser.jsonlikedict.dict_parsing_functions import parse_list_of_elements +from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.jsonlikedict.extracted_licensing_info_parser import ExtractedLicensingInfoParser from spdx.parser.jsonlikedict.file_parser import FileParser from spdx.parser.logger import Logger diff --git a/src/spdx/parser/jsonlikedict/license_expression_parser.py b/src/spdx/parser/jsonlikedict/license_expression_parser.py index 2e3f41c11..07a634960 100644 --- a/src/spdx/parser/jsonlikedict/license_expression_parser.py +++ b/src/spdx/parser/jsonlikedict/license_expression_parser.py @@ -15,8 +15,8 @@ from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone from spdx.parser.error import SPDXParsingError -from spdx.parser.jsonlikedict.dict_parsing_functions import append_parsed_field_or_log_error, \ - raise_parsing_error_if_logger_has_messages +from spdx.parser.jsonlikedict.dict_parsing_functions import append_parsed_field_or_log_error +from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages from spdx.parser.logger import Logger diff --git a/src/spdx/parser/jsonlikedict/package_parser.py b/src/spdx/parser/jsonlikedict/package_parser.py index c57edd977..1acb6afb8 100644 --- a/src/spdx/parser/jsonlikedict/package_parser.py +++ b/src/spdx/parser/jsonlikedict/package_parser.py @@ -21,8 +21,8 @@ from spdx.parser.jsonlikedict.actor_parser import ActorParser from spdx.parser.jsonlikedict.checksum_parser import ChecksumParser from spdx.parser.jsonlikedict.dict_parsing_functions import append_parsed_field_or_log_error, \ - raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, construct_or_raise_parsing_error, \ - parse_field_or_log_error, parse_field_or_no_assertion_or_none, parse_field_or_no_assertion + json_str_to_enum_name, parse_field_or_log_error, parse_field_or_no_assertion_or_none, parse_field_or_no_assertion +from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.datetime_conversions import datetime_from_str from spdx.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser from spdx.parser.logger import Logger diff --git a/src/spdx/parser/jsonlikedict/relationship_parser.py b/src/spdx/parser/jsonlikedict/relationship_parser.py index 2550f63c5..c78d2771d 100644 --- a/src/spdx/parser/jsonlikedict/relationship_parser.py +++ b/src/spdx/parser/jsonlikedict/relationship_parser.py @@ -13,10 +13,9 @@ from spdx.model.relationship import Relationship, RelationshipType from common.typing.constructor_type_errors import ConstructorTypeErrors from spdx.parser.error import SPDXParsingError -from spdx.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \ - json_str_to_enum_name, \ - construct_or_raise_parsing_error, \ +from spdx.parser.jsonlikedict.dict_parsing_functions import json_str_to_enum_name, \ parse_field_or_log_error, parse_field_or_no_assertion_or_none, delete_duplicates_from_list +from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.logger import Logger diff --git a/src/spdx/parser/jsonlikedict/snippet_parser.py b/src/spdx/parser/jsonlikedict/snippet_parser.py index 63964dcd4..264301a5f 100644 --- a/src/spdx/parser/jsonlikedict/snippet_parser.py +++ b/src/spdx/parser/jsonlikedict/snippet_parser.py @@ -16,8 +16,9 @@ from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone from spdx.parser.error import SPDXParsingError -from spdx.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error, parse_field_or_log_error, \ +from spdx.parser.jsonlikedict.dict_parsing_functions import parse_field_or_log_error, \ parse_field_or_no_assertion_or_none +from spdx.parser.parsing_functions import construct_or_raise_parsing_error from spdx.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser from spdx.parser.logger import Logger diff --git a/src/spdx/parser/parsing_functions.py b/src/spdx/parser/parsing_functions.py new file mode 100644 index 000000000..5beaef5f1 --- /dev/null +++ b/src/spdx/parser/parsing_functions.py @@ -0,0 +1,31 @@ +# Copyright (c) 2023 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 typing import Any, Dict + +from common.typing.constructor_type_errors import ConstructorTypeErrors +from spdx.parser.error import SPDXParsingError +from spdx.parser.logger import Logger + + +def construct_or_raise_parsing_error(object_to_construct: Any, args_for_construction: Dict) -> Any: + try: + constructed_object = object_to_construct(**args_for_construction) + except ConstructorTypeErrors as err: + raise SPDXParsingError([f"Error while constructing {object_to_construct.__name__}: {err.get_messages()}"]) + return constructed_object + + +def raise_parsing_error_if_logger_has_messages(logger: Logger, parsed_object_name: str = None): + if logger.has_messages(): + if parsed_object_name: + raise SPDXParsingError([f"Error while parsing {parsed_object_name}: {logger.get_messages()}"]) + else: + raise SPDXParsingError(logger.get_messages()) From e7deaa19ebbb86cbf5924e8e1a4d0b9d33df0bce Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Feb 2023 10:59:15 +0100 Subject: [PATCH 233/362] move actor_parser.py as it is format-agnostic and will be used for json, yaml, xml and rdf parsing Signed-off-by: Meret Behrens --- src/spdx/parser/{jsonlikedict => }/actor_parser.py | 0 src/spdx/parser/jsonlikedict/annotation_parser.py | 2 +- src/spdx/parser/jsonlikedict/creation_info_parser.py | 2 +- src/spdx/parser/jsonlikedict/package_parser.py | 2 +- tests/spdx/{parser/jsonlikedict => }/test_actor_parser.py | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename src/spdx/parser/{jsonlikedict => }/actor_parser.py (100%) rename tests/spdx/{parser/jsonlikedict => }/test_actor_parser.py (97%) diff --git a/src/spdx/parser/jsonlikedict/actor_parser.py b/src/spdx/parser/actor_parser.py similarity index 100% rename from src/spdx/parser/jsonlikedict/actor_parser.py rename to src/spdx/parser/actor_parser.py diff --git a/src/spdx/parser/jsonlikedict/annotation_parser.py b/src/spdx/parser/jsonlikedict/annotation_parser.py index 564ec79ad..f81da9ea4 100644 --- a/src/spdx/parser/jsonlikedict/annotation_parser.py +++ b/src/spdx/parser/jsonlikedict/annotation_parser.py @@ -14,7 +14,7 @@ from spdx.model.actor import Actor from spdx.model.annotation import Annotation, AnnotationType from spdx.parser.error import SPDXParsingError -from spdx.parser.jsonlikedict.actor_parser import ActorParser +from spdx.parser.actor_parser import ActorParser from spdx.parser.jsonlikedict.dict_parsing_functions import parse_field_or_log_error, append_parsed_field_or_log_error from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.datetime_conversions import datetime_from_str diff --git a/src/spdx/parser/jsonlikedict/creation_info_parser.py b/src/spdx/parser/jsonlikedict/creation_info_parser.py index 81725a116..992ae8106 100644 --- a/src/spdx/parser/jsonlikedict/creation_info_parser.py +++ b/src/spdx/parser/jsonlikedict/creation_info_parser.py @@ -17,7 +17,7 @@ from spdx.model.external_document_ref import ExternalDocumentRef from spdx.model.version import Version from spdx.parser.error import SPDXParsingError -from spdx.parser.jsonlikedict.actor_parser import ActorParser +from spdx.parser.actor_parser import ActorParser from spdx.parser.jsonlikedict.checksum_parser import ChecksumParser from spdx.parser.jsonlikedict.dict_parsing_functions import append_parsed_field_or_log_error, \ parse_field_or_log_error, \ diff --git a/src/spdx/parser/jsonlikedict/package_parser.py b/src/spdx/parser/jsonlikedict/package_parser.py index 1acb6afb8..57300337d 100644 --- a/src/spdx/parser/jsonlikedict/package_parser.py +++ b/src/spdx/parser/jsonlikedict/package_parser.py @@ -18,7 +18,7 @@ from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone from spdx.parser.error import SPDXParsingError -from spdx.parser.jsonlikedict.actor_parser import ActorParser +from spdx.parser.actor_parser import ActorParser from spdx.parser.jsonlikedict.checksum_parser import ChecksumParser from spdx.parser.jsonlikedict.dict_parsing_functions import append_parsed_field_or_log_error, \ json_str_to_enum_name, parse_field_or_log_error, parse_field_or_no_assertion_or_none, parse_field_or_no_assertion diff --git a/tests/spdx/parser/jsonlikedict/test_actor_parser.py b/tests/spdx/test_actor_parser.py similarity index 97% rename from tests/spdx/parser/jsonlikedict/test_actor_parser.py rename to tests/spdx/test_actor_parser.py index 3938d9536..2e956a14f 100644 --- a/tests/spdx/parser/jsonlikedict/test_actor_parser.py +++ b/tests/spdx/test_actor_parser.py @@ -13,7 +13,7 @@ from spdx.model.actor import ActorType from spdx.parser.error import SPDXParsingError -from spdx.parser.jsonlikedict.actor_parser import ActorParser +from spdx.parser.actor_parser import ActorParser @pytest.mark.parametrize("actor_string,expected_type,expected_name,expected_mail", [ From b0e48b5310062324c30db96096f7de5d22bd37de Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 31 Jan 2023 19:18:59 +0100 Subject: [PATCH 234/362] [issue-456] add creation_info parser Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/__init__.py | 0 src/spdx/parser/rdf/creation_info_parser.py | 93 + .../parser/rdf/graph_parsing_functions.py | 31 + src/spdx/parser/rdf/rdf_parser.py | 38 + .../formats/SPDXRdfExample-v2.3.spdx.rdf.xml | 4339 +++++++++++++++++ tests/spdx/parser/rdf/__init__.py | 0 .../rdf/data/file_to_test_rdf_parser.rdf.xml | 185 + .../parser/rdf/test_creation_info_parser.py | 67 + tests/spdx/parser/rdf/test_rdf_parser.py | 29 + 9 files changed, 4782 insertions(+) create mode 100644 src/spdx/parser/rdf/__init__.py create mode 100644 src/spdx/parser/rdf/creation_info_parser.py create mode 100644 src/spdx/parser/rdf/graph_parsing_functions.py create mode 100644 src/spdx/parser/rdf/rdf_parser.py create mode 100644 tests/spdx/data/formats/SPDXRdfExample-v2.3.spdx.rdf.xml create mode 100644 tests/spdx/parser/rdf/__init__.py create mode 100644 tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml create mode 100644 tests/spdx/parser/rdf/test_creation_info_parser.py create mode 100644 tests/spdx/parser/rdf/test_rdf_parser.py diff --git a/src/spdx/parser/rdf/__init__.py b/src/spdx/parser/rdf/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/spdx/parser/rdf/creation_info_parser.py b/src/spdx/parser/rdf/creation_info_parser.py new file mode 100644 index 000000000..c652d22ca --- /dev/null +++ b/src/spdx/parser/rdf/creation_info_parser.py @@ -0,0 +1,93 @@ +# Copyright (c) 2023 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. +import sys +from typing import Tuple +from urllib.parse import urldefrag + +from rdflib import Graph, RDFS, RDF +from rdflib.exceptions import UniquenessError +from rdflib.term import URIRef + +from spdx.parser.error import SPDXParsingError +from spdx.parser.logger import Logger +from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages +from spdx.parser.rdf.graph_parsing_functions import parse_literal +from spdx.rdfschema.namespace import SPDX_NAMESPACE, LICENSE_NAMESPACE + +from spdx.datetime_conversions import datetime_from_str +from spdx.model.document import CreationInfo +from spdx.model.external_document_ref import ExternalDocumentRef +from spdx.model.version import Version +from spdx.parser.jsonlikedict.actor_parser import ActorParser + + +def parse_creation_info(graph: Graph) -> Tuple[CreationInfo, URIRef]: + logger = Logger() + namespace, spdx_id, doc_node = parse_namespace_and_spdx_id(graph) + spec_version = parse_literal(logger, graph, doc_node, SPDX_NAMESPACE.specVersion) + data_license = parse_literal(logger, graph, doc_node, SPDX_NAMESPACE.dataLicense, prefix=LICENSE_NAMESPACE) + comment = parse_literal(logger, graph, doc_node, RDFS.comment) + name = parse_literal(logger, graph, doc_node, SPDX_NAMESPACE.name) + + creation_info_node = graph.value(predicate=RDF.type, object=SPDX_NAMESPACE.CreationInfo) + if not creation_info_node: + logger.append("CreationInfo does not exist.") + raise SPDXParsingError([f"Error while parsing document {name}: {logger.get_messages()}"]) + + created = parse_literal(logger, graph, creation_info_node, SPDX_NAMESPACE.created, + method_to_apply=datetime_from_str) + license_list_version = parse_literal(logger, graph, creation_info_node, SPDX_NAMESPACE.licenseListVersion, + method_to_apply=Version.from_string) + creator_comment = parse_literal(logger, graph, creation_info_node, RDFS.comment) + creators = [] + for (_, _, creator_literal) in graph.triples((creation_info_node, SPDX_NAMESPACE.creator, None)): + creators.append(ActorParser.parse_actor(creator_literal)) + external_document_refs = [] + for (_, _, external_document_node) in graph.triples((parent_node, SPDX_NAMESPACE.externalDocumentRef, None)): + external_document_refs.append(parse_external_document_refs(external_document_node)) + + raise_parsing_error_if_logger_has_messages(logger, "CreationInfo") + creation_info = construct_or_raise_parsing_error(CreationInfo, dict(spdx_id=spdx_id, document_namespace=namespace, + spdx_version=spec_version, name=name, + data_license=data_license, + document_comment=comment, created=created, + license_list_version=license_list_version, + creator_comment=creator_comment, + creators=creators)) + return creation_info + + +def parse_namespace_and_spdx_id(graph: Graph) -> (str, str): + try: + subject = graph.value(predicate=RDF.type, object=SPDX_NAMESPACE.SpdxDocument, any=False) + except UniquenessError: + sys.exit("Multiple SpdxDocuments found, can't parse rdf file.") + + if not subject: + sys.exit("No SpdxDocument found, can't parse rdf file.") + if not "#" in subject: + sys.exit("No '#' found in the URI of SpdxDocument, " + "the URI for the SpdxDocument should be the namespace appended by '#SPDXRef-DOCUMENT.") + + namespace, spdx_id = urldefrag(subject) + + if not namespace: + sys.exit( + "No namespace found, the URI for the SpdxDocument should be the namespace appended by '#SPDXRef-DOCUMENT.") + + if not spdx_id: + spdx_id = None + + return namespace, spdx_id, subject + + +def parse_external_document_refs(external_document_node: Node) -> ExternalDocumentRef: + pass diff --git a/src/spdx/parser/rdf/graph_parsing_functions.py b/src/spdx/parser/rdf/graph_parsing_functions.py new file mode 100644 index 000000000..2fc8f1a44 --- /dev/null +++ b/src/spdx/parser/rdf/graph_parsing_functions.py @@ -0,0 +1,31 @@ +# Copyright (c) 2023 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 typing import Any, Callable + +from rdflib import Graph +from rdflib.exceptions import UniquenessError +from rdflib.term import Node + +from spdx.parser.logger import Logger + + +def parse_literal(logger: Logger, graph: Graph, subject: Node, predicate: Node, default: Any = None, + method_to_apply: Callable = lambda x: x, prefix: str = ""): + try: + value = graph.value(subject=subject, predicate=predicate, default=default, any=False) + except UniquenessError: + logger.append(f"Multiple values for unique value {predicate} found.") + return + + if value: + return method_to_apply(value.removeprefix(prefix)) + + return default diff --git a/src/spdx/parser/rdf/rdf_parser.py b/src/spdx/parser/rdf/rdf_parser.py new file mode 100644 index 000000000..34ad9613e --- /dev/null +++ b/src/spdx/parser/rdf/rdf_parser.py @@ -0,0 +1,38 @@ +# Copyright (c) 2023 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 rdflib import Graph + +from spdx.model.document import Document +from spdx.parser.error import SPDXParsingError +from spdx.parser.logger import Logger +from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages +from spdx.parser.rdf.creation_info_parser import parse_creation_info + + +def parse_from_file(file_name: str) -> Document: + graph = Graph() + with open(file_name) as file: + graph.parse(file, format="xml") + + document: Document = translate_graph_to_document(graph) + return document + + +def translate_graph_to_document(graph: Graph) -> Document: + logger = Logger() + try: + creation_info, doc_node = parse_creation_info(graph) + except SPDXParsingError as err: + logger.extend(err.get_messages()) + creation_info = None + raise_parsing_error_if_logger_has_messages(logger) + document = construct_or_raise_parsing_error(Document, dict(creation_info=creation_info)) + return document diff --git a/tests/spdx/data/formats/SPDXRdfExample-v2.3.spdx.rdf.xml b/tests/spdx/data/formats/SPDXRdfExample-v2.3.spdx.rdf.xml new file mode 100644 index 000000000..cd70429a3 --- /dev/null +++ b/tests/spdx/data/formats/SPDXRdfExample-v2.3.spdx.rdf.xml @@ -0,0 +1,4339 @@ + + + from linux kernel + Copyright 2008-2010 John Smith + The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz. + + + + + + 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 + + + Copyright 2010, 2011 Source Auditor Inc. + Open Logic Inc. + ./src/org/spdx/parser/DOAPProject.java + Black Duck Software In.c + Source Auditor Inc. + SPDX Technical Team Members + + + <<beginOptional>>Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +<<endOptional>><<beginOptional>> TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +<<endOptional>> + + <<var;name="bullet";original="1.";match=".{0,20}">> Definitions. + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + <<var;name="bullet";original="2.";match=".{0,20}">> Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + <<var;name="bullet";original="3.";match=".{0,20}">> Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + + <<var;name="bullet";original="4.";match=".{0,20}">> Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + <<var;name="bullet";original="(a)";match=".{0,20}">> You must give any other recipients of the Work or Derivative Works a copy of this License; and + + <<var;name="bullet";original="(b)";match=".{0,20}">> You must cause any modified files to carry prominent notices stating that You changed the files; and + + <<var;name="bullet";original="(c)";match=".{0,20}">> You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + <<var;name="bullet";original="(d)";match=".{0,20}">> If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + <<var;name="bullet";original="5.";match=".{0,20}">> Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + <<var;name="bullet";original="6.";match=".{0,20}">> Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + <<var;name="bullet";original="7.";match=".{0,20}">> Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + <<var;name="bullet";original="8.";match=".{0,20}">> Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + <<var;name="bullet";original="9.";match=".{0,20}">> Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.<<beginOptional>> END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright <<var;name="copyright";original="[yyyy] [name of copyright owner]";match=".+">> + +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. + +<<endOptional>> + This license was released January 2004 + Apache License 2.0 + + <p>Copyright <var class="replacable-license-text"> [yyyy] [name of copyright owner]</var></p> + + <p>Licensed under the Apache License, Version 2.0 (the &quot;License&quot;); + <br /> + +you may not use this file except in compliance with the License. + <br /> + +You may obtain a copy of the License at + </p> + + <p>http://www.apache.org/licenses/LICENSE-2.0</p> + + <p>Unless required by applicable law or agreed to in writing, software + <br /> + +distributed under the License is distributed on an &quot;AS IS&quot; BASIS, + <br /> + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + <br /> + +See the License for the specific language governing permissions and + <br /> + +limitations under the License. + </p> + + + https://www.apache.org/licenses/LICENSE-2.0 + Copyright [yyyy] [name of copyright owner] + +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. + + + Copyright <<var;name="copyright";original="[yyyy] [name of copyright owner]";match=".+">> + +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. + + + + <div class="optional-license-text"> + <p>Apache License + <br /> + +Version 2.0, January 2004 + <br /> + +http://www.apache.org/licenses/ + </p> + + </div> + <div class="optional-license-text"> + <p>TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION</p> + + </div> + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> 1.</var> + Definitions. + +<ul style="list-style:none"> + +<li> + <p>&quot;License&quot; shall mean the terms and conditions for use, reproduction, and distribution + as defined by Sections 1 through 9 of this document.</p> + + </li> + +<li> + <p>&quot;Licensor&quot; shall mean the copyright owner or entity authorized by the copyright owner + that is granting the License.</p> + + </li> + +<li> + <p>&quot;Legal Entity&quot; shall mean the union of the acting entity and all other entities that + control, are controlled by, or are under common control with that entity. For the purposes of + this definition, &quot;control&quot; means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or otherwise, or (ii) ownership of + fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such + entity.</p> + + </li> + +<li> + <p>&quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity exercising + permissions granted by this License.</p> + + </li> + +<li> + <p>&quot;Source&quot; form shall mean the preferred form for making modifications, including but not + limited to software source code, documentation source, and configuration files.</p> + + </li> + +<li> + <p>&quot;Object&quot; form shall mean any form resulting from mechanical transformation or + translation of a Source form, including but not limited to compiled object code, generated + documentation, and conversions to other media types.</p> + + </li> + +<li> + <p>&quot;Work&quot; shall mean the work of authorship, whether in Source or Object form, made + available under the License, as indicated by a copyright notice that is included in or + attached to the work (an example is provided in the Appendix below).</p> + + </li> + +<li> + <p>&quot;Derivative Works&quot; shall mean any work, whether in Source or Object form, that is based + on (or derived from) the Work and for which the editorial revisions, annotations, + elaborations, or other modifications represent, as a whole, an original work of authorship. + For the purposes of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative + Works thereof.</p> + + </li> + +<li> + <p>&quot;Contribution&quot; shall mean any work of authorship, including the original version of the + Work and any modifications or additions to that Work or Derivative Works thereof, that is + intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an + individual or Legal Entity authorized to submit on behalf of the copyright owner. For the + purposes of this definition, &quot;submitted&quot; means any form of electronic, verbal, or + written communication sent to the Licensor or its representatives, including but not limited + to communication on electronic mailing lists, source code control systems, and issue tracking + systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and + improving the Work, but excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as &quot;Not a Contribution.&quot;</p> + + </li> + +<li> + <p>&quot;Contributor&quot; shall mean Licensor and any individual or Legal Entity on behalf of whom + a Contribution has been received by Licensor and subsequently incorporated within the + Work.</p> + + </li> + +</ul> + </li> + +<li> + <var class="replacable-license-text"> 2.</var> + Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor + hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, + irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, + publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or + Object form. + </li> + +<li> + <var class="replacable-license-text"> 3.</var> + Grant of Patent License. Subject to the terms and conditions of this License, each Contributor + hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, + irrevocable (except as stated in this section) patent license to make, have made, use, offer + to sell, sell, import, and otherwise transfer the Work, where such license applies only to + those patent claims licensable by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) with the Work to which such + Contribution(s) was submitted. If You institute patent litigation against any entity + (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a + Contribution incorporated within the Work constitutes direct or contributory patent + infringement, then any patent licenses granted to You under this License for that Work shall + terminate as of the date such litigation is filed. + </li> + +<li> + <var class="replacable-license-text"> 4.</var> + Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof + in any medium, with or without modifications, and in Source or Object form, provided that You + meet the following conditions: + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> (a)</var> + You must give any other recipients of the Work or Derivative Works a copy of this License; and + </li> + +<li> + <var class="replacable-license-text"> (b)</var> + You must cause any modified files to carry prominent notices stating that You changed the files; and + </li> + +<li> + <var class="replacable-license-text"> (c)</var> + You must retain, in the Source form of any Derivative Works that You distribute, all + copyright, patent, trademark, and attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of the Derivative Works; and + </li> + +<li> + <var class="replacable-license-text"> (d)</var> + If the Work includes a &quot;NOTICE&quot; text file as part of its distribution, then any + Derivative Works that You distribute must include a readable copy of the attribution + notices contained within such NOTICE file, excluding those notices that do not pertain to + any part of the Derivative Works, in at least one of the following places: within a NOTICE + text file distributed as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, within a display generated + by the Derivative Works, if and wherever such third-party notices normally appear. The + contents of the NOTICE file are for informational purposes only and do not modify the + License. You may add Your own attribution notices within Derivative Works that You + distribute, alongside or as an addendum to the NOTICE text from the Work, provided that + such additional attribution notices cannot be construed as modifying the License. + <p>You may add Your own copyright statement to Your modifications and may provide additional or + different license terms and conditions for use, reproduction, or distribution of Your + modifications, or for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with the conditions stated + in this License.</p> + + </li> + +</ul> + </li> + +<li> + <var class="replacable-license-text"> 5.</var> + Submission of Contributions. Unless You explicitly state otherwise, any Contribution + intentionally submitted for inclusion in the Work by You to the Licensor shall be under the + terms and conditions of this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate + license agreement you may have executed with Licensor regarding such Contributions. + </li> + +<li> + <var class="replacable-license-text"> 6.</var> + Trademarks. This License does not grant permission to use the trade names, trademarks, service + marks, or product names of the Licensor, except as required for reasonable and customary use + in describing the origin of the Work and reproducing the content of the NOTICE file. + </li> + +<li> + <var class="replacable-license-text"> 7.</var> + Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor + provides the Work (and each Contributor provides its Contributions) on an &quot;AS IS&quot; + BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, + without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, + or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any risks associated with Your + exercise of permissions under this License. + </li> + +<li> + <var class="replacable-license-text"> 8.</var> + Limitation of Liability. In no event and under no legal theory, whether in tort (including + negligence), contract, or otherwise, unless required by applicable law (such as deliberate and + grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for + damages, including any direct, indirect, special, incidental, or consequential damages of any + character arising as a result of this License or out of the use or inability to use the Work + (including but not limited to damages for loss of goodwill, work stoppage, computer failure or + malfunction, or any and all other commercial damages or losses), even if such Contributor has + been advised of the possibility of such damages. + </li> + +<li> + <var class="replacable-license-text"> 9.</var> + Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works + thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, + indemnity, or other liability obligations and/or rights consistent with this License. However, + in accepting such obligations, You may act only on Your own behalf and on Your sole + responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability incurred by, or claims asserted + against, such Contributor by reason of your accepting any such warranty or additional + liability. + </li> + +</ul> + + <div class="optional-license-text"> + <p>END OF TERMS AND CONDITIONS</p> + + <p>APPENDIX: How to apply the Apache License to your work.</p> + + <p>To apply the Apache License to your work, attach the following boilerplate notice, with the fields + enclosed by brackets &quot;[]&quot; replaced with your own identifying information. (Don&apos;t + include the brackets!) The text should be enclosed in the appropriate comment syntax for the file + format. We also recommend that a file or class name and description of purpose be included on the same + &quot;printed page&quot; as the copyright notice for easier identification within third-party + archives.</p> + + <p>Copyright <var class="replacable-license-text"> [yyyy] [name of copyright owner]</var></p> + + <p>Licensed under the Apache License, Version 2.0 (the &quot;License&quot;); + <br /> + +you may not use this file except in compliance with the License. + <br /> + +You may obtain a copy of the License at + </p> + + <p>http://www.apache.org/licenses/LICENSE-2.0</p> + + <p>Unless required by applicable law or agreed to in writing, software + <br /> + +distributed under the License is distributed on an &quot;AS IS&quot; BASIS, + <br /> + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + <br /> + +See the License for the specific language governing permissions and + <br /> + +limitations under the License. + </p> + + </div> + + true + true + https://opensource.org/licenses/Apache-2.0 + + + 1 + 2022-08-12T00:16:54Z + https://opensource.org/licenses/Apache-2.0 + N/A + false + true + false + + + + + 0 + 2022-08-12T00:16:53Z + https://www.apache.org/licenses/LICENSE-2.0 + true + false + true + true + + + false + Apache-2.0 + Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. + + + + Protecode Inc. + + + + + + + <<beginOptional>>GNU GENERAL PUBLIC LICENSE + +Version 2, June 1991 + +<<endOptional>> + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. <<var;name="incComma";original="";match=",|">> + +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301<<beginOptional>>, <<endOptional>> USA + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +<<var;name="termsTitle";original="";match="GNU GENERAL PUBLIC LICENSE|">> TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + <<var;name="bullet";original="0.";match=".{0,20}">> This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". + + Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + + <<var;name="bullet";original="1.";match=".{0,20}">> You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. + + You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + + <<var;name="bullet";original="2.";match=".{0,20}">> You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + <<var;name="bullet";original="a)";match=".{0,20}">> You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + + <<var;name="bullet";original="b)";match=".{0,20}">> You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. + + <<var;name="bullet";original="c)";match=".{0,20}">> If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) + + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + + In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + + <<var;name="bullet";original="3.";match=".{0,20}">> You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + + <<var;name="bullet";original="a)";match=".{0,20}">> Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + + <<var;name="bullet";original="b)";match=".{0,20}">> Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + + <<var;name="bullet";original="c)";match=".{0,20}">> Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) + + The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + + If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + + <<var;name="bullet";original="4.";match=".{0,20}">> You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + + <<var;name="bullet";original="5.";match=".{0,20}">> You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + + <<var;name="bullet";original="6.";match=".{0,20}">> Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + + <<var;name="bullet";original="7.";match=".{0,20}">> If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. + + If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + + It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + + <<var;name="bullet";original="8.";match=".{0,20}">> If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + + <<var;name="bullet";original="9.";match=".{0,20}">> The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + + <<var;name="bullet";original="10.";match=".{0,20}">> If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + + NO WARRANTY + + <<var;name="bullet";original="11.";match=".{0,20}">> BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + <<var;name="bullet";original="12.";match=".{0,20}">> IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.<<beginOptional>> END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + +<<beginOptional>><<<endOptional>>one line to give the program's name and <<var;name="ideaArticle";original="an";match="a brief|an">> idea of what it does.<<beginOptional>>><<endOptional>> + +Copyright (C)<<beginOptional>><<<endOptional>> <<var;name="templateYear";original="yyyy";match="yyyy|year">><<beginOptional>>> <<endOptional>><<beginOptional>> <<<endOptional>>name of author<<beginOptional>>><<endOptional>> + +This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301<<beginOptional>>, <<endOptional>> USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it starts in an interactive mode: + +Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. + +<<beginOptional>><<<endOptional>>signature of Ty Coon<<beginOptional>> ><<endOptional>>, 1 April 1989 Ty Coon, President of Vice + +<<endOptional>><<beginOptional>> This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. + +<<endOptional>> + https://opensource.org/licenses/GPL-2.0 + This license was released: June 1991. This license identifier refers to the choice to use the code under GPL-2.0-only, as distinguished from use of code under GPL-2.0-or-later (i.e., GPL-2.0 or some later version). The license notice (as seen in the Standard License Header field below) states which of these applies to the code in the file. The example in the exhibit to the license shows the license notice for the "or later" approach. + GPL-2.0-only + https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html + true + + + 1 + 2022-08-12T00:23:31Z + https://opensource.org/licenses/GPL-2.0 + N/A + false + true + false + + + Copyright (C) <<var;name="copyright";original="yyyy name of author";match=".+">> + +This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301<<beginOptional>>, <<endOptional>> USA. + + + false + GNU General Public License v2.0 only + + <div class="optional-license-text"> + <p> + GNU GENERAL PUBLIC LICENSE<br /> + + Version 2, June 1991 + </p> + + </div> + <p> + Copyright (C) 1989, 1991 Free Software Foundation, Inc.<var class="replacable-license-text"> </var><br /> + + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301<var class="optional-license-text">, </var> + USA + </p> + + <p> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + </p> + + <p> + Preamble + </p> + + <p> + The licenses for most software are designed to take away your freedom + to share and change it. By contrast, the GNU General Public License is + intended to guarantee your freedom to share and change free software--to + make sure the software is free for all its users. This General Public + License applies to most of the Free Software Foundation&apos;s software + and to any other program whose authors commit to using it. (Some other + Free Software Foundation software is covered by the GNU Lesser General + Public License instead.) You can apply it to your programs, too. + </p> + + <p> + When we speak of free software, we are referring to freedom, not price. + Our General Public Licenses are designed to make sure that you have + the freedom to distribute copies of free software (and charge for + this service if you wish), that you receive source code or can get + it if you want it, that you can change the software or use pieces of + it in new free programs; and that you know you can do these things. + </p> + + <p> + To protect your rights, we need to make restrictions that forbid + anyone to deny you these rights or to ask you to surrender the + rights. These restrictions translate to certain responsibilities for + you if you distribute copies of the software, or if you modify it. + </p> + + <p> + For example, if you distribute copies of such a program, whether gratis + or for a fee, you must give the recipients all the rights that you + have. You must make sure that they, too, receive or can get the source + code. And you must show them these terms so they know their rights. + </p> + + <p> + We protect your rights with two steps: (1) copyright the + software, and (2) offer you this license which gives you legal + permission to copy, distribute and/or modify the software. + </p> + + <p> + Also, for each author&apos;s protection and ours, we want to make + certain that everyone understands that there is no warranty for + this free software. If the software is modified by someone else + and passed on, we want its recipients to know that what they + have is not the original, so that any problems introduced by + others will not reflect on the original authors&apos; reputations. + </p> + + <p> + Finally, any free program is threatened constantly by software patents. + We wish to avoid the danger that redistributors of a free program + will individually obtain patent licenses, in effect making the program + proprietary. To prevent this, we have made it clear that any patent + must be licensed for everyone&apos;s free use or not licensed at all. + </p> + + <p> + The precise terms and conditions for copying, + distribution and modification follow. + </p> + + <p> + <var class="replacable-license-text"> </var> + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + </p> + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> 0.</var> + This License applies to any program or other work which contains a + notice placed by the copyright holder saying it may be distributed + under the terms of this General Public License. The &quot;Program&quot;, below, + refers to any such program or work, and a &quot;work based on the Program&quot; + means either the Program or any derivative work under copyright law: + that is to say, a work containing the Program or a portion of it, + either verbatim or with modifications and/or translated into another + language. (Hereinafter, translation is included without limitation + in the term &quot;modification&quot;.) Each licensee is addressed as &quot;you&quot;. + <p> + Activities other than copying, distribution and modification are + not covered by this License; they are outside its scope. The act + of running the Program is not restricted, and the output from the + Program is covered only if its contents constitute a work based + on the Program (independent of having been made by running the + Program). Whether that is true depends on what the Program does. + </p> + + </li> + +<li> + <var class="replacable-license-text"> 1.</var> + You may copy and distribute verbatim copies of the Program&apos;s source + code as you receive it, in any medium, provided that you conspicuously + and appropriately publish on each copy an appropriate copyright notice + and disclaimer of warranty; keep intact all the notices that refer to + this License and to the absence of any warranty; and give any other + recipients of the Program a copy of this License along with the Program. + <p> + You may charge a fee for the physical act of + transferring a copy, and you may at your option + offer warranty protection in exchange for a fee. + </p> + + </li> + +<li> + <var class="replacable-license-text"> 2.</var> + You may modify your copy or copies of the Program or any portion + of it, thus forming a work based on the Program, and copy and + distribute such modifications or work under the terms of Section + 1 above, provided that you also meet all of these conditions: + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> a)</var> + You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + </li> + +<li> + <var class="replacable-license-text"> b)</var> + You must cause any work that you distribute or publish, + that in whole or in part contains or is derived from the + Program or any part thereof, to be licensed as a whole at no + charge to all third parties under the terms of this License. + </li> + +<li> + <var class="replacable-license-text"> c)</var> + If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display + an announcement including an appropriate copyright notice and + a notice that there is no warranty (or else, saying that you + provide a warranty) and that users may redistribute the program + under these conditions, and telling the user how to view a copy + of this License. (Exception: if the Program itself is interactive + but does not normally print such an announcement, your work + based on the Program is not required to print an announcement.) + </li> + +</ul> + <p> + These requirements apply to the modified work as a whole. If + identifiable sections of that work are not derived from the + Program, and can be reasonably considered independent and separate + works in themselves, then this License, and its terms, do not + apply to those sections when you distribute them as separate + works. But when you distribute the same sections as part of a + whole which is a work based on the Program, the distribution + of the whole must be on the terms of this License, whose + permissions for other licensees extend to the entire whole, + and thus to each and every part regardless of who wrote it. + </p> + + <p> + Thus, it is not the intent of this section to claim rights or + contest your rights to work written entirely by you; rather, + the intent is to exercise the right to control the distribution + of derivative or collective works based on the Program. + </p> + + <p> + In addition, mere aggregation of another work not based on + the Program with the Program (or with a work based on the + Program) on a volume of a storage or distribution medium does + not bring the other work under the scope of this License. + </p> + + </li> + +<li> + <var class="replacable-license-text"> 3.</var> + You may copy and distribute the Program (or a work based on it, + under Section 2) in object code or executable form under the terms of + Sections 1 and 2 above provided that you also do one of the following: + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> a)</var> + Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and + 2 above on a medium customarily used for software interchange; or, + </li> + +<li> + <var class="replacable-license-text"> b)</var> + Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to + be distributed under the terms of Sections 1 and 2 above + on a medium customarily used for software interchange; or, + </li> + +<li> + <var class="replacable-license-text"> c)</var> + Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative + is allowed only for noncommercial distribution and only if + you received the program in object code or executable form + with such an offer, in accord with Subsection b above.) + </li> + +</ul> + <p> + The source code for a work means the preferred form of the work + for making modifications to it. For an executable work, complete + source code means all the source code for all modules it contains, + plus any associated interface definition files, plus the scripts + used to control compilation and installation of the executable. + However, as a special exception, the source code distributed + need not include anything that is normally distributed (in either + source or binary form) with the major components (compiler, + kernel, and so on) of the operating system on which the executable + runs, unless that component itself accompanies the executable. + </p> + + <p> + If distribution of executable or object code is made by offering + access to copy from a designated place, then offering equivalent + access to copy the source code from the same place counts as + distribution of the source code, even though third parties are + not compelled to copy the source along with the object code. + </p> + + </li> + +<li> + <var class="replacable-license-text"> 4.</var> + You may not copy, modify, sublicense, or distribute the Program + except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense or distribute the Program + is void, and will automatically terminate your rights under + this License. However, parties who have received copies, or + rights, from you under this License will not have their licenses + terminated so long as such parties remain in full compliance. + </li> + +<li> + <var class="replacable-license-text"> 5.</var> + You are not required to accept this License, since you have + not signed it. However, nothing else grants you permission to + modify or distribute the Program or its derivative works. These + actions are prohibited by law if you do not accept this License. + Therefore, by modifying or distributing the Program (or any + work based on the Program), you indicate your acceptance of this + License to do so, and all its terms and conditions for copying, + distributing or modifying the Program or works based on it. + </li> + +<li> + <var class="replacable-license-text"> 6.</var> + Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the + original licensor to copy, distribute or modify the Program subject to + these terms and conditions. You may not impose any further restrictions + on the recipients&apos; exercise of the rights granted herein. You are not + responsible for enforcing compliance by third parties to this License. + </li> + +<li> + <var class="replacable-license-text"> 7.</var> + If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement + or otherwise) that contradict the conditions of this License, + they do not excuse you from the conditions of this License. If you + cannot distribute so as to satisfy simultaneously your obligations + under this License and any other pertinent obligations, then as a + consequence you may not distribute the Program at all. For example, + if a patent license would not permit royalty-free redistribution of + the Program by all those who receive copies directly or indirectly + through you, then the only way you could satisfy both it and this + License would be to refrain entirely from distribution of the Program. + <p> + If any portion of this section is held invalid or + unenforceable under any particular circumstance, the + balance of the section is intended to apply and the section + as a whole is intended to apply in other circumstances. + </p> + + <p> + It is not the purpose of this section to induce you to infringe + any patents or other property right claims or to contest + validity of any such claims; this section has the sole purpose + of protecting the integrity of the free software distribution + system, which is implemented by public license practices. Many + people have made generous contributions to the wide range of + software distributed through that system in reliance on consistent + application of that system; it is up to the author/donor to + decide if he or she is willing to distribute software through + any other system and a licensee cannot impose that choice. + </p> + + <p> + This section is intended to make thoroughly clear what is + believed to be a consequence of the rest of this License. + </p> + + </li> + +<li> + <var class="replacable-license-text"> 8.</var> + If the distribution and/or use of the Program is restricted in + certain countries either by patents or by copyrighted interfaces, + the original copyright holder who places the Program under this + License may add an explicit geographical distribution limitation + excluding those countries, so that distribution is permitted only + in or among countries not thus excluded. In such case, this License + incorporates the limitation as if written in the body of this License. + </li> + +<li> + <var class="replacable-license-text"> 9.</var> + The Free Software Foundation may publish revised and/or new + versions of the General Public License from time to time. Such + new versions will be similar in spirit to the present version, + but may differ in detail to address new problems or concerns. + <p> + Each version is given a distinguishing version number. If the + Program specifies a version number of this License which applies + to it and &quot;any later version&quot;, you have the option of following + the terms and conditions either of that version or of any later + version published by the Free Software Foundation. If the Program + does not specify a version number of this License, you may choose + any version ever published by the Free Software Foundation. + </p> + + </li> + +<li> + <var class="replacable-license-text"> 10.</var> + If you wish to incorporate parts of the Program into other free + programs whose distribution conditions are different, write to the + author to ask for permission. For software which is copyrighted by the + Free Software Foundation, write to the Free Software Foundation; we + sometimes make exceptions for this. Our decision will be guided by the + two goals of preserving the free status of all derivatives of our free + software and of promoting the sharing and reuse of software generally. + <p> + NO WARRANTY + </p> + + </li> + +<li> + <var class="replacable-license-text"> 11.</var> + BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY + FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT + WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER + PARTIES PROVIDE THE PROGRAM &quot;AS IS&quot; WITHOUT WARRANTY OF ANY KIND, + EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF + THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU + ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + </li> + +<li> + <var class="replacable-license-text"> 12.</var> + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR + REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR + DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL + DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM + (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED + INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF + THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER + OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + </li> + +</ul> + <div class="optional-license-text"> + <p> + END OF TERMS AND CONDITIONS + </p> + + <p> + How to Apply These Terms to Your New Programs + </p> + + <p> + If you develop a new program, and you want it to be + of the greatest possible use to the public, the best + way to achieve this is to make it free software which + everyone can redistribute and change under these terms. + </p> + + <p> + To do so, attach the following notices to the program. It is safest + to attach them to the start of each source file to most effectively + convey the exclusion of warranty; and each file should have at least + the &quot;copyright&quot; line and a pointer to where the full notice is found. + </p> + + <p> + <var class="optional-license-text">&lt;</var>one line to give the program&apos;s name and <var class="replacable-license-text"> an</var> idea of what it does.<var class="optional-license-text">&gt;</var> + <br /> + + Copyright (C) + <var class="optional-license-text">&lt;</var><var class="replacable-license-text"> yyyy</var><var class="optional-license-text">&gt; </var> + <var class="optional-license-text"> &lt;</var>name of author<var class="optional-license-text">&gt;</var> + </p> + + <p> + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version + 2 of the License, or (at your option) any later version. + </p> + + <p> + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the GNU General Public License for more details. + </p> + + <p> + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301<var class="optional-license-text">, </var> + USA. + </p> + + <p> + Also add information on how to + contact you by electronic and paper mail. + </p> + + <p> + If the program is interactive, make it output a short + notice like this when it starts in an interactive mode: + </p> + + <p> + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details + type `show w&apos;. This is free software, and you are welcome to + redistribute it under certain conditions; type `show c&apos; for details. + </p> + + <p> + The hypothetical commands `show w&apos; and `show c&apos; should show the + appropriate parts of the General Public License. Of course, the commands + you use may be called something other than `show w&apos; and `show c&apos;; they + could even be mouse-clicks or menu items--whatever suits your program. + </p> + + <p> + You should also get your employer (if you work as a programmer) + or your school, if any, to sign a &quot;copyright disclaimer&quot; for + the program, if necessary. Here is a sample; alter the names: + </p> + + <p> + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision&apos; (which makes passes at compilers) written by James Hacker. + </p> + + <p> + <var class="optional-license-text">&lt;</var>signature of Ty Coon<var class="optional-license-text"> &gt;</var>, + 1 April 1989 Ty Coon, President of Vice + </p> + + </div> + <div class="optional-license-text"> + <p> + This General Public License does not permit incorporating your program into + proprietary programs. If your program is a subroutine library, you may + consider it more useful to permit linking proprietary applications with the + library. If this is what you want to do, use the GNU Lesser General + Public License instead of this License. + </p> + + </div> + + + + 0 + 2022-08-12T00:23:30Z + https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html + true + false + true + true + + + true + GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. + + c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author + + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. + +signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice + + Copyright (C) yyyy name of author + +This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + + + Copyright (C) <var class="replacable-license-text"> yyyy name of author</var> + <p> + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2. + </p> + + <p> + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the GNU General Public License for more details. + </p> + + <p> + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301<var class="optional-license-text">, </var> + USA. + </p> + + + + + + + + + + 420 + + + + + + 310 + + + + + + + + + + 23 + + + + + + 5 + + + + + + This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0. + + + + + This package has been shipped in source and binary form. The binaries were created with gcc 4.5.1 and expect to link to compatible system run time libraries. + 2010-01-29T18:30:22Z + Person: Jane Doe () + Organization: ExampleCodeInspect () + Tool: LicenseFind-1.0 + 3.17 + + + + + Person: Suzanne Reviewer + Another example reviewer. + + 2011-03-13T00:00:00Z + + + + + Person: Jane Doe () + Document level annotation + + 2010-01-29T18:30:22Z + + + + + false + true + <<beginOptional>><<beginOptional>>Creative Commons<<beginOptional>> Legal Code<<endOptional>> + +<<endOptional>> + +CC0 1.0 Universal + +<<endOptional>><<beginOptional>> CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. + +<<endOptional>> + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. + + <<var;name="bullet";original="1.";match=".{0,20}">> Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: + + <<var;name="bullet";original="i.";match=".{0,20}">> the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; + + <<var;name="bullet";original="ii.";match=".{0,20}">> moral rights retained by the original author(s) and/or performer(s); + + <<var;name="bullet";original="iii.";match=".{0,20}">> publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; + + <<var;name="bullet";original="iv.";match=".{0,20}">> rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; + + <<var;name="bullet";original="v.";match=".{0,20}">> rights protecting the extraction, dissemination, use and reuse of data in a Work; + + <<var;name="bullet";original="vi.";match=".{0,20}">> database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and + + <<var;name="bullet";original="vii.";match=".{0,20}">> other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. + + <<var;name="bullet";original="2.";match=".{0,20}">> Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. + + <<var;name="bullet";original="3.";match=".{0,20}">> Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. + + <<var;name="bullet";original="4.";match=".{0,20}">> Limitations and Disclaimers. + + <<var;name="bullet";original="a.";match=".{0,20}">> No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. + + <<var;name="bullet";original="b.";match=".{0,20}">> Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. + + <<var;name="bullet";original="c.";match=".{0,20}">> Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. + + <<var;name="bullet";original="d.";match=".{0,20}">> Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.<<beginOptional>> <<var;name="upstreamLink";original="";match="For more information, please see <http://creativecommons.org/publicdomain/zero/1.0/>">><<endOptional>> + + <div class="optional-license-text"> + <div class="optional-license-text"> + <p>Creative Commons<var class="optional-license-text"> Legal Code</var></p> + + </div> + <p>CC0 1.0 Universal</p> + + </div> + <div class="optional-license-text"> + <p>CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS + DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION + ON AN &quot;AS-IS&quot; BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT + OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE + USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER.</p> + + </div> + + <p>Statement of Purpose</p> + + <p>The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related + Rights (defined below) upon the creator and subsequent owner(s) (each and all, an &quot;owner&quot;) + of an original work of authorship and/or a database (each, a &quot;Work&quot;).</p> + + <p>Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a + commons of creative, cultural and scientific works (&quot;Commons&quot;) that the public can reliably + and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse + and redistribute as freely as possible in any form whatsoever and for any purposes, including without + limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a + free culture and the further production of creative, cultural and scientific works, or to gain + reputation or greater distribution for their Work in part through the use and efforts of others.</p> + + <p>For these and/or other purposes and motivations, and without any expectation of additional consideration + or compensation, the person associating CC0 with a Work (the &quot;Affirmer&quot;), to the extent that + he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to + the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and + Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.</p> + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> 1.</var> + Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and + related or neighboring rights (&quot;Copyright and Related Rights&quot;). Copyright and + Related Rights include, but are not limited to, the following: + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> i.</var> + the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; + </li> + +<li> + <var class="replacable-license-text"> ii.</var> + moral rights retained by the original author(s) and/or performer(s); + </li> + +<li> + <var class="replacable-license-text"> iii.</var> + publicity and privacy rights pertaining to a person&apos;s image or likeness depicted in a Work; + </li> + +<li> + <var class="replacable-license-text"> iv.</var> + rights protecting against unfair competition in regards to a Work, subject to the limitations + in paragraph 4(a), below; + </li> + +<li> + <var class="replacable-license-text"> v.</var> + rights protecting the extraction, dissemination, use and reuse of data in a Work; + </li> + +<li> + <var class="replacable-license-text"> vi.</var> + database rights (such as those arising under Directive 96/9/EC of the European Parliament and + of the Council of 11 March 1996 on the legal protection of databases, and under any + national implementation thereof, including any amended or successor version of such + directive); and + </li> + +<li> + <var class="replacable-license-text"> vii.</var> + other similar, equivalent or corresponding rights throughout the world based on applicable + law or treaty, and any national implementations thereof. + </li> + +</ul> + </li> + +<li> + <var class="replacable-license-text"> 2.</var> + Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, + Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, + and surrenders all of Affirmer&apos;s Copyright and Related Rights and associated claims and + causes of action, whether now known or unknown (including existing as well as future claims + and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum + duration provided by applicable law or treaty (including future time extensions), (iii) in any + current or future medium and for any number of copies, and (iv) for any purpose whatsoever, + including without limitation commercial, advertising or promotional purposes (the + &quot;Waiver&quot;). Affirmer makes the Waiver for the benefit of each member of the public at + large and to the detriment of Affirmer&apos;s heirs and successors, fully intending that such + Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other + legal or equitable action to disrupt the quiet enjoyment of the Work by the public as + contemplated by Affirmer&apos;s express Statement of Purpose. + </li> + +<li> + <var class="replacable-license-text"> 3.</var> + Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid + or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent + permitted taking into account Affirmer&apos;s express Statement of Purpose. In addition, to + the extent the Waiver is so judged Affirmer hereby grants to each affected person a + royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and + unconditional license to exercise Affirmer&apos;s Copyright and Related Rights in the Work (i) + in all territories worldwide, (ii) for the maximum duration provided by applicable law or + treaty (including future time extensions), (iii) in any current or future medium and for any + number of copies, and (iv) for any purpose whatsoever, including without limitation + commercial, advertising or promotional purposes (the &quot;License&quot;). The License shall + be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of + the License for any reason be judged legally invalid or ineffective under applicable law, such + partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and + in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her + remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and + causes of action with respect to the Work, in either case contrary to Affirmer&apos;s express + Statement of Purpose. + </li> + +<li> + <var class="replacable-license-text"> 4.</var> + Limitations and Disclaimers. + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> a.</var> + No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed + or otherwise affected by this document. + </li> + +<li> + <var class="replacable-license-text"> b.</var> + Affirmer offers the Work as-is and makes no representations or warranties of any kind + concerning the Work, express, implied, statutory or otherwise, including without + limitation warranties of title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or the present or + absence of errors, whether or not discoverable, all to the greatest extent permissible + under applicable law. + </li> + +<li> + <var class="replacable-license-text"> c.</var> + Affirmer disclaims responsibility for clearing rights of other persons that may apply to the + Work or any use thereof, including without limitation any person&apos;s Copyright and + Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any + necessary consents, permissions or other rights required for any use of the Work. + </li> + +<li> + <var class="replacable-license-text"> d.</var> + Affirmer understands and acknowledges that Creative Commons is not a party to this document + and has no duty or obligation with respect to this CC0 or use of the Work. + </li> + +</ul> + </li> + +</ul> + <var class="optional-license-text"> <var class="replacable-license-text"> </var></var> + + false + + + 0 + 2022-08-12T00:19:24Z + https://creativecommons.org/publicdomain/zero/1.0/legalcode + false + false + true + true + + + Creative Commons Zero v1.0 Universal + CC0-1.0 + Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. + + https://creativecommons.org/publicdomain/zero/1.0/legalcode + + + + + http://justasample.url.com + http://people.apache.org/~andyc/neko/LICENSE + LicenseRef-3 + This is tye CyperNeko License + The CyberNeko Software License, Version 1.0 + + +(C) Copyright 2002-2005, Andy Clark. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +3. The end-user documentation included with the redistribution, + if any, must include the following acknowledgment: + "This product includes software developed by Andy Clark." + Alternately, this acknowledgment may appear in the software itself, + if and wherever such third-party acknowledgments normally appear. + +4. The names "CyberNeko" and "NekoHTML" must not be used to endorse + or promote products derived from this software without prior + written permission. For written permission, please contact + andyc@cyberneko.net. + +5. Products derived from this software may not be called "CyberNeko", + nor may "CyberNeko" appear in their name, without prior written + permission of the author. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + CyberNeko License + + + + + http://people.freebsd.org/~phk/ + LicenseRef-Beerware-4.2 + The beerware license has a couple of other standard variants. + "THE BEER-WARE LICENSE" (Revision 42): +phk@FreeBSD.ORG wrote this file. As long as you retain this notice you +can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp + Beer-Ware License (Version 42) + + + + + + + + + d6a770ba38583ed4bb4525bd96e50461655d2759 + + + DocumentRef-spdx-tool-1.2 + + + + + LicenseRef-4 + /* + * (c) Copyright 2009 University of Bristol + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + + + LicenseRef-2 + This package includes the GRDDL parser developed by Hewlett Packard under the following license: +© Copyright 2007 Hewlett-Packard Development Company, LP + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + SPDX-2.3 + + + + + + + + + + GNU LIBRARY GENERAL PUBLIC LICENSE + +Version 2, June 1991 + +Copyright (C) 1991 Free Software Foundation, Inc. +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. + +This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. + +Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. + +Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. + +The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. + +Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. + +However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. + +The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. + +Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. + +(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. + +However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. + +When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. + +6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: + + a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. + + d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. + + b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). + +To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + one line to give the library's name and an idea of what it does. + Copyright (C) year name of author + + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in +the library `Frob' (a library for tweaking knobs) written +by James Random Hacker. + +signature of Ty Coon, 1 April 1990 +Ty Coon, President of Vice + +That's all there is to it! + LGPL-2.0-only + https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html + false + + <div class="optional-license-text"> + <p> + GNU LIBRARY GENERAL PUBLIC LICENSE + </p> + + <p> + Version 2, June 1991 + </p> + + </div> + <div class="replacable-license-text"> + <p> + Copyright (C) 1991 Free Software Foundation, Inc.<br /> + + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + </p> + + </div> + <p> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + </p> + + <p> + [This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + </p> + + <p> + Preamble + </p> + + <p> + The licenses for most software are designed to take away your + freedom to share and change it. By contrast, the GNU General Public + Licenses are intended to guarantee your freedom to share and change + free software--to make sure the software is free for all its users. + </p> + + <p> + This license, the Library General Public License, applies + to some specially designated Free Software Foundation + software, and to any other libraries whose authors + decide to use it. You can use it for your libraries, too. + </p> + + <p> + When we speak of free software, we are referring to freedom, not price. + Our General Public Licenses are designed to make sure that you have + the freedom to distribute copies of free software (and charge for + this service if you wish), that you receive source code or can get + it if you want it, that you can change the software or use pieces of + it in new free programs; and that you know you can do these things. + </p> + + <p> + To protect your rights, we need to make restrictions that forbid + anyone to deny you these rights or to ask you to surrender the + rights. These restrictions translate to certain responsibilities for + you if you distribute copies of the library, or if you modify it. + </p> + + <p> + For example, if you distribute copies of the library, whether gratis + or for a fee, you must give the recipients all the rights that we + gave you. You must make sure that they, too, receive or can get the + source code. If you link a program with the library, you must provide + complete object files to the recipients so that they can relink them + with the library, after making changes to the library and recompiling + it. And you must show them these terms so they know their rights. + </p> + + <p> + Our method of protecting your rights has two steps: (1) copyright + the library, and (2) offer you this license which gives you + legal permission to copy, distribute and/or modify the library. + </p> + + <p> + Also, for each distributor&apos;s protection, we want to make certain + that everyone understands that there is no warranty for this + free library. If the library is modified by someone else and + passed on, we want its recipients to know that what they have + is not the original version, so that any problems introduced by + others will not reflect on the original authors&apos; reputations. + </p> + + <p> + Finally, any free program is threatened constantly by software + patents. We wish to avoid the danger that companies distributing + free software will individually obtain patent licenses, thus + in effect transforming the program into proprietary software. + To prevent this, we have made it clear that any patent must + be licensed for everyone&apos;s free use or not licensed at all. + </p> + + <p> + Most GNU software, including some libraries, is covered by the + ordinary GNU General Public License, which was designed for utility + programs. This license, the GNU Library General Public License, + applies to certain designated libraries. This license is quite + different from the ordinary one; be sure to read it in full, and don&apos;t + assume that anything in it is the same as in the ordinary license. + </p> + + <p> + The reason we have a separate public license for some libraries is + that they blur the distinction we usually make between modifying + or adding to a program and simply using it. Linking a program with + a library, without changing the library, is in some sense simply + using the library, and is analogous to running a utility program + or application program. However, in a textual and legal sense, the + linked executable is a combined work, a derivative of the original + library, and the ordinary General Public License treats it as such. + </p> + + <p> + Because of this blurred distinction, using the ordinary General + Public License for libraries did not effectively promote software + sharing, because most developers did not use the libraries. We + concluded that weaker conditions might promote sharing better. + </p> + + <p> + However, unrestricted linking of non-free programs would deprive the + users of those programs of all benefit from the free status of the + libraries themselves. This Library General Public License is intended + to permit developers of non-free programs to use free libraries, while + preserving your freedom as a user of such programs to change the free + libraries that are incorporated in them. (We have not seen how to + achieve this as regards changes in header files, but we have achieved + it as regards changes in the actual functions of the Library.) The + hope is that this will lead to faster development of free libraries. + </p> + + <p> + The precise terms and conditions for copying, distribution + and modification follow. Pay close attention to the difference + between a &quot;work based on the library&quot; and a &quot;work that uses + the library&quot;. The former contains code derived from the + library, while the latter only works together with the library. + </p> + + <p> + Note that it is possible for a library to be covered by the + ordinary General Public License rather than by this special one. + </p> + + <p> + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + </p> + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> 0.</var> + This License Agreement applies to any software library + which contains a notice placed by the copyright holder or + other authorized party saying it may be distributed under + the terms of this Library General Public License (also + called &quot;this License&quot;). Each licensee is addressed as &quot;you&quot;. + <p> + A &quot;library&quot; means a collection of software functions and/or data + prepared so as to be conveniently linked with application programs + (which use some of those functions and data) to form executables. + </p> + + <p> + The &quot;Library&quot;, below, refers to any such software library or work + which has been distributed under these terms. A &quot;work based on + the Library&quot; means either the Library or any derivative work under + copyright law: that is to say, a work containing the Library or a + portion of it, either verbatim or with modifications and/or translated + straightforwardly into another language. (Hereinafter, translation + is included without limitation in the term &quot;modification&quot;.) + </p> + + <p> + &quot;Source code&quot; for a work means the preferred form of the work + for making modifications to it. For a library, complete source + code means all the source code for all modules it contains, + plus any associated interface definition files, plus the scripts + used to control compilation and installation of the library. + </p> + + <p> + Activities other than copying, distribution and modification are + not covered by this License; they are outside its scope. The act of + running a program using the Library is not restricted, and output + from such a program is covered only if its contents constitute a + work based on the Library (independent of the use of the Library + in a tool for writing it). Whether that is true depends on what + the Library does and what the program that uses the Library does. + </p> + + </li> + +<li> + <var class="replacable-license-text"> 1.</var> + You may copy and distribute verbatim copies of the Library&apos;s complete + source code as you receive it, in any medium, provided that you + conspicuously and appropriately publish on each copy an appropriate + copyright notice and disclaimer of warranty; keep intact all the + notices that refer to this License and to the absence of any warranty; + and distribute a copy of this License along with the Library. + <p> + You may charge a fee for the physical act of + transferring a copy, and you may at your option + offer warranty protection in exchange for a fee. + </p> + + </li> + +<li> + <var class="replacable-license-text"> 2.</var> + You may modify your copy or copies of the Library or any portion + of it, thus forming a work based on the Library, and copy and + distribute such modifications or work under the terms of Section + 1 above, provided that you also meet all of these conditions: + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> a)</var> + The modified work must itself be a software library. + </li> + +<li> + <var class="replacable-license-text"> b)</var> + You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + </li> + +<li> + <var class="replacable-license-text"> c)</var> + You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + </li> + +<li> + <var class="replacable-license-text"> d)</var> + If a facility in the modified Library refers to a function + or a table of data to be supplied by an application program + that uses the facility, other than as an argument passed + when the facility is invoked, then you must make a good faith + effort to ensure that, in the event an application does not + supply such function or table, the facility still operates, + and performs whatever part of its purpose remains meaningful. + <p> + (For example, a function in a library to compute square roots + has a purpose that is entirely well-defined independent of + the application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function + must be optional: if the application does not supply it, + the square root function must still compute square roots.) + </p> + + </li> + +</ul> + <p> + These requirements apply to the modified work as a whole. If + identifiable sections of that work are not derived from the + Library, and can be reasonably considered independent and separate + works in themselves, then this License, and its terms, do not + apply to those sections when you distribute them as separate + works. But when you distribute the same sections as part of a + whole which is a work based on the Library, the distribution + of the whole must be on the terms of this License, whose + permissions for other licensees extend to the entire whole, + and thus to each and every part regardless of who wrote it. + </p> + + <p> + Thus, it is not the intent of this section to claim rights or + contest your rights to work written entirely by you; rather, + the intent is to exercise the right to control the distribution + of derivative or collective works based on the Library. + </p> + + <p> + In addition, mere aggregation of another work not based on + the Library with the Library (or with a work based on the + Library) on a volume of a storage or distribution medium does + not bring the other work under the scope of this License. + </p> + + </li> + +<li> + <var class="replacable-license-text"> 3.</var> + You may opt to apply the terms of the ordinary GNU General + Public License instead of this License to a given copy of the + Library. To do this, you must alter all the notices that refer + to this License, so that they refer to the ordinary GNU General + Public License, version 2, instead of to this License. (If a + newer version than version 2 of the ordinary GNU General Public + License has appeared, then you can specify that version instead + if you wish.) Do not make any other change in these notices. + <p> + Once this change is made in a given copy, it is irreversible for + that copy, so the ordinary GNU General Public License applies to + all subsequent copies and derivative works made from that copy. + </p> + + <p> + This option is useful when you wish to copy part of the + code of the Library into a program that is not a library. + </p> + + </li> + +<li> + <var class="replacable-license-text"> 4.</var> + You may copy and distribute the Library (or a portion or derivative + of it, under Section 2) in object code or executable form under + the terms of Sections 1 and 2 above provided that you accompany + it with the complete corresponding machine-readable source code, + which must be distributed under the terms of Sections 1 and 2 + above on a medium customarily used for software interchange. + <p> + If distribution of object code is made by offering access to copy + from a designated place, then offering equivalent access to copy + the source code from the same place satisfies the requirement + to distribute the source code, even though third parties are + not compelled to copy the source along with the object code. + </p> + + </li> + +<li> + <var class="replacable-license-text"> 5.</var> + A program that contains no derivative of any portion of the + Library, but is designed to work with the Library by being compiled + or linked with it, is called a &quot;work that uses the Library&quot;. + Such a work, in isolation, is not a derivative work of the + Library, and therefore falls outside the scope of this License. + <p> + However, linking a &quot;work that uses the Library&quot; with the Library + creates an executable that is a derivative of the Library (because + it contains portions of the Library), rather than a &quot;work that uses + the library&quot;. The executable is therefore covered by this License. + Section 6 states terms for distribution of such executables. + </p> + + <p> + When a &quot;work that uses the Library&quot; uses material from a header + file that is part of the Library, the object code for the work may + be a derivative work of the Library even though the source code is + not. Whether this is true is especially significant if the work can + be linked without the Library, or if the work is itself a library. + The threshold for this to be true is not precisely defined by law. + </p> + + <p> + If such an object file uses only numerical parameters, data + structure layouts and accessors, and small macros and small inline + functions (ten lines or less in length), then the use of the + object file is unrestricted, regardless of whether it is legally + a derivative work. (Executables containing this object code + plus portions of the Library will still fall under Section 6.) + </p> + + <p> + Otherwise, if the work is a derivative of the Library, you may + distribute the object code for the work under the terms of Section + 6. Any executables containing that work also fall under Section 6, + whether or not they are linked directly with the Library itself. + </p> + + </li> + +<li> + <var class="replacable-license-text"> 6.</var> + As an exception to the Sections above, you may also compile or + link a &quot;work that uses the Library&quot; with the Library to produce + a work containing portions of the Library, and distribute + that work under terms of your choice, provided that the terms + permit modification of the work for the customer&apos;s own use + and reverse engineering for debugging such modifications. + <p> + You must give prominent notice with each copy of the work + that the Library is used in it and that the Library and its + use are covered by this License. You must supply a copy of + this License. If the work during execution displays copyright + notices, you must include the copyright notice for the Library + among them, as well as a reference directing the user to the + copy of this License. Also, you must do one of these things: + </p> + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> a)</var> + Accompany the work with the complete corresponding machine-readable + source code for the Library including whatever changes were used in + the work (which must be distributed under Sections 1 and 2 above); + and, if the work is an executable linked with the Library, with the + complete machine-readable &quot;work that uses the Library&quot;, as object + code and/or source code, so that the user can modify the Library + and then relink to produce a modified executable containing the + modified Library. (It is understood that the user who changes the + contents of definitions files in the Library will not necessarily be + able to recompile the application to use the modified definitions.) + </li> + +<li> + <var class="replacable-license-text"> b)</var> + Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no + more than the cost of performing this distribution. + </li> + +<li> + <var class="replacable-license-text"> c)</var> + If distribution of the work is made by offering access to + copy from a designated place, offer equivalent access to + copy the above specified materials from the same place. + </li> + +<li> + <var class="replacable-license-text"> d)</var> + Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + </li> + +</ul> + <p> + For an executable, the required form of the &quot;work that uses + the Library&quot; must include any data and utility programs needed + for reproducing the executable from it. However, as a special + exception, the source code distributed need not include + anything that is normally distributed (in either source or + binary form) with the major components (compiler, kernel, + and so on) of the operating system on which the executable + runs, unless that component itself accompanies the executable. + </p> + + <p> + It may happen that this requirement contradicts the + license restrictions of other proprietary libraries that + do not normally accompany the operating system. Such + a contradiction means you cannot use both them and the + Library together in an executable that you distribute. + </p> + + </li> + +<li> + <var class="replacable-license-text"> 7.</var> + You may place library facilities that are a work based on the + Library side-by-side in a single library together with other library + facilities not covered by this License, and distribute such a + combined library, provided that the separate distribution of the + work based on the Library and of the other library facilities is + otherwise permitted, and provided that you do these two things: + </li> + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> a)</var> + Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities. + This must be distributed under the terms of the Sections above. + </li> + +<li> + <var class="replacable-license-text"> b)</var> + Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + </li> + +</ul> + +<li> + <var class="replacable-license-text"> 8.</var> + You may not copy, modify, sublicense, link with, or distribute the + Library except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense, link with, or distribute + the Library is void, and will automatically terminate your rights + under this License. However, parties who have received copies, or + rights, from you under this License will not have their licenses + terminated so long as such parties remain in full compliance. + </li> + +<li> + <var class="replacable-license-text"> 9.</var> + You are not required to accept this License, since you have + not signed it. However, nothing else grants you permission to + modify or distribute the Library or its derivative works. These + actions are prohibited by law if you do not accept this License. + Therefore, by modifying or distributing the Library (or any + work based on the Library), you indicate your acceptance of this + License to do so, and all its terms and conditions for copying, + distributing or modifying the Library or works based on it. + </li> + +<li> + <var class="replacable-license-text"> 10.</var> + Each time you redistribute the Library (or any work based on + the Library), the recipient automatically receives a license + from the original licensor to copy, distribute, link with or + modify the Library subject to these terms and conditions. You + may not impose any further restrictions on the recipients&apos; + exercise of the rights granted herein. You are not responsible + for enforcing compliance by third parties to this License. + </li> + +<li> + <var class="replacable-license-text"> 11.</var> + If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement + or otherwise) that contradict the conditions of this License, + they do not excuse you from the conditions of this License. If you + cannot distribute so as to satisfy simultaneously your obligations + under this License and any other pertinent obligations, then as a + consequence you may not distribute the Library at all. For example, + if a patent license would not permit royalty-free redistribution of + the Library by all those who receive copies directly or indirectly + through you, then the only way you could satisfy both it and this + License would be to refrain entirely from distribution of the Library. + <p> + If any portion of this section is held invalid or + unenforceable under any particular circumstance, the + balance of the section is intended to apply, and the section + as a whole is intended to apply in other circumstances. + </p> + + <p> + It is not the purpose of this section to induce you to infringe + any patents or other property right claims or to contest + validity of any such claims; this section has the sole purpose + of protecting the integrity of the free software distribution + system which is implemented by public license practices. Many + people have made generous contributions to the wide range of + software distributed through that system in reliance on consistent + application of that system; it is up to the author/donor to + decide if he or she is willing to distribute software through + any other system and a licensee cannot impose that choice. + </p> + + <p> + This section is intended to make thoroughly clear what is + believed to be a consequence of the rest of this License. + </p> + + </li> + +<li> + <var class="replacable-license-text"> 12.</var> + If the distribution and/or use of the Library is restricted in + certain countries either by patents or by copyrighted interfaces, + the original copyright holder who places the Library under this + License may add an explicit geographical distribution limitation + excluding those countries, so that distribution is permitted only + in or among countries not thus excluded. In such case, this License + incorporates the limitation as if written in the body of this License. + </li> + +<li> + <var class="replacable-license-text"> 13.</var> + The Free Software Foundation may publish revised and/or new versions + of the Library General Public License from time to time. Such + new versions will be similar in spirit to the present version, + but may differ in detail to address new problems or concerns. + <p> + Each version is given a distinguishing version number. If + the Library specifies a version number of this License which + applies to it and &quot;any later version&quot;, you have the option of + following the terms and conditions either of that version or of + any later version published by the Free Software Foundation. If + the Library does not specify a license version number, you may + choose any version ever published by the Free Software Foundation. + </p> + + </li> + +<li> + <var class="replacable-license-text"> 14.</var> + If you wish to incorporate parts of the Library into other free programs + whose distribution conditions are incompatible with these, write to + the author to ask for permission. For software which is copyrighted by + the Free Software Foundation, write to the Free Software Foundation; we + sometimes make exceptions for this. Our decision will be guided by the + two goals of preserving the free status of all derivatives of our free + software and of promoting the sharing and reuse of software generally. + <p> + NO WARRANTY + </p> + + </li> + +<li> + <var class="replacable-license-text"> 15.</var> + BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY + FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT + WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER + PARTIES PROVIDE THE LIBRARY &quot;AS IS&quot; WITHOUT WARRANTY OF ANY KIND, + EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF + THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU + ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + </li> + +<li> + <var class="replacable-license-text"> 16.</var> + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR + REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR + DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL + DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY + (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED + INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF + THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER + OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + </li> + +</ul> + <div class="optional-license-text"> + <p> + END OF TERMS AND CONDITIONS + </p> + + <p> + How to Apply These Terms to Your New Libraries + </p> + + <p> + If you develop a new library, and you want it to be of the greatest + possible use to the public, we recommend making it free software + that everyone can redistribute and change. You can do so by + permitting redistribution under these terms (or, alternatively, + under the terms of the ordinary General Public License). + </p> + + <p> + To apply these terms, attach the following notices to the + library. It is safest to attach them to the start of each + source file to most effectively convey the exclusion of + warranty; and each file should have at least the &quot;copyright&quot; + line and a pointer to where the full notice is found. + </p> + + <p> + one line to give the library&apos;s name + and an idea of what it does.<br /> + + Copyright (C) year name of author + </p> + + <p> + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + </p> + + <p> + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + the GNU Library General Public License for more details. + </p> + + <p> + You should have received a copy of the GNU Library + General Public License along with this library; if + not, write to the Free Software Foundation, Inc., 51 + Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + </p> + + <p> + Also add information on how to contact you by electronic and paper mail. + </p> + + <p> + You should also get your employer (if you work as a programmer) + or your school, if any, to sign a &quot;copyright disclaimer&quot; for + the library, if necessary. Here is a sample; alter the names: + </p> + + <p> + Yoyodyne, Inc., hereby disclaims all copyright interest in<br /> + + the library `Frob&apos; (a library for tweaking knobs) written<br /> + + by James Random Hacker. + </p> + + <p> + signature of Ty Coon, 1 April 1990<br /> + + Ty Coon, President of Vice + </p> + + <p> + That&apos;s all there is to it! + </p> + + </div> + + true + Copyright (C) year name of author + +This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; version 2. + +This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. + +You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + + + + 0 + 2022-08-12T00:25:56Z + https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html + true + false + true + true + + + <<beginOptional>>GNU LIBRARY GENERAL PUBLIC LICENSE + +Version 2, June 1991 + +<<endOptional>> <<var;name="copyright";original="Copyright (C) 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA ";match=".{0,5000}">> + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. + +This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. + +Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. + +Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. + +The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. + +Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. + +However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. + +The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. + +Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + <<var;name="bullet";original="0.";match=".{0,20}">> This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. + + Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. + + <<var;name="bullet";original="1.";match=".{0,20}">> You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. + + You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + + <<var;name="bullet";original="2.";match=".{0,20}">> You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + <<var;name="bullet";original="a)";match=".{0,20}">> The modified work must itself be a software library. + + <<var;name="bullet";original="b)";match=".{0,20}">> You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. + + <<var;name="bullet";original="c)";match=".{0,20}">> You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. + + <<var;name="bullet";original="d)";match=".{0,20}">> If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. + + (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) + + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. + + In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + + <<var;name="bullet";original="3.";match=".{0,20}">> You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. + + Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of the Library into a program that is not a library. + + <<var;name="bullet";original="4.";match=".{0,20}">> You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. + + <<var;name="bullet";original="5.";match=".{0,20}">> A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. + + <<var;name="bullet";original="6.";match=".{0,20}">> As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: + + <<var;name="bullet";original="a)";match=".{0,20}">> Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) + + <<var;name="bullet";original="b)";match=".{0,20}">> Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. + + <<var;name="bullet";original="c)";match=".{0,20}">> If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. + + <<var;name="bullet";original="d)";match=".{0,20}">> Verify that the user has already received a copy of these materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + + It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. + + <<var;name="bullet";original="7.";match=".{0,20}">> You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: + + <<var;name="bullet";original="a)";match=".{0,20}">> Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. + + <<var;name="bullet";original="b)";match=".{0,20}">> Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. + + <<var;name="bullet";original="8.";match=".{0,20}">> You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + + <<var;name="bullet";original="9.";match=".{0,20}">> You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. + + <<var;name="bullet";original="10.";match=".{0,20}">> Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + + <<var;name="bullet";original="11.";match=".{0,20}">> If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. + + If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. + + It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + + <<var;name="bullet";original="12.";match=".{0,20}">> If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + + <<var;name="bullet";original="13.";match=".{0,20}">> The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. + + <<var;name="bullet";original="14.";match=".{0,20}">> If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + + NO WARRANTY + + <<var;name="bullet";original="15.";match=".{0,20}">> BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + <<var;name="bullet";original="16.";match=".{0,20}">> IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.<<beginOptional>> END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). + +To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + +one line to give the library's name and an idea of what it does. + +Copyright (C) year name of author + +This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. + +You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in + +the library `Frob' (a library for tweaking knobs) written + +by James Random Hacker. + +signature of Ty Coon, 1 April 1990 + +Ty Coon, President of Vice + +That's all there is to it! + +<<endOptional>> + This license was released: June 1991. This license has been superseded by LGPL-2.1. This license identifier refers to the choice to use the code under LGPL-2.0-only, as distinguished from use of code under LGPL-2.0-or-later (i.e., LGPL-2.0 or some later version). The license notice (as seen in the Standard License Header field below) states which of these applies to the code in the file. The example in the exhibit to the license shows the license notice for the "or later" approach. + GNU Library General Public License v2 only + Copyright (C) <<var;name="copyright";original="year name of author";match=".+">> + +This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; version 2. + +This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. + +You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + + + Copyright (C) <var class="replacable-license-text"> year name of author</var> + <p>This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; version 2.</p> + + <p>This library is distributed + in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the GNU Library General Public License for more details.</p> + + <p>You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + </p> + + + + + + + http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz + + + Person: Package Commenter + Package level annotation + + 2011-01-29T18:30:22Z + + + 2.11.1 + + + + + + Apache Commons Lang +Copyright 2001-2011 The Apache Software Foundation + +This product includes software developed by +The Apache Software Foundation (http://www.apache.org/). + +This product includes software from the Spring Framework, +under the Apache License 2.0 (see: StringUtils.containsWhitespace()) + + + + c2b4e1c67a2d28fced849ee1bb76e7391b93f125 + + + + + Apache Software Foundation + + + + + + + ./lib-source/commons-lang3-3.1-sources.jar + Copyright 2001-2011 The Apache Software Foundation + This file is used by Jena + + + + + + + + + + This is the external ref for Acme + + acmecorp/acmenator/4.1.3-alpha + + + true + + + + 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd + + + + + + + + + LicenseRef-1 + /* + * (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + This license is used by Jena + Hewlett Packard Inc. + + (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP + Apache Software Foundation + + This file belongs to Jena + + + + 3ab4e1c67a2d28fced849ee1bb76e7391b93f125 + + + ./lib-source/jena-2.6.3-sources.jar + + + + + + + + + + + + + The license for this project changed with the release of version x.y. The version of the project included here post-dates the license change. + + + + aaabd89c926ab525c242e6621f2f5fa73aa4afe3d9e24aed727faaadd6af38b620bdb623dd2b4788b1c8086984af8706 + + + + + + 85ed0817af83a24ad8da68c2b5094de69833983c + + + + + + + Saxon + + + + 85ed0817af83a24ad8da68c2b5094de69833983c + + + The Saxon package is a collection of tools for processing XML documents. + http://saxon.sourceforge.net/ + 8.8 + Other versions available for a commercial license + https://sourceforge.net/projects/saxon/files/Saxon-B/8.8.0.7/saxonb8-8-0-7j.zip/download + saxonB-8.8.zip + + + https://opensource.org/licenses/MPL-1.0 + + + 1 + 2022-08-12T00:25:48Z + https://opensource.org/licenses/MPL-1.0 + N/A + false + true + false + + + MOZILLA PUBLIC LICENSE +Version 1.0 + +1. Definitions. + + 1.1. ``Contributor'' means each entity that creates or contributes to the creation of Modifications. + + 1.2. ``Contributor Version'' means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor. + + 1.3. ``Covered Code'' means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof. + + 1.4. ``Electronic Distribution Mechanism'' means a mechanism generally accepted in the software development community for the electronic transfer of data. + + 1.5. ``Executable'' means Covered Code in any form other than Source Code. + + 1.6. ``Initial Developer'' means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A. + + 1.7. ``Larger Work'' means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. + + 1.8. ``License'' means this document. + + 1.9. ``Modifications'' means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is: + + A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications. + + B. Any new file that contains any part of the Original Code or previous Modifications. + + 1.10. ``Original Code'' means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License. + + 1.11. ``Source Code'' means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or a list of source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge. + + 1.12. ``You'' means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, ``You'' includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, ``control'' means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. +The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: + + (a) to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, or as part of a Larger Work; and + + (b) under patents now or hereafter owned or controlled by Initial Developer, to make, have made, use and sell (``Utilize'') the Original Code (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Original Code (or portions thereof) and not to any greater extent that may be necessary to Utilize further Modifications or combinations. + + 2.2. Contributor Grant. +Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: + + (a) to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code or as part of a Larger Work; and + + (b) under patents now or hereafter owned or controlled by Contributor, to Utilize the Contributor Version (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Contributor Version (or portions thereof), and not to any greater extent that may be necessary to Utilize further Modifications or combinations. + +3. Distribution Obligations. + + 3.1. Application of License. + The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5. + + 3.2. Availability of Source Code. + Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. + You must cause all Covered Code to which you contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + + (a) Third Party Claims. + If You have knowledge that a party claims an intellectual property right in particular functionality or code (or its utilization under this License), you must include a text file with the source code distribution titled ``LEGAL'' which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If you obtain such knowledge after You make Your Modification available as described in Section 3.2, You shall promptly modify the LEGAL file in all copies You make available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained. + + (b) Contributor APIs. + If Your Modification is an application programming interface and You own or control patents which are reasonably necessary to implement that API, you must also include this information in the LEGAL file. + + 3.5. Required Notices. + You must duplicate the notice in Exhibit A in each file of the Source Code, and this License in any documentation for the Source Code, where You describe recipients' rights relating to Covered Code. If You created one or more Modification(s), You may add your name as a Contributor to the notice described in Exhibit A. If it is not possible to put such notice in a particular Source Code file due to its structure, then you must include such notice in a location (such as a relevant directory file) where a user would be likely to look for such a notice. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. + You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. + + 3.7. Larger Works. + You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. +If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. + +5. Application of this License. +This License applies to code to which the Initial Developer has attached the notice in Exhibit A, and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions. + Netscape Communications Corporation (``Netscape'') may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number. + + 6.2. Effect of New Versions. + Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netscape. No one other than Netscape has the right to modify the terms applicable to Covered Code created under this License. + + 6.3. Derivative Works. + If you create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), you must (a) rename Your license so that the phrases ``Mozilla'', ``MOZILLAPL'', ``MOZPL'', ``Netscape'', ``NPL'' or any confusingly similar phrase do not appear anywhere in your license and (b) otherwise make it clear that your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.) + +7. DISCLAIMER OF WARRANTY. +COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN ``AS IS'' BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. TERMINATION. +This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. + +9. LIMITATION OF LIABILITY. +UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO YOU OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THAT EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +10. U.S. GOVERNMENT END USERS. +The Covered Code is a ``commercial item,'' as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of ``commercial computer software'' and ``commercial computer software documentation,'' as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein. + +11. MISCELLANEOUS. +This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in, the United States of America: (a) unless otherwise agreed in writing, all disputes relating to this License (excepting any dispute relating to intellectual property rights) shall be subject to final and binding arbitration, with the losing party paying all costs of arbitration; (b) any arbitration relating to this Agreement shall be held in Santa Clara County, California, under the auspices of JAMS/EndDispute; and (c) any litigation relating to this Agreement shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. + +12. RESPONSIBILITY FOR CLAIMS. +Except in cases where another Contributor has failed to comply with Section 3.4, You are responsible for damages arising, directly or indirectly, out of Your utilization of rights under this License, based on the number of copies of Covered Code you made available, the revenues you received from utilizing such rights, and other relevant factors. You agree to work with affected parties to distribute responsibility on an equitable basis. + +EXHIBIT A. + +``The contents of this file are subject to the Mozilla Public License Version 1.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.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. + +The Original Code is ______________________________________. + +The Initial Developer of the Original Code is ________________________. Portions created by ______________________ are Copyright (C) ______ _______________________. All Rights Reserved. + +Contributor(s): ______________________________________.'' + + + 0 + 2022-08-12T00:25:47Z + http://www.mozilla.org/MPL/MPL-1.0.html + true + false + true + true + + + This license has been superseded by v1.1 + http://www.mozilla.org/MPL/MPL-1.0.html + Mozilla Public License 1.0 + "The contents of this file are subject to the Mozilla Public License Version 1.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.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. + +The Original Code is _____ . The Initial Developer of the Original Code is _____ . Portions created by _____ are Copyright (C) _____ . All Rights Reserved. Contributor(s): _____ ." + + + <<beginOptional>>"<<endOptional>>The contents of this file are subject to the Mozilla Public License Version 1.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.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. + +The Original Code is <<var;name="code";original="_____";match=".+">> . The Initial Developer of the Original Code is <<var;name="InitialDeveloper";original="_____";match=".+">> . Portions created by <<var;name="createdby";original="_____";match=".+">> are Copyright (C) <<var;name="copyright";original="_____";match=".+">> . All Rights Reserved. Contributor(s): <<var;name="contributor";original="_____";match=".+">> .<<beginOptional>>"<<endOptional>> + + + <<beginOptional>>MOZILLA PUBLIC LICENSE + +Version 1.0 + +<<endOptional>> + + <<var;name="bullet";original="1.";match=".{0,20}">> Definitions. + + <<var;name="bullet";original="1.1.";match=".{0,20}">> "Contributor" means each entity that creates or contributes to the creation of Modifications. + + <<var;name="bullet";original="1.2.";match=".{0,20}">> "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor. + + <<var;name="bullet";original="1.3.";match=".{0,20}">> "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof. + + <<var;name="bullet";original="1.4.";match=".{0,20}">> "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data. + + <<var;name="bullet";original="1.5.";match=".{0,20}">> "Executable" means Covered Code in any form other than Source Code. + + <<var;name="bullet";original="1.6.";match=".{0,20}">> "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A. + + <<var;name="bullet";original="1.7.";match=".{0,20}">> "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. + + <<var;name="bullet";original="1.8.";match=".{0,20}">> "License" means this document. + + <<var;name="bullet";original="1.9.";match=".{0,20}">> "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is: + + <<var;name="bullet";original="A.";match=".{0,20}">> Any addition to or deletion from the contents of a file containing Original Code or previous Modifications. + + <<var;name="bullet";original="B.";match=".{0,20}">> Any new file that contains any part of the Original Code or previous Modifications. + + <<var;name="bullet";original="1.10.";match=".{0,20}">> "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License. + + <<var;name="bullet";original="1.11.";match=".{0,20}">> "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or a list of source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge. + + <<var;name="bullet";original="1.12.";match=".{0,20}">> "You" means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity. + + <<var;name="bullet";original="2.";match=".{0,20}">> Source Code License. + + <<var;name="bullet";original="2.1.";match=".{0,20}">> The Initial Developer Grant. + + The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: + + <<var;name="bullet";original="(a)";match=".{0,20}">> to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, or as part of a Larger Work; and + + <<var;name="bullet";original="(b)";match=".{0,20}">> under patents now or hereafter owned or controlled by Initial Developer, to make, have made, use and sell ("Utilize") the Original Code (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Original Code (or portions thereof) and not to any greater extent that may be necessary to Utilize further Modifications or combinations. + + <<var;name="bullet";original="2.2.";match=".{0,20}">> Contributor Grant. + + Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: + + <<var;name="bullet";original="(a)";match=".{0,20}">> to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code or as part of a Larger Work; and + + <<var;name="bullet";original="(b)";match=".{0,20}">> under patents now or hereafter owned or controlled by Contributor, to Utilize the Contributor Version (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Contributor Version (or portions thereof), and not to any greater extent that may be necessary to Utilize further Modifications or combinations. + + <<var;name="bullet";original="3.";match=".{0,20}">> Distribution Obligations. + + <<var;name="bullet";original="3.1.";match=".{0,20}">> Application of License. + + The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5. + + <<var;name="bullet";original="3.2.";match=".{0,20}">> Availability of Source Code. + + Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party. + + <<var;name="bullet";original="3.3.";match=".{0,20}">> Description of Modifications. + + You must cause all Covered Code to which you contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code. + + <<var;name="bullet";original="3.4.";match=".{0,20}">> Intellectual Property Matters + + <<var;name="bullet";original="(a)";match=".{0,20}">> Third Party Claims. + + If You have knowledge that a party claims an intellectual property right in particular functionality or code (or its utilization under this License), you must include a text file with the source code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If you obtain such knowledge after You make Your Modification available as described in Section 3.2, You shall promptly modify the LEGAL file in all copies You make available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained. + + <<var;name="bullet";original="(b)";match=".{0,20}">> Contributor APIs. + + If Your Modification is an application programming interface and You own or control patents which are reasonably necessary to implement that API, you must also include this information in the LEGAL file. + + <<var;name="bullet";original="3.5.";match=".{0,20}">> Required Notices. + + You must duplicate the notice in Exhibit A in each file of the Source Code, and this License in any documentation for the Source Code, where You describe recipients' rights relating to Covered Code. If You created one or more Modification(s), You may add your name as a Contributor to the notice described in Exhibit A. If it is not possible to put such notice in a particular Source Code file due to its structure, then you must include such notice in a location (such as a relevant directory file) where a user would be likely to look for such a notice. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. + + <<var;name="bullet";original="3.6.";match=".{0,20}">> Distribution of Executable Versions. + + You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. + + <<var;name="bullet";original="3.7.";match=".{0,20}">> Larger Works. + + You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code. + + <<var;name="bullet";original="4.";match=".{0,20}">> Inability to Comply Due to Statute or Regulation. + + If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. + + <<var;name="bullet";original="5.";match=".{0,20}">> Application of this License. + + This License applies to code to which the Initial Developer has attached the notice in Exhibit A, and to related Covered Code. + + <<var;name="bullet";original="6.";match=".{0,20}">> Versions of the License. + + <<var;name="bullet";original="6.1.";match=".{0,20}">> New Versions. + + Netscape Communications Corporation ("Netscape") may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number. + + <<var;name="bullet";original="6.2.";match=".{0,20}">> Effect of New Versions. + + Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netscape. No one other than Netscape has the right to modify the terms applicable to Covered Code created under this License. + + <<var;name="bullet";original="6.3.";match=".{0,20}">> Derivative Works. + + If you create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), you must (a) rename Your license so that the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", "NPL" or any confusingly similar phrase do not appear anywhere in your license and (b) otherwise make it clear that your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.) + + <<var;name="bullet";original="7.";match=".{0,20}">> DISCLAIMER OF WARRANTY. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + + <<var;name="bullet";original="8.";match=".{0,20}">> TERMINATION. + + This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. + + <<var;name="bullet";original="9.";match=".{0,20}">> LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO YOU OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THAT EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + + <<var;name="bullet";original="10.";match=".{0,20}">> U.S. GOVERNMENT END USERS. + + The Covered Code is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein. + + <<var;name="bullet";original="11.";match=".{0,20}">> MISCELLANEOUS. + + This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in, the United States of America: (a) unless otherwise agreed in writing, all disputes relating to this License (excepting any dispute relating to intellectual property rights) shall be subject to final and binding arbitration, with the losing party paying all costs of arbitration; (b) any arbitration relating to this Agreement shall be held in Santa Clara County, California, under the auspices of JAMS/EndDispute; and (c) any litigation relating to this Agreement shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. + + <<var;name="bullet";original="12.";match=".{0,20}">> RESPONSIBILITY FOR CLAIMS. + + Except in cases where another Contributor has failed to comply with Section 3.4, You are responsible for damages arising, directly or indirectly, out of Your utilization of rights under this License, based on the number of copies of Covered Code you made available, the revenues you received from utilizing such rights, and other relevant factors. You agree to work with affected parties to distribute responsibility on an equitable basis.<<beginOptional>> EXHIBIT A. + +<<beginOptional>>"<<endOptional>>The contents of this file are subject to the Mozilla Public License Version 1.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.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. + +The Original Code is <<var;name="code";original="_____";match=".+">> . The Initial Developer of the Original Code is <<var;name="InitialDeveloper";original="_____";match=".+">> . Portions created by <<var;name="createdby";original="_____";match=".+">> are Copyright (C) <<var;name="copyright";original="_____";match=".+">> . All Rights Reserved. Contributor(s): <<var;name="contributor";original="_____";match=".+">> .<<beginOptional>>"<<endOptional>> + +<<endOptional>> + true + + <p><var class="optional-license-text">&quot;</var>The contents of this file are subject to the Mozilla Public License Version 1.0 (the + &quot;License&quot;); you may not use this file except in compliance with the License. You may obtain + a copy of the License at http://www.mozilla.org/MPL/</p> + + <p>Software distributed under the License is distributed on an &quot;AS IS&quot; basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the License for the specific language governing rights and + limitations under the License.</p> + + <p>The Original Code is + <var class="replacable-license-text"> _____</var>. The Initial Developer of the Original Code is + <var class="replacable-license-text"> _____</var>. Portions created by + <var class="replacable-license-text"> _____</var> are Copyright (C) + <var class="replacable-license-text"> _____</var>. All Rights Reserved. Contributor(s): + <var class="replacable-license-text"> _____</var>.<var class="optional-license-text">&quot;</var> + </p> + + + false + + <div class="optional-license-text"> + <p>MOZILLA PUBLIC LICENSE + <br /> + +Version 1.0 + </p> + + </div> + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> 1.</var> + Definitions. + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> 1.1.</var> + &quot;Contributor&quot; means each entity that creates or contributes to the creation of + Modifications. + </li> + +<li> + <var class="replacable-license-text"> 1.2.</var> + &quot;Contributor Version&quot; means the combination of the Original Code, prior + Modifications used by a Contributor, and the Modifications made by that particular + Contributor. + </li> + +<li> + <var class="replacable-license-text"> 1.3.</var> + &quot;Covered Code&quot; means the Original Code or Modifications or the combination of the + Original Code and Modifications, in each case including portions thereof. + </li> + +<li> + <var class="replacable-license-text"> 1.4.</var> + &quot;Electronic Distribution Mechanism&quot; means a mechanism generally accepted in the + software development community for the electronic transfer of data. + </li> + +<li> + <var class="replacable-license-text"> 1.5.</var> + &quot;Executable&quot; means Covered Code in any form other than Source Code. + </li> + +<li> + <var class="replacable-license-text"> 1.6.</var> + &quot;Initial Developer&quot; means the individual or entity identified as the Initial + Developer in the Source Code notice required by Exhibit A. + </li> + +<li> + <var class="replacable-license-text"> 1.7.</var> + &quot;Larger Work&quot; means a work which combines Covered Code or portions thereof with + code not governed by the terms of this License. + </li> + +<li> + <var class="replacable-license-text"> 1.8.</var> + &quot;License&quot; means this document. + </li> + +<li> + <var class="replacable-license-text"> 1.9.</var> + &quot;Modifications&quot; means any addition to or deletion from the substance or structure + of either the Original Code or any previous Modifications. When Covered Code is released + as a series of files, a Modification is: + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> A.</var> + Any addition to or deletion from the contents of a file containing Original Code or + previous Modifications. + </li> + +<li> + <var class="replacable-license-text"> B.</var> + Any new file that contains any part of the Original Code or previous Modifications. + </li> + +</ul> + </li> + +<li> + <var class="replacable-license-text"> 1.10.</var> + &quot;Original Code&quot; means Source Code of computer software code which is described in + the Source Code notice required by Exhibit A as Original Code, and which, at the time of + its release under this License is not already Covered Code governed by this License. + </li> + +<li> + <var class="replacable-license-text"> 1.11.</var> + &quot;Source Code&quot; means the preferred form of the Covered Code for making + modifications to it, including all modules it contains, plus any associated interface + definition files, scripts used to control compilation and installation of an Executable, + or a list of source code differential comparisons against either the Original Code or + another well known, available Covered Code of the Contributor&apos;s choice. The Source + Code can be in a compressed or archival form, provided the appropriate decompression or + de-archiving software is widely available for no charge. + </li> + +<li> + <var class="replacable-license-text"> 1.12.</var> + &quot;You&quot; means an individual or a legal entity exercising rights under, and + complying with all of the terms of, this License or a future version of this License + issued under Section 6.1. For legal entities, &quot;You&quot; includes any entity which + controls, is controlled by, or is under common control with You. For purposes of this + definition, &quot;control&quot; means (a) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or otherwise, or (b) ownership + of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such + entity. + </li> + +</ul> + </li> + +<li> + <var class="replacable-license-text"> 2.</var> + Source Code License. + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> 2.1.</var> + The Initial Developer Grant. + <br /> + + The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive + license, subject to third party intellectual property claims: + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> (a)</var> + to use, reproduce, modify, display, perform, sublicense and distribute the Original Code + (or portions thereof) with or without Modifications, or as part of a Larger Work; + and + </li> + +<li> + <var class="replacable-license-text"> (b)</var> + under patents now or hereafter owned or controlled by Initial Developer, to make, have + made, use and sell (&quot;Utilize&quot;) the Original Code (or portions thereof), + but solely to the extent that any such patent is reasonably necessary to enable You to + Utilize the Original Code (or portions thereof) and not to any greater extent that may + be necessary to Utilize further Modifications or combinations. + </li> + +</ul> + </li> + +<li> + <var class="replacable-license-text"> 2.2.</var> + Contributor Grant. + <br /> + + Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license, + subject to third party intellectual property claims: + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> (a)</var> + to use, reproduce, modify, display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an unmodified basis, with + other Modifications, as Covered Code or as part of a Larger Work; and + </li> + +<li> + <var class="replacable-license-text"> (b)</var> + under patents now or hereafter owned or controlled by Contributor, to Utilize the + Contributor Version (or portions thereof), but solely to the extent that any such + patent is reasonably necessary to enable You to Utilize the Contributor Version (or + portions thereof), and not to any greater extent that may be necessary to Utilize + further Modifications or combinations. + </li> + +</ul> + </li> + +</ul> + </li> + +<li> + <var class="replacable-license-text"> 3.</var> + Distribution Obligations. + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> 3.1.</var> + Application of License. + <br /> + + The Modifications which You create or to which You contribute are governed by the terms + of this License, including without limitation Section 2.2. The Source Code version + of Covered Code may be distributed only under the terms of this License or a + future version of this License released under Section 6.1, and You must include a + copy of this License with every copy of the Source Code You distribute. You may + not offer or impose any terms on any Source Code version that alters or restricts + the applicable version of this License or the recipients&apos; rights hereunder. + However, You may include an additional document offering the additional rights + described in Section 3.5. + + </li> + +<li> + <var class="replacable-license-text"> 3.2.</var> + Availability of Source Code. + <br /> + + Any Modification which You create or to which You contribute must be made available in + Source Code form under the terms of this License either on the same media as an + Executable version or via an accepted Electronic Distribution Mechanism to anyone + to whom you made an Executable version available; and if made available via + Electronic Distribution Mechanism, must remain available for at least twelve (12) + months after the date it initially became available, or at least six (6) months + after a subsequent version of that particular Modification has been made available + to such recipients. You are responsible for ensuring that the Source Code version + remains available even if the Electronic Distribution Mechanism is maintained by a + third party. + + </li> + +<li> + <var class="replacable-license-text"> 3.3.</var> + Description of Modifications. + <br /> + + You must cause all Covered Code to which you contribute to contain a file documenting + the changes You made to create that Covered Code and the date of any change. You + must include a prominent statement that the Modification is derived, directly or + indirectly, from Original Code provided by the Initial Developer and including the + name of the Initial Developer in (a) the Source Code, and (b) in any notice in an + Executable version or related documentation in which You describe the origin or + ownership of the Covered Code. + + </li> + +<li> + <var class="replacable-license-text"> 3.4.</var> + Intellectual Property Matters + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> (a)</var> + Third Party Claims. + <br /> + + If You have knowledge that a party claims an intellectual property right in + particular functionality or code (or its utilization under this License), you + must include a text file with the source code distribution titled + &quot;LEGAL&quot; which describes the claim and the party making the claim + in sufficient detail that a recipient will know whom to contact. If you obtain + such knowledge after You make Your Modification available as described in + Section 3.2, You shall promptly modify the LEGAL file in all copies You make + available thereafter and shall take other steps (such as notifying appropriate + mailing lists or newsgroups) reasonably calculated to inform those who + received the Covered Code that new knowledge has been obtained. + + </li> + +<li> + <var class="replacable-license-text"> (b)</var> + Contributor APIs. + <br /> + + If Your Modification is an application programming interface and You own or control + patents which are reasonably necessary to implement that API, you must also + include this information in the LEGAL file. + + </li> + +</ul> + </li> + +<li> + <var class="replacable-license-text"> 3.5.</var> + Required Notices. + <br /> + + You must duplicate the notice in Exhibit A in each file of the Source Code, and this + License in any documentation for the Source Code, where You describe + recipients&apos; rights relating to Covered Code. If You created one or more + Modification(s), You may add your name as a Contributor to the notice described in + Exhibit A. If it is not possible to put such notice in a particular Source Code + file due to its structure, then you must include such notice in a location (such + as a relevant directory file) where a user would be likely to look for such a + notice. You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered Code. + However, You may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear than any such + warranty, support, indemnity or liability obligation is offered by You alone, and + You hereby agree to indemnify the Initial Developer and every Contributor for any + liability incurred by the Initial Developer or such Contributor as a result of + warranty, support, indemnity or liability terms You offer. + + </li> + +<li> + <var class="replacable-license-text"> 3.6.</var> + Distribution of Executable Versions. + <br /> + + You may distribute Covered Code in Executable form only if the requirements of Section + 3.1-3.5 have been met for that Covered Code, and if You include a notice stating + that the Source Code version of the Covered Code is available under the terms of + this License, including a description of how and where You have fulfilled the + obligations of Section 3.2. The notice must be conspicuously included in any + notice in an Executable version, related documentation or collateral in which You + describe recipients&apos; rights relating to the Covered Code. You may distribute + the Executable version of Covered Code under a license of Your choice, which may + contain terms different from this License, provided that You are in compliance + with the terms of this License and that the license for the Executable version + does not attempt to limit or alter the recipient&apos;s rights in the Source Code + version from the rights set forth in this License. If You distribute the + Executable version under a different license You must make it absolutely clear + that any terms which differ from this License are offered by You alone, not by the + Initial Developer or any Contributor. You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred by the Initial + Developer or such Contributor as a result of any such terms You offer. + + </li> + +<li> + <var class="replacable-license-text"> 3.7.</var> + Larger Works. + <br /> + + You may create a Larger Work by combining Covered Code with other code not governed by + the terms of this License and distribute the Larger Work as a single product. In + such a case, You must make sure the requirements of this License are fulfilled for + the Covered Code. + + </li> + +</ul> + </li> + +<li> + <var class="replacable-license-text"> 4.</var> + Inability to Comply Due to Statute or Regulation. + <br /> + + If it is impossible for You to comply with any of the terms of this License with respect to + some or all of the Covered Code due to statute or regulation then You must: (a) comply + with the terms of this License to the maximum extent possible; and (b) describe the + limitations and the code they affect. Such description must be included in the LEGAL + file described in Section 3.4 and must be included with all distributions of the + Source Code. Except to the extent prohibited by statute or regulation, such + description must be sufficiently detailed for a recipient of ordinary skill to be able + to understand it. + + </li> + +<li> + <var class="replacable-license-text"> 5.</var> + Application of this License. + <br /> + + This License applies to code to which the Initial Developer has attached the notice in + Exhibit A, and to related Covered Code. + + </li> + +<li> + <var class="replacable-license-text"> 6.</var> + Versions of the License. + +<ul style="list-style:none"> + +<li> + <var class="replacable-license-text"> 6.1.</var> + New Versions. + <br /> + + Netscape Communications Corporation (&quot;Netscape&quot;) may publish revised and/or + new versions of the License from time to time. Each version will be given a + distinguishing version number. + + </li> + +<li> + <var class="replacable-license-text"> 6.2.</var> + Effect of New Versions. + <br /> + + Once Covered Code has been published under a particular version of the License, You may + always continue to use it under the terms of that version. You may also choose to + use such Covered Code under the terms of any subsequent version of the License + published by Netscape. No one other than Netscape has the right to modify the + terms applicable to Covered Code created under this License. + + </li> + +<li> + <var class="replacable-license-text"> 6.3.</var> + Derivative Works. + <br /> + + If you create or use a modified version of this License (which you may only do in order + to apply it to code which is not already Covered Code governed by this License), + you must (a) rename Your license so that the phrases &quot;Mozilla&quot;, + &quot;MOZILLAPL&quot;, &quot;MOZPL&quot;, &quot;Netscape&quot;, + &quot;NPL&quot; or any confusingly similar phrase do not appear anywhere in your + license and (b) otherwise make it clear that your version of the license contains + terms which differ from the Mozilla Public License and Netscape Public License. + (Filling in the name of the Initial Developer, Original Code or Contributor in the + notice described in Exhibit A shall not of themselves be deemed to be + modifications of this License.) + + </li> + +</ul> + </li> + +<li> + <var class="replacable-license-text"> 7.</var> + DISCLAIMER OF WARRANTY. + <br /> + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN &quot;AS IS&quot; BASIS, WITHOUT + WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, + WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A + PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND + PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE + IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE + COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY + CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS + AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + + </li> + +<li> + <var class="replacable-license-text"> 8.</var> + TERMINATION. + <br /> + + This License and the rights granted hereunder will terminate automatically if You fail to + comply with terms herein and fail to cure such breach within 30 days of becoming aware + of the breach. All sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their nature, must + remain in effect beyond the termination of this License shall survive. + + </li> + +<li> + <var class="replacable-license-text"> 9.</var> + LIMITATION OF LIABILITY. + <br /> + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), + CONTRACT, OR OTHERWISE, SHALL THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY + DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO YOU + OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF + ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK + STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR + LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH + DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR + PERSONAL INJURY RESULTING FROM SUCH PARTY&apos;S NEGLIGENCE TO THE EXTENT APPLICABLE + LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR + LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THAT EXCLUSION AND LIMITATION + MAY NOT APPLY TO YOU. + + </li> + +<li> + <var class="replacable-license-text"> 10.</var> + U.S. GOVERNMENT END USERS. + <br /> + + The Covered Code is a &quot;commercial item,&quot; as that term is defined in 48 C.F.R. + 2.101 (Oct. 1995), consisting of &quot;commercial computer software&quot; and + &quot;commercial computer software documentation,&quot; as such terms are used in 48 + C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 + through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code + with only those rights set forth herein. + + </li> + +<li> + <var class="replacable-license-text"> 11.</var> + MISCELLANEOUS. + <br /> + + This License represents the complete agreement concerning subject matter hereof. If any + provision of this License is held to be unenforceable, such provision shall be + reformed only to the extent necessary to make it enforceable. This License shall be + governed by California law provisions (except to the extent applicable law, if any, + provides otherwise), excluding its conflict-of-law provisions. With respect to + disputes in which at least one party is a citizen of, or an entity chartered or + registered to do business in, the United States of America: (a) unless otherwise + agreed in writing, all disputes relating to this License (excepting any dispute + relating to intellectual property rights) shall be subject to final and binding + arbitration, with the losing party paying all costs of arbitration; (b) any + arbitration relating to this Agreement shall be held in Santa Clara County, + California, under the auspices of JAMS/EndDispute; and (c) any litigation relating to + this Agreement shall be subject to the jurisdiction of the Federal Courts of the + Northern District of California, with venue lying in Santa Clara County, California, + with the losing party responsible for costs, including without limitation, court costs + and reasonable attorneys fees and expenses. The application of the United Nations + Convention on Contracts for the International Sale of Goods is expressly excluded. Any + law or regulation which provides that the language of a contract shall be construed + against the drafter shall not apply to this License. + + </li> + +<li> + <var class="replacable-license-text"> 12.</var> + RESPONSIBILITY FOR CLAIMS. + <br /> + + Except in cases where another Contributor has failed to comply with Section 3.4, You are responsible for + damages arising, directly or indirectly, out of Your utilization of rights under this License, based + on the number of copies of Covered Code you made available, the revenues you received from utilizing + such rights, and other relevant factors. You agree to work with affected parties to distribute + responsibility on an equitable basis. + </li> + +</ul> + <div class="optional-license-text"> + <p>EXHIBIT A.</p> + + <p><var class="optional-license-text">&quot;</var>The contents of this file are subject to the Mozilla Public License Version 1.0 (the + &quot;License&quot;); you may not use this file except in compliance with the License. You may obtain + a copy of the License at http://www.mozilla.org/MPL/</p> + + <p>Software distributed under the License is distributed on an &quot;AS IS&quot; basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the License for the specific language governing rights and + limitations under the License.</p> + + <p>The Original Code is + <var class="replacable-license-text"> _____</var>. The Initial Developer of the Original Code is + <var class="replacable-license-text"> _____</var>. Portions created by + <var class="replacable-license-text"> _____</var> are Copyright (C) + <var class="replacable-license-text"> _____</var>. All Rights Reserved. Contributor(s): + <var class="replacable-license-text"> _____</var>.<var class="optional-license-text">&quot;</var> + </p> + + </div> + + MPL-1.0 + + + false + Copyright Saxonica Ltd + + + + + + + http://ftp.gnu.org/gnu/glibc + glibc + 2011-01-29T18:30:22Z + + + + + + + + + + + 624c1abb3664f4b35547e7c73864ad24 + + + Person: Jane Doe (jane.doe@example.com) + The GNU C Library is free software. See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed. License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually. + Organization: ExampleCodeInspect (contact@example.com) + + + + + + + + + + + http://www.openjena.org/ + + + + + pkg:maven/org.apache.jena/apache-jena@3.12.0 + + + 3.12.0 + https://search.maven.org/remotecontent?filepath=org/apache/jena/apache-jena/3.12.0/apache-jena-3.12.0.tar.gz + Jena + false + + + + + + Specification Documentation + + + + fff4e1c67a2d28fced849ee1bb76e7391b93f125 + + + ./docs/myspec.pdf + + + + + + 2012-01-29T18:30:22Z + uses glibc-2_11-branch from git://sourceware.org/git/glibc.git. + glibc-2.11.1.tar.gz + 2014-01-29T18:30:22Z + + + + + + + GNU C library. + + + d6a770ba38583ed4bb4525bd96e50461655d2758 + ./package.spdx + + + + + + + cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* + + + Copyright 2008-2010 John Smith + The GNU C Library defines functions that are specified by the ISO C standard, as well as additional features specific to POSIX and other derivatives of the Unix operating system, and extensions specific to GNU systems. + + + + + + This document was created using SPDX 2.0 using licenses from the web site. + + + + + + + + + + + ./package/foo.c + The concluded license was taken from the package level that the file was included in. + + + + + + + + + Person: File Commenter + File level annotation + + 2011-01-29T18:30:22Z + + + + + + + + 624c1abb3664f4b35547e7c73864ad24 + + + + + + d6a770ba38583ed4bb4525bd96e50461655d2758 + + + Copyright 2008-2010 John Smith + The Regents of the University of California + Modified by Paul Mundt lethal@linux-sh.org + + + + + + + + IBM Corporation + Copyright (c) 2001 Aaron Lehmann aaroni@vitelus.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + The concluded license was taken from the package level that the file was included in. +This information was found in the COPYING.txt file in the xyz directory. + + + + + + SPDX-Tools-v2.0 + + + Person: Joe Reviewer + This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses + + 2010-02-10T00:00:00Z + + + + + + + + + + + + http://commons.apache.org/proper/commons-lang/ + NOASSERTION + + NOASSERTION + Apache Commons Lang + + false + + diff --git a/tests/spdx/parser/rdf/__init__.py b/tests/spdx/parser/rdf/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml b/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml new file mode 100644 index 000000000..e1d02851d --- /dev/null +++ b/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml @@ -0,0 +1,185 @@ + + + + + + + + + + + + + org.apache.tomcat:tomcat:9.0.0.M4 + + + externalPackageRefComment + + + + + + + + + https://homepage.com + Person: supplierName (some@mail.com) + packageSummary + true + 2022-12-03T00:00:00Z + packageAttributionText + packageDescription + packageComment + + + ./exclude.py + 85ed0817af83a24ad8da68c2b5094de69833983c + + + packageLicenseComment + packageName + 2022-12-01T00:00:00Z + 12.2 + Person: originatorName (some@mail.com) + + + packageCopyrightText + 2022-12-02T00:00:00Z + + + 71c4025dd9897b364f3ebbb42c484ff43d00791c + + + + ./packageFileName + sourceInfo + https://download.com + + + snippetComment + + + + + + + + + + + + + + + fileComment + + copyrightText + fileContributor + + + annotationComment + Person: annotatorName (some@mail.com) + 2022-12-01T00:00:00Z + + + + licenseComment + + + + 71c4025dd9897b364f3ebbb42c484ff43d00791c + + + ./fileName.py + + + fileNotice + fileAttributionText + + + 3 + + + + + + 4 + + + + + + + + + + 2 + + + + + 1 + + + + + + snippetAttributionText + + snippetLicenseComment + licenseCopyrightText + snippetName + + + + + + + + + + SPDX-2.3 + documentComment + + + + + + 71c4025dd9897b364f3ebbb42c484ff43d00791c + + + + + + documentName + + + extractedText + https://see.also + LicenseRef-1 + licenseComment + licenseName + + + + + + + + + + + 2022-12-01T00:00:00Z + creatorComment + 3.19 + Person: creatorName (some@mail.com) + + + + diff --git a/tests/spdx/parser/rdf/test_creation_info_parser.py b/tests/spdx/parser/rdf/test_creation_info_parser.py new file mode 100644 index 000000000..dae099a0b --- /dev/null +++ b/tests/spdx/parser/rdf/test_creation_info_parser.py @@ -0,0 +1,67 @@ +# Copyright (c) 2023 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. +import os +from datetime import datetime +from typing import Tuple, List + +import pytest +from rdflib import Graph, RDF, URIRef +from rdflib.term import Node + +from spdx.model.version import Version + +from spdx.model.actor import Actor, ActorType + +from spdx.parser.rdf.creation_info_parser import parse_creation_info, parse_namespace_and_spdx_id +from spdx.rdfschema.namespace import SPDX_NAMESPACE + + +def test_parse_creation_info(): + graph = Graph().parse(os.path.join(os.path.dirname(__file__), "data/file_to_test_rdf_parser.rdf.xml")) + + creation_info, _ = parse_creation_info(graph) + assert creation_info.spdx_id == "SPDXRef-DOCUMENT" + assert creation_info.spdx_version == "SPDX-2.3" + assert creation_info.name == "documentName" + assert creation_info.document_namespace == "https://some.namespace" + assert creation_info.creators == [Actor(ActorType.PERSON, "creatorName", "some@mail.com")] + assert creation_info.created == datetime(2022, 12, 1, 0, 0) + assert creation_info.creator_comment == "creatorComment" + assert creation_info.data_license == "CC0-1.0" + assert creation_info.license_list_version == Version(3, 19) + assert creation_info.document_comment == "documentComment" + + +def test_parse_namespace_and_spdx_id(): + graph = Graph().add((URIRef("docNamespace#spdxID"), RDF.type, SPDX_NAMESPACE.SpdxDocument)) + + namespace, spdx_id, _ = parse_namespace_and_spdx_id(graph) + + assert namespace == "docNamespace" + assert spdx_id == "spdxID" + + +@pytest.mark.parametrize("triples,error_message", + [([(URIRef("docNamespace"), RDF.type, SPDX_NAMESPACE.SpdxDocument)], + r"No '#' found in the URI of SpdxDocument"), + ([(URIRef(""), RDF.type, URIRef(""))], r"No SpdxDocument found, can't parse rdf file."), + ([(URIRef("#SPDXRef-DOCUMENT"), RDF.type, SPDX_NAMESPACE.SpdxDocument)], + "No namespace found"), + ([(URIRef("docNamespace1"), RDF.type, SPDX_NAMESPACE.SpdxDocument), + (URIRef("docNamespace2"), RDF.type, SPDX_NAMESPACE.SpdxDocument)], + "Multiple SpdxDocuments found")]) +def test_parse_namespace_and_spdx_id_with_system_exit(triples: List[Tuple[Node, Node, Node]], error_message: str): + graph = Graph() + for triple in triples: + graph = graph.add(triple) + + with pytest.raises(SystemExit, match=error_message): + parse_namespace_and_spdx_id(graph) diff --git a/tests/spdx/parser/rdf/test_rdf_parser.py b/tests/spdx/parser/rdf/test_rdf_parser.py new file mode 100644 index 000000000..4fadd7319 --- /dev/null +++ b/tests/spdx/parser/rdf/test_rdf_parser.py @@ -0,0 +1,29 @@ +# Copyright (c) 2023 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. +import os + +import pytest + +from spdx.model.document import Document +from spdx.parser.rdf import rdf_parser + + +def test_rdf_parser_file_not_found(): + with pytest.raises(FileNotFoundError, match="No such file or directory") as err: + wrong_file_path = os.path.join(os.path.dirname(__file__), 'hnjfkjsedhnflsiafg.json') + rdf_parser.parse_from_file(wrong_file_path) + + +def test_rdf_parser_with_2_3_example(): + doc = rdf_parser.parse_from_file( + os.path.join(os.path.dirname(__file__), "../../data/formats/SPDXRdfExample-v2.3.spdx.rdf.xml")) + + assert type(doc) == Document From 659b2f712e8725b49b30544cc988b4053077ecce Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Feb 2023 14:36:16 +0100 Subject: [PATCH 235/362] [issue-456] add graph parsing functions Signed-off-by: Meret Behrens --- .../parser/rdf/graph_parsing_functions.py | 41 ++++++++++++++++++- .../parser/rdf/test_graph_parsing_function.py | 34 +++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 tests/spdx/parser/rdf/test_graph_parsing_function.py diff --git a/src/spdx/parser/rdf/graph_parsing_functions.py b/src/spdx/parser/rdf/graph_parsing_functions.py index 2fc8f1a44..5ba1500d1 100644 --- a/src/spdx/parser/rdf/graph_parsing_functions.py +++ b/src/spdx/parser/rdf/graph_parsing_functions.py @@ -8,13 +8,16 @@ # 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 typing import Any, Callable +from typing import Any, Callable, Union, Optional -from rdflib import Graph +from rdflib import Graph, URIRef from rdflib.exceptions import UniquenessError from rdflib.term import Node +from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING +from spdx.model.spdx_none import SpdxNone, SPDX_NONE_STRING from spdx.parser.logger import Logger +from spdx.rdfschema.namespace import SPDX_NAMESPACE def parse_literal(logger: Logger, graph: Graph, subject: Node, predicate: Node, default: Any = None, @@ -29,3 +32,37 @@ def parse_literal(logger: Logger, graph: Graph, subject: Node, predicate: Node, return method_to_apply(value.removeprefix(prefix)) return default + + +def parse_literal_or_no_assertion_or_none(logger: Logger, graph: Graph, subject: Node, predicate: Node, + default: Any = None, method_to_apply: Callable = lambda x: x): + try: + value = graph.value(subject=subject, predicate=predicate, default=default, any=False) + except UniquenessError: + logger.append(f"Multiple values for unique value {predicate} found.") + return + if not value: + return default + if value == SPDX_NAMESPACE.noassertion: + return SpdxNoAssertion() + if value == SPDX_NAMESPACE.none: + return SpdxNone() + return method_to_apply(value.toPython()) + + +def str_to_no_assertion_or_none(value: str) -> Union[str, SpdxNone, SpdxNoAssertion]: + if value == SPDX_NO_ASSERTION_STRING: + return SpdxNoAssertion() + if value == SPDX_NONE_STRING: + return SpdxNone() + return value + + +def parse_spdx_id(resource: URIRef, doc_namespace: str) -> Optional[str]: + if not resource: + return None + if resource.startswith(f"{doc_namespace}#"): + spdx_id = resource.fragment + else: + spdx_id = resource.toPython() + return spdx_id or None diff --git a/tests/spdx/parser/rdf/test_graph_parsing_function.py b/tests/spdx/parser/rdf/test_graph_parsing_function.py new file mode 100644 index 000000000..8c1c9faaa --- /dev/null +++ b/tests/spdx/parser/rdf/test_graph_parsing_function.py @@ -0,0 +1,34 @@ +# Copyright (c) 2023 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. +import pytest +from rdflib import URIRef + +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone +from spdx.parser.rdf.graph_parsing_functions import str_to_no_assertion_or_none, parse_spdx_id + + +@pytest.mark.parametrize("value,expected",[("NOASSERTION", SpdxNoAssertion()), ("NONE", SpdxNone()), ("test", "test"), + ("Noassertion", "Noassertion")]) +def test_str_to_no_assertion_or_none(value, expected): + result = str_to_no_assertion_or_none(value) + + assert result == expected + +@pytest.mark.parametrize("resource,doc_namespace," + "expected", [(URIRef("docNamespace#SPDXRef-Test"), "docNamespace", "SPDXRef-Test"), + (URIRef("docNamespaceSPDXRef-Test"), "docNamespace", "docNamespaceSPDXRef-Test"), + (URIRef("differentNamespace#SPDXRef-Test"), "docNamespace", "differentNamespace#SPDXRef-Test"), + (None, "", None),]) +def test_parse_spdx_id(resource, doc_namespace, expected): + spdx_id = parse_spdx_id(resource, doc_namespace) + + assert spdx_id == expected From c81d14a1aef7a58eca65e17a6153095603dc6d01 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Feb 2023 14:43:16 +0100 Subject: [PATCH 236/362] [issue-456] add snippet parser Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/rdf_parser.py | 14 +++- src/spdx/parser/rdf/snippet_parser.py | 69 ++++++++++++++++++++ tests/spdx/parser/rdf/test_rdf_parser.py | 1 + tests/spdx/parser/rdf/test_snippet_parser.py | 36 ++++++++++ 4 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 src/spdx/parser/rdf/snippet_parser.py create mode 100644 tests/spdx/parser/rdf/test_snippet_parser.py diff --git a/src/spdx/parser/rdf/rdf_parser.py b/src/spdx/parser/rdf/rdf_parser.py index 34ad9613e..eed65f524 100644 --- a/src/spdx/parser/rdf/rdf_parser.py +++ b/src/spdx/parser/rdf/rdf_parser.py @@ -8,13 +8,15 @@ # 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 rdflib import Graph +from rdflib import Graph, RDF from spdx.model.document import Document from spdx.parser.error import SPDXParsingError from spdx.parser.logger import Logger from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.rdf.creation_info_parser import parse_creation_info +from spdx.parser.rdf.snippet_parser import parse_snippet +from spdx.rdfschema.namespace import SPDX_NAMESPACE def parse_from_file(file_name: str) -> Document: @@ -33,6 +35,14 @@ def translate_graph_to_document(graph: Graph) -> Document: except SPDXParsingError as err: logger.extend(err.get_messages()) creation_info = None + + snippets = [] + for (snippet_node, _, _) in graph.triples((None, RDF.type, SPDX_NAMESPACE.Snippet)): + try: + snippets.append(parse_snippet(snippet_node, graph, creation_info.document_namespace)) + except SPDXParsingError as err: + logger.extend(err.get_messages()) + raise_parsing_error_if_logger_has_messages(logger) - document = construct_or_raise_parsing_error(Document, dict(creation_info=creation_info)) + document = construct_or_raise_parsing_error(Document, dict(creation_info=creation_info, snippets=snippets)) return document diff --git a/src/spdx/parser/rdf/snippet_parser.py b/src/spdx/parser/rdf/snippet_parser.py new file mode 100644 index 000000000..2dba23353 --- /dev/null +++ b/src/spdx/parser/rdf/snippet_parser.py @@ -0,0 +1,69 @@ +# Copyright (c) 2023 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 typing import Tuple, Optional + +from rdflib import Graph, RDF, RDFS +from rdflib.term import URIRef, Node + +from spdx.model.snippet import Snippet +from spdx.parser.logger import Logger +from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages +from spdx.parser.rdf.graph_parsing_functions import parse_literal, str_to_no_assertion_or_none, parse_spdx_id +from spdx.rdfschema.namespace import SPDX_NAMESPACE, POINTER_NAMESPACE + + +def parse_snippet(snippet_node: URIRef, graph: Graph, doc_namespace: str) -> Snippet: + logger = Logger() + spdx_id = parse_spdx_id(snippet_node, doc_namespace) + file_spdx_id_uri = graph.value(subject=snippet_node, predicate=SPDX_NAMESPACE.snippetFromFile) + file_spdx_id = parse_spdx_id(file_spdx_id_uri, doc_namespace) + byte_range = parse_ranges(snippet_node, graph, POINTER_NAMESPACE.ByteOffsetPointer, POINTER_NAMESPACE.offset) + line_range = parse_ranges(snippet_node, graph, POINTER_NAMESPACE.LineCharPointer, POINTER_NAMESPACE.lineNumber) + + license_comment = parse_literal(logger, graph, snippet_node, SPDX_NAMESPACE.licenseComments) + copyright_text = parse_literal(logger, graph, snippet_node, SPDX_NAMESPACE.copyrightText, + method_to_apply=str_to_no_assertion_or_none) + comment = parse_literal(logger, graph, snippet_node, RDFS.comment) + name = parse_literal(logger, graph, snippet_node, SPDX_NAMESPACE.name) + attribution_texts = [] + for (_, _, attribution_text_literal) in graph.triples((snippet_node, SPDX_NAMESPACE.attributionText, None)): + attribution_texts.append(attribution_text_literal.toPython()) + + raise_parsing_error_if_logger_has_messages(logger, "Snippet") + snippet = construct_or_raise_parsing_error(Snippet, + dict(spdx_id=spdx_id, file_spdx_id=file_spdx_id, byte_range=byte_range, + line_range=line_range, license_concluded=None, + license_info_in_snippet=None, license_comment=license_comment, + copyright_text=copyright_text, comment=comment, name=name, + attribution_texts=attribution_texts)) + return snippet + + +def parse_ranges(snippet_node: URIRef, graph: Graph, pointer: Node, member: Node) -> Tuple[int, int]: + start_range = None + end_range = None + for (_, _, start_end_pointer) in graph.triples((snippet_node, SPDX_NAMESPACE.range, None)): + for (_, _, pointer_node) in graph.triples((start_end_pointer, POINTER_NAMESPACE.startPointer, None)): + for (typed_pointer_node, _, _) in graph.triples((pointer_node, RDF.type, pointer)): + start_range = parse_range_value(graph, typed_pointer_node, member) + for (_, _, pointer_node) in graph.triples((start_end_pointer, POINTER_NAMESPACE.endPointer, None)): + for (typed_pointer_node, _, _) in graph.triples((pointer_node, RDF.type, pointer)): + end_range = parse_range_value(graph, typed_pointer_node, member) + return start_range, end_range + + +def parse_range_value(graph: Graph, pointer_node: Node, predicate: Node) -> Optional[int]: + value = graph.value(pointer_node, predicate) + if value: + value = int(value) + return value + + diff --git a/tests/spdx/parser/rdf/test_rdf_parser.py b/tests/spdx/parser/rdf/test_rdf_parser.py index 4fadd7319..9186612ba 100644 --- a/tests/spdx/parser/rdf/test_rdf_parser.py +++ b/tests/spdx/parser/rdf/test_rdf_parser.py @@ -27,3 +27,4 @@ def test_rdf_parser_with_2_3_example(): os.path.join(os.path.dirname(__file__), "../../data/formats/SPDXRdfExample-v2.3.spdx.rdf.xml")) assert type(doc) == Document + assert len(doc.snippets) == 1 diff --git a/tests/spdx/parser/rdf/test_snippet_parser.py b/tests/spdx/parser/rdf/test_snippet_parser.py new file mode 100644 index 000000000..326c16f8c --- /dev/null +++ b/tests/spdx/parser/rdf/test_snippet_parser.py @@ -0,0 +1,36 @@ +# Copyright (c) 2023 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. +import os + +from rdflib import Graph, RDF + +from spdx.parser.rdf.snippet_parser import parse_snippet +from spdx.rdfschema.namespace import SPDX_NAMESPACE + + +def test_parse_snippet(): + graph = Graph().parse(os.path.join(os.path.dirname(__file__), "data/file_to_test_rdf_parser.rdf.xml")) + snippet_node = graph.value(predicate=RDF.type, object=SPDX_NAMESPACE.Snippet) + doc_namespace = "https://some.namespace" + + snippet = parse_snippet(snippet_node, graph, doc_namespace) + + assert snippet.spdx_id == "SPDXRef-Snippet" + assert snippet.file_spdx_id == "SPDXRef-File" + assert snippet.byte_range == (1, 2) + assert snippet.line_range == (3, 4) + assert snippet.license_concluded == None + assert snippet.license_info_in_snippet == None + assert snippet.license_comment == "snippetLicenseComment" + assert snippet.copyright_text == "licenseCopyrightText" + assert snippet.comment == "snippetComment" + assert snippet.name == "snippetName" + assert snippet.attribution_texts == ["snippetAttributionText"] From 1be51c401fe9c7066feae660a1e55631ef86405f Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 2 Feb 2023 09:10:59 +0100 Subject: [PATCH 237/362] [issue-456] add checksum parser Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/checksum_parser.py | 43 ++++++++++++ tests/spdx/parser/rdf/test_checksum_parser.py | 70 +++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 src/spdx/parser/rdf/checksum_parser.py create mode 100644 tests/spdx/parser/rdf/test_checksum_parser.py diff --git a/src/spdx/parser/rdf/checksum_parser.py b/src/spdx/parser/rdf/checksum_parser.py new file mode 100644 index 000000000..b851a6c6d --- /dev/null +++ b/src/spdx/parser/rdf/checksum_parser.py @@ -0,0 +1,43 @@ +# Copyright (c) 2023 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 rdflib import Graph, URIRef +from spdx.parser.error import SPDXParsingError + +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.parser.logger import Logger +from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages +from spdx.parser.rdf.graph_parsing_functions import parse_literal +from spdx.rdfschema.namespace import SPDX_NAMESPACE + + +def parse_checksum(parent_node: URIRef, graph: Graph) -> Checksum: + logger = Logger() + algorithm = graph.value(parent_node, SPDX_NAMESPACE.algorithm, default="") + try: + algorithm = convert_rdf_to_algorithm(algorithm) + except KeyError: + logger.append(f"Invalid ChecksumAlgorithm: {algorithm}") + algorithm = None + value = parse_literal(logger, graph, parent_node, SPDX_NAMESPACE.checksumValue) + raise_parsing_error_if_logger_has_messages(logger, "Checksum") + checksum = construct_or_raise_parsing_error(Checksum, dict(algorithm=algorithm, value=value)) + return checksum + + +def convert_rdf_to_algorithm(algorithm: str) -> ChecksumAlgorithm: + algorithm = algorithm.removeprefix(SPDX_NAMESPACE.checksumAlgorithm_).upper() + if "BLAKE2B" in algorithm: + algorithm = algorithm.replace("BLAKE2B", "BLAKE2B_") + try: + checksum = ChecksumAlgorithm[algorithm] + except KeyError: + raise SPDXParsingError([f"Invalid value for ChecksumAlgorithm: {algorithm}"]) + return checksum diff --git a/tests/spdx/parser/rdf/test_checksum_parser.py b/tests/spdx/parser/rdf/test_checksum_parser.py new file mode 100644 index 000000000..698d1dba9 --- /dev/null +++ b/tests/spdx/parser/rdf/test_checksum_parser.py @@ -0,0 +1,70 @@ +# Copyright (c) 2023 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. +import os + +import pytest +from rdflib import Graph, RDF, URIRef +from spdx.parser.error import SPDXParsingError + +from spdx.model.checksum import ChecksumAlgorithm +from spdx.parser.rdf.checksum_parser import parse_checksum, convert_rdf_to_algorithm +from spdx.rdfschema.namespace import SPDX_NAMESPACE + + +def test_parse_checksum(): + graph = Graph().parse(os.path.join(os.path.dirname(__file__), + "data/file_to_test_rdf_parser.rdf.xml")) + checksum_node = graph.value(subject=URIRef("https://some.namespace#DocumentRef-external"), + predicate=SPDX_NAMESPACE.checksum) + + checksum = parse_checksum(checksum_node, graph) + + assert checksum.algorithm == ChecksumAlgorithm.SHA1 + assert checksum.value == "71c4025dd9897b364f3ebbb42c484ff43d00791c" + + +@pytest.mark.parametrize("rdf_element,expected", [(SPDX_NAMESPACE.checksumAlgorithm_sha1, ChecksumAlgorithm.SHA1), + (SPDX_NAMESPACE.checksumAlgorithm_sha224, ChecksumAlgorithm.SHA224), + (SPDX_NAMESPACE.checksumAlgorithm_sha256, ChecksumAlgorithm.SHA256), + (SPDX_NAMESPACE.checksumAlgorithm_sha384, ChecksumAlgorithm.SHA384), + (SPDX_NAMESPACE.checksumAlgorithm_sha512, ChecksumAlgorithm.SHA512), + (SPDX_NAMESPACE.checksumAlgorithm_sha3_256, + ChecksumAlgorithm.SHA3_256), + (SPDX_NAMESPACE.checksumAlgorithm_sha3_384, + ChecksumAlgorithm.SHA3_384), + (SPDX_NAMESPACE.checksumAlgorithm_sha3_512, + ChecksumAlgorithm.SHA3_512), + (SPDX_NAMESPACE.checksumAlgorithm_blake2b256, + ChecksumAlgorithm.BLAKE2B_256), + (SPDX_NAMESPACE.checksumAlgorithm_blake2b384, + ChecksumAlgorithm.BLAKE2B_384), + (SPDX_NAMESPACE.checksumAlgorithm_blake2b512, + ChecksumAlgorithm.BLAKE2B_512), + (SPDX_NAMESPACE.checksumAlgorithm_blake3, ChecksumAlgorithm.BLAKE3), + (SPDX_NAMESPACE.checksumAlgorithm_md2, ChecksumAlgorithm.MD2), + (SPDX_NAMESPACE.checksumAlgorithm_md4, ChecksumAlgorithm.MD4), + (SPDX_NAMESPACE.checksumAlgorithm_md5, ChecksumAlgorithm.MD5), + (SPDX_NAMESPACE.checksumAlgorithm_md6, ChecksumAlgorithm.MD6), + (SPDX_NAMESPACE.checksumAlgorithm_adler32, ChecksumAlgorithm.ADLER32) + ]) +def test_convert_rdf_to_algorithm(rdf_element, expected): + algorithm = convert_rdf_to_algorithm(rdf_element) + + assert algorithm == expected + + +@pytest.mark.parametrize("invalid_rdf_element", + [SPDX_NAMESPACE.checksumAlgorithm_blake2b_512, SPDX_NAMESPACE.checksumAlgorithm_BLAKE2b_512, + SPDX_NAMESPACE.checksumAlgorithm_sha3512, SPDX_NAMESPACE.checksumAlgorithm_sha513, + SPDX_NAMESPACE.checksumalgorithm_blake2b_512]) +def test_convert_invalid_rdf_to_algorithm(invalid_rdf_element): + with pytest.raises(SPDXParsingError): + convert_rdf_to_algorithm(invalid_rdf_element) From 01e9aa299dc62d7df68e9d897aae3274b8c3bbd1 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 2 Feb 2023 09:30:44 +0100 Subject: [PATCH 238/362] [issue-456] add external document ref parser Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/creation_info_parser.py | 27 ++++++++++++++----- .../parser/rdf/test_creation_info_parser.py | 18 ++++++++++++- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/spdx/parser/rdf/creation_info_parser.py b/src/spdx/parser/rdf/creation_info_parser.py index c652d22ca..b4debc7ff 100644 --- a/src/spdx/parser/rdf/creation_info_parser.py +++ b/src/spdx/parser/rdf/creation_info_parser.py @@ -19,7 +19,8 @@ from spdx.parser.error import SPDXParsingError from spdx.parser.logger import Logger from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages -from spdx.parser.rdf.graph_parsing_functions import parse_literal +from spdx.parser.rdf.checksum_parser import parse_checksum +from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id from spdx.rdfschema.namespace import SPDX_NAMESPACE, LICENSE_NAMESPACE from spdx.datetime_conversions import datetime_from_str @@ -51,8 +52,8 @@ def parse_creation_info(graph: Graph) -> Tuple[CreationInfo, URIRef]: for (_, _, creator_literal) in graph.triples((creation_info_node, SPDX_NAMESPACE.creator, None)): creators.append(ActorParser.parse_actor(creator_literal)) external_document_refs = [] - for (_, _, external_document_node) in graph.triples((parent_node, SPDX_NAMESPACE.externalDocumentRef, None)): - external_document_refs.append(parse_external_document_refs(external_document_node)) + for (_, _, external_document_node) in graph.triples((doc_node, SPDX_NAMESPACE.externalDocumentRef, None)): + external_document_refs.append(parse_external_document_refs(external_document_node, graph, namespace)) raise_parsing_error_if_logger_has_messages(logger, "CreationInfo") creation_info = construct_or_raise_parsing_error(CreationInfo, dict(spdx_id=spdx_id, document_namespace=namespace, @@ -61,8 +62,9 @@ def parse_creation_info(graph: Graph) -> Tuple[CreationInfo, URIRef]: document_comment=comment, created=created, license_list_version=license_list_version, creator_comment=creator_comment, - creators=creators)) - return creation_info + creators=creators, + external_document_refs=external_document_refs)) + return creation_info, doc_node def parse_namespace_and_spdx_id(graph: Graph) -> (str, str): @@ -89,5 +91,16 @@ def parse_namespace_and_spdx_id(graph: Graph) -> (str, str): return namespace, spdx_id, subject -def parse_external_document_refs(external_document_node: Node) -> ExternalDocumentRef: - pass +def parse_external_document_refs(external_document_node: URIRef, graph: Graph, + doc_namespace: str) -> ExternalDocumentRef: + logger = Logger() + document_ref_id = parse_spdx_id(external_document_node, doc_namespace) + document_uri = parse_literal(logger, graph, external_document_node, SPDX_NAMESPACE.spdxDocument) + checksum = None + for (_, _, checksum_node) in graph.triples((external_document_node, SPDX_NAMESPACE.checksum, None)): + checksum = parse_checksum(checksum_node, graph) + external_document_ref = construct_or_raise_parsing_error(ExternalDocumentRef, dict(document_ref_id=document_ref_id, + document_uri=document_uri, + checksum=checksum)) + + return external_document_ref diff --git a/tests/spdx/parser/rdf/test_creation_info_parser.py b/tests/spdx/parser/rdf/test_creation_info_parser.py index dae099a0b..516b0ffdf 100644 --- a/tests/spdx/parser/rdf/test_creation_info_parser.py +++ b/tests/spdx/parser/rdf/test_creation_info_parser.py @@ -15,12 +15,14 @@ import pytest from rdflib import Graph, RDF, URIRef from rdflib.term import Node +from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.model.version import Version from spdx.model.actor import Actor, ActorType -from spdx.parser.rdf.creation_info_parser import parse_creation_info, parse_namespace_and_spdx_id +from spdx.parser.rdf.creation_info_parser import parse_creation_info, parse_namespace_and_spdx_id, \ + parse_external_document_refs from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -65,3 +67,17 @@ def test_parse_namespace_and_spdx_id_with_system_exit(triples: List[Tuple[Node, with pytest.raises(SystemExit, match=error_message): parse_namespace_and_spdx_id(graph) + + +def test_parse_external_document_refs(): + graph = Graph().parse(os.path.join(os.path.dirname(__file__), "data/file_to_test_rdf_parser.rdf.xml")) + doc_namespace = "https://some.namespace" + external_doc_ref_node = graph.value(subject=URIRef(f"{doc_namespace}#SPDXRef-DOCUMENT"), + predicate=SPDX_NAMESPACE.externalDocumentRef) + + external_document_ref = parse_external_document_refs(external_doc_ref_node, graph, doc_namespace) + + assert external_document_ref.document_ref_id == "DocumentRef-external" + assert external_document_ref.checksum == Checksum(ChecksumAlgorithm.SHA1, + "71c4025dd9897b364f3ebbb42c484ff43d00791c") + assert external_document_ref.document_uri == "https://namespace.com" From 8d38c3fe49bb8554436828814fc69d3c7314dada Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 2 Feb 2023 12:07:19 +0100 Subject: [PATCH 239/362] [issue-456] add file parser Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/file_parser.py | 61 +++++++++++++++++++++++ src/spdx/parser/rdf/rdf_parser.py | 11 +++- tests/spdx/parser/rdf/test_file_parser.py | 58 +++++++++++++++++++++ tests/spdx/parser/rdf/test_rdf_parser.py | 1 + 4 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 src/spdx/parser/rdf/file_parser.py create mode 100644 tests/spdx/parser/rdf/test_file_parser.py diff --git a/src/spdx/parser/rdf/file_parser.py b/src/spdx/parser/rdf/file_parser.py new file mode 100644 index 000000000..02c2c335f --- /dev/null +++ b/src/spdx/parser/rdf/file_parser.py @@ -0,0 +1,61 @@ +# Copyright (c) 2023 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 rdflib import URIRef, Graph, RDFS + +from spdx.model.file import File, FileType +from spdx.parser.logger import Logger +from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages +from spdx.parser.rdf.checksum_parser import parse_checksum +from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, str_to_no_assertion_or_none +from spdx.rdfschema.namespace import SPDX_NAMESPACE + + +def parse_file(file_node: URIRef, graph: Graph, doc_namespace: str) -> File: + logger = Logger() + spdx_id = parse_spdx_id(file_node, doc_namespace) + name = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.fileName) + checksums = [] + for (_,_,checksum_node) in graph.triples((file_node, SPDX_NAMESPACE.checksum, None)): + checksums.append(parse_checksum(checksum_node, graph)) + + file_types = [] + for (_,_,file_type_ref) in graph.triples((file_node, SPDX_NAMESPACE.fileType, None)): + try: + file_types.append(convert_uri_ref_to_file_type(file_type_ref)) + except KeyError: + logger.append(f"Invalid FileType: {file_type_ref}") + license_comment = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.licenseComments) + copyright_text = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.copyrightText, + method_to_apply=str_to_no_assertion_or_none) + file_contributors = [] + for (_,_,file_contributor) in graph.triples((file_node, SPDX_NAMESPACE.fileContributor,None)): + file_contributors.append(file_contributor.toPython()) + + notice_text = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.noticeText) + comment = parse_literal(logger, graph, file_node, RDFS.comment) + attribution_texts = [] + for (_, _, attribution_text_literal) in graph.triples((file_node, SPDX_NAMESPACE.attributionText, None)): + attribution_texts.append(attribution_text_literal.toPython()) + raise_parsing_error_if_logger_has_messages(logger, "File") + file = construct_or_raise_parsing_error(File, dict(name=name, spdx_id=spdx_id, checksums=checksums, + attribution_texts=attribution_texts, comment=comment, + copyright_text=copyright_text, file_type=file_types, + contributors=file_contributors, + license_comment=license_comment, + license_concluded=None, + license_info_in_file=None, + notice=notice_text)) + return file + + +def convert_uri_ref_to_file_type(file_type_ref: URIRef) -> FileType: + file_type = file_type_ref.removeprefix(SPDX_NAMESPACE).replace("fileType_", "").upper() + return FileType[file_type] diff --git a/src/spdx/parser/rdf/rdf_parser.py b/src/spdx/parser/rdf/rdf_parser.py index eed65f524..e2eeb1a64 100644 --- a/src/spdx/parser/rdf/rdf_parser.py +++ b/src/spdx/parser/rdf/rdf_parser.py @@ -15,6 +15,7 @@ from spdx.parser.logger import Logger from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.rdf.creation_info_parser import parse_creation_info +from spdx.parser.rdf.file_parser import parse_file from spdx.parser.rdf.snippet_parser import parse_snippet from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -36,6 +37,13 @@ def translate_graph_to_document(graph: Graph) -> Document: logger.extend(err.get_messages()) creation_info = None + files = [] + for (file_node, _, _) in graph.triples((None, RDF.type, SPDX_NAMESPACE.File)): + try: + files.append(parse_file(file_node, graph, creation_info.document_namespace)) + except SPDXParsingError as err: + logger.extend(err.get_messages()) + snippets = [] for (snippet_node, _, _) in graph.triples((None, RDF.type, SPDX_NAMESPACE.Snippet)): try: @@ -44,5 +52,6 @@ def translate_graph_to_document(graph: Graph) -> Document: logger.extend(err.get_messages()) raise_parsing_error_if_logger_has_messages(logger) - document = construct_or_raise_parsing_error(Document, dict(creation_info=creation_info, snippets=snippets)) + document = construct_or_raise_parsing_error(Document, + dict(creation_info=creation_info, snippets=snippets, files=files)) return document diff --git a/tests/spdx/parser/rdf/test_file_parser.py b/tests/spdx/parser/rdf/test_file_parser.py new file mode 100644 index 000000000..e2bd9ffd1 --- /dev/null +++ b/tests/spdx/parser/rdf/test_file_parser.py @@ -0,0 +1,58 @@ +# Copyright (c) 2023 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. +import os + +import pytest +from rdflib import Graph, RDF +from spdx.model.checksum import Checksum, ChecksumAlgorithm + +from spdx.model.file import FileType +from spdx.parser.rdf.file_parser import convert_uri_ref_to_file_type, parse_file +from spdx.rdfschema.namespace import SPDX_NAMESPACE + + +def test_parse_file(): + graph = Graph().parse(os.path.join(os.path.dirname(__file__), "data/file_to_test_rdf_parser.rdf.xml")) + file_node = graph.value(predicate=RDF.type, object=SPDX_NAMESPACE.File) + doc_namespace = "https://some.namespace" + + file = parse_file(file_node, graph, doc_namespace) + + assert file.name == "./fileName.py" + assert file.spdx_id == "SPDXRef-File" + assert file.checksums == [Checksum(ChecksumAlgorithm.SHA1, "71c4025dd9897b364f3ebbb42c484ff43d00791c")] + assert file.file_type == [FileType.TEXT] + assert file.comment == "fileComment" + assert file.copyright_text == "copyrightText" + assert file.contributors == ["fileContributor"] + assert file.license_comment == "licenseComment" + assert file.notice == "fileNotice" + assert file.attribution_texts == ["fileAttributionText"] + +@pytest.mark.parametrize("uri_ref,expected", [(SPDX_NAMESPACE.fileType_source, FileType.SOURCE), + (SPDX_NAMESPACE.fileType_binary, FileType.BINARY), + (SPDX_NAMESPACE.fileType_archive, FileType.ARCHIVE), + (SPDX_NAMESPACE.fileType_application, FileType.APPLICATION), + (SPDX_NAMESPACE.fileType_audio, FileType.AUDIO), + (SPDX_NAMESPACE.fileType_image, FileType.IMAGE), + (SPDX_NAMESPACE.fileType_text, FileType.TEXT), + (SPDX_NAMESPACE.fileType_video, FileType.VIDEO), + (SPDX_NAMESPACE.fileType_documentation, FileType.DOCUMENTATION), + (SPDX_NAMESPACE.fileType_spdx, FileType.SPDX), + (SPDX_NAMESPACE.fileType_other, FileType.OTHER)]) +def test_convert_uri_ref_to_file_type(uri_ref, expected): + file_type = convert_uri_ref_to_file_type(uri_ref) + + assert file_type == expected + +def test_convert_uri_ref_to_file_type_error(): + with pytest.raises(KeyError): + convert_uri_ref_to_file_type(SPDX_NAMESPACE.filetype_SPDX) diff --git a/tests/spdx/parser/rdf/test_rdf_parser.py b/tests/spdx/parser/rdf/test_rdf_parser.py index 9186612ba..8ee85e97a 100644 --- a/tests/spdx/parser/rdf/test_rdf_parser.py +++ b/tests/spdx/parser/rdf/test_rdf_parser.py @@ -28,3 +28,4 @@ def test_rdf_parser_with_2_3_example(): assert type(doc) == Document assert len(doc.snippets) == 1 + assert len(doc.files) == 5 From cf512022f79b129421c0fba7b7acd43259f6d7e8 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 2 Feb 2023 13:11:40 +0100 Subject: [PATCH 240/362] [issue-456] add annotation parser Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/annotation_parser.py | 43 +++++++++++++++++++ src/spdx/parser/rdf/rdf_parser.py | 8 +++- .../spdx/parser/rdf/test_annotation_parser.py | 34 +++++++++++++++ tests/spdx/parser/rdf/test_rdf_parser.py | 1 + 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/spdx/parser/rdf/annotation_parser.py create mode 100644 tests/spdx/parser/rdf/test_annotation_parser.py diff --git a/src/spdx/parser/rdf/annotation_parser.py b/src/spdx/parser/rdf/annotation_parser.py new file mode 100644 index 000000000..c686f3064 --- /dev/null +++ b/src/spdx/parser/rdf/annotation_parser.py @@ -0,0 +1,43 @@ +# Copyright (c) 2023 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 rdflib import URIRef, Graph, RDFS + +from spdx.datetime_conversions import datetime_from_str +from spdx.model.annotation import Annotation, AnnotationType +from spdx.parser.jsonlikedict.actor_parser import ActorParser +from spdx.parser.logger import Logger +from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error +from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id +from spdx.rdfschema.namespace import SPDX_NAMESPACE + + +def parse_annotation(annotation_node: URIRef, graph: Graph, parent_ref: URIRef, doc_namespace) -> Annotation: + logger = Logger() + spdx_id = parse_spdx_id(parent_ref, doc_namespace) + annotator = parse_literal(logger, graph, annotation_node, SPDX_NAMESPACE.annotator, + method_to_apply=ActorParser.parse_actor) + annotation_type = graph.value(annotation_node, SPDX_NAMESPACE.annotationType) + if annotation_type: + annotation_type = annotation_type.removeprefix(SPDX_NAMESPACE).replace("annotationType_", "").upper() + try: + annotation_type = AnnotationType[annotation_type] + except KeyError: + logger.append(f"Invalid AnnotationType: {annotation_type}") + annotation_date = parse_literal(logger, graph, annotation_node, SPDX_NAMESPACE.annotationDate, + method_to_apply=datetime_from_str) + annotation_comment = parse_literal(logger, graph, annotation_node, RDFS.comment) + + raise_parsing_error_if_logger_has_messages(logger, "Annotation") + annotation = construct_or_raise_parsing_error(Annotation, dict(spdx_id=spdx_id, annotation_type=annotation_type, + annotator=annotator, annotation_date=annotation_date, + annotation_comment=annotation_comment)) + + return annotation diff --git a/src/spdx/parser/rdf/rdf_parser.py b/src/spdx/parser/rdf/rdf_parser.py index e2eeb1a64..80dcec7ff 100644 --- a/src/spdx/parser/rdf/rdf_parser.py +++ b/src/spdx/parser/rdf/rdf_parser.py @@ -14,6 +14,7 @@ from spdx.parser.error import SPDXParsingError from spdx.parser.logger import Logger from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages +from spdx.parser.rdf.annotation_parser import parse_annotation from spdx.parser.rdf.creation_info_parser import parse_creation_info from spdx.parser.rdf.file_parser import parse_file from spdx.parser.rdf.snippet_parser import parse_snippet @@ -51,7 +52,12 @@ def translate_graph_to_document(graph: Graph) -> Document: except SPDXParsingError as err: logger.extend(err.get_messages()) + annotations = [] + for (parent_node, _, annotation_node) in graph.triples((None, SPDX_NAMESPACE.annotation, None)): + annotations.append(parse_annotation(annotation_node, graph, parent_node, creation_info.document_namespace)) + raise_parsing_error_if_logger_has_messages(logger) document = construct_or_raise_parsing_error(Document, - dict(creation_info=creation_info, snippets=snippets, files=files)) + dict(creation_info=creation_info, snippets=snippets, files=files, + annotations=annotations)) return document diff --git a/tests/spdx/parser/rdf/test_annotation_parser.py b/tests/spdx/parser/rdf/test_annotation_parser.py new file mode 100644 index 000000000..d0b6068c2 --- /dev/null +++ b/tests/spdx/parser/rdf/test_annotation_parser.py @@ -0,0 +1,34 @@ +# Copyright (c) 2023 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. +import os +from datetime import datetime + +from rdflib import Graph, URIRef + +from spdx.model.actor import Actor, ActorType +from spdx.model.annotation import AnnotationType +from spdx.parser.rdf.annotation_parser import parse_annotation +from spdx.rdfschema.namespace import SPDX_NAMESPACE + + +def test_parse_annotation(): + graph = Graph().parse(os.path.join(os.path.dirname(__file__), "data/file_to_test_rdf_parser.rdf.xml")) + doc_namespace = "https://some.namespace" + file_node = URIRef(f"{doc_namespace}#SPDXRef-File") + annotation_node = graph.value(subject=file_node, predicate=SPDX_NAMESPACE.annotation) + + annotation = parse_annotation(annotation_node, graph, file_node, doc_namespace) + + assert annotation.spdx_id == "SPDXRef-File" + assert annotation.annotation_type == AnnotationType.REVIEW + assert annotation.annotator == Actor(ActorType.PERSON, "annotatorName", "some@mail.com") + assert annotation.annotation_date == datetime(2022, 12, 1, 0, 0) + assert annotation.annotation_comment == "annotationComment" diff --git a/tests/spdx/parser/rdf/test_rdf_parser.py b/tests/spdx/parser/rdf/test_rdf_parser.py index 8ee85e97a..96b86ca19 100644 --- a/tests/spdx/parser/rdf/test_rdf_parser.py +++ b/tests/spdx/parser/rdf/test_rdf_parser.py @@ -29,3 +29,4 @@ def test_rdf_parser_with_2_3_example(): assert type(doc) == Document assert len(doc.snippets) == 1 assert len(doc.files) == 5 + assert len(doc.annotations) == 5 From 4957223235d133f6b4c9ede44a4e4343d48f497c Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 2 Feb 2023 14:29:42 +0100 Subject: [PATCH 241/362] [issue-456] add package parser Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/package_parser.py | 117 +++++++++++++++++++ src/spdx/parser/rdf/rdf_parser.py | 10 +- tests/spdx/parser/rdf/test_package_parser.py | 46 ++++++++ tests/spdx/parser/rdf/test_rdf_parser.py | 1 + 4 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 src/spdx/parser/rdf/package_parser.py create mode 100644 tests/spdx/parser/rdf/test_package_parser.py diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py new file mode 100644 index 000000000..96a7f1b3b --- /dev/null +++ b/src/spdx/parser/rdf/package_parser.py @@ -0,0 +1,117 @@ +# Copyright (c) 2023 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 typing import Optional, Union + +from rdflib import URIRef, Graph, RDFS, DOAP +from rdflib.exceptions import UniquenessError + +from spdx.datetime_conversions import datetime_from_str +from spdx.model.actor import Actor +from spdx.model.package import Package, PackagePurpose +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.parser.error import SPDXParsingError +from spdx.parser.jsonlikedict.actor_parser import ActorParser +from spdx.parser.logger import Logger +from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error +from spdx.parser.rdf.checksum_parser import parse_checksum +from spdx.parser.rdf.graph_parsing_functions import parse_spdx_id, parse_literal, str_to_no_assertion_or_none +from spdx.rdfschema.namespace import SPDX_NAMESPACE + + +def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Package: + logger = Logger() + spdx_id = parse_spdx_id(package_node, doc_namespace) + name = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.name) + download_location = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.downloadLocation) + checksums = [] + for (_, _, checksum_node) in graph.triples((package_node, SPDX_NAMESPACE.checksum, None)): + checksums.append(parse_checksum(checksum_node, graph)) + + version_info = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.versionInfo) + package_file_name = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.packageFileName) + try: + supplier = parse_actor_or_no_assertion(logger, graph, package_node, SPDX_NAMESPACE.supplier) + except SPDXParsingError as err: + logger.extend(err.get_messages()) + supplier = None + try: + originator = parse_actor_or_no_assertion(logger, graph, package_node, SPDX_NAMESPACE.originator) + except SPDXParsingError as err: + logger.extend(err.get_messages()) + originator = None + + files_analyzed = bool(graph.value(package_node, SPDX_NAMESPACE.filesAnalyzed, default=True)) + license_comment = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.licenseComments) + comment = parse_literal(logger, graph, package_node, RDFS.comment) + summary = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.summary) + description = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.description) + copyright_text = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.copyrightText, + method_to_apply=str_to_no_assertion_or_none) + source_info = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.sourceInfo) + try: + primary_package_purpose = parse_primary_package_purpose(package_node, graph) + except KeyError: + logger.append(f"Invalid PackagePurpose: {graph.value(package_node, SPDX_NAMESPACE.primaryPackagePurpose)}") + primary_package_purpose = None + homepage = parse_literal(logger, graph, package_node, DOAP.homepage) + attribution_texts = [] + for (_, _, attribution_text_literal) in graph.triples((package_node, SPDX_NAMESPACE.attributionText, None)): + attribution_texts.append(attribution_text_literal.toPython()) + + release_date = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.releaseDate, + method_to_apply=datetime_from_str) + built_date = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.builtDate, + method_to_apply=datetime_from_str) + valid_until_date = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.validUntilDate, + method_to_apply=datetime_from_str) + + raise_parsing_error_if_logger_has_messages(logger, "Package") + package = construct_or_raise_parsing_error(Package, + dict(name=name, spdx_id=spdx_id, download_location=download_location, + version=version_info, file_name=package_file_name, + supplier=supplier, originator=originator, + files_analyzed=files_analyzed, + verification_code=None, + checksums=checksums, homepage=homepage, + source_info=source_info, + license_concluded=None, + license_info_from_files=None, + license_declared=None, + license_comment=license_comment, + copyright_text=copyright_text, summary=summary, + description=description, comment=comment, + external_references=None, + attribution_texts=attribution_texts, + primary_package_purpose=primary_package_purpose, + release_date=release_date, built_date=built_date, + valid_until_date=valid_until_date)) + + return package + + +def parse_actor_or_no_assertion(logger, graph, parent_node, predicate) -> Optional[Union[SpdxNoAssertion, Actor]]: + try: + value = graph.value(parent_node, predicate, any=False) + except UniquenessError: + logger.append(f"Multiple values for unique value {predicate} found.") + return + if not value: + return None + if value == "NOASSERTION": + return SpdxNoAssertion() + return ActorParser.parse_actor(value) + + +def parse_primary_package_purpose(package_node: URIRef, graph: Graph) -> Optional[PackagePurpose]: + primary_package_purpose_ref = graph.value(package_node, SPDX_NAMESPACE.primaryPackagePurpose) + if not primary_package_purpose_ref: + return None + return PackagePurpose[primary_package_purpose_ref.fragment.replace("purpose_", "").upper()] diff --git a/src/spdx/parser/rdf/rdf_parser.py b/src/spdx/parser/rdf/rdf_parser.py index 80dcec7ff..76343bebe 100644 --- a/src/spdx/parser/rdf/rdf_parser.py +++ b/src/spdx/parser/rdf/rdf_parser.py @@ -17,6 +17,7 @@ from spdx.parser.rdf.annotation_parser import parse_annotation from spdx.parser.rdf.creation_info_parser import parse_creation_info from spdx.parser.rdf.file_parser import parse_file +from spdx.parser.rdf.package_parser import parse_package from spdx.parser.rdf.snippet_parser import parse_snippet from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -38,6 +39,13 @@ def translate_graph_to_document(graph: Graph) -> Document: logger.extend(err.get_messages()) creation_info = None + packages = [] + for (package_node, _, _) in graph.triples((None, RDF.type, SPDX_NAMESPACE.Package)): + try: + packages.append(parse_package(package_node, graph, creation_info.document_namespace)) + except SPDXParsingError as err: + logger.extend(err.get_messages()) + files = [] for (file_node, _, _) in graph.triples((None, RDF.type, SPDX_NAMESPACE.File)): try: @@ -59,5 +67,5 @@ def translate_graph_to_document(graph: Graph) -> Document: raise_parsing_error_if_logger_has_messages(logger) document = construct_or_raise_parsing_error(Document, dict(creation_info=creation_info, snippets=snippets, files=files, - annotations=annotations)) + annotations=annotations, packages=packages)) return document diff --git a/tests/spdx/parser/rdf/test_package_parser.py b/tests/spdx/parser/rdf/test_package_parser.py new file mode 100644 index 000000000..7b5a9469d --- /dev/null +++ b/tests/spdx/parser/rdf/test_package_parser.py @@ -0,0 +1,46 @@ +# Copyright (c) 2023 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. +import os + +from rdflib import RDF, Graph + +from spdx.model.actor import Actor, ActorType +from spdx.model.checksum import ChecksumAlgorithm, Checksum +from spdx.model.package import PackagePurpose +from spdx.parser.rdf.package_parser import parse_package +from spdx.rdfschema.namespace import SPDX_NAMESPACE + + +def test_package_parser(): + graph = Graph().parse(os.path.join(os.path.dirname(__file__), "data/file_to_test_rdf_parser.rdf.xml")) + package_node = graph.value(predicate=RDF.type, object=SPDX_NAMESPACE.Package) + doc_namespace = "https://some.namespace" + + package = parse_package(package_node, graph, doc_namespace) + + assert package.spdx_id == "SPDXRef-Package" + assert package.name == "packageName" + assert package.download_location == "https://download.com" + assert package.version == "12.2" + assert package.file_name == "./packageFileName" + assert package.homepage == "https://homepage.com" + assert package.files_analyzed == True + assert package.checksums == [Checksum(ChecksumAlgorithm.SHA1, "71c4025dd9897b364f3ebbb42c484ff43d00791c")] + assert package.source_info == "sourceInfo" + assert package.license_comment == "packageLicenseComment" + assert package.copyright_text == "packageCopyrightText" + assert package.summary == "packageSummary" + assert package.description == "packageDescription" + assert package.comment == "packageComment" + assert package.attribution_texts == ["packageAttributionText"] + assert package.primary_package_purpose == PackagePurpose.SOURCE + assert package.supplier == Actor(ActorType.PERSON, "supplierName", "some@mail.com") + assert package.originator == Actor(ActorType.PERSON, "originatorName", "some@mail.com") diff --git a/tests/spdx/parser/rdf/test_rdf_parser.py b/tests/spdx/parser/rdf/test_rdf_parser.py index 96b86ca19..84bf23ab2 100644 --- a/tests/spdx/parser/rdf/test_rdf_parser.py +++ b/tests/spdx/parser/rdf/test_rdf_parser.py @@ -30,3 +30,4 @@ def test_rdf_parser_with_2_3_example(): assert len(doc.snippets) == 1 assert len(doc.files) == 5 assert len(doc.annotations) == 5 + assert len(doc.packages) == 4 From 9ffee3013bba76a60e19f1d0281dbbe4278090dc Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 8 Feb 2023 09:18:44 +0100 Subject: [PATCH 242/362] [issue-456] add package verification code parser Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/package_parser.py | 34 ++++++++++++++++++-- tests/spdx/parser/rdf/test_package_parser.py | 4 ++- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index 96a7f1b3b..d39467157 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -15,7 +15,7 @@ from spdx.datetime_conversions import datetime_from_str from spdx.model.actor import Actor -from spdx.model.package import Package, PackagePurpose +from spdx.model.package import Package, PackagePurpose, ExternalPackageRef, PackageVerificationCode from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.parser.error import SPDXParsingError from spdx.parser.jsonlikedict.actor_parser import ActorParser @@ -47,7 +47,11 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac except SPDXParsingError as err: logger.extend(err.get_messages()) originator = None - + try: + verification_code = parse_package_verification_code(package_node, graph) + except SPDXParsingError as err: + logger.append(err.get_messages()) + verification_code = None files_analyzed = bool(graph.value(package_node, SPDX_NAMESPACE.filesAnalyzed, default=True)) license_comment = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.licenseComments) comment = parse_literal(logger, graph, package_node, RDFS.comment) @@ -79,7 +83,7 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac version=version_info, file_name=package_file_name, supplier=supplier, originator=originator, files_analyzed=files_analyzed, - verification_code=None, + verification_code=verification_code, checksums=checksums, homepage=homepage, source_info=source_info, license_concluded=None, @@ -115,3 +119,27 @@ def parse_primary_package_purpose(package_node: URIRef, graph: Graph) -> Optiona if not primary_package_purpose_ref: return None return PackagePurpose[primary_package_purpose_ref.fragment.replace("purpose_", "").upper()] + + +def parse_package_verification_code(package_node: URIRef, graph: Graph) -> Optional[PackageVerificationCode]: + try: + package_node = graph.value(package_node, SPDX_NAMESPACE.packageVerificationCode, any=False) + except UniquenessError: + raise SPDXParsingError([f"Multiple values for unique value {SPDX_NAMESPACE.packageVerificationCode} found."]) + if not package_node: + return None + logger = Logger() + value = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.packageVerificationCodeValue) + excluded_files = [] + for (_, _, excluded_file_literal) in graph.triples( + (package_node, SPDX_NAMESPACE.packageVerificationCodeExcludedFile, None)): + excluded_files.append(excluded_file_literal.toPython()) + + raise_parsing_error_if_logger_has_messages(logger, "PackageVerificationCode") + package_verification_code = construct_or_raise_parsing_error(PackageVerificationCode, dict(value=value, + excluded_files=excluded_files)) + return package_verification_code + + +def parse_external_package_ref() -> ExternalPackageRef: + pass diff --git a/tests/spdx/parser/rdf/test_package_parser.py b/tests/spdx/parser/rdf/test_package_parser.py index 7b5a9469d..9f571e50d 100644 --- a/tests/spdx/parser/rdf/test_package_parser.py +++ b/tests/spdx/parser/rdf/test_package_parser.py @@ -14,7 +14,7 @@ from spdx.model.actor import Actor, ActorType from spdx.model.checksum import ChecksumAlgorithm, Checksum -from spdx.model.package import PackagePurpose +from spdx.model.package import PackagePurpose, PackageVerificationCode from spdx.parser.rdf.package_parser import parse_package from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -37,6 +37,8 @@ def test_package_parser(): assert package.source_info == "sourceInfo" assert package.license_comment == "packageLicenseComment" assert package.copyright_text == "packageCopyrightText" + assert package.verification_code == PackageVerificationCode(value="85ed0817af83a24ad8da68c2b5094de69833983c", + excluded_files=["./exclude.py"]) assert package.summary == "packageSummary" assert package.description == "packageDescription" assert package.comment == "packageComment" From a03f08356b147418a3e62028da28342eb6b6c7f6 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 8 Feb 2023 11:36:05 +0100 Subject: [PATCH 243/362] [issue-456] add helper method in casing_tools and move the module as it is used in the parser and writer layer Signed-off-by: Meret Behrens --- src/spdx/{writer => }/casing_tools.py | 5 ++++ src/spdx/jsonschema/converter.py | 2 +- src/spdx/writer/rdf/annotation_writer.py | 2 +- src/spdx/writer/rdf/file_writer.py | 2 +- src/spdx/writer/rdf/package_writer.py | 2 +- src/spdx/writer/rdf/relationship_writer.py | 2 +- tests/spdx/test_casing_tools.py | 29 ++++++++++++++++++++++ 7 files changed, 39 insertions(+), 5 deletions(-) rename src/spdx/{writer => }/casing_tools.py (82%) create mode 100644 tests/spdx/test_casing_tools.py diff --git a/src/spdx/writer/casing_tools.py b/src/spdx/casing_tools.py similarity index 82% rename from src/spdx/writer/casing_tools.py rename to src/spdx/casing_tools.py index b14543093..fdbc07f15 100644 --- a/src/spdx/writer/casing_tools.py +++ b/src/spdx/casing_tools.py @@ -14,3 +14,8 @@ def snake_case_to_camel_case(snake_case_string: str) -> str: each_word_capitalized = sub(r"[_\-]+", " ", snake_case_string).title().replace(" ", "") return each_word_capitalized[0].lower() + each_word_capitalized[1:] + + +def camel_case_to_snake_case(camel_case_string: str) -> str: + snake_case_string = sub("(?!^)([A-Z]+)", r"_\1", camel_case_string).lower() + return snake_case_string diff --git a/src/spdx/jsonschema/converter.py b/src/spdx/jsonschema/converter.py index e6f362568..4fddd4e1d 100644 --- a/src/spdx/jsonschema/converter.py +++ b/src/spdx/jsonschema/converter.py @@ -13,7 +13,7 @@ from spdx.jsonschema.json_property import JsonProperty from spdx.model.document import Document -from spdx.writer.casing_tools import snake_case_to_camel_case +from spdx.casing_tools import snake_case_to_camel_case MISSING_IMPLEMENTATION_MESSAGE = "Must be implemented" diff --git a/src/spdx/writer/rdf/annotation_writer.py b/src/spdx/writer/rdf/annotation_writer.py index 56b272329..e1e275d77 100644 --- a/src/spdx/writer/rdf/annotation_writer.py +++ b/src/spdx/writer/rdf/annotation_writer.py @@ -14,7 +14,7 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.model.annotation import Annotation -from spdx.writer.casing_tools import snake_case_to_camel_case +from spdx.casing_tools import snake_case_to_camel_case from spdx.writer.rdf.writer_utils import add_namespace_to_spdx_id from spdx.rdfschema.namespace import SPDX_NAMESPACE diff --git a/src/spdx/writer/rdf/file_writer.py b/src/spdx/writer/rdf/file_writer.py index 76d6209f4..79b82c26a 100644 --- a/src/spdx/writer/rdf/file_writer.py +++ b/src/spdx/writer/rdf/file_writer.py @@ -13,7 +13,7 @@ from rdflib import Graph, URIRef, Literal, RDF, RDFS from spdx.model.file import File -from spdx.writer.casing_tools import snake_case_to_camel_case +from spdx.casing_tools import snake_case_to_camel_case from spdx.writer.rdf.checksum_writer import add_checksum_to_graph from spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion from spdx.writer.rdf.writer_utils import add_optional_literal, add_namespace_to_spdx_id diff --git a/src/spdx/writer/rdf/package_writer.py b/src/spdx/writer/rdf/package_writer.py index 602c6992b..890cf8f12 100644 --- a/src/spdx/writer/rdf/package_writer.py +++ b/src/spdx/writer/rdf/package_writer.py @@ -13,7 +13,7 @@ from rdflib import Graph, URIRef, RDF, Literal, XSD, BNode, DOAP, RDFS from spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion -from spdx.writer.casing_tools import snake_case_to_camel_case +from spdx.casing_tools import snake_case_to_camel_case from spdx.writer.rdf.checksum_writer import add_checksum_to_graph from spdx.model.package import Package, PackageVerificationCode, ExternalPackageRef, \ diff --git a/src/spdx/writer/rdf/relationship_writer.py b/src/spdx/writer/rdf/relationship_writer.py index 5f7ab274a..66bc95e24 100644 --- a/src/spdx/writer/rdf/relationship_writer.py +++ b/src/spdx/writer/rdf/relationship_writer.py @@ -15,7 +15,7 @@ from spdx.model.relationship import Relationship from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone -from spdx.writer.casing_tools import snake_case_to_camel_case +from spdx.casing_tools import snake_case_to_camel_case from spdx.writer.rdf.writer_utils import add_namespace_to_spdx_id from spdx.rdfschema.namespace import SPDX_NAMESPACE diff --git a/tests/spdx/test_casing_tools.py b/tests/spdx/test_casing_tools.py new file mode 100644 index 000000000..c5be2beb3 --- /dev/null +++ b/tests/spdx/test_casing_tools.py @@ -0,0 +1,29 @@ +# Copyright (c) 2023 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. +import pytest + +from spdx.casing_tools import snake_case_to_camel_case, camel_case_to_snake_case + + +@pytest.mark.parametrize("snake_case_str,camel_case_str", [("snake_case", "snakeCase")]) +def test_snake_case_to_camel_case(snake_case_str, camel_case_str): + camel_case = snake_case_to_camel_case(snake_case_str) + + assert camel_case == camel_case_str + + +@pytest.mark.parametrize("camel_case_str,snake_case_str", + [("camelCase", "camel_case"), ("camelCaseMore", "camel_case_more"), + ("CamelCase", "camel_case")]) +def test_camel_case_to_snake_case(camel_case_str, snake_case_str): + snake_case = camel_case_to_snake_case(camel_case_str) + + assert snake_case == snake_case_str From 4d58389b6d2f3ba6f15fde9e9ba6e16610467bf6 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 8 Feb 2023 12:08:52 +0100 Subject: [PATCH 244/362] [issue-456] add parser for ExternalPackageRefs Signed-off-by: Meret Behrens --- .../parser/rdf/graph_parsing_functions.py | 18 +++++++++-- src/spdx/parser/rdf/package_parser.py | 31 ++++++++++++++----- tests/spdx/parser/rdf/test_package_parser.py | 19 ++++++++++-- 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/src/spdx/parser/rdf/graph_parsing_functions.py b/src/spdx/parser/rdf/graph_parsing_functions.py index 5ba1500d1..8ccb64a24 100644 --- a/src/spdx/parser/rdf/graph_parsing_functions.py +++ b/src/spdx/parser/rdf/graph_parsing_functions.py @@ -8,7 +8,8 @@ # 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 typing import Any, Callable, Union, Optional +from enum import Enum +from typing import Any, Callable, Union, Optional, Type from rdflib import Graph, URIRef from rdflib.exceptions import UniquenessError @@ -16,8 +17,10 @@ from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING from spdx.model.spdx_none import SpdxNone, SPDX_NONE_STRING +from spdx.parser.error import SPDXParsingError from spdx.parser.logger import Logger from spdx.rdfschema.namespace import SPDX_NAMESPACE +from spdx.casing_tools import camel_case_to_snake_case def parse_literal(logger: Logger, graph: Graph, subject: Node, predicate: Node, default: Any = None, @@ -29,10 +32,19 @@ def parse_literal(logger: Logger, graph: Graph, subject: Node, predicate: Node, return if value: - return method_to_apply(value.removeprefix(prefix)) - + try: + return method_to_apply(value.removeprefix(prefix)) + except SPDXParsingError as err: + logger.extend(err.get_messages()) + return default return default +def parse_enum_value(enum_str: str, enum_class: Type[Enum]) -> Enum: + try: + return enum_class[camel_case_to_snake_case(enum_str).upper()] + except KeyError: + raise SPDXParsingError([f"Invalid value for {enum_class}: {enum_str}"]) + def parse_literal_or_no_assertion_or_none(logger: Logger, graph: Graph, subject: Node, predicate: Node, default: Any = None, method_to_apply: Callable = lambda x: x): diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index d39467157..fca8a00e6 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -15,16 +15,17 @@ from spdx.datetime_conversions import datetime_from_str from spdx.model.actor import Actor -from spdx.model.package import Package, PackagePurpose, ExternalPackageRef, PackageVerificationCode +from spdx.model.package import Package, PackagePurpose, ExternalPackageRef, PackageVerificationCode, \ + ExternalPackageRefCategory from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.parser.error import SPDXParsingError from spdx.parser.jsonlikedict.actor_parser import ActorParser from spdx.parser.logger import Logger from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error from spdx.parser.rdf.checksum_parser import parse_checksum -from spdx.parser.rdf.graph_parsing_functions import parse_spdx_id, parse_literal, str_to_no_assertion_or_none -from spdx.rdfschema.namespace import SPDX_NAMESPACE - +from spdx.parser.rdf.graph_parsing_functions import parse_spdx_id, parse_literal, str_to_no_assertion_or_none, \ + parse_enum_value +from spdx.rdfschema.namespace import SPDX_NAMESPACE, REFERENCE_NAMESPACE def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Package: logger = Logger() @@ -52,6 +53,9 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac except SPDXParsingError as err: logger.append(err.get_messages()) verification_code = None + external_package_refs = [] + for (_, _, external_package_ref_node) in graph.triples((package_node, SPDX_NAMESPACE.externalRef, None)): + external_package_refs.append(parse_external_package_ref(external_package_ref_node, graph)) files_analyzed = bool(graph.value(package_node, SPDX_NAMESPACE.filesAnalyzed, default=True)) license_comment = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.licenseComments) comment = parse_literal(logger, graph, package_node, RDFS.comment) @@ -92,7 +96,7 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac license_comment=license_comment, copyright_text=copyright_text, summary=summary, description=description, comment=comment, - external_references=None, + external_references=external_package_refs, attribution_texts=attribution_texts, primary_package_purpose=primary_package_purpose, release_date=release_date, built_date=built_date, @@ -141,5 +145,18 @@ def parse_package_verification_code(package_node: URIRef, graph: Graph) -> Optio return package_verification_code -def parse_external_package_ref() -> ExternalPackageRef: - pass +def parse_external_package_ref(external_package_ref_node: URIRef, graph: Graph) -> ExternalPackageRef: + logger = Logger() + ref_locator = parse_literal(logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceLocator) + ref_category = parse_literal(logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceCategory, + prefix=SPDX_NAMESPACE.referenceCategory_, + method_to_apply=lambda x: parse_enum_value(x, ExternalPackageRefCategory)) + ref_type = parse_literal(logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceType, + prefix=REFERENCE_NAMESPACE) + comment = parse_literal(logger, graph, external_package_ref_node, RDFS.comment) + + raise_parsing_error_if_logger_has_messages(logger, "ExternalPackageRef") + external_package_ref = construct_or_raise_parsing_error(ExternalPackageRef, + dict(category=ref_category, reference_type=ref_type, + locator=ref_locator, comment=comment)) + return external_package_ref diff --git a/tests/spdx/parser/rdf/test_package_parser.py b/tests/spdx/parser/rdf/test_package_parser.py index 9f571e50d..1a8ffde8c 100644 --- a/tests/spdx/parser/rdf/test_package_parser.py +++ b/tests/spdx/parser/rdf/test_package_parser.py @@ -14,8 +14,8 @@ from spdx.model.actor import Actor, ActorType from spdx.model.checksum import ChecksumAlgorithm, Checksum -from spdx.model.package import PackagePurpose, PackageVerificationCode -from spdx.parser.rdf.package_parser import parse_package +from spdx.model.package import PackagePurpose, PackageVerificationCode, ExternalPackageRefCategory +from spdx.parser.rdf.package_parser import parse_package, parse_external_package_ref from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -39,6 +39,7 @@ def test_package_parser(): assert package.copyright_text == "packageCopyrightText" assert package.verification_code == PackageVerificationCode(value="85ed0817af83a24ad8da68c2b5094de69833983c", excluded_files=["./exclude.py"]) + assert len(package.external_references) == 1 assert package.summary == "packageSummary" assert package.description == "packageDescription" assert package.comment == "packageComment" @@ -46,3 +47,17 @@ def test_package_parser(): assert package.primary_package_purpose == PackagePurpose.SOURCE assert package.supplier == Actor(ActorType.PERSON, "supplierName", "some@mail.com") assert package.originator == Actor(ActorType.PERSON, "originatorName", "some@mail.com") + + +def test_external_package_ref_parser(): + + graph = Graph().parse(os.path.join(os.path.dirname(__file__), "data/file_to_test_rdf_parser.rdf.xml")) + package_node = graph.value(predicate=RDF.type, object=SPDX_NAMESPACE.Package) + external_package_ref_node = graph.value(package_node, SPDX_NAMESPACE.externalRef) + + external_package_ref = parse_external_package_ref(external_package_ref_node, graph) + + assert external_package_ref.category == ExternalPackageRefCategory.PACKAGE_MANAGER + assert external_package_ref.locator == "org.apache.tomcat:tomcat:9.0.0.M4" + assert external_package_ref.reference_type == "maven-central" + assert external_package_ref.comment == "externalPackageRefComment" From 43239b12b022f3a08361329e8d821f814ef22fc9 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 8 Feb 2023 13:09:27 +0100 Subject: [PATCH 245/362] [issue-456] use enum helper method for primary package purpose Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/package_parser.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index fca8a00e6..ea8dbbf3a 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -27,6 +27,7 @@ parse_enum_value from spdx.rdfschema.namespace import SPDX_NAMESPACE, REFERENCE_NAMESPACE + def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Package: logger = Logger() spdx_id = parse_spdx_id(package_node, doc_namespace) @@ -64,11 +65,9 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac copyright_text = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.copyrightText, method_to_apply=str_to_no_assertion_or_none) source_info = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.sourceInfo) - try: - primary_package_purpose = parse_primary_package_purpose(package_node, graph) - except KeyError: - logger.append(f"Invalid PackagePurpose: {graph.value(package_node, SPDX_NAMESPACE.primaryPackagePurpose)}") - primary_package_purpose = None + primary_package_purpose = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.primaryPackagePurpose, + prefix=SPDX_NAMESPACE.purpose_, + method_to_apply=lambda x: parse_enum_value(x, PackagePurpose)) homepage = parse_literal(logger, graph, package_node, DOAP.homepage) attribution_texts = [] for (_, _, attribution_text_literal) in graph.triples((package_node, SPDX_NAMESPACE.attributionText, None)): @@ -118,13 +117,6 @@ def parse_actor_or_no_assertion(logger, graph, parent_node, predicate) -> Option return ActorParser.parse_actor(value) -def parse_primary_package_purpose(package_node: URIRef, graph: Graph) -> Optional[PackagePurpose]: - primary_package_purpose_ref = graph.value(package_node, SPDX_NAMESPACE.primaryPackagePurpose) - if not primary_package_purpose_ref: - return None - return PackagePurpose[primary_package_purpose_ref.fragment.replace("purpose_", "").upper()] - - def parse_package_verification_code(package_node: URIRef, graph: Graph) -> Optional[PackageVerificationCode]: try: package_node = graph.value(package_node, SPDX_NAMESPACE.packageVerificationCode, any=False) From ade9eaeadcb6abf8b8d15d61ca5db7595fa22fb5 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 8 Feb 2023 13:14:48 +0100 Subject: [PATCH 246/362] [issue-456] use enum helper method for annotation type Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/annotation_parser.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/spdx/parser/rdf/annotation_parser.py b/src/spdx/parser/rdf/annotation_parser.py index c686f3064..c2995e21d 100644 --- a/src/spdx/parser/rdf/annotation_parser.py +++ b/src/spdx/parser/rdf/annotation_parser.py @@ -15,7 +15,7 @@ from spdx.parser.jsonlikedict.actor_parser import ActorParser from spdx.parser.logger import Logger from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error -from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id +from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, parse_enum_value from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -24,13 +24,9 @@ def parse_annotation(annotation_node: URIRef, graph: Graph, parent_ref: URIRef, spdx_id = parse_spdx_id(parent_ref, doc_namespace) annotator = parse_literal(logger, graph, annotation_node, SPDX_NAMESPACE.annotator, method_to_apply=ActorParser.parse_actor) - annotation_type = graph.value(annotation_node, SPDX_NAMESPACE.annotationType) - if annotation_type: - annotation_type = annotation_type.removeprefix(SPDX_NAMESPACE).replace("annotationType_", "").upper() - try: - annotation_type = AnnotationType[annotation_type] - except KeyError: - logger.append(f"Invalid AnnotationType: {annotation_type}") + annotation_type = parse_literal(logger, graph, annotation_node, SPDX_NAMESPACE.annotationType, + prefix=SPDX_NAMESPACE.annotationType_, + method_to_apply=lambda x: parse_enum_value(x, AnnotationType)) annotation_date = parse_literal(logger, graph, annotation_node, SPDX_NAMESPACE.annotationDate, method_to_apply=datetime_from_str) annotation_comment = parse_literal(logger, graph, annotation_node, RDFS.comment) From 82a0048dde0d9db6fab44be95f5c290cc55d5005 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 8 Feb 2023 13:29:31 +0100 Subject: [PATCH 247/362] [issue-456] use helper method for checksum algorithm Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/checksum_parser.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/spdx/parser/rdf/checksum_parser.py b/src/spdx/parser/rdf/checksum_parser.py index b851a6c6d..4864005c9 100644 --- a/src/spdx/parser/rdf/checksum_parser.py +++ b/src/spdx/parser/rdf/checksum_parser.py @@ -20,12 +20,8 @@ def parse_checksum(parent_node: URIRef, graph: Graph) -> Checksum: logger = Logger() - algorithm = graph.value(parent_node, SPDX_NAMESPACE.algorithm, default="") - try: - algorithm = convert_rdf_to_algorithm(algorithm) - except KeyError: - logger.append(f"Invalid ChecksumAlgorithm: {algorithm}") - algorithm = None + algorithm = parse_literal(logger, graph, parent_node, SPDX_NAMESPACE.algorithm, + method_to_apply=convert_rdf_to_algorithm) value = parse_literal(logger, graph, parent_node, SPDX_NAMESPACE.checksumValue) raise_parsing_error_if_logger_has_messages(logger, "Checksum") checksum = construct_or_raise_parsing_error(Checksum, dict(algorithm=algorithm, value=value)) From a40687dc7993326b63616c2daac2dbd1d1e65c41 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 8 Feb 2023 14:06:15 +0100 Subject: [PATCH 248/362] [issue-456] add relationship parser Signed-off-by: Meret Behrens --- .../parser/rdf/graph_parsing_functions.py | 2 +- src/spdx/parser/rdf/rdf_parser.py | 9 +++- src/spdx/parser/rdf/relationship_parser.py | 41 +++++++++++++++++++ .../rdf/data/file_to_test_rdf_parser.rdf.xml | 1 + tests/spdx/parser/rdf/test_rdf_parser.py | 2 + .../parser/rdf/test_relationship_parser.py | 31 ++++++++++++++ 6 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 src/spdx/parser/rdf/relationship_parser.py create mode 100644 tests/spdx/parser/rdf/test_relationship_parser.py diff --git a/src/spdx/parser/rdf/graph_parsing_functions.py b/src/spdx/parser/rdf/graph_parsing_functions.py index 8ccb64a24..e0d7bdebf 100644 --- a/src/spdx/parser/rdf/graph_parsing_functions.py +++ b/src/spdx/parser/rdf/graph_parsing_functions.py @@ -59,7 +59,7 @@ def parse_literal_or_no_assertion_or_none(logger: Logger, graph: Graph, subject: return SpdxNoAssertion() if value == SPDX_NAMESPACE.none: return SpdxNone() - return method_to_apply(value.toPython()) + return method_to_apply(value) def str_to_no_assertion_or_none(value: str) -> Union[str, SpdxNone, SpdxNoAssertion]: diff --git a/src/spdx/parser/rdf/rdf_parser.py b/src/spdx/parser/rdf/rdf_parser.py index 76343bebe..f8157a3ea 100644 --- a/src/spdx/parser/rdf/rdf_parser.py +++ b/src/spdx/parser/rdf/rdf_parser.py @@ -18,6 +18,7 @@ from spdx.parser.rdf.creation_info_parser import parse_creation_info from spdx.parser.rdf.file_parser import parse_file from spdx.parser.rdf.package_parser import parse_package +from spdx.parser.rdf.relationship_parser import parse_relationship from spdx.parser.rdf.snippet_parser import parse_snippet from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -64,8 +65,14 @@ def translate_graph_to_document(graph: Graph) -> Document: for (parent_node, _, annotation_node) in graph.triples((None, SPDX_NAMESPACE.annotation, None)): annotations.append(parse_annotation(annotation_node, graph, parent_node, creation_info.document_namespace)) + relationships = [] + for (parent_node, _, relationship_node) in graph.triples((None, SPDX_NAMESPACE.relationship, None)): + relationships.append( + parse_relationship(relationship_node, graph, parent_node, creation_info.document_namespace)) + raise_parsing_error_if_logger_has_messages(logger) document = construct_or_raise_parsing_error(Document, dict(creation_info=creation_info, snippets=snippets, files=files, - annotations=annotations, packages=packages)) + annotations=annotations, packages=packages, + relationships=relationships)) return document diff --git a/src/spdx/parser/rdf/relationship_parser.py b/src/spdx/parser/rdf/relationship_parser.py new file mode 100644 index 000000000..3706104b3 --- /dev/null +++ b/src/spdx/parser/rdf/relationship_parser.py @@ -0,0 +1,41 @@ +# Copyright (c) 2023 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 rdflib import URIRef, Graph, RDFS + +from spdx.model.relationship import Relationship, RelationshipType +from spdx.parser.logger import Logger +from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error +from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_enum_value, \ + parse_literal_or_no_assertion_or_none, parse_spdx_id +from spdx.rdfschema.namespace import SPDX_NAMESPACE + + +def parse_relationship(relationship_node: URIRef, graph: Graph, parent_node: URIRef, + doc_namespace: str) -> Relationship: + logger = Logger() + spdx_element_id = parse_spdx_id(parent_node, doc_namespace) + + relationship_type = parse_literal(logger, graph, relationship_node, SPDX_NAMESPACE.relationshipType, + prefix=SPDX_NAMESPACE.relationshipType_, + method_to_apply=lambda x: parse_enum_value(x, RelationshipType)) + related_spdx_element = parse_literal_or_no_assertion_or_none(logger, graph, relationship_node, + SPDX_NAMESPACE.relatedSpdxElement, + method_to_apply=lambda x: parse_spdx_id(x, + doc_namespace)) + + comment = parse_literal(logger, graph, relationship_node, RDFS.comment) + raise_parsing_error_if_logger_has_messages(logger, "Relationship") + relationship = construct_or_raise_parsing_error(Relationship, + dict(spdx_element_id=spdx_element_id, + relationship_type=relationship_type, + related_spdx_element_id=related_spdx_element, comment=comment)) + + return relationship diff --git a/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml b/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml index e1d02851d..f832f8f09 100644 --- a/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml +++ b/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml @@ -171,6 +171,7 @@ + relationshipComment diff --git a/tests/spdx/parser/rdf/test_rdf_parser.py b/tests/spdx/parser/rdf/test_rdf_parser.py index 84bf23ab2..896e2cf0a 100644 --- a/tests/spdx/parser/rdf/test_rdf_parser.py +++ b/tests/spdx/parser/rdf/test_rdf_parser.py @@ -31,3 +31,5 @@ def test_rdf_parser_with_2_3_example(): assert len(doc.files) == 5 assert len(doc.annotations) == 5 assert len(doc.packages) == 4 + assert len(doc.relationships) == 13 + diff --git a/tests/spdx/parser/rdf/test_relationship_parser.py b/tests/spdx/parser/rdf/test_relationship_parser.py new file mode 100644 index 000000000..b4b794bcf --- /dev/null +++ b/tests/spdx/parser/rdf/test_relationship_parser.py @@ -0,0 +1,31 @@ +# Copyright (c) 2023 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. +import os + +from rdflib import Graph, RDF + +from spdx.model.relationship import RelationshipType +from spdx.parser.rdf.relationship_parser import parse_relationship +from spdx.rdfschema.namespace import SPDX_NAMESPACE + + +def test_relationship_parser(): + graph = Graph().parse(os.path.join(os.path.dirname(__file__), "data/file_to_test_rdf_parser.rdf.xml")) + parent_node = graph.value(predicate=RDF.type, object=SPDX_NAMESPACE.SpdxDocument) + relationship_node = graph.value(subject=parent_node, predicate=SPDX_NAMESPACE.relationship) + doc_namespace = "https://some.namespace" + + relationship = parse_relationship(relationship_node, graph, parent_node, doc_namespace) + + assert relationship.spdx_element_id == "SPDXRef-DOCUMENT" + assert relationship.relationship_type == RelationshipType.DESCRIBES + assert relationship.related_spdx_element_id == "SPDXRef-File" + assert relationship.comment == "relationshipComment" From 41bd9a0da7599856cf18ba7cab3f817a2711d993 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Feb 2023 10:33:15 +0100 Subject: [PATCH 249/362] [issue-456] use NamespaceManager to translate external document namespaces prefix to the short identifier Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/creation_info_parser.py | 3 ++- .../parser/rdf/graph_parsing_functions.py | 12 +++++---- .../parser/rdf/test_graph_parsing_function.py | 25 +++++++++++-------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/spdx/parser/rdf/creation_info_parser.py b/src/spdx/parser/rdf/creation_info_parser.py index b4debc7ff..ef61d0288 100644 --- a/src/spdx/parser/rdf/creation_info_parser.py +++ b/src/spdx/parser/rdf/creation_info_parser.py @@ -12,7 +12,7 @@ from typing import Tuple from urllib.parse import urldefrag -from rdflib import Graph, RDFS, RDF +from rdflib import Graph, RDFS, RDF, Namespace from rdflib.exceptions import UniquenessError from rdflib.term import URIRef @@ -102,5 +102,6 @@ def parse_external_document_refs(external_document_node: URIRef, graph: Graph, external_document_ref = construct_or_raise_parsing_error(ExternalDocumentRef, dict(document_ref_id=document_ref_id, document_uri=document_uri, checksum=checksum)) + graph.bind(external_document_ref.document_ref_id, Namespace(external_document_ref.document_uri + "#")) return external_document_ref diff --git a/src/spdx/parser/rdf/graph_parsing_functions.py b/src/spdx/parser/rdf/graph_parsing_functions.py index e0d7bdebf..505033753 100644 --- a/src/spdx/parser/rdf/graph_parsing_functions.py +++ b/src/spdx/parser/rdf/graph_parsing_functions.py @@ -13,6 +13,7 @@ from rdflib import Graph, URIRef from rdflib.exceptions import UniquenessError +from rdflib.namespace import NamespaceManager from rdflib.term import Node from spdx.model.spdx_no_assertion import SpdxNoAssertion, SPDX_NO_ASSERTION_STRING @@ -70,11 +71,12 @@ def str_to_no_assertion_or_none(value: str) -> Union[str, SpdxNone, SpdxNoAssert return value -def parse_spdx_id(resource: URIRef, doc_namespace: str) -> Optional[str]: +def parse_spdx_id(resource: URIRef, doc_namespace: str, graph: Graph) -> Optional[str]: if not resource: return None if resource.startswith(f"{doc_namespace}#"): - spdx_id = resource.fragment - else: - spdx_id = resource.toPython() - return spdx_id or None + return resource.fragment + if "#" in resource: + namespace_manager = NamespaceManager(graph) + return namespace_manager.normalizeUri(resource) + return resource.toPython() or None diff --git a/tests/spdx/parser/rdf/test_graph_parsing_function.py b/tests/spdx/parser/rdf/test_graph_parsing_function.py index 8c1c9faaa..b945640ad 100644 --- a/tests/spdx/parser/rdf/test_graph_parsing_function.py +++ b/tests/spdx/parser/rdf/test_graph_parsing_function.py @@ -9,26 +9,31 @@ # See the License for the specific language governing permissions and # limitations under the License. import pytest -from rdflib import URIRef +from rdflib import URIRef, Graph, Namespace from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone from spdx.parser.rdf.graph_parsing_functions import str_to_no_assertion_or_none, parse_spdx_id -@pytest.mark.parametrize("value,expected",[("NOASSERTION", SpdxNoAssertion()), ("NONE", SpdxNone()), ("test", "test"), - ("Noassertion", "Noassertion")]) +@pytest.mark.parametrize("value,expected", [("NOASSERTION", SpdxNoAssertion()), ("NONE", SpdxNone()), ("test", "test"), + ("Noassertion", "Noassertion")]) def test_str_to_no_assertion_or_none(value, expected): result = str_to_no_assertion_or_none(value) assert result == expected -@pytest.mark.parametrize("resource,doc_namespace," - "expected", [(URIRef("docNamespace#SPDXRef-Test"), "docNamespace", "SPDXRef-Test"), - (URIRef("docNamespaceSPDXRef-Test"), "docNamespace", "docNamespaceSPDXRef-Test"), - (URIRef("differentNamespace#SPDXRef-Test"), "docNamespace", "differentNamespace#SPDXRef-Test"), - (None, "", None),]) -def test_parse_spdx_id(resource, doc_namespace, expected): - spdx_id = parse_spdx_id(resource, doc_namespace) + +@pytest.mark.parametrize("resource,doc_namespace,ext_namespace_mapping,expected", + [(URIRef("docNamespace#SPDXRef-Test"), "docNamespace", ("", Namespace("")), "SPDXRef-Test"), + (URIRef("docNamespaceSPDXRef-Test"), "docNamespace", ("", Namespace("")), + "docNamespaceSPDXRef-Test"), + (URIRef("differentNamespace#SPDXRef-Test"), "docNamespace", + ("extDoc", Namespace("differentNamespace#")), "extDoc:SPDXRef-Test"), + (None, "", ("", Namespace("")), None)]) +def test_parse_spdx_id(resource, doc_namespace, ext_namespace_mapping, expected): + graph = Graph() + graph.bind(*ext_namespace_mapping) + spdx_id = parse_spdx_id(resource, doc_namespace, graph) assert spdx_id == expected From 9d8d3598a493e816a646b9047cdbb2f13b99a1d2 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Feb 2023 10:34:11 +0100 Subject: [PATCH 250/362] [issue-456] add extracted licensing info parser Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/annotation_parser.py | 2 +- src/spdx/parser/rdf/creation_info_parser.py | 2 +- .../rdf/extracted_licensing_info_parser.py | 38 +++++++++++++++++++ src/spdx/parser/rdf/file_parser.py | 2 +- src/spdx/parser/rdf/package_parser.py | 2 +- src/spdx/parser/rdf/rdf_parser.py | 7 +++- src/spdx/parser/rdf/relationship_parser.py | 5 ++- src/spdx/parser/rdf/snippet_parser.py | 4 +- .../test_extracted_licensing_info_parser.py | 30 +++++++++++++++ tests/spdx/parser/rdf/test_rdf_parser.py | 4 +- 10 files changed, 85 insertions(+), 11 deletions(-) create mode 100644 src/spdx/parser/rdf/extracted_licensing_info_parser.py create mode 100644 tests/spdx/parser/rdf/test_extracted_licensing_info_parser.py diff --git a/src/spdx/parser/rdf/annotation_parser.py b/src/spdx/parser/rdf/annotation_parser.py index c2995e21d..8f1e7c773 100644 --- a/src/spdx/parser/rdf/annotation_parser.py +++ b/src/spdx/parser/rdf/annotation_parser.py @@ -21,7 +21,7 @@ def parse_annotation(annotation_node: URIRef, graph: Graph, parent_ref: URIRef, doc_namespace) -> Annotation: logger = Logger() - spdx_id = parse_spdx_id(parent_ref, doc_namespace) + spdx_id = parse_spdx_id(parent_ref, doc_namespace, graph) annotator = parse_literal(logger, graph, annotation_node, SPDX_NAMESPACE.annotator, method_to_apply=ActorParser.parse_actor) annotation_type = parse_literal(logger, graph, annotation_node, SPDX_NAMESPACE.annotationType, diff --git a/src/spdx/parser/rdf/creation_info_parser.py b/src/spdx/parser/rdf/creation_info_parser.py index ef61d0288..35e0c2f42 100644 --- a/src/spdx/parser/rdf/creation_info_parser.py +++ b/src/spdx/parser/rdf/creation_info_parser.py @@ -94,7 +94,7 @@ def parse_namespace_and_spdx_id(graph: Graph) -> (str, str): def parse_external_document_refs(external_document_node: URIRef, graph: Graph, doc_namespace: str) -> ExternalDocumentRef: logger = Logger() - document_ref_id = parse_spdx_id(external_document_node, doc_namespace) + document_ref_id = parse_spdx_id(external_document_node, doc_namespace, graph) document_uri = parse_literal(logger, graph, external_document_node, SPDX_NAMESPACE.spdxDocument) checksum = None for (_, _, checksum_node) in graph.triples((external_document_node, SPDX_NAMESPACE.checksum, None)): diff --git a/src/spdx/parser/rdf/extracted_licensing_info_parser.py b/src/spdx/parser/rdf/extracted_licensing_info_parser.py new file mode 100644 index 000000000..d26b5cbf3 --- /dev/null +++ b/src/spdx/parser/rdf/extracted_licensing_info_parser.py @@ -0,0 +1,38 @@ +# Copyright (c) 2023 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 rdflib import URIRef, Graph, RDFS +from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_literal_or_no_assertion + +from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error + +from spdx.model.extracted_licensing_info import ExtractedLicensingInfo +from spdx.parser.logger import Logger +from spdx.rdfschema.namespace import SPDX_NAMESPACE + + +def parse_extracted_licensing_info(extracted_licensing_info_node: URIRef, graph: Graph) -> ExtractedLicensingInfo: + logger = Logger() + license_id = parse_literal(logger, graph, extracted_licensing_info_node, SPDX_NAMESPACE.licenseId) + extracted_text = parse_literal(logger, graph, extracted_licensing_info_node, SPDX_NAMESPACE.extractedText) + comment = parse_literal(logger, graph, extracted_licensing_info_node, RDFS.comment) + license_name = parse_literal_or_no_assertion(logger, graph, extracted_licensing_info_node, SPDX_NAMESPACE.name) + cross_references = [] + for (_, _, cross_reference_node) in graph.triples( + (extracted_licensing_info_node, RDFS.seeAlso, None)): + cross_references.append(cross_reference_node.toPython()) + raise_parsing_error_if_logger_has_messages(logger, "ExtractedLicensingInfo") + extracted_licensing_info = construct_or_raise_parsing_error(ExtractedLicensingInfo, dict(license_id=license_id, + extracted_text=extracted_text, + comment=comment, + license_name=license_name, + cross_references=cross_references)) + + return extracted_licensing_info diff --git a/src/spdx/parser/rdf/file_parser.py b/src/spdx/parser/rdf/file_parser.py index 02c2c335f..6aa278dc4 100644 --- a/src/spdx/parser/rdf/file_parser.py +++ b/src/spdx/parser/rdf/file_parser.py @@ -20,7 +20,7 @@ def parse_file(file_node: URIRef, graph: Graph, doc_namespace: str) -> File: logger = Logger() - spdx_id = parse_spdx_id(file_node, doc_namespace) + spdx_id = parse_spdx_id(file_node, doc_namespace, graph) name = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.fileName) checksums = [] for (_,_,checksum_node) in graph.triples((file_node, SPDX_NAMESPACE.checksum, None)): diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index ea8dbbf3a..2fd705a51 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -30,7 +30,7 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Package: logger = Logger() - spdx_id = parse_spdx_id(package_node, doc_namespace) + spdx_id = parse_spdx_id(package_node, doc_namespace, graph) name = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.name) download_location = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.downloadLocation) checksums = [] diff --git a/src/spdx/parser/rdf/rdf_parser.py b/src/spdx/parser/rdf/rdf_parser.py index f8157a3ea..b6c7d32c9 100644 --- a/src/spdx/parser/rdf/rdf_parser.py +++ b/src/spdx/parser/rdf/rdf_parser.py @@ -16,6 +16,7 @@ from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.rdf.annotation_parser import parse_annotation from spdx.parser.rdf.creation_info_parser import parse_creation_info +from spdx.parser.rdf.extracted_licensing_info_parser import parse_extracted_licensing_info from spdx.parser.rdf.file_parser import parse_file from spdx.parser.rdf.package_parser import parse_package from spdx.parser.rdf.relationship_parser import parse_relationship @@ -70,9 +71,13 @@ def translate_graph_to_document(graph: Graph) -> Document: relationships.append( parse_relationship(relationship_node, graph, parent_node, creation_info.document_namespace)) + extracted_licensing_infos = [] + for (_, _, extracted_licensing_info_node) in graph.triples((None, SPDX_NAMESPACE.hasExtractedLicensingInfo, None)): + extracted_licensing_infos.append(parse_extracted_licensing_info(extracted_licensing_info_node, graph)) raise_parsing_error_if_logger_has_messages(logger) document = construct_or_raise_parsing_error(Document, dict(creation_info=creation_info, snippets=snippets, files=files, annotations=annotations, packages=packages, - relationships=relationships)) + relationships=relationships, + extracted_licensing_info=extracted_licensing_infos)) return document diff --git a/src/spdx/parser/rdf/relationship_parser.py b/src/spdx/parser/rdf/relationship_parser.py index 3706104b3..c3ef32023 100644 --- a/src/spdx/parser/rdf/relationship_parser.py +++ b/src/spdx/parser/rdf/relationship_parser.py @@ -21,7 +21,7 @@ def parse_relationship(relationship_node: URIRef, graph: Graph, parent_node: URIRef, doc_namespace: str) -> Relationship: logger = Logger() - spdx_element_id = parse_spdx_id(parent_node, doc_namespace) + spdx_element_id = parse_spdx_id(parent_node, doc_namespace, graph) relationship_type = parse_literal(logger, graph, relationship_node, SPDX_NAMESPACE.relationshipType, prefix=SPDX_NAMESPACE.relationshipType_, @@ -29,7 +29,8 @@ def parse_relationship(relationship_node: URIRef, graph: Graph, parent_node: URI related_spdx_element = parse_literal_or_no_assertion_or_none(logger, graph, relationship_node, SPDX_NAMESPACE.relatedSpdxElement, method_to_apply=lambda x: parse_spdx_id(x, - doc_namespace)) + doc_namespace, + graph)) comment = parse_literal(logger, graph, relationship_node, RDFS.comment) raise_parsing_error_if_logger_has_messages(logger, "Relationship") diff --git a/src/spdx/parser/rdf/snippet_parser.py b/src/spdx/parser/rdf/snippet_parser.py index 2dba23353..d0620fe0d 100644 --- a/src/spdx/parser/rdf/snippet_parser.py +++ b/src/spdx/parser/rdf/snippet_parser.py @@ -22,9 +22,9 @@ def parse_snippet(snippet_node: URIRef, graph: Graph, doc_namespace: str) -> Snippet: logger = Logger() - spdx_id = parse_spdx_id(snippet_node, doc_namespace) + spdx_id = parse_spdx_id(snippet_node, doc_namespace, graph) file_spdx_id_uri = graph.value(subject=snippet_node, predicate=SPDX_NAMESPACE.snippetFromFile) - file_spdx_id = parse_spdx_id(file_spdx_id_uri, doc_namespace) + file_spdx_id = parse_spdx_id(file_spdx_id_uri, doc_namespace, graph) byte_range = parse_ranges(snippet_node, graph, POINTER_NAMESPACE.ByteOffsetPointer, POINTER_NAMESPACE.offset) line_range = parse_ranges(snippet_node, graph, POINTER_NAMESPACE.LineCharPointer, POINTER_NAMESPACE.lineNumber) diff --git a/tests/spdx/parser/rdf/test_extracted_licensing_info_parser.py b/tests/spdx/parser/rdf/test_extracted_licensing_info_parser.py new file mode 100644 index 000000000..eb3776f97 --- /dev/null +++ b/tests/spdx/parser/rdf/test_extracted_licensing_info_parser.py @@ -0,0 +1,30 @@ +# Copyright (c) 2023 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. +import os + +from rdflib import Graph, RDF + +from spdx.parser.rdf.extracted_licensing_info_parser import parse_extracted_licensing_info +from spdx.rdfschema.namespace import SPDX_NAMESPACE + + +def test_parse_extracted_licensing_info(): + graph = Graph().parse(os.path.join(os.path.dirname(__file__), "data/file_to_test_rdf_parser.rdf.xml")) + doc_node = graph.value(predicate=RDF.type, object=SPDX_NAMESPACE.SpdxDocument) + extracted_licensing_info_node = graph.value(subject=doc_node, predicate=SPDX_NAMESPACE.hasExtractedLicensingInfo) + + extracted_licensing_info = parse_extracted_licensing_info(extracted_licensing_info_node, graph) + + assert extracted_licensing_info.license_id == "LicenseRef-1" + assert extracted_licensing_info.extracted_text == "extractedText" + assert extracted_licensing_info.comment == "licenseComment" + assert extracted_licensing_info.license_name == "licenseName" + assert extracted_licensing_info.cross_references == ["https://see.also"] diff --git a/tests/spdx/parser/rdf/test_rdf_parser.py b/tests/spdx/parser/rdf/test_rdf_parser.py index 896e2cf0a..769e5c8e2 100644 --- a/tests/spdx/parser/rdf/test_rdf_parser.py +++ b/tests/spdx/parser/rdf/test_rdf_parser.py @@ -27,9 +27,9 @@ def test_rdf_parser_with_2_3_example(): os.path.join(os.path.dirname(__file__), "../../data/formats/SPDXRdfExample-v2.3.spdx.rdf.xml")) assert type(doc) == Document - assert len(doc.snippets) == 1 + assert len(doc.snippets) == 1 assert len(doc.files) == 5 assert len(doc.annotations) == 5 assert len(doc.packages) == 4 assert len(doc.relationships) == 13 - + assert len(doc.extracted_licensing_info) == 5 From 041642842a68b937b730b1c82bdbddfaf5a7adae Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 8 Feb 2023 16:01:37 +0100 Subject: [PATCH 251/362] [issue-456] allow no assertion in package download location Signed-off-by: Meret Behrens --- .../parser/rdf/graph_parsing_functions.py | 19 +++++++++++++++++-- src/spdx/parser/rdf/package_parser.py | 5 +++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/spdx/parser/rdf/graph_parsing_functions.py b/src/spdx/parser/rdf/graph_parsing_functions.py index 505033753..d5e34d571 100644 --- a/src/spdx/parser/rdf/graph_parsing_functions.py +++ b/src/spdx/parser/rdf/graph_parsing_functions.py @@ -40,6 +40,7 @@ def parse_literal(logger: Logger, graph: Graph, subject: Node, predicate: Node, return default return default + def parse_enum_value(enum_str: str, enum_class: Type[Enum]) -> Enum: try: return enum_class[camel_case_to_snake_case(enum_str).upper()] @@ -56,13 +57,27 @@ def parse_literal_or_no_assertion_or_none(logger: Logger, graph: Graph, subject: return if not value: return default - if value == SPDX_NAMESPACE.noassertion: + if value == SPDX_NAMESPACE.noassertion or value.toPython() == SPDX_NO_ASSERTION_STRING: return SpdxNoAssertion() - if value == SPDX_NAMESPACE.none: + if value == SPDX_NAMESPACE.none or value.toPython() == SPDX_NONE_STRING: return SpdxNone() return method_to_apply(value) +def parse_literal_or_no_assertion(logger: Logger, graph: Graph, subject: Node, predicate: Node, + default: Any = None, method_to_apply: Callable = lambda x: x): + try: + value = graph.value(subject=subject, predicate=predicate, default=default, any=False) + except UniquenessError: + logger.append(f"Multiple values for unique value {predicate} found.") + return + if not value: + return default + if value == SPDX_NAMESPACE.noassertion: + return SpdxNoAssertion() + return method_to_apply(value) + + def str_to_no_assertion_or_none(value: str) -> Union[str, SpdxNone, SpdxNoAssertion]: if value == SPDX_NO_ASSERTION_STRING: return SpdxNoAssertion() diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index 2fd705a51..e98f8241b 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -24,7 +24,7 @@ from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error from spdx.parser.rdf.checksum_parser import parse_checksum from spdx.parser.rdf.graph_parsing_functions import parse_spdx_id, parse_literal, str_to_no_assertion_or_none, \ - parse_enum_value + parse_enum_value, parse_literal_or_no_assertion_or_none from spdx.rdfschema.namespace import SPDX_NAMESPACE, REFERENCE_NAMESPACE @@ -32,7 +32,8 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac logger = Logger() spdx_id = parse_spdx_id(package_node, doc_namespace, graph) name = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.name) - download_location = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.downloadLocation) + download_location = parse_literal_or_no_assertion_or_none(logger, graph, package_node, SPDX_NAMESPACE.downloadLocation, + method_to_apply=str) checksums = [] for (_, _, checksum_node) in graph.triples((package_node, SPDX_NAMESPACE.checksum, None)): checksums.append(parse_checksum(checksum_node, graph)) From 6d4c238177876dd7121a605a842316dbc1313072 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Feb 2023 10:44:12 +0100 Subject: [PATCH 252/362] [issue-456, refactor] extract method to get a unique value, sort methods Signed-off-by: Meret Behrens --- .../parser/rdf/graph_parsing_functions.py | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/src/spdx/parser/rdf/graph_parsing_functions.py b/src/spdx/parser/rdf/graph_parsing_functions.py index d5e34d571..f2a0b9e8f 100644 --- a/src/spdx/parser/rdf/graph_parsing_functions.py +++ b/src/spdx/parser/rdf/graph_parsing_functions.py @@ -26,56 +26,60 @@ def parse_literal(logger: Logger, graph: Graph, subject: Node, predicate: Node, default: Any = None, method_to_apply: Callable = lambda x: x, prefix: str = ""): + value = get_unique_value(logger, graph, subject, predicate, default) + if not value: + return default try: - value = graph.value(subject=subject, predicate=predicate, default=default, any=False) - except UniquenessError: - logger.append(f"Multiple values for unique value {predicate} found.") - return - - if value: - try: - return method_to_apply(value.removeprefix(prefix)) - except SPDXParsingError as err: - logger.extend(err.get_messages()) - return default - return default - - -def parse_enum_value(enum_str: str, enum_class: Type[Enum]) -> Enum: - try: - return enum_class[camel_case_to_snake_case(enum_str).upper()] - except KeyError: - raise SPDXParsingError([f"Invalid value for {enum_class}: {enum_str}"]) + return method_to_apply(value.removeprefix(prefix)) + except SPDXParsingError as err: + logger.extend(err.get_messages()) + return default def parse_literal_or_no_assertion_or_none(logger: Logger, graph: Graph, subject: Node, predicate: Node, default: Any = None, method_to_apply: Callable = lambda x: x): - try: - value = graph.value(subject=subject, predicate=predicate, default=default, any=False) - except UniquenessError: - logger.append(f"Multiple values for unique value {predicate} found.") - return + value = get_unique_value(logger, graph, subject, predicate, default) if not value: return default if value == SPDX_NAMESPACE.noassertion or value.toPython() == SPDX_NO_ASSERTION_STRING: return SpdxNoAssertion() if value == SPDX_NAMESPACE.none or value.toPython() == SPDX_NONE_STRING: return SpdxNone() - return method_to_apply(value) + try: + return method_to_apply(value) + except SPDXParsingError as err: + logger.extend(err.get_messages()) + return default def parse_literal_or_no_assertion(logger: Logger, graph: Graph, subject: Node, predicate: Node, default: Any = None, method_to_apply: Callable = lambda x: x): + value = get_unique_value(logger, graph, subject, predicate, default) + if not value: + return default + if value == SPDX_NAMESPACE.noassertion or value.toPython() == SPDX_NO_ASSERTION_STRING: + return SpdxNoAssertion() + try: + return method_to_apply(value) + except SPDXParsingError as err: + logger.extend(err.get_messages()) + return default + + +def get_unique_value(logger: Logger, graph: Graph, subject: Node, predicate: Node, default: Any) -> Any: try: value = graph.value(subject=subject, predicate=predicate, default=default, any=False) + return value except UniquenessError: logger.append(f"Multiple values for unique value {predicate} found.") - return - if not value: return default - if value == SPDX_NAMESPACE.noassertion: - return SpdxNoAssertion() - return method_to_apply(value) + + +def parse_enum_value(enum_str: str, enum_class: Type[Enum]) -> Enum: + try: + return enum_class[camel_case_to_snake_case(enum_str).upper()] + except KeyError: + raise SPDXParsingError([f"Invalid value for {enum_class}: {enum_str}"]) def str_to_no_assertion_or_none(value: str) -> Union[str, SpdxNone, SpdxNoAssertion]: From effcda1c2b34ddac6fb49d51564460fad229f1dd Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Feb 2023 10:47:36 +0100 Subject: [PATCH 253/362] [issue-456, refactor] replace str_or_no_assertion_or_none with parse helper method Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/file_parser.py | 12 ++++++------ src/spdx/parser/rdf/graph_parsing_functions.py | 8 -------- src/spdx/parser/rdf/package_parser.py | 11 ++++++----- src/spdx/parser/rdf/snippet_parser.py | 8 +++----- tests/spdx/parser/rdf/test_graph_parsing_function.py | 12 +----------- 5 files changed, 16 insertions(+), 35 deletions(-) diff --git a/src/spdx/parser/rdf/file_parser.py b/src/spdx/parser/rdf/file_parser.py index 6aa278dc4..1b9b5d17c 100644 --- a/src/spdx/parser/rdf/file_parser.py +++ b/src/spdx/parser/rdf/file_parser.py @@ -14,7 +14,7 @@ from spdx.parser.logger import Logger from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.rdf.checksum_parser import parse_checksum -from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, str_to_no_assertion_or_none +from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, parse_literal_or_no_assertion_or_none from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -23,20 +23,20 @@ def parse_file(file_node: URIRef, graph: Graph, doc_namespace: str) -> File: spdx_id = parse_spdx_id(file_node, doc_namespace, graph) name = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.fileName) checksums = [] - for (_,_,checksum_node) in graph.triples((file_node, SPDX_NAMESPACE.checksum, None)): + for (_, _, checksum_node) in graph.triples((file_node, SPDX_NAMESPACE.checksum, None)): checksums.append(parse_checksum(checksum_node, graph)) file_types = [] - for (_,_,file_type_ref) in graph.triples((file_node, SPDX_NAMESPACE.fileType, None)): + for (_, _, file_type_ref) in graph.triples((file_node, SPDX_NAMESPACE.fileType, None)): try: file_types.append(convert_uri_ref_to_file_type(file_type_ref)) except KeyError: logger.append(f"Invalid FileType: {file_type_ref}") license_comment = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.licenseComments) - copyright_text = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.copyrightText, - method_to_apply=str_to_no_assertion_or_none) + copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, file_node, SPDX_NAMESPACE.copyrightText, + method_to_apply=str) file_contributors = [] - for (_,_,file_contributor) in graph.triples((file_node, SPDX_NAMESPACE.fileContributor,None)): + for (_, _, file_contributor) in graph.triples((file_node, SPDX_NAMESPACE.fileContributor, None)): file_contributors.append(file_contributor.toPython()) notice_text = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.noticeText) diff --git a/src/spdx/parser/rdf/graph_parsing_functions.py b/src/spdx/parser/rdf/graph_parsing_functions.py index f2a0b9e8f..79180385d 100644 --- a/src/spdx/parser/rdf/graph_parsing_functions.py +++ b/src/spdx/parser/rdf/graph_parsing_functions.py @@ -82,14 +82,6 @@ def parse_enum_value(enum_str: str, enum_class: Type[Enum]) -> Enum: raise SPDXParsingError([f"Invalid value for {enum_class}: {enum_str}"]) -def str_to_no_assertion_or_none(value: str) -> Union[str, SpdxNone, SpdxNoAssertion]: - if value == SPDX_NO_ASSERTION_STRING: - return SpdxNoAssertion() - if value == SPDX_NONE_STRING: - return SpdxNone() - return value - - def parse_spdx_id(resource: URIRef, doc_namespace: str, graph: Graph) -> Optional[str]: if not resource: return None diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index e98f8241b..75e66cf1f 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -23,8 +23,8 @@ from spdx.parser.logger import Logger from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error from spdx.parser.rdf.checksum_parser import parse_checksum -from spdx.parser.rdf.graph_parsing_functions import parse_spdx_id, parse_literal, str_to_no_assertion_or_none, \ - parse_enum_value, parse_literal_or_no_assertion_or_none +from spdx.parser.rdf.graph_parsing_functions import parse_spdx_id, parse_literal, parse_enum_value, \ + parse_literal_or_no_assertion_or_none from spdx.rdfschema.namespace import SPDX_NAMESPACE, REFERENCE_NAMESPACE @@ -32,7 +32,8 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac logger = Logger() spdx_id = parse_spdx_id(package_node, doc_namespace, graph) name = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.name) - download_location = parse_literal_or_no_assertion_or_none(logger, graph, package_node, SPDX_NAMESPACE.downloadLocation, + download_location = parse_literal_or_no_assertion_or_none(logger, graph, package_node, + SPDX_NAMESPACE.downloadLocation, method_to_apply=str) checksums = [] for (_, _, checksum_node) in graph.triples((package_node, SPDX_NAMESPACE.checksum, None)): @@ -63,8 +64,8 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac comment = parse_literal(logger, graph, package_node, RDFS.comment) summary = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.summary) description = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.description) - copyright_text = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.copyrightText, - method_to_apply=str_to_no_assertion_or_none) + copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, package_node, SPDX_NAMESPACE.copyrightText, + method_to_apply=str) source_info = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.sourceInfo) primary_package_purpose = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.primaryPackagePurpose, prefix=SPDX_NAMESPACE.purpose_, diff --git a/src/spdx/parser/rdf/snippet_parser.py b/src/spdx/parser/rdf/snippet_parser.py index d0620fe0d..81d9765fd 100644 --- a/src/spdx/parser/rdf/snippet_parser.py +++ b/src/spdx/parser/rdf/snippet_parser.py @@ -16,7 +16,7 @@ from spdx.model.snippet import Snippet from spdx.parser.logger import Logger from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages -from spdx.parser.rdf.graph_parsing_functions import parse_literal, str_to_no_assertion_or_none, parse_spdx_id +from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, parse_literal_or_no_assertion_or_none from spdx.rdfschema.namespace import SPDX_NAMESPACE, POINTER_NAMESPACE @@ -29,8 +29,8 @@ def parse_snippet(snippet_node: URIRef, graph: Graph, doc_namespace: str) -> Sni line_range = parse_ranges(snippet_node, graph, POINTER_NAMESPACE.LineCharPointer, POINTER_NAMESPACE.lineNumber) license_comment = parse_literal(logger, graph, snippet_node, SPDX_NAMESPACE.licenseComments) - copyright_text = parse_literal(logger, graph, snippet_node, SPDX_NAMESPACE.copyrightText, - method_to_apply=str_to_no_assertion_or_none) + copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, snippet_node, SPDX_NAMESPACE.copyrightText, + method_to_apply=str) comment = parse_literal(logger, graph, snippet_node, RDFS.comment) name = parse_literal(logger, graph, snippet_node, SPDX_NAMESPACE.name) attribution_texts = [] @@ -65,5 +65,3 @@ def parse_range_value(graph: Graph, pointer_node: Node, predicate: Node) -> Opti if value: value = int(value) return value - - diff --git a/tests/spdx/parser/rdf/test_graph_parsing_function.py b/tests/spdx/parser/rdf/test_graph_parsing_function.py index b945640ad..147c7241d 100644 --- a/tests/spdx/parser/rdf/test_graph_parsing_function.py +++ b/tests/spdx/parser/rdf/test_graph_parsing_function.py @@ -11,17 +11,7 @@ import pytest from rdflib import URIRef, Graph, Namespace -from spdx.model.spdx_no_assertion import SpdxNoAssertion -from spdx.model.spdx_none import SpdxNone -from spdx.parser.rdf.graph_parsing_functions import str_to_no_assertion_or_none, parse_spdx_id - - -@pytest.mark.parametrize("value,expected", [("NOASSERTION", SpdxNoAssertion()), ("NONE", SpdxNone()), ("test", "test"), - ("Noassertion", "Noassertion")]) -def test_str_to_no_assertion_or_none(value, expected): - result = str_to_no_assertion_or_none(value) - - assert result == expected +from spdx.parser.rdf.graph_parsing_functions import parse_spdx_id @pytest.mark.parametrize("resource,doc_namespace,ext_namespace_mapping,expected", From cf388fd9629e2d235cc581cd786f885397f85f6e Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Feb 2023 10:56:37 +0100 Subject: [PATCH 254/362] [issue-456] add license expression parser Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/file_parser.py | 7 ++- .../parser/rdf/license_expression_parser.py | 60 +++++++++++++++++++ src/spdx/parser/rdf/package_parser.py | 13 +++- src/spdx/parser/rdf/snippet_parser.py | 8 ++- tests/spdx/parser/rdf/test_file_parser.py | 2 + .../rdf/test_license_expression_parser.py | 38 ++++++++++++ tests/spdx/parser/rdf/test_package_parser.py | 3 + tests/spdx/parser/rdf/test_snippet_parser.py | 3 +- 8 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 src/spdx/parser/rdf/license_expression_parser.py create mode 100644 tests/spdx/parser/rdf/test_license_expression_parser.py diff --git a/src/spdx/parser/rdf/file_parser.py b/src/spdx/parser/rdf/file_parser.py index 1b9b5d17c..1687cee76 100644 --- a/src/spdx/parser/rdf/file_parser.py +++ b/src/spdx/parser/rdf/file_parser.py @@ -15,6 +15,7 @@ from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.rdf.checksum_parser import parse_checksum from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, parse_literal_or_no_assertion_or_none +from spdx.parser.rdf.license_expression_parser import parse_license_expression from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -32,6 +33,10 @@ def parse_file(file_node: URIRef, graph: Graph, doc_namespace: str) -> File: file_types.append(convert_uri_ref_to_file_type(file_type_ref)) except KeyError: logger.append(f"Invalid FileType: {file_type_ref}") + license_concluded = parse_literal_or_no_assertion_or_none(logger, graph, file_node, + SPDX_NAMESPACE.licenseConcluded, + method_to_apply=lambda x: parse_license_expression(x, + graph)) license_comment = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.licenseComments) copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, file_node, SPDX_NAMESPACE.copyrightText, method_to_apply=str) @@ -50,7 +55,7 @@ def parse_file(file_node: URIRef, graph: Graph, doc_namespace: str) -> File: copyright_text=copyright_text, file_type=file_types, contributors=file_contributors, license_comment=license_comment, - license_concluded=None, + license_concluded=license_concluded, license_info_in_file=None, notice=notice_text)) return file diff --git a/src/spdx/parser/rdf/license_expression_parser.py b/src/spdx/parser/rdf/license_expression_parser.py new file mode 100644 index 000000000..9093a5cb7 --- /dev/null +++ b/src/spdx/parser/rdf/license_expression_parser.py @@ -0,0 +1,60 @@ +# Copyright (c) 2023 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 rdflib import Graph, RDF +from license_expression import LicenseExpression, get_spdx_licensing +from rdflib.term import Node +from spdx.parser.error import SPDXParsingError + +from spdx.rdfschema.namespace import SPDX_NAMESPACE, LICENSE_NAMESPACE + + +def parse_license_expression(license_expression_node: Node, graph: Graph) -> LicenseExpression: + spdx_licensing = get_spdx_licensing() + expression = "" + if license_expression_node.startswith(LICENSE_NAMESPACE): + expression = license_expression_node.removeprefix(LICENSE_NAMESPACE) + return spdx_licensing.parse(expression) + + node_type = graph.value(license_expression_node, RDF.type) + if node_type == SPDX_NAMESPACE.ConjunctiveLicenseSet: + members = dict() + for index, (_, _, member_node) in enumerate( + graph.triples((license_expression_node, SPDX_NAMESPACE.member, None))): + members[index] = parse_license_expression(member_node, graph) + if len(members) > 2: + raise SPDXParsingError([f"A ConjunctiveLicenseSet can only have two members."]) + expression = f"{members[0]} AND {members[1]}" + if node_type == SPDX_NAMESPACE.DisjunctiveLicenseSet: + members = dict() + for index, (_, _, member_node) in enumerate( + graph.triples((license_expression_node, SPDX_NAMESPACE.member, None))): + members[index] = parse_license_expression(member_node, graph) + if len(members) > 2: + raise SPDXParsingError([f"A DisjunctiveLicenseSet can only have two members."]) + expression = f"{members[0]} OR {members[1]}" + if node_type == SPDX_NAMESPACE.WithExceptionOperator: + license_expression = parse_license_expression(graph.value(license_expression_node, SPDX_NAMESPACE.member), + graph) + exception = parse_license_exception(graph.value(license_expression_node, SPDX_NAMESPACE.licenseException), + graph) + expression = f"{license_expression} WITH {exception}" + + return spdx_licensing.parse(expression) + + +def parse_license_exception(exception_node: Node, graph: Graph) -> str: + if exception_node.startswith(LICENSE_NAMESPACE): + exception = exception_node.removeprefix(LICENSE_NAMESPACE) + else: + exception = graph.value(exception_node, SPDX_NAMESPACE.licenseExceptionId).toPython() + return exception + +# need to be able to parse a ListedLicense as in spdx-spec example diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index 75e66cf1f..750fe6273 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -25,6 +25,7 @@ from spdx.parser.rdf.checksum_parser import parse_checksum from spdx.parser.rdf.graph_parsing_functions import parse_spdx_id, parse_literal, parse_enum_value, \ parse_literal_or_no_assertion_or_none +from spdx.parser.rdf.license_expression_parser import parse_license_expression from spdx.rdfschema.namespace import SPDX_NAMESPACE, REFERENCE_NAMESPACE @@ -60,6 +61,14 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac for (_, _, external_package_ref_node) in graph.triples((package_node, SPDX_NAMESPACE.externalRef, None)): external_package_refs.append(parse_external_package_ref(external_package_ref_node, graph)) files_analyzed = bool(graph.value(package_node, SPDX_NAMESPACE.filesAnalyzed, default=True)) + license_concluded = parse_literal_or_no_assertion_or_none(logger, graph, package_node, + SPDX_NAMESPACE.licenseConcluded, + method_to_apply=lambda x: parse_license_expression(x, + graph)) + license_declared = parse_literal_or_no_assertion_or_none(logger, graph, package_node, + SPDX_NAMESPACE.licenseDeclared, + method_to_apply=lambda x: parse_license_expression(x, + graph)) license_comment = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.licenseComments) comment = parse_literal(logger, graph, package_node, RDFS.comment) summary = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.summary) @@ -91,9 +100,9 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac verification_code=verification_code, checksums=checksums, homepage=homepage, source_info=source_info, - license_concluded=None, + license_concluded=license_concluded, license_info_from_files=None, - license_declared=None, + license_declared=license_declared, license_comment=license_comment, copyright_text=copyright_text, summary=summary, description=description, comment=comment, diff --git a/src/spdx/parser/rdf/snippet_parser.py b/src/spdx/parser/rdf/snippet_parser.py index 81d9765fd..21b66251c 100644 --- a/src/spdx/parser/rdf/snippet_parser.py +++ b/src/spdx/parser/rdf/snippet_parser.py @@ -17,6 +17,7 @@ from spdx.parser.logger import Logger from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, parse_literal_or_no_assertion_or_none +from spdx.parser.rdf.license_expression_parser import parse_license_expression from spdx.rdfschema.namespace import SPDX_NAMESPACE, POINTER_NAMESPACE @@ -27,7 +28,10 @@ def parse_snippet(snippet_node: URIRef, graph: Graph, doc_namespace: str) -> Sni file_spdx_id = parse_spdx_id(file_spdx_id_uri, doc_namespace, graph) byte_range = parse_ranges(snippet_node, graph, POINTER_NAMESPACE.ByteOffsetPointer, POINTER_NAMESPACE.offset) line_range = parse_ranges(snippet_node, graph, POINTER_NAMESPACE.LineCharPointer, POINTER_NAMESPACE.lineNumber) - + license_concluded = parse_literal_or_no_assertion_or_none(logger, graph, snippet_node, + SPDX_NAMESPACE.licenseConcluded, + method_to_apply=lambda x: parse_license_expression(x, + graph)) license_comment = parse_literal(logger, graph, snippet_node, SPDX_NAMESPACE.licenseComments) copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, snippet_node, SPDX_NAMESPACE.copyrightText, method_to_apply=str) @@ -40,7 +44,7 @@ def parse_snippet(snippet_node: URIRef, graph: Graph, doc_namespace: str) -> Sni raise_parsing_error_if_logger_has_messages(logger, "Snippet") snippet = construct_or_raise_parsing_error(Snippet, dict(spdx_id=spdx_id, file_spdx_id=file_spdx_id, byte_range=byte_range, - line_range=line_range, license_concluded=None, + line_range=line_range, license_concluded=license_concluded, license_info_in_snippet=None, license_comment=license_comment, copyright_text=copyright_text, comment=comment, name=name, attribution_texts=attribution_texts)) diff --git a/tests/spdx/parser/rdf/test_file_parser.py b/tests/spdx/parser/rdf/test_file_parser.py index e2bd9ffd1..ae3ecb0f2 100644 --- a/tests/spdx/parser/rdf/test_file_parser.py +++ b/tests/spdx/parser/rdf/test_file_parser.py @@ -11,6 +11,7 @@ import os import pytest +from license_expression import get_spdx_licensing from rdflib import Graph, RDF from spdx.model.checksum import Checksum, ChecksumAlgorithm @@ -33,6 +34,7 @@ def test_parse_file(): assert file.comment == "fileComment" assert file.copyright_text == "copyrightText" assert file.contributors == ["fileContributor"] + assert file.license_concluded == get_spdx_licensing().parse("MIT AND GPL-2.0") assert file.license_comment == "licenseComment" assert file.notice == "fileNotice" assert file.attribution_texts == ["fileAttributionText"] diff --git a/tests/spdx/parser/rdf/test_license_expression_parser.py b/tests/spdx/parser/rdf/test_license_expression_parser.py new file mode 100644 index 000000000..5d2112647 --- /dev/null +++ b/tests/spdx/parser/rdf/test_license_expression_parser.py @@ -0,0 +1,38 @@ +# Copyright (c) 2023 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. +import os + +from license_expression import get_spdx_licensing +from rdflib import Graph, RDF, URIRef + +from spdx.parser.rdf.license_expression_parser import parse_license_expression +from spdx.rdfschema.namespace import SPDX_NAMESPACE +from spdx.writer.rdf.license_expression_writer import add_license_expression_to_graph + + +def test_license_expression_parser(): + graph = Graph().parse(os.path.join(os.path.dirname(__file__), "data/file_to_test_rdf_parser.rdf.xml")) + package_node = graph.value(predicate=RDF.type, object=SPDX_NAMESPACE.Package) + license_expression_node = graph.value(subject=package_node, predicate=SPDX_NAMESPACE.licenseConcluded) + + license_expression = parse_license_expression(license_expression_node, graph) + + assert license_expression == get_spdx_licensing().parse("GPL-2.0 AND MIT") + +def test_license_expression_parser_with_writer(): + license_expression = get_spdx_licensing().parse("GPL-2.0 WITH exception") + graph = Graph() + add_license_expression_to_graph(license_expression, graph, URIRef("test"), URIRef("predicate"), "anyURI") + + expression_noe = graph.value(URIRef("test"), URIRef("predicate")) + license_expression_parsed = parse_license_expression(expression_noe,graph) + + assert license_expression_parsed == license_expression diff --git a/tests/spdx/parser/rdf/test_package_parser.py b/tests/spdx/parser/rdf/test_package_parser.py index 1a8ffde8c..b917a2020 100644 --- a/tests/spdx/parser/rdf/test_package_parser.py +++ b/tests/spdx/parser/rdf/test_package_parser.py @@ -10,6 +10,7 @@ # limitations under the License. import os +from license_expression import get_spdx_licensing from rdflib import RDF, Graph from spdx.model.actor import Actor, ActorType @@ -35,6 +36,8 @@ def test_package_parser(): assert package.files_analyzed == True assert package.checksums == [Checksum(ChecksumAlgorithm.SHA1, "71c4025dd9897b364f3ebbb42c484ff43d00791c")] assert package.source_info == "sourceInfo" + assert package.license_concluded == get_spdx_licensing().parse("MIT AND GPL-2.0") + assert package.license_declared == get_spdx_licensing().parse("MIT AND GPL-2.0") assert package.license_comment == "packageLicenseComment" assert package.copyright_text == "packageCopyrightText" assert package.verification_code == PackageVerificationCode(value="85ed0817af83a24ad8da68c2b5094de69833983c", diff --git a/tests/spdx/parser/rdf/test_snippet_parser.py b/tests/spdx/parser/rdf/test_snippet_parser.py index 326c16f8c..c0d322e90 100644 --- a/tests/spdx/parser/rdf/test_snippet_parser.py +++ b/tests/spdx/parser/rdf/test_snippet_parser.py @@ -10,6 +10,7 @@ # limitations under the License. import os +from license_expression import get_spdx_licensing from rdflib import Graph, RDF from spdx.parser.rdf.snippet_parser import parse_snippet @@ -27,7 +28,7 @@ def test_parse_snippet(): assert snippet.file_spdx_id == "SPDXRef-File" assert snippet.byte_range == (1, 2) assert snippet.line_range == (3, 4) - assert snippet.license_concluded == None + assert snippet.license_concluded == get_spdx_licensing().parse("MIT AND GPL-2.0") assert snippet.license_info_in_snippet == None assert snippet.license_comment == "snippetLicenseComment" assert snippet.copyright_text == "licenseCopyrightText" From 1f37d61dfd653d81c5150c472158f56b8e575f32 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Feb 2023 11:42:20 +0100 Subject: [PATCH 255/362] [issue-456, refactor] extract method to apply parsing method or log error, change order of parameters, rename method_to_apply -> parsing_method Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/annotation_parser.py | 8 ++--- src/spdx/parser/rdf/checksum_parser.py | 2 +- src/spdx/parser/rdf/creation_info_parser.py | 7 ++-- src/spdx/parser/rdf/file_parser.py | 9 +++--- .../parser/rdf/graph_parsing_functions.py | 32 +++++++++---------- src/spdx/parser/rdf/package_parser.py | 30 ++++++++--------- src/spdx/parser/rdf/relationship_parser.py | 10 +++--- src/spdx/parser/rdf/snippet_parser.py | 6 ++-- 8 files changed, 50 insertions(+), 54 deletions(-) diff --git a/src/spdx/parser/rdf/annotation_parser.py b/src/spdx/parser/rdf/annotation_parser.py index 8f1e7c773..4b6a29cc7 100644 --- a/src/spdx/parser/rdf/annotation_parser.py +++ b/src/spdx/parser/rdf/annotation_parser.py @@ -23,12 +23,12 @@ def parse_annotation(annotation_node: URIRef, graph: Graph, parent_ref: URIRef, logger = Logger() spdx_id = parse_spdx_id(parent_ref, doc_namespace, graph) annotator = parse_literal(logger, graph, annotation_node, SPDX_NAMESPACE.annotator, - method_to_apply=ActorParser.parse_actor) + parsing_method=ActorParser.parse_actor) annotation_type = parse_literal(logger, graph, annotation_node, SPDX_NAMESPACE.annotationType, - prefix=SPDX_NAMESPACE.annotationType_, - method_to_apply=lambda x: parse_enum_value(x, AnnotationType)) + parsing_method=lambda x: parse_enum_value(x, AnnotationType, + SPDX_NAMESPACE.annotationType_)) annotation_date = parse_literal(logger, graph, annotation_node, SPDX_NAMESPACE.annotationDate, - method_to_apply=datetime_from_str) + parsing_method=datetime_from_str) annotation_comment = parse_literal(logger, graph, annotation_node, RDFS.comment) raise_parsing_error_if_logger_has_messages(logger, "Annotation") diff --git a/src/spdx/parser/rdf/checksum_parser.py b/src/spdx/parser/rdf/checksum_parser.py index 4864005c9..c973a02e5 100644 --- a/src/spdx/parser/rdf/checksum_parser.py +++ b/src/spdx/parser/rdf/checksum_parser.py @@ -21,7 +21,7 @@ def parse_checksum(parent_node: URIRef, graph: Graph) -> Checksum: logger = Logger() algorithm = parse_literal(logger, graph, parent_node, SPDX_NAMESPACE.algorithm, - method_to_apply=convert_rdf_to_algorithm) + parsing_method=convert_rdf_to_algorithm) value = parse_literal(logger, graph, parent_node, SPDX_NAMESPACE.checksumValue) raise_parsing_error_if_logger_has_messages(logger, "Checksum") checksum = construct_or_raise_parsing_error(Checksum, dict(algorithm=algorithm, value=value)) diff --git a/src/spdx/parser/rdf/creation_info_parser.py b/src/spdx/parser/rdf/creation_info_parser.py index 35e0c2f42..d929df35b 100644 --- a/src/spdx/parser/rdf/creation_info_parser.py +++ b/src/spdx/parser/rdf/creation_info_parser.py @@ -34,7 +34,8 @@ def parse_creation_info(graph: Graph) -> Tuple[CreationInfo, URIRef]: logger = Logger() namespace, spdx_id, doc_node = parse_namespace_and_spdx_id(graph) spec_version = parse_literal(logger, graph, doc_node, SPDX_NAMESPACE.specVersion) - data_license = parse_literal(logger, graph, doc_node, SPDX_NAMESPACE.dataLicense, prefix=LICENSE_NAMESPACE) + data_license = parse_literal(logger, graph, doc_node, SPDX_NAMESPACE.dataLicense, + parsing_method=lambda x: x.removeprefix(LICENSE_NAMESPACE)) comment = parse_literal(logger, graph, doc_node, RDFS.comment) name = parse_literal(logger, graph, doc_node, SPDX_NAMESPACE.name) @@ -44,9 +45,9 @@ def parse_creation_info(graph: Graph) -> Tuple[CreationInfo, URIRef]: raise SPDXParsingError([f"Error while parsing document {name}: {logger.get_messages()}"]) created = parse_literal(logger, graph, creation_info_node, SPDX_NAMESPACE.created, - method_to_apply=datetime_from_str) + parsing_method=datetime_from_str) license_list_version = parse_literal(logger, graph, creation_info_node, SPDX_NAMESPACE.licenseListVersion, - method_to_apply=Version.from_string) + parsing_method=Version.from_string) creator_comment = parse_literal(logger, graph, creation_info_node, RDFS.comment) creators = [] for (_, _, creator_literal) in graph.triples((creation_info_node, SPDX_NAMESPACE.creator, None)): diff --git a/src/spdx/parser/rdf/file_parser.py b/src/spdx/parser/rdf/file_parser.py index 1687cee76..fc7daf26c 100644 --- a/src/spdx/parser/rdf/file_parser.py +++ b/src/spdx/parser/rdf/file_parser.py @@ -33,13 +33,12 @@ def parse_file(file_node: URIRef, graph: Graph, doc_namespace: str) -> File: file_types.append(convert_uri_ref_to_file_type(file_type_ref)) except KeyError: logger.append(f"Invalid FileType: {file_type_ref}") - license_concluded = parse_literal_or_no_assertion_or_none(logger, graph, file_node, - SPDX_NAMESPACE.licenseConcluded, - method_to_apply=lambda x: parse_license_expression(x, - graph)) + license_concluded = parse_literal_or_no_assertion_or_none(logger, graph, file_node, SPDX_NAMESPACE.licenseConcluded, + parsing_method=lambda x: parse_license_expression(x, + graph)) license_comment = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.licenseComments) copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, file_node, SPDX_NAMESPACE.copyrightText, - method_to_apply=str) + parsing_method=str) file_contributors = [] for (_, _, file_contributor) in graph.triples((file_node, SPDX_NAMESPACE.fileContributor, None)): file_contributors.append(file_contributor.toPython()) diff --git a/src/spdx/parser/rdf/graph_parsing_functions.py b/src/spdx/parser/rdf/graph_parsing_functions.py index 79180385d..c41fb3730 100644 --- a/src/spdx/parser/rdf/graph_parsing_functions.py +++ b/src/spdx/parser/rdf/graph_parsing_functions.py @@ -24,20 +24,24 @@ from spdx.casing_tools import camel_case_to_snake_case -def parse_literal(logger: Logger, graph: Graph, subject: Node, predicate: Node, default: Any = None, - method_to_apply: Callable = lambda x: x, prefix: str = ""): +def parse_literal(logger: Logger, graph: Graph, subject: Node, predicate: Node, parsing_method: Callable = str, + default: Any = None): value = get_unique_value(logger, graph, subject, predicate, default) if not value: return default + return apply_parsing_method_or_log_error(logger, value, parsing_method, default) + + +def apply_parsing_method_or_log_error(logger: Logger, value: Any, parsing_method: Callable = str, default: Any = None): try: - return method_to_apply(value.removeprefix(prefix)) + return parsing_method(value) except SPDXParsingError as err: logger.extend(err.get_messages()) return default def parse_literal_or_no_assertion_or_none(logger: Logger, graph: Graph, subject: Node, predicate: Node, - default: Any = None, method_to_apply: Callable = lambda x: x): + parsing_method: Callable = str, default: Any = None): value = get_unique_value(logger, graph, subject, predicate, default) if not value: return default @@ -45,25 +49,17 @@ def parse_literal_or_no_assertion_or_none(logger: Logger, graph: Graph, subject: return SpdxNoAssertion() if value == SPDX_NAMESPACE.none or value.toPython() == SPDX_NONE_STRING: return SpdxNone() - try: - return method_to_apply(value) - except SPDXParsingError as err: - logger.extend(err.get_messages()) - return default + return apply_parsing_method_or_log_error(logger, value, parsing_method, default) def parse_literal_or_no_assertion(logger: Logger, graph: Graph, subject: Node, predicate: Node, - default: Any = None, method_to_apply: Callable = lambda x: x): + parsing_method: Callable = str, default: Any = None): value = get_unique_value(logger, graph, subject, predicate, default) if not value: return default if value == SPDX_NAMESPACE.noassertion or value.toPython() == SPDX_NO_ASSERTION_STRING: return SpdxNoAssertion() - try: - return method_to_apply(value) - except SPDXParsingError as err: - logger.extend(err.get_messages()) - return default + return apply_parsing_method_or_log_error(logger, value, parsing_method, default) def get_unique_value(logger: Logger, graph: Graph, subject: Node, predicate: Node, default: Any) -> Any: @@ -75,9 +71,11 @@ def get_unique_value(logger: Logger, graph: Graph, subject: Node, predicate: Nod return default -def parse_enum_value(enum_str: str, enum_class: Type[Enum]) -> Enum: +def parse_enum_value(enum_str: str, enum_class: Type[Enum], prefix: str) -> Enum: try: - return enum_class[camel_case_to_snake_case(enum_str).upper()] + enum_without_rdf_prefix = enum_str.removeprefix(prefix) + value = camel_case_to_snake_case(enum_without_rdf_prefix).upper() + return enum_class[value] except KeyError: raise SPDXParsingError([f"Invalid value for {enum_class}: {enum_str}"]) diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index 750fe6273..616f113f8 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -34,8 +34,7 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac spdx_id = parse_spdx_id(package_node, doc_namespace, graph) name = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.name) download_location = parse_literal_or_no_assertion_or_none(logger, graph, package_node, - SPDX_NAMESPACE.downloadLocation, - method_to_apply=str) + SPDX_NAMESPACE.downloadLocation, parsing_method=str) checksums = [] for (_, _, checksum_node) in graph.triples((package_node, SPDX_NAMESPACE.checksum, None)): checksums.append(parse_checksum(checksum_node, graph)) @@ -63,33 +62,32 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac files_analyzed = bool(graph.value(package_node, SPDX_NAMESPACE.filesAnalyzed, default=True)) license_concluded = parse_literal_or_no_assertion_or_none(logger, graph, package_node, SPDX_NAMESPACE.licenseConcluded, - method_to_apply=lambda x: parse_license_expression(x, - graph)) + parsing_method=lambda x: parse_license_expression(x, + graph)) license_declared = parse_literal_or_no_assertion_or_none(logger, graph, package_node, SPDX_NAMESPACE.licenseDeclared, - method_to_apply=lambda x: parse_license_expression(x, - graph)) + parsing_method=lambda x: parse_license_expression(x, + graph)) license_comment = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.licenseComments) comment = parse_literal(logger, graph, package_node, RDFS.comment) summary = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.summary) description = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.description) copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, package_node, SPDX_NAMESPACE.copyrightText, - method_to_apply=str) + parsing_method=str) source_info = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.sourceInfo) primary_package_purpose = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.primaryPackagePurpose, - prefix=SPDX_NAMESPACE.purpose_, - method_to_apply=lambda x: parse_enum_value(x, PackagePurpose)) + parsing_method=lambda x: parse_enum_value(x, PackagePurpose, + SPDX_NAMESPACE.purpose_)) homepage = parse_literal(logger, graph, package_node, DOAP.homepage) attribution_texts = [] for (_, _, attribution_text_literal) in graph.triples((package_node, SPDX_NAMESPACE.attributionText, None)): attribution_texts.append(attribution_text_literal.toPython()) release_date = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.releaseDate, - method_to_apply=datetime_from_str) - built_date = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.builtDate, - method_to_apply=datetime_from_str) + parsing_method=datetime_from_str) + built_date = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.builtDate, parsing_method=datetime_from_str) valid_until_date = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.validUntilDate, - method_to_apply=datetime_from_str) + parsing_method=datetime_from_str) raise_parsing_error_if_logger_has_messages(logger, "Package") package = construct_or_raise_parsing_error(Package, @@ -152,10 +150,10 @@ def parse_external_package_ref(external_package_ref_node: URIRef, graph: Graph) logger = Logger() ref_locator = parse_literal(logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceLocator) ref_category = parse_literal(logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceCategory, - prefix=SPDX_NAMESPACE.referenceCategory_, - method_to_apply=lambda x: parse_enum_value(x, ExternalPackageRefCategory)) + parsing_method=lambda x: parse_enum_value(x, ExternalPackageRefCategory, + SPDX_NAMESPACE.referenceCategory_, )) ref_type = parse_literal(logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceType, - prefix=REFERENCE_NAMESPACE) + parsing_method=lambda x: x.removeprefix(REFERENCE_NAMESPACE)) comment = parse_literal(logger, graph, external_package_ref_node, RDFS.comment) raise_parsing_error_if_logger_has_messages(logger, "ExternalPackageRef") diff --git a/src/spdx/parser/rdf/relationship_parser.py b/src/spdx/parser/rdf/relationship_parser.py index c3ef32023..9492de662 100644 --- a/src/spdx/parser/rdf/relationship_parser.py +++ b/src/spdx/parser/rdf/relationship_parser.py @@ -24,13 +24,13 @@ def parse_relationship(relationship_node: URIRef, graph: Graph, parent_node: URI spdx_element_id = parse_spdx_id(parent_node, doc_namespace, graph) relationship_type = parse_literal(logger, graph, relationship_node, SPDX_NAMESPACE.relationshipType, - prefix=SPDX_NAMESPACE.relationshipType_, - method_to_apply=lambda x: parse_enum_value(x, RelationshipType)) + parsing_method=lambda x: parse_enum_value(x, RelationshipType, + SPDX_NAMESPACE.relationshipType_)) related_spdx_element = parse_literal_or_no_assertion_or_none(logger, graph, relationship_node, SPDX_NAMESPACE.relatedSpdxElement, - method_to_apply=lambda x: parse_spdx_id(x, - doc_namespace, - graph)) + parsing_method=lambda x: parse_spdx_id(x, + doc_namespace, + graph)) comment = parse_literal(logger, graph, relationship_node, RDFS.comment) raise_parsing_error_if_logger_has_messages(logger, "Relationship") diff --git a/src/spdx/parser/rdf/snippet_parser.py b/src/spdx/parser/rdf/snippet_parser.py index 21b66251c..42a7e001d 100644 --- a/src/spdx/parser/rdf/snippet_parser.py +++ b/src/spdx/parser/rdf/snippet_parser.py @@ -30,11 +30,11 @@ def parse_snippet(snippet_node: URIRef, graph: Graph, doc_namespace: str) -> Sni line_range = parse_ranges(snippet_node, graph, POINTER_NAMESPACE.LineCharPointer, POINTER_NAMESPACE.lineNumber) license_concluded = parse_literal_or_no_assertion_or_none(logger, graph, snippet_node, SPDX_NAMESPACE.licenseConcluded, - method_to_apply=lambda x: parse_license_expression(x, - graph)) + parsing_method=lambda x: parse_license_expression(x, + graph)) license_comment = parse_literal(logger, graph, snippet_node, SPDX_NAMESPACE.licenseComments) copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, snippet_node, SPDX_NAMESPACE.copyrightText, - method_to_apply=str) + parsing_method=str) comment = parse_literal(logger, graph, snippet_node, RDFS.comment) name = parse_literal(logger, graph, snippet_node, SPDX_NAMESPACE.name) attribution_texts = [] From a946a6a2e9dab18f0485efc3cd699266709b9075 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Feb 2023 12:07:30 +0100 Subject: [PATCH 256/362] [issue-456] parse list of license expressions Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/file_parser.py | 9 +++++++-- src/spdx/parser/rdf/graph_parsing_functions.py | 10 ++++++++++ src/spdx/parser/rdf/package_parser.py | 9 +++++++-- src/spdx/parser/rdf/snippet_parser.py | 11 +++++++++-- tests/spdx/parser/rdf/test_file_parser.py | 5 +++++ tests/spdx/parser/rdf/test_package_parser.py | 6 ++++-- tests/spdx/parser/rdf/test_snippet_parser.py | 4 +++- 7 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/spdx/parser/rdf/file_parser.py b/src/spdx/parser/rdf/file_parser.py index fc7daf26c..f3d190862 100644 --- a/src/spdx/parser/rdf/file_parser.py +++ b/src/spdx/parser/rdf/file_parser.py @@ -14,7 +14,8 @@ from spdx.parser.logger import Logger from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.rdf.checksum_parser import parse_checksum -from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, parse_literal_or_no_assertion_or_none +from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, parse_literal_or_no_assertion_or_none, \ + get_correct_typed_value from spdx.parser.rdf.license_expression_parser import parse_license_expression from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -36,6 +37,10 @@ def parse_file(file_node: URIRef, graph: Graph, doc_namespace: str) -> File: license_concluded = parse_literal_or_no_assertion_or_none(logger, graph, file_node, SPDX_NAMESPACE.licenseConcluded, parsing_method=lambda x: parse_license_expression(x, graph)) + license_info_in_file = [] + for (_, _, license_info_from_files_node) in graph.triples((file_node, SPDX_NAMESPACE.licenseInfoInFile, None)): + license_info_in_file.append( + get_correct_typed_value(logger, license_info_from_files_node, lambda x: parse_license_expression(x, graph))) license_comment = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.licenseComments) copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, file_node, SPDX_NAMESPACE.copyrightText, parsing_method=str) @@ -55,7 +60,7 @@ def parse_file(file_node: URIRef, graph: Graph, doc_namespace: str) -> File: contributors=file_contributors, license_comment=license_comment, license_concluded=license_concluded, - license_info_in_file=None, + license_info_in_file=license_info_in_file, notice=notice_text)) return file diff --git a/src/spdx/parser/rdf/graph_parsing_functions.py b/src/spdx/parser/rdf/graph_parsing_functions.py index c41fb3730..03d02cb8c 100644 --- a/src/spdx/parser/rdf/graph_parsing_functions.py +++ b/src/spdx/parser/rdf/graph_parsing_functions.py @@ -52,6 +52,16 @@ def parse_literal_or_no_assertion_or_none(logger: Logger, graph: Graph, subject: return apply_parsing_method_or_log_error(logger, value, parsing_method, default) +def get_correct_typed_value(logger: Logger, value: Any, parsing_method: Callable = str, default: Any = None): + if not value: + return default + if value == SPDX_NAMESPACE.noassertion or value.toPython() == SPDX_NO_ASSERTION_STRING: + return SpdxNoAssertion() + if value == SPDX_NAMESPACE.none or value.toPython() == SPDX_NONE_STRING: + return SpdxNone() + return apply_parsing_method_or_log_error(logger, value, parsing_method, default) + + def parse_literal_or_no_assertion(logger: Logger, graph: Graph, subject: Node, predicate: Node, parsing_method: Callable = str, default: Any = None): value = get_unique_value(logger, graph, subject, predicate, default) diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index 616f113f8..c21d6b2f6 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -24,7 +24,7 @@ from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error from spdx.parser.rdf.checksum_parser import parse_checksum from spdx.parser.rdf.graph_parsing_functions import parse_spdx_id, parse_literal, parse_enum_value, \ - parse_literal_or_no_assertion_or_none + parse_literal_or_no_assertion_or_none, get_correct_typed_value from spdx.parser.rdf.license_expression_parser import parse_license_expression from spdx.rdfschema.namespace import SPDX_NAMESPACE, REFERENCE_NAMESPACE @@ -68,6 +68,11 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac SPDX_NAMESPACE.licenseDeclared, parsing_method=lambda x: parse_license_expression(x, graph)) + license_info_from_files = [] + for (_, _, license_info_from_files_node) in graph.triples( + (package_node, SPDX_NAMESPACE.licenseInfoFromFiles, None)): + license_info_from_files.append( + get_correct_typed_value(logger, license_info_from_files_node, lambda x: parse_license_expression(x, graph))) license_comment = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.licenseComments) comment = parse_literal(logger, graph, package_node, RDFS.comment) summary = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.summary) @@ -99,7 +104,7 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac checksums=checksums, homepage=homepage, source_info=source_info, license_concluded=license_concluded, - license_info_from_files=None, + license_info_from_files=license_info_from_files, license_declared=license_declared, license_comment=license_comment, copyright_text=copyright_text, summary=summary, diff --git a/src/spdx/parser/rdf/snippet_parser.py b/src/spdx/parser/rdf/snippet_parser.py index 42a7e001d..92b7f879a 100644 --- a/src/spdx/parser/rdf/snippet_parser.py +++ b/src/spdx/parser/rdf/snippet_parser.py @@ -16,7 +16,8 @@ from spdx.model.snippet import Snippet from spdx.parser.logger import Logger from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages -from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, parse_literal_or_no_assertion_or_none +from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, parse_literal_or_no_assertion_or_none, \ + get_correct_typed_value from spdx.parser.rdf.license_expression_parser import parse_license_expression from spdx.rdfschema.namespace import SPDX_NAMESPACE, POINTER_NAMESPACE @@ -32,6 +33,11 @@ def parse_snippet(snippet_node: URIRef, graph: Graph, doc_namespace: str) -> Sni SPDX_NAMESPACE.licenseConcluded, parsing_method=lambda x: parse_license_expression(x, graph)) + license_info_in_snippet = [] + for (_, _, license_info_in_snippet_node) in graph.triples( + (snippet_node, SPDX_NAMESPACE.licenseInfoInSnippet, None)): + license_info_in_snippet.append( + get_correct_typed_value(logger, license_info_in_snippet_node, lambda x: parse_license_expression(x, graph))) license_comment = parse_literal(logger, graph, snippet_node, SPDX_NAMESPACE.licenseComments) copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, snippet_node, SPDX_NAMESPACE.copyrightText, parsing_method=str) @@ -45,7 +51,8 @@ def parse_snippet(snippet_node: URIRef, graph: Graph, doc_namespace: str) -> Sni snippet = construct_or_raise_parsing_error(Snippet, dict(spdx_id=spdx_id, file_spdx_id=file_spdx_id, byte_range=byte_range, line_range=line_range, license_concluded=license_concluded, - license_info_in_snippet=None, license_comment=license_comment, + license_info_in_snippet=license_info_in_snippet, + license_comment=license_comment, copyright_text=copyright_text, comment=comment, name=name, attribution_texts=attribution_texts)) return snippet diff --git a/tests/spdx/parser/rdf/test_file_parser.py b/tests/spdx/parser/rdf/test_file_parser.py index ae3ecb0f2..eba8ec682 100644 --- a/tests/spdx/parser/rdf/test_file_parser.py +++ b/tests/spdx/parser/rdf/test_file_parser.py @@ -9,6 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import os +from unittest import TestCase import pytest from license_expression import get_spdx_licensing @@ -35,10 +36,13 @@ def test_parse_file(): assert file.copyright_text == "copyrightText" assert file.contributors == ["fileContributor"] assert file.license_concluded == get_spdx_licensing().parse("MIT AND GPL-2.0") + TestCase().assertCountEqual(file.license_info_in_file, + [get_spdx_licensing().parse("MIT"), get_spdx_licensing().parse("GPL-2.0")]) assert file.license_comment == "licenseComment" assert file.notice == "fileNotice" assert file.attribution_texts == ["fileAttributionText"] + @pytest.mark.parametrize("uri_ref,expected", [(SPDX_NAMESPACE.fileType_source, FileType.SOURCE), (SPDX_NAMESPACE.fileType_binary, FileType.BINARY), (SPDX_NAMESPACE.fileType_archive, FileType.ARCHIVE), @@ -55,6 +59,7 @@ def test_convert_uri_ref_to_file_type(uri_ref, expected): assert file_type == expected + def test_convert_uri_ref_to_file_type_error(): with pytest.raises(KeyError): convert_uri_ref_to_file_type(SPDX_NAMESPACE.filetype_SPDX) diff --git a/tests/spdx/parser/rdf/test_package_parser.py b/tests/spdx/parser/rdf/test_package_parser.py index b917a2020..f1e9615af 100644 --- a/tests/spdx/parser/rdf/test_package_parser.py +++ b/tests/spdx/parser/rdf/test_package_parser.py @@ -9,6 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import os +from unittest import TestCase from license_expression import get_spdx_licensing from rdflib import RDF, Graph @@ -38,6 +39,8 @@ def test_package_parser(): assert package.source_info == "sourceInfo" assert package.license_concluded == get_spdx_licensing().parse("MIT AND GPL-2.0") assert package.license_declared == get_spdx_licensing().parse("MIT AND GPL-2.0") + TestCase().assertCountEqual(package.license_info_from_files, + [get_spdx_licensing().parse("MIT"), get_spdx_licensing().parse("GPL-2.0")]) assert package.license_comment == "packageLicenseComment" assert package.copyright_text == "packageCopyrightText" assert package.verification_code == PackageVerificationCode(value="85ed0817af83a24ad8da68c2b5094de69833983c", @@ -53,10 +56,9 @@ def test_package_parser(): def test_external_package_ref_parser(): - graph = Graph().parse(os.path.join(os.path.dirname(__file__), "data/file_to_test_rdf_parser.rdf.xml")) package_node = graph.value(predicate=RDF.type, object=SPDX_NAMESPACE.Package) - external_package_ref_node = graph.value(package_node, SPDX_NAMESPACE.externalRef) + external_package_ref_node = graph.value(package_node, SPDX_NAMESPACE.externalRef) external_package_ref = parse_external_package_ref(external_package_ref_node, graph) diff --git a/tests/spdx/parser/rdf/test_snippet_parser.py b/tests/spdx/parser/rdf/test_snippet_parser.py index c0d322e90..c5ff277a5 100644 --- a/tests/spdx/parser/rdf/test_snippet_parser.py +++ b/tests/spdx/parser/rdf/test_snippet_parser.py @@ -9,6 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import os +from unittest import TestCase from license_expression import get_spdx_licensing from rdflib import Graph, RDF @@ -29,7 +30,8 @@ def test_parse_snippet(): assert snippet.byte_range == (1, 2) assert snippet.line_range == (3, 4) assert snippet.license_concluded == get_spdx_licensing().parse("MIT AND GPL-2.0") - assert snippet.license_info_in_snippet == None + TestCase().assertCountEqual(snippet.license_info_in_snippet, + [get_spdx_licensing().parse("MIT"), get_spdx_licensing().parse("GPL-2.0")]) assert snippet.license_comment == "snippetLicenseComment" assert snippet.copyright_text == "licenseCopyrightText" assert snippet.comment == "snippetComment" From 7c898248ee39ee5b2e98fc4996450d386f576848 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Feb 2023 12:43:53 +0100 Subject: [PATCH 257/362] [issue-456] also parse URIRefs with the documents namespace as prefix as license expression (could be ListedLicense, ExtractedLicensingInfo or simple reference) Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/file_parser.py | 9 ++++--- .../parser/rdf/license_expression_parser.py | 13 +++++----- src/spdx/parser/rdf/package_parser.py | 17 ++++++------ src/spdx/parser/rdf/snippet_parser.py | 10 +++---- .../rdf/test_license_expression_parser.py | 26 +++++++++++++------ 5 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/spdx/parser/rdf/file_parser.py b/src/spdx/parser/rdf/file_parser.py index f3d190862..f725f4964 100644 --- a/src/spdx/parser/rdf/file_parser.py +++ b/src/spdx/parser/rdf/file_parser.py @@ -34,13 +34,14 @@ def parse_file(file_node: URIRef, graph: Graph, doc_namespace: str) -> File: file_types.append(convert_uri_ref_to_file_type(file_type_ref)) except KeyError: logger.append(f"Invalid FileType: {file_type_ref}") - license_concluded = parse_literal_or_no_assertion_or_none(logger, graph, file_node, SPDX_NAMESPACE.licenseConcluded, - parsing_method=lambda x: parse_license_expression(x, - graph)) + license_concluded = parse_literal_or_no_assertion_or_none( + logger, graph, file_node, SPDX_NAMESPACE.licenseConcluded, + parsing_method=lambda x: parse_license_expression(x, graph, doc_namespace)) license_info_in_file = [] for (_, _, license_info_from_files_node) in graph.triples((file_node, SPDX_NAMESPACE.licenseInfoInFile, None)): license_info_in_file.append( - get_correct_typed_value(logger, license_info_from_files_node, lambda x: parse_license_expression(x, graph))) + get_correct_typed_value(logger, license_info_from_files_node, + lambda x: parse_license_expression(x, graph, doc_namespace))) license_comment = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.licenseComments) copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, file_node, SPDX_NAMESPACE.copyrightText, parsing_method=str) diff --git a/src/spdx/parser/rdf/license_expression_parser.py b/src/spdx/parser/rdf/license_expression_parser.py index 9093a5cb7..545bb19e3 100644 --- a/src/spdx/parser/rdf/license_expression_parser.py +++ b/src/spdx/parser/rdf/license_expression_parser.py @@ -16,19 +16,22 @@ from spdx.rdfschema.namespace import SPDX_NAMESPACE, LICENSE_NAMESPACE -def parse_license_expression(license_expression_node: Node, graph: Graph) -> LicenseExpression: +def parse_license_expression(license_expression_node: Node, graph: Graph, doc_namespace: str) -> LicenseExpression: spdx_licensing = get_spdx_licensing() expression = "" if license_expression_node.startswith(LICENSE_NAMESPACE): expression = license_expression_node.removeprefix(LICENSE_NAMESPACE) return spdx_licensing.parse(expression) + if license_expression_node.startswith(doc_namespace): + expression = license_expression_node.fragment + return spdx_licensing.parse(expression) node_type = graph.value(license_expression_node, RDF.type) if node_type == SPDX_NAMESPACE.ConjunctiveLicenseSet: members = dict() for index, (_, _, member_node) in enumerate( graph.triples((license_expression_node, SPDX_NAMESPACE.member, None))): - members[index] = parse_license_expression(member_node, graph) + members[index] = parse_license_expression(member_node, graph, doc_namespace) if len(members) > 2: raise SPDXParsingError([f"A ConjunctiveLicenseSet can only have two members."]) expression = f"{members[0]} AND {members[1]}" @@ -36,13 +39,13 @@ def parse_license_expression(license_expression_node: Node, graph: Graph) -> Lic members = dict() for index, (_, _, member_node) in enumerate( graph.triples((license_expression_node, SPDX_NAMESPACE.member, None))): - members[index] = parse_license_expression(member_node, graph) + members[index] = parse_license_expression(member_node, graph, doc_namespace) if len(members) > 2: raise SPDXParsingError([f"A DisjunctiveLicenseSet can only have two members."]) expression = f"{members[0]} OR {members[1]}" if node_type == SPDX_NAMESPACE.WithExceptionOperator: license_expression = parse_license_expression(graph.value(license_expression_node, SPDX_NAMESPACE.member), - graph) + graph, doc_namespace) exception = parse_license_exception(graph.value(license_expression_node, SPDX_NAMESPACE.licenseException), graph) expression = f"{license_expression} WITH {exception}" @@ -56,5 +59,3 @@ def parse_license_exception(exception_node: Node, graph: Graph) -> str: else: exception = graph.value(exception_node, SPDX_NAMESPACE.licenseExceptionId).toPython() return exception - -# need to be able to parse a ListedLicense as in spdx-spec example diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index c21d6b2f6..3999cd7a1 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -60,19 +60,18 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac for (_, _, external_package_ref_node) in graph.triples((package_node, SPDX_NAMESPACE.externalRef, None)): external_package_refs.append(parse_external_package_ref(external_package_ref_node, graph)) files_analyzed = bool(graph.value(package_node, SPDX_NAMESPACE.filesAnalyzed, default=True)) - license_concluded = parse_literal_or_no_assertion_or_none(logger, graph, package_node, - SPDX_NAMESPACE.licenseConcluded, - parsing_method=lambda x: parse_license_expression(x, - graph)) - license_declared = parse_literal_or_no_assertion_or_none(logger, graph, package_node, - SPDX_NAMESPACE.licenseDeclared, - parsing_method=lambda x: parse_license_expression(x, - graph)) + license_concluded = parse_literal_or_no_assertion_or_none( + logger, graph, package_node, SPDX_NAMESPACE.licenseConcluded, + parsing_method=lambda x: parse_license_expression(x, graph, doc_namespace)) + license_declared = parse_literal_or_no_assertion_or_none( + logger, graph, package_node, SPDX_NAMESPACE.licenseDeclared, + parsing_method=lambda x: parse_license_expression(x, graph, doc_namespace)) license_info_from_files = [] for (_, _, license_info_from_files_node) in graph.triples( (package_node, SPDX_NAMESPACE.licenseInfoFromFiles, None)): license_info_from_files.append( - get_correct_typed_value(logger, license_info_from_files_node, lambda x: parse_license_expression(x, graph))) + get_correct_typed_value(logger, license_info_from_files_node, + lambda x: parse_license_expression(x, graph, doc_namespace))) license_comment = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.licenseComments) comment = parse_literal(logger, graph, package_node, RDFS.comment) summary = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.summary) diff --git a/src/spdx/parser/rdf/snippet_parser.py b/src/spdx/parser/rdf/snippet_parser.py index 92b7f879a..e62074594 100644 --- a/src/spdx/parser/rdf/snippet_parser.py +++ b/src/spdx/parser/rdf/snippet_parser.py @@ -29,15 +29,15 @@ def parse_snippet(snippet_node: URIRef, graph: Graph, doc_namespace: str) -> Sni file_spdx_id = parse_spdx_id(file_spdx_id_uri, doc_namespace, graph) byte_range = parse_ranges(snippet_node, graph, POINTER_NAMESPACE.ByteOffsetPointer, POINTER_NAMESPACE.offset) line_range = parse_ranges(snippet_node, graph, POINTER_NAMESPACE.LineCharPointer, POINTER_NAMESPACE.lineNumber) - license_concluded = parse_literal_or_no_assertion_or_none(logger, graph, snippet_node, - SPDX_NAMESPACE.licenseConcluded, - parsing_method=lambda x: parse_license_expression(x, - graph)) + license_concluded = parse_literal_or_no_assertion_or_none( + logger, graph, snippet_node, SPDX_NAMESPACE.licenseConcluded, + parsing_method=lambda x: parse_license_expression(x, graph, doc_namespace)) license_info_in_snippet = [] for (_, _, license_info_in_snippet_node) in graph.triples( (snippet_node, SPDX_NAMESPACE.licenseInfoInSnippet, None)): license_info_in_snippet.append( - get_correct_typed_value(logger, license_info_in_snippet_node, lambda x: parse_license_expression(x, graph))) + get_correct_typed_value(logger, license_info_in_snippet_node, + lambda x: parse_license_expression(x, graph, doc_namespace))) license_comment = parse_literal(logger, graph, snippet_node, SPDX_NAMESPACE.licenseComments) copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, snippet_node, SPDX_NAMESPACE.copyrightText, parsing_method=str) diff --git a/tests/spdx/parser/rdf/test_license_expression_parser.py b/tests/spdx/parser/rdf/test_license_expression_parser.py index 5d2112647..2d6671e2a 100644 --- a/tests/spdx/parser/rdf/test_license_expression_parser.py +++ b/tests/spdx/parser/rdf/test_license_expression_parser.py @@ -9,9 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. import os +from unittest import TestCase from license_expression import get_spdx_licensing from rdflib import Graph, RDF, URIRef +from spdx.parser.rdf import rdf_parser from spdx.parser.rdf.license_expression_parser import parse_license_expression from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -23,16 +25,24 @@ def test_license_expression_parser(): package_node = graph.value(predicate=RDF.type, object=SPDX_NAMESPACE.Package) license_expression_node = graph.value(subject=package_node, predicate=SPDX_NAMESPACE.licenseConcluded) - license_expression = parse_license_expression(license_expression_node, graph) + license_expression = parse_license_expression(license_expression_node, graph, "https://some.namespace#") assert license_expression == get_spdx_licensing().parse("GPL-2.0 AND MIT") -def test_license_expression_parser_with_writer(): - license_expression = get_spdx_licensing().parse("GPL-2.0 WITH exception") - graph = Graph() - add_license_expression_to_graph(license_expression, graph, URIRef("test"), URIRef("predicate"), "anyURI") - expression_noe = graph.value(URIRef("test"), URIRef("predicate")) - license_expression_parsed = parse_license_expression(expression_noe,graph) +def test_license_expression_parser_with_coupled_licenses(): + doc = rdf_parser.parse_from_file( + os.path.join(os.path.dirname(__file__), "../../data/formats/SPDXRdfExample-v2.3.spdx.rdf.xml")) - assert license_expression_parsed == license_expression + packages_by_spdx_id = {package.spdx_id: package for package in doc.packages} + files_by_spdx_id = {file.spdx_id: file for file in doc.files} + + assert packages_by_spdx_id["SPDXRef-Package"].license_declared == get_spdx_licensing().parse( + "LGPL-2.0-only AND LicenseRef-3") + assert packages_by_spdx_id["SPDXRef-Package"].license_concluded == get_spdx_licensing().parse( + "LGPL-2.0-only OR LicenseRef-3") + TestCase().assertCountEqual(packages_by_spdx_id["SPDXRef-Package"].license_info_from_files, + [get_spdx_licensing().parse("GPL-2.0"), get_spdx_licensing().parse("LicenseRef-1"), + get_spdx_licensing().parse("LicenseRef-2")]) + + assert files_by_spdx_id["SPDXRef-JenaLib"].license_concluded == get_spdx_licensing().parse("LicenseRef-1") From 565962379f1b6f81e22f2e990f27a6e2e2b19194 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Feb 2023 13:44:15 +0100 Subject: [PATCH 258/362] [issue-456] add rdf parser to cli tool Signed-off-by: Meret Behrens --- src/spdx/parser/parse_anything.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/spdx/parser/parse_anything.py b/src/spdx/parser/parse_anything.py index 0180abe92..34b15d1dd 100644 --- a/src/spdx/parser/parse_anything.py +++ b/src/spdx/parser/parse_anything.py @@ -10,6 +10,7 @@ # limitations under the License. from spdx.formats import file_name_to_format, FileFormat from spdx.parser.json import json_parser +from spdx.parser.rdf import rdf_parser from spdx.parser.xml import xml_parser from spdx.parser.yaml import yaml_parser @@ -17,7 +18,7 @@ def parse_file(file_name: str): input_format = file_name_to_format(file_name) if input_format == FileFormat.RDF_XML: - raise NotImplementedError("Currently, the rdf parser is not implemented") + return rdf_parser.parse_from_file(file_name) elif input_format == FileFormat.TAG_VALUE: raise NotImplementedError("Currently, the tag-value parser is not implemented") elif input_format == FileFormat.JSON: From 924a259c8eb19b63310f0c02dcbb6d346a955dec Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Feb 2023 13:46:17 +0100 Subject: [PATCH 259/362] [issue-456] delete RDFExample from specVersion 2.1 as the tool currently only supports SPDX 2.3 Signed-off-by: Meret Behrens --- tests/spdx/data/formats/SPDXRdfExample.rdf | 325 --------------------- 1 file changed, 325 deletions(-) delete mode 100644 tests/spdx/data/formats/SPDXRdfExample.rdf diff --git a/tests/spdx/data/formats/SPDXRdfExample.rdf b/tests/spdx/data/formats/SPDXRdfExample.rdf deleted file mode 100644 index d5912d8a8..000000000 --- a/tests/spdx/data/formats/SPDXRdfExample.rdf +++ /dev/null @@ -1,325 +0,0 @@ - - - - - - from linux kernel - Copyright 2008-2010 John Smith - The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz. - This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0-or-later. - - - - - Sample_Document-V2.1 - - - 2010-02-03T00:00:00Z - This is an example of an SPDX spreadsheet format - Tool: SourceAuditor-V1.2 - Organization: Source Auditor Inc. - Person: Gary O'Neall - - - SPDX-2.1 - - - DocumentRef-spdx-tool-2.1 - - - - d6a770ba38583ed4bb4525bd96e50461655d2759 - - - - - - - > - - - /* - * (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - LicenseRef-1 - - - - - http://www.openjena.org/ - Jena - - - This license is used by Jena - Jenna-2.6.3/jena-2.6.3-sources.jar - - - (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP - - This file belongs to Jena - - - 3ab4e1c67a2d28fced849ee1bb76e7391b93f125 - - - - - - 3ab4e1c67a2d28fced849ee1bb76e7391b93f1250000000000000000 - - - - - - - - This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses - 2010-02-10T00:00:00Z - Person: Joe Reviewer - - - - - - - Another example reviewer. - 2011-03-13T00:00:00Z - Person: Suzanne Reviewer - - - - - - This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses - 2012-06-13T00:00:00Z - Person: Jim Reviewer - - - - - - - - - - - Copyright 2010, 2011 Source Auditor Inc. - - - - - - - 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 - - - - - - 2fd4e1c67a2d28fced849ee1bb76e7391b93eb120000000000000000 - - - - src/org/spdx/parser/DOAPProject.java - - - - - - http://www.spdx.org/tools - true - Organization:Linux Foundation - - - - - 4e3211c67a2d28fced849ee1bb76e7391b93feba - SpdxTranslatorSpdx.txt - SpdxTranslatorSpdx.rdf - - - - - 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 - - - - - - - - This package includes the GRDDL parser developed by Hewlett Packard under the following license: -© Copyright 2007 Hewlett-Packard Development Company, LP - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - LicenseRef-2 - - - - - - - - - /* - * (c) Copyright 2009 University of Bristol - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - LicenseRef-4 - - - - - http://justasample.url.com - http://people.apache.org/~andyc/neko/LICENSE - CyberNeko License - This is tye CyperNeko License - The CyberNeko Software License, Version 1.0 - - -(C) Copyright 2002-2005, Andy Clark. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - -3. The end-user documentation included with the redistribution, - if any, must include the following acknowledgment: - "This product includes software developed by Andy Clark." - Alternately, this acknowledgment may appear in the software itself, - if and wherever such third-party acknowledgments normally appear. - -4. The names "CyberNeko" and "NekoHTML" must not be used to endorse - or promote products derived from this software without prior - written permission. For written permission, please contact - andyc@cyberneko.net. - -5. Products derived from this software may not be called "CyberNeko", - nor may "CyberNeko" appear in their name, without prior written - permission of the author. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - LicenseRef-3 - - - - - Version 1.0 of the SPDX Translator application - - - 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 - - - - spdxtranslator-1.0.zip - This utility translates and SPDX RDF XML document to a spreadsheet, translates a spreadsheet to an SPDX RDF XML document and translates an SPDX RDFa document to an SPDX RDF XML document. - - SPDX Translator - Version 0.9.2 - - - - Copyright 2010, 2011 Source Auditor Inc. - - - - - - - - - - - - Organization:SPDX - The declared license information can be found in the NOTICE file at the root of the archive file - SPDX Translator utility - - - - - - org.apache.commons:commons-lang:3.2.1 - NIST National Vulnerability Database (NVD) describes security vulnerabilities (CVEs) which affect Vendor Product Version acmecorp:acmenator:6.6.6 - - - - - This is a sample spreadsheet - - - - - From 7b6f956175f904a43a5a51bb9473652a329087d0 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Feb 2023 13:59:35 +0100 Subject: [PATCH 260/362] [issue-456, refactor] use generic helper methods instead of individual helper method, rename argument Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/annotation_parser.py | 4 +- src/spdx/parser/rdf/package_parser.py | 55 ++++++------------------ 2 files changed, 15 insertions(+), 44 deletions(-) diff --git a/src/spdx/parser/rdf/annotation_parser.py b/src/spdx/parser/rdf/annotation_parser.py index 4b6a29cc7..b676686b5 100644 --- a/src/spdx/parser/rdf/annotation_parser.py +++ b/src/spdx/parser/rdf/annotation_parser.py @@ -19,9 +19,9 @@ from spdx.rdfschema.namespace import SPDX_NAMESPACE -def parse_annotation(annotation_node: URIRef, graph: Graph, parent_ref: URIRef, doc_namespace) -> Annotation: +def parse_annotation(annotation_node: URIRef, graph: Graph, parent_node: URIRef, doc_namespace) -> Annotation: logger = Logger() - spdx_id = parse_spdx_id(parent_ref, doc_namespace, graph) + spdx_id = parse_spdx_id(parent_node, doc_namespace, graph) annotator = parse_literal(logger, graph, annotation_node, SPDX_NAMESPACE.annotator, parsing_method=ActorParser.parse_actor) annotation_type = parse_literal(logger, graph, annotation_node, SPDX_NAMESPACE.annotationType, diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index 3999cd7a1..09faf022e 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -11,20 +11,16 @@ from typing import Optional, Union from rdflib import URIRef, Graph, RDFS, DOAP -from rdflib.exceptions import UniquenessError from spdx.datetime_conversions import datetime_from_str -from spdx.model.actor import Actor from spdx.model.package import Package, PackagePurpose, ExternalPackageRef, PackageVerificationCode, \ ExternalPackageRefCategory -from spdx.model.spdx_no_assertion import SpdxNoAssertion -from spdx.parser.error import SPDXParsingError from spdx.parser.jsonlikedict.actor_parser import ActorParser from spdx.parser.logger import Logger from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error from spdx.parser.rdf.checksum_parser import parse_checksum from spdx.parser.rdf.graph_parsing_functions import parse_spdx_id, parse_literal, parse_enum_value, \ - parse_literal_or_no_assertion_or_none, get_correct_typed_value + parse_literal_or_no_assertion_or_none, get_correct_typed_value, parse_literal_or_no_assertion from spdx.parser.rdf.license_expression_parser import parse_license_expression from spdx.rdfschema.namespace import SPDX_NAMESPACE, REFERENCE_NAMESPACE @@ -41,21 +37,14 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac version_info = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.versionInfo) package_file_name = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.packageFileName) - try: - supplier = parse_actor_or_no_assertion(logger, graph, package_node, SPDX_NAMESPACE.supplier) - except SPDXParsingError as err: - logger.extend(err.get_messages()) - supplier = None - try: - originator = parse_actor_or_no_assertion(logger, graph, package_node, SPDX_NAMESPACE.originator) - except SPDXParsingError as err: - logger.extend(err.get_messages()) - originator = None - try: - verification_code = parse_package_verification_code(package_node, graph) - except SPDXParsingError as err: - logger.append(err.get_messages()) - verification_code = None + + supplier = parse_literal_or_no_assertion(logger, graph, package_node, SPDX_NAMESPACE.supplier, + parsing_method=ActorParser.parse_actor) + originator = parse_literal_or_no_assertion(logger, graph, package_node, SPDX_NAMESPACE.originator, + parsing_method=ActorParser.parse_actor) + verification_code = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.packageVerificationCode, + parsing_method=lambda x: parse_package_verification_code(x, graph)) + external_package_refs = [] for (_, _, external_package_ref_node) in graph.triples((package_node, SPDX_NAMESPACE.externalRef, None)): external_package_refs.append(parse_external_package_ref(external_package_ref_node, graph)) @@ -117,31 +106,13 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac return package -def parse_actor_or_no_assertion(logger, graph, parent_node, predicate) -> Optional[Union[SpdxNoAssertion, Actor]]: - try: - value = graph.value(parent_node, predicate, any=False) - except UniquenessError: - logger.append(f"Multiple values for unique value {predicate} found.") - return - if not value: - return None - if value == "NOASSERTION": - return SpdxNoAssertion() - return ActorParser.parse_actor(value) - - -def parse_package_verification_code(package_node: URIRef, graph: Graph) -> Optional[PackageVerificationCode]: - try: - package_node = graph.value(package_node, SPDX_NAMESPACE.packageVerificationCode, any=False) - except UniquenessError: - raise SPDXParsingError([f"Multiple values for unique value {SPDX_NAMESPACE.packageVerificationCode} found."]) - if not package_node: - return None +def parse_package_verification_code(package_verification_code_node: URIRef, graph: Graph) -> Optional[ + PackageVerificationCode]: logger = Logger() - value = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.packageVerificationCodeValue) + value = parse_literal(logger, graph, package_verification_code_node, SPDX_NAMESPACE.packageVerificationCodeValue) excluded_files = [] for (_, _, excluded_file_literal) in graph.triples( - (package_node, SPDX_NAMESPACE.packageVerificationCodeExcludedFile, None)): + (package_verification_code_node, SPDX_NAMESPACE.packageVerificationCodeExcludedFile, None)): excluded_files.append(excluded_file_literal.toPython()) raise_parsing_error_if_logger_has_messages(logger, "PackageVerificationCode") From 7b1c2ac8022aafb86f7394c115185e59bce4b682 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Feb 2023 14:04:14 +0100 Subject: [PATCH 261/362] [issue-456] move actor_parser.py as it is format-agnostic and used for json,yaml,xml and rdf parsing Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/annotation_parser.py | 2 +- src/spdx/parser/rdf/creation_info_parser.py | 2 +- src/spdx/parser/rdf/package_parser.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/spdx/parser/rdf/annotation_parser.py b/src/spdx/parser/rdf/annotation_parser.py index b676686b5..0223230da 100644 --- a/src/spdx/parser/rdf/annotation_parser.py +++ b/src/spdx/parser/rdf/annotation_parser.py @@ -12,7 +12,7 @@ from spdx.datetime_conversions import datetime_from_str from spdx.model.annotation import Annotation, AnnotationType -from spdx.parser.jsonlikedict.actor_parser import ActorParser +from spdx.parser.actor_parser import ActorParser from spdx.parser.logger import Logger from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, parse_enum_value diff --git a/src/spdx/parser/rdf/creation_info_parser.py b/src/spdx/parser/rdf/creation_info_parser.py index d929df35b..4ea484584 100644 --- a/src/spdx/parser/rdf/creation_info_parser.py +++ b/src/spdx/parser/rdf/creation_info_parser.py @@ -27,7 +27,7 @@ from spdx.model.document import CreationInfo from spdx.model.external_document_ref import ExternalDocumentRef from spdx.model.version import Version -from spdx.parser.jsonlikedict.actor_parser import ActorParser +from spdx.parser.actor_parser import ActorParser def parse_creation_info(graph: Graph) -> Tuple[CreationInfo, URIRef]: diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index 09faf022e..12ab09e4b 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -8,14 +8,14 @@ # 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 typing import Optional, Union +from typing import Optional from rdflib import URIRef, Graph, RDFS, DOAP from spdx.datetime_conversions import datetime_from_str from spdx.model.package import Package, PackagePurpose, ExternalPackageRef, PackageVerificationCode, \ ExternalPackageRefCategory -from spdx.parser.jsonlikedict.actor_parser import ActorParser +from spdx.parser.actor_parser import ActorParser from spdx.parser.logger import Logger from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error from spdx.parser.rdf.checksum_parser import parse_checksum From 9623b42f21c73c5298398fcf7c4f4a7a98bc24f8 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Feb 2023 14:44:38 +0100 Subject: [PATCH 262/362] [issue-456, refactor] adapt type hints, delete unused imports, rename, reformat Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/annotation_parser.py | 2 +- src/spdx/parser/rdf/checksum_parser.py | 1 + src/spdx/parser/rdf/creation_info_parser.py | 8 ++- .../rdf/extracted_licensing_info_parser.py | 3 +- .../parser/rdf/graph_parsing_functions.py | 7 ++- .../parser/rdf/license_expression_parser.py | 8 ++- src/spdx/parser/rdf/package_parser.py | 12 ++-- src/spdx/parser/rdf/rdf_parser.py | 61 +++++++++---------- src/spdx/parser/rdf/relationship_parser.py | 14 ++--- tests/spdx/parser/rdf/test_checksum_parser.py | 2 +- .../rdf/test_license_expression_parser.py | 4 +- 11 files changed, 60 insertions(+), 62 deletions(-) diff --git a/src/spdx/parser/rdf/annotation_parser.py b/src/spdx/parser/rdf/annotation_parser.py index 0223230da..88fe0ae65 100644 --- a/src/spdx/parser/rdf/annotation_parser.py +++ b/src/spdx/parser/rdf/annotation_parser.py @@ -19,7 +19,7 @@ from spdx.rdfschema.namespace import SPDX_NAMESPACE -def parse_annotation(annotation_node: URIRef, graph: Graph, parent_node: URIRef, doc_namespace) -> Annotation: +def parse_annotation(annotation_node: URIRef, graph: Graph, parent_node: URIRef, doc_namespace: str) -> Annotation: logger = Logger() spdx_id = parse_spdx_id(parent_node, doc_namespace, graph) annotator = parse_literal(logger, graph, annotation_node, SPDX_NAMESPACE.annotator, diff --git a/src/spdx/parser/rdf/checksum_parser.py b/src/spdx/parser/rdf/checksum_parser.py index c973a02e5..3d1eea31f 100644 --- a/src/spdx/parser/rdf/checksum_parser.py +++ b/src/spdx/parser/rdf/checksum_parser.py @@ -23,6 +23,7 @@ def parse_checksum(parent_node: URIRef, graph: Graph) -> Checksum: algorithm = parse_literal(logger, graph, parent_node, SPDX_NAMESPACE.algorithm, parsing_method=convert_rdf_to_algorithm) value = parse_literal(logger, graph, parent_node, SPDX_NAMESPACE.checksumValue) + raise_parsing_error_if_logger_has_messages(logger, "Checksum") checksum = construct_or_raise_parsing_error(Checksum, dict(algorithm=algorithm, value=value)) return checksum diff --git a/src/spdx/parser/rdf/creation_info_parser.py b/src/spdx/parser/rdf/creation_info_parser.py index 4ea484584..8f8938798 100644 --- a/src/spdx/parser/rdf/creation_info_parser.py +++ b/src/spdx/parser/rdf/creation_info_parser.py @@ -97,12 +97,14 @@ def parse_external_document_refs(external_document_node: URIRef, graph: Graph, logger = Logger() document_ref_id = parse_spdx_id(external_document_node, doc_namespace, graph) document_uri = parse_literal(logger, graph, external_document_node, SPDX_NAMESPACE.spdxDocument) - checksum = None - for (_, _, checksum_node) in graph.triples((external_document_node, SPDX_NAMESPACE.checksum, None)): - checksum = parse_checksum(checksum_node, graph) + checksum = parse_literal(logger, graph, external_document_node, SPDX_NAMESPACE.checksum, + parsing_method=lambda x: parse_checksum(x, graph)) external_document_ref = construct_or_raise_parsing_error(ExternalDocumentRef, dict(document_ref_id=document_ref_id, document_uri=document_uri, checksum=checksum)) + + # To replace the external doc namespaces by the ref id in spdx ids later (e.g. in a relationship), we need to bind + # the namespace to the graph. graph.bind(external_document_ref.document_ref_id, Namespace(external_document_ref.document_uri + "#")) return external_document_ref diff --git a/src/spdx/parser/rdf/extracted_licensing_info_parser.py b/src/spdx/parser/rdf/extracted_licensing_info_parser.py index d26b5cbf3..f9c278953 100644 --- a/src/spdx/parser/rdf/extracted_licensing_info_parser.py +++ b/src/spdx/parser/rdf/extracted_licensing_info_parser.py @@ -25,8 +25,7 @@ def parse_extracted_licensing_info(extracted_licensing_info_node: URIRef, graph: comment = parse_literal(logger, graph, extracted_licensing_info_node, RDFS.comment) license_name = parse_literal_or_no_assertion(logger, graph, extracted_licensing_info_node, SPDX_NAMESPACE.name) cross_references = [] - for (_, _, cross_reference_node) in graph.triples( - (extracted_licensing_info_node, RDFS.seeAlso, None)): + for (_, _, cross_reference_node) in graph.triples((extracted_licensing_info_node, RDFS.seeAlso, None)): cross_references.append(cross_reference_node.toPython()) raise_parsing_error_if_logger_has_messages(logger, "ExtractedLicensingInfo") extracted_licensing_info = construct_or_raise_parsing_error(ExtractedLicensingInfo, dict(license_id=license_id, diff --git a/src/spdx/parser/rdf/graph_parsing_functions.py b/src/spdx/parser/rdf/graph_parsing_functions.py index 03d02cb8c..b20626cff 100644 --- a/src/spdx/parser/rdf/graph_parsing_functions.py +++ b/src/spdx/parser/rdf/graph_parsing_functions.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from enum import Enum -from typing import Any, Callable, Union, Optional, Type +from typing import Any, Callable, Optional, Type from rdflib import Graph, URIRef from rdflib.exceptions import UniquenessError @@ -37,8 +37,9 @@ def apply_parsing_method_or_log_error(logger: Logger, value: Any, parsing_method return parsing_method(value) except SPDXParsingError as err: logger.extend(err.get_messages()) - return default - + except (TypeError, ValueError) as err: + logger.extend(err.args[0]) + return default def parse_literal_or_no_assertion_or_none(logger: Logger, graph: Graph, subject: Node, predicate: Node, parsing_method: Callable = str, default: Any = None): diff --git a/src/spdx/parser/rdf/license_expression_parser.py b/src/spdx/parser/rdf/license_expression_parser.py index 545bb19e3..5f2bcb89d 100644 --- a/src/spdx/parser/rdf/license_expression_parser.py +++ b/src/spdx/parser/rdf/license_expression_parser.py @@ -8,15 +8,17 @@ # 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 typing import Union + from rdflib import Graph, RDF from license_expression import LicenseExpression, get_spdx_licensing -from rdflib.term import Node +from rdflib.term import Identifier, URIRef, BNode, Node from spdx.parser.error import SPDXParsingError from spdx.rdfschema.namespace import SPDX_NAMESPACE, LICENSE_NAMESPACE -def parse_license_expression(license_expression_node: Node, graph: Graph, doc_namespace: str) -> LicenseExpression: +def parse_license_expression(license_expression_node: Union[URIRef, BNode, Node], graph: Graph, doc_namespace: str) -> LicenseExpression: spdx_licensing = get_spdx_licensing() expression = "" if license_expression_node.startswith(LICENSE_NAMESPACE): @@ -53,7 +55,7 @@ def parse_license_expression(license_expression_node: Node, graph: Graph, doc_na return spdx_licensing.parse(expression) -def parse_license_exception(exception_node: Node, graph: Graph) -> str: +def parse_license_exception(exception_node: Identifier, graph: Graph) -> str: if exception_node.startswith(LICENSE_NAMESPACE): exception = exception_node.removeprefix(LICENSE_NAMESPACE) else: diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index 12ab09e4b..b78c8cf9e 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -68,9 +68,9 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, package_node, SPDX_NAMESPACE.copyrightText, parsing_method=str) source_info = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.sourceInfo) - primary_package_purpose = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.primaryPackagePurpose, - parsing_method=lambda x: parse_enum_value(x, PackagePurpose, - SPDX_NAMESPACE.purpose_)) + primary_package_purpose = parse_literal( + logger, graph, package_node, SPDX_NAMESPACE.primaryPackagePurpose, + parsing_method=lambda x: parse_enum_value(x, PackagePurpose, SPDX_NAMESPACE.purpose_)) homepage = parse_literal(logger, graph, package_node, DOAP.homepage) attribution_texts = [] for (_, _, attribution_text_literal) in graph.triples((package_node, SPDX_NAMESPACE.attributionText, None)): @@ -124,9 +124,9 @@ def parse_package_verification_code(package_verification_code_node: URIRef, grap def parse_external_package_ref(external_package_ref_node: URIRef, graph: Graph) -> ExternalPackageRef: logger = Logger() ref_locator = parse_literal(logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceLocator) - ref_category = parse_literal(logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceCategory, - parsing_method=lambda x: parse_enum_value(x, ExternalPackageRefCategory, - SPDX_NAMESPACE.referenceCategory_, )) + ref_category = parse_literal( + logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceCategory, + parsing_method=lambda x: parse_enum_value(x, ExternalPackageRefCategory, SPDX_NAMESPACE.referenceCategory_, )) ref_type = parse_literal(logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceType, parsing_method=lambda x: x.removeprefix(REFERENCE_NAMESPACE)) comment = parse_literal(logger, graph, external_package_ref_node, RDFS.comment) diff --git a/src/spdx/parser/rdf/rdf_parser.py b/src/spdx/parser/rdf/rdf_parser.py index b6c7d32c9..e343f4b26 100644 --- a/src/spdx/parser/rdf/rdf_parser.py +++ b/src/spdx/parser/rdf/rdf_parser.py @@ -34,6 +34,7 @@ def parse_from_file(file_name: str) -> Document: def translate_graph_to_document(graph: Graph) -> Document: + parsed_fields = dict() logger = Logger() try: creation_info, doc_node = parse_creation_info(graph) @@ -41,43 +42,39 @@ def translate_graph_to_document(graph: Graph) -> Document: logger.extend(err.get_messages()) creation_info = None - packages = [] - for (package_node, _, _) in graph.triples((None, RDF.type, SPDX_NAMESPACE.Package)): - try: - packages.append(parse_package(package_node, graph, creation_info.document_namespace)) - except SPDXParsingError as err: - logger.extend(err.get_messages()) + parsed_fields["creation_info"] = creation_info - files = [] - for (file_node, _, _) in graph.triples((None, RDF.type, SPDX_NAMESPACE.File)): - try: - files.append(parse_file(file_node, graph, creation_info.document_namespace)) - except SPDXParsingError as err: - logger.extend(err.get_messages()) + for element, triple, parsing_method in [("packages", (None, RDF.type, SPDX_NAMESPACE.Package), parse_package), + ("files", (None, RDF.type, SPDX_NAMESPACE.File), parse_file), + ("snippets", (None, RDF.type, SPDX_NAMESPACE.Snippet), parse_snippet)]: + elements = [] + for (element_node, _, _) in graph.triples(triple): + try: + elements.append(parsing_method(element_node, graph, creation_info.document_namespace)) + except SPDXParsingError as err: + logger.extend(err.get_messages()) + parsed_fields[element] = elements + + for element, triple, parsing_method in [("annotations", (None, SPDX_NAMESPACE.annotation, None), parse_annotation), + ("relationships", (None, SPDX_NAMESPACE.relationship, None), + parse_relationship)]: + elements = [] + for (parent_node, _, element_node) in graph.triples(triple): + try: + elements.append(parsing_method(element_node, graph, parent_node, creation_info.document_namespace)) + except SPDXParsingError as err: + logger.extend(err.get_messages()) + parsed_fields[element] = elements - snippets = [] - for (snippet_node, _, _) in graph.triples((None, RDF.type, SPDX_NAMESPACE.Snippet)): + extracted_licensing_infos = [] + for (_, _, extracted_licensing_info_node) in graph.triples((None, SPDX_NAMESPACE.hasExtractedLicensingInfo, None)): try: - snippets.append(parse_snippet(snippet_node, graph, creation_info.document_namespace)) + extracted_licensing_infos.append(parse_extracted_licensing_info(extracted_licensing_info_node, graph)) except SPDXParsingError as err: logger.extend(err.get_messages()) + parsed_fields["extracted_licensing_info"] = extracted_licensing_infos - annotations = [] - for (parent_node, _, annotation_node) in graph.triples((None, SPDX_NAMESPACE.annotation, None)): - annotations.append(parse_annotation(annotation_node, graph, parent_node, creation_info.document_namespace)) - - relationships = [] - for (parent_node, _, relationship_node) in graph.triples((None, SPDX_NAMESPACE.relationship, None)): - relationships.append( - parse_relationship(relationship_node, graph, parent_node, creation_info.document_namespace)) - - extracted_licensing_infos = [] - for (_, _, extracted_licensing_info_node) in graph.triples((None, SPDX_NAMESPACE.hasExtractedLicensingInfo, None)): - extracted_licensing_infos.append(parse_extracted_licensing_info(extracted_licensing_info_node, graph)) raise_parsing_error_if_logger_has_messages(logger) - document = construct_or_raise_parsing_error(Document, - dict(creation_info=creation_info, snippets=snippets, files=files, - annotations=annotations, packages=packages, - relationships=relationships, - extracted_licensing_info=extracted_licensing_infos)) + document = construct_or_raise_parsing_error(Document, parsed_fields) + return document diff --git a/src/spdx/parser/rdf/relationship_parser.py b/src/spdx/parser/rdf/relationship_parser.py index 9492de662..c9a49403e 100644 --- a/src/spdx/parser/rdf/relationship_parser.py +++ b/src/spdx/parser/rdf/relationship_parser.py @@ -23,14 +23,12 @@ def parse_relationship(relationship_node: URIRef, graph: Graph, parent_node: URI logger = Logger() spdx_element_id = parse_spdx_id(parent_node, doc_namespace, graph) - relationship_type = parse_literal(logger, graph, relationship_node, SPDX_NAMESPACE.relationshipType, - parsing_method=lambda x: parse_enum_value(x, RelationshipType, - SPDX_NAMESPACE.relationshipType_)) - related_spdx_element = parse_literal_or_no_assertion_or_none(logger, graph, relationship_node, - SPDX_NAMESPACE.relatedSpdxElement, - parsing_method=lambda x: parse_spdx_id(x, - doc_namespace, - graph)) + relationship_type = parse_literal( + logger, graph, relationship_node, SPDX_NAMESPACE.relationshipType, + parsing_method=lambda x: parse_enum_value(x, RelationshipType, SPDX_NAMESPACE.relationshipType_)) + related_spdx_element = parse_literal_or_no_assertion_or_none( + logger, graph, relationship_node, SPDX_NAMESPACE.relatedSpdxElement, + parsing_method=lambda x: parse_spdx_id(x, doc_namespace, graph)) comment = parse_literal(logger, graph, relationship_node, RDFS.comment) raise_parsing_error_if_logger_has_messages(logger, "Relationship") diff --git a/tests/spdx/parser/rdf/test_checksum_parser.py b/tests/spdx/parser/rdf/test_checksum_parser.py index 698d1dba9..26b8e0149 100644 --- a/tests/spdx/parser/rdf/test_checksum_parser.py +++ b/tests/spdx/parser/rdf/test_checksum_parser.py @@ -11,7 +11,7 @@ import os import pytest -from rdflib import Graph, RDF, URIRef +from rdflib import Graph, URIRef from spdx.parser.error import SPDXParsingError from spdx.model.checksum import ChecksumAlgorithm diff --git a/tests/spdx/parser/rdf/test_license_expression_parser.py b/tests/spdx/parser/rdf/test_license_expression_parser.py index 2d6671e2a..0913a961a 100644 --- a/tests/spdx/parser/rdf/test_license_expression_parser.py +++ b/tests/spdx/parser/rdf/test_license_expression_parser.py @@ -12,13 +12,11 @@ from unittest import TestCase from license_expression import get_spdx_licensing -from rdflib import Graph, RDF, URIRef +from rdflib import Graph, RDF from spdx.parser.rdf import rdf_parser from spdx.parser.rdf.license_expression_parser import parse_license_expression from spdx.rdfschema.namespace import SPDX_NAMESPACE -from spdx.writer.rdf.license_expression_writer import add_license_expression_to_graph - def test_license_expression_parser(): graph = Graph().parse(os.path.join(os.path.dirname(__file__), "data/file_to_test_rdf_parser.rdf.xml")) From 2eb4fe6995cafc9c07e17a589fc87e537da876cd Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Feb 2023 15:44:53 +0100 Subject: [PATCH 263/362] [issue-456] add helper method to remove prefix to support Python 3.7 and Python 3.8 Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/checksum_parser.py | 4 ++-- src/spdx/parser/rdf/creation_info_parser.py | 4 ++-- src/spdx/parser/rdf/file_parser.py | 4 ++-- src/spdx/parser/rdf/graph_parsing_functions.py | 11 ++++++++++- src/spdx/parser/rdf/license_expression_parser.py | 8 +++++--- src/spdx/parser/rdf/package_parser.py | 4 ++-- tests/spdx/parser/rdf/test_graph_parsing_function.py | 10 +++++++++- 7 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/spdx/parser/rdf/checksum_parser.py b/src/spdx/parser/rdf/checksum_parser.py index 3d1eea31f..4ec73059e 100644 --- a/src/spdx/parser/rdf/checksum_parser.py +++ b/src/spdx/parser/rdf/checksum_parser.py @@ -14,7 +14,7 @@ from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.parser.logger import Logger from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages -from spdx.parser.rdf.graph_parsing_functions import parse_literal +from spdx.parser.rdf.graph_parsing_functions import parse_literal, remove_prefix from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -30,7 +30,7 @@ def parse_checksum(parent_node: URIRef, graph: Graph) -> Checksum: def convert_rdf_to_algorithm(algorithm: str) -> ChecksumAlgorithm: - algorithm = algorithm.removeprefix(SPDX_NAMESPACE.checksumAlgorithm_).upper() + algorithm = remove_prefix(algorithm, SPDX_NAMESPACE.checksumAlgorithm_).upper() if "BLAKE2B" in algorithm: algorithm = algorithm.replace("BLAKE2B", "BLAKE2B_") try: diff --git a/src/spdx/parser/rdf/creation_info_parser.py b/src/spdx/parser/rdf/creation_info_parser.py index 8f8938798..522b21e53 100644 --- a/src/spdx/parser/rdf/creation_info_parser.py +++ b/src/spdx/parser/rdf/creation_info_parser.py @@ -20,7 +20,7 @@ from spdx.parser.logger import Logger from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.rdf.checksum_parser import parse_checksum -from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id +from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, remove_prefix from spdx.rdfschema.namespace import SPDX_NAMESPACE, LICENSE_NAMESPACE from spdx.datetime_conversions import datetime_from_str @@ -35,7 +35,7 @@ def parse_creation_info(graph: Graph) -> Tuple[CreationInfo, URIRef]: namespace, spdx_id, doc_node = parse_namespace_and_spdx_id(graph) spec_version = parse_literal(logger, graph, doc_node, SPDX_NAMESPACE.specVersion) data_license = parse_literal(logger, graph, doc_node, SPDX_NAMESPACE.dataLicense, - parsing_method=lambda x: x.removeprefix(LICENSE_NAMESPACE)) + parsing_method=lambda x: remove_prefix(x, LICENSE_NAMESPACE)) comment = parse_literal(logger, graph, doc_node, RDFS.comment) name = parse_literal(logger, graph, doc_node, SPDX_NAMESPACE.name) diff --git a/src/spdx/parser/rdf/file_parser.py b/src/spdx/parser/rdf/file_parser.py index f725f4964..ab3a2e936 100644 --- a/src/spdx/parser/rdf/file_parser.py +++ b/src/spdx/parser/rdf/file_parser.py @@ -15,7 +15,7 @@ from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.rdf.checksum_parser import parse_checksum from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, parse_literal_or_no_assertion_or_none, \ - get_correct_typed_value + get_correct_typed_value, remove_prefix from spdx.parser.rdf.license_expression_parser import parse_license_expression from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -67,5 +67,5 @@ def parse_file(file_node: URIRef, graph: Graph, doc_namespace: str) -> File: def convert_uri_ref_to_file_type(file_type_ref: URIRef) -> FileType: - file_type = file_type_ref.removeprefix(SPDX_NAMESPACE).replace("fileType_", "").upper() + file_type = remove_prefix(file_type_ref, SPDX_NAMESPACE.fileType_).upper() return FileType[file_type] diff --git a/src/spdx/parser/rdf/graph_parsing_functions.py b/src/spdx/parser/rdf/graph_parsing_functions.py index b20626cff..5b5eea148 100644 --- a/src/spdx/parser/rdf/graph_parsing_functions.py +++ b/src/spdx/parser/rdf/graph_parsing_functions.py @@ -41,6 +41,7 @@ def apply_parsing_method_or_log_error(logger: Logger, value: Any, parsing_method logger.extend(err.args[0]) return default + def parse_literal_or_no_assertion_or_none(logger: Logger, graph: Graph, subject: Node, predicate: Node, parsing_method: Callable = str, default: Any = None): value = get_unique_value(logger, graph, subject, predicate, default) @@ -84,7 +85,7 @@ def get_unique_value(logger: Logger, graph: Graph, subject: Node, predicate: Nod def parse_enum_value(enum_str: str, enum_class: Type[Enum], prefix: str) -> Enum: try: - enum_without_rdf_prefix = enum_str.removeprefix(prefix) + enum_without_rdf_prefix = remove_prefix(enum_str, prefix) value = camel_case_to_snake_case(enum_without_rdf_prefix).upper() return enum_class[value] except KeyError: @@ -100,3 +101,11 @@ def parse_spdx_id(resource: URIRef, doc_namespace: str, graph: Graph) -> Optiona namespace_manager = NamespaceManager(graph) return namespace_manager.normalizeUri(resource) return resource.toPython() or None + + +# Python 3.9 introduced the method removeprefix() for strings, but as we are also supporting Python 3.7 and 3.8 we need +# to write our own helper method to delete prefixes. +def remove_prefix(string: str, prefix: str) -> str: + if string.startswith(prefix): + return string[len(prefix):] + return string diff --git a/src/spdx/parser/rdf/license_expression_parser.py b/src/spdx/parser/rdf/license_expression_parser.py index 5f2bcb89d..9fa0bb2fd 100644 --- a/src/spdx/parser/rdf/license_expression_parser.py +++ b/src/spdx/parser/rdf/license_expression_parser.py @@ -14,15 +14,17 @@ from license_expression import LicenseExpression, get_spdx_licensing from rdflib.term import Identifier, URIRef, BNode, Node from spdx.parser.error import SPDXParsingError +from spdx.parser.rdf.graph_parsing_functions import remove_prefix from spdx.rdfschema.namespace import SPDX_NAMESPACE, LICENSE_NAMESPACE -def parse_license_expression(license_expression_node: Union[URIRef, BNode, Node], graph: Graph, doc_namespace: str) -> LicenseExpression: +def parse_license_expression(license_expression_node: Union[URIRef, BNode, Node], graph: Graph, + doc_namespace: str) -> LicenseExpression: spdx_licensing = get_spdx_licensing() expression = "" if license_expression_node.startswith(LICENSE_NAMESPACE): - expression = license_expression_node.removeprefix(LICENSE_NAMESPACE) + expression = remove_prefix(license_expression_node, LICENSE_NAMESPACE) return spdx_licensing.parse(expression) if license_expression_node.startswith(doc_namespace): expression = license_expression_node.fragment @@ -57,7 +59,7 @@ def parse_license_expression(license_expression_node: Union[URIRef, BNode, Node] def parse_license_exception(exception_node: Identifier, graph: Graph) -> str: if exception_node.startswith(LICENSE_NAMESPACE): - exception = exception_node.removeprefix(LICENSE_NAMESPACE) + exception = remove_prefix(exception_node, LICENSE_NAMESPACE) else: exception = graph.value(exception_node, SPDX_NAMESPACE.licenseExceptionId).toPython() return exception diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index b78c8cf9e..43e651b1a 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -20,7 +20,7 @@ from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error from spdx.parser.rdf.checksum_parser import parse_checksum from spdx.parser.rdf.graph_parsing_functions import parse_spdx_id, parse_literal, parse_enum_value, \ - parse_literal_or_no_assertion_or_none, get_correct_typed_value, parse_literal_or_no_assertion + parse_literal_or_no_assertion_or_none, get_correct_typed_value, parse_literal_or_no_assertion, remove_prefix from spdx.parser.rdf.license_expression_parser import parse_license_expression from spdx.rdfschema.namespace import SPDX_NAMESPACE, REFERENCE_NAMESPACE @@ -128,7 +128,7 @@ def parse_external_package_ref(external_package_ref_node: URIRef, graph: Graph) logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceCategory, parsing_method=lambda x: parse_enum_value(x, ExternalPackageRefCategory, SPDX_NAMESPACE.referenceCategory_, )) ref_type = parse_literal(logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceType, - parsing_method=lambda x: x.removeprefix(REFERENCE_NAMESPACE)) + parsing_method=lambda x: remove_prefix(x, REFERENCE_NAMESPACE)) comment = parse_literal(logger, graph, external_package_ref_node, RDFS.comment) raise_parsing_error_if_logger_has_messages(logger, "ExternalPackageRef") diff --git a/tests/spdx/parser/rdf/test_graph_parsing_function.py b/tests/spdx/parser/rdf/test_graph_parsing_function.py index 147c7241d..c2b76203b 100644 --- a/tests/spdx/parser/rdf/test_graph_parsing_function.py +++ b/tests/spdx/parser/rdf/test_graph_parsing_function.py @@ -11,7 +11,7 @@ import pytest from rdflib import URIRef, Graph, Namespace -from spdx.parser.rdf.graph_parsing_functions import parse_spdx_id +from spdx.parser.rdf.graph_parsing_functions import parse_spdx_id, remove_prefix @pytest.mark.parametrize("resource,doc_namespace,ext_namespace_mapping,expected", @@ -27,3 +27,11 @@ def test_parse_spdx_id(resource, doc_namespace, ext_namespace_mapping, expected) spdx_id = parse_spdx_id(resource, doc_namespace, graph) assert spdx_id == expected + + +@pytest.mark.parametrize("string,prefix,expected", [("prefixString", "prefix", "String"), + ("prefixString", "refix", "prefixString")]) +def test_remove_prefix(string, prefix, expected): + shorten_string = remove_prefix(string, prefix) + + assert expected == shorten_string From 3e83f43c118eb47648122d0cfddb7026da575120 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 10 Feb 2023 10:34:13 +0100 Subject: [PATCH 264/362] [issue-456] allow multiple members in ConjunctiveLicenseSet and DisjunctiveLicenseSet Signed-off-by: Meret Behrens --- .../parser/rdf/license_expression_parser.py | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/spdx/parser/rdf/license_expression_parser.py b/src/spdx/parser/rdf/license_expression_parser.py index 9fa0bb2fd..b178252e0 100644 --- a/src/spdx/parser/rdf/license_expression_parser.py +++ b/src/spdx/parser/rdf/license_expression_parser.py @@ -13,7 +13,6 @@ from rdflib import Graph, RDF from license_expression import LicenseExpression, get_spdx_licensing from rdflib.term import Identifier, URIRef, BNode, Node -from spdx.parser.error import SPDXParsingError from spdx.parser.rdf.graph_parsing_functions import remove_prefix from spdx.rdfschema.namespace import SPDX_NAMESPACE, LICENSE_NAMESPACE @@ -32,21 +31,15 @@ def parse_license_expression(license_expression_node: Union[URIRef, BNode, Node] node_type = graph.value(license_expression_node, RDF.type) if node_type == SPDX_NAMESPACE.ConjunctiveLicenseSet: - members = dict() - for index, (_, _, member_node) in enumerate( - graph.triples((license_expression_node, SPDX_NAMESPACE.member, None))): - members[index] = parse_license_expression(member_node, graph, doc_namespace) - if len(members) > 2: - raise SPDXParsingError([f"A ConjunctiveLicenseSet can only have two members."]) - expression = f"{members[0]} AND {members[1]}" + members = [] + for (_, _, member_node) in graph.triples((license_expression_node, SPDX_NAMESPACE.member, None)): + members.append(parse_license_expression(member_node, graph, doc_namespace)) + expression = " AND ".join([str(member) for member in members]) if node_type == SPDX_NAMESPACE.DisjunctiveLicenseSet: - members = dict() - for index, (_, _, member_node) in enumerate( - graph.triples((license_expression_node, SPDX_NAMESPACE.member, None))): - members[index] = parse_license_expression(member_node, graph, doc_namespace) - if len(members) > 2: - raise SPDXParsingError([f"A DisjunctiveLicenseSet can only have two members."]) - expression = f"{members[0]} OR {members[1]}" + members = [] + for (_, _, member_node) in graph.triples((license_expression_node, SPDX_NAMESPACE.member, None)): + members.append(parse_license_expression(member_node, graph, doc_namespace)) + expression = " OR ".join([str(member) for member in members]) if node_type == SPDX_NAMESPACE.WithExceptionOperator: license_expression = parse_license_expression(graph.value(license_expression_node, SPDX_NAMESPACE.member), graph, doc_namespace) From be4f6471f605f85cde1f2a9bc9272ae4fc49a9fb Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 10 Feb 2023 12:54:44 +0100 Subject: [PATCH 265/362] [issue-456] strip parsed values by default to prevent new lines and leading/ trailing whitespaces in string values Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/graph_parsing_functions.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/spdx/parser/rdf/graph_parsing_functions.py b/src/spdx/parser/rdf/graph_parsing_functions.py index 5b5eea148..db874bcbc 100644 --- a/src/spdx/parser/rdf/graph_parsing_functions.py +++ b/src/spdx/parser/rdf/graph_parsing_functions.py @@ -24,15 +24,16 @@ from spdx.casing_tools import camel_case_to_snake_case -def parse_literal(logger: Logger, graph: Graph, subject: Node, predicate: Node, parsing_method: Callable = str, - default: Any = None): +def parse_literal(logger: Logger, graph: Graph, subject: Node, predicate: Node, + parsing_method: Callable = lambda x: x.strip(), default: Any = None): value = get_unique_value(logger, graph, subject, predicate, default) if not value: return default return apply_parsing_method_or_log_error(logger, value, parsing_method, default) -def apply_parsing_method_or_log_error(logger: Logger, value: Any, parsing_method: Callable = str, default: Any = None): +def apply_parsing_method_or_log_error(logger: Logger, value: Any, parsing_method: Callable = lambda x: x.strip(), + default: Any = None): try: return parsing_method(value) except SPDXParsingError as err: @@ -43,7 +44,7 @@ def apply_parsing_method_or_log_error(logger: Logger, value: Any, parsing_method def parse_literal_or_no_assertion_or_none(logger: Logger, graph: Graph, subject: Node, predicate: Node, - parsing_method: Callable = str, default: Any = None): + parsing_method: Callable = lambda x: x.strip(), default: Any = None): value = get_unique_value(logger, graph, subject, predicate, default) if not value: return default @@ -54,7 +55,8 @@ def parse_literal_or_no_assertion_or_none(logger: Logger, graph: Graph, subject: return apply_parsing_method_or_log_error(logger, value, parsing_method, default) -def get_correct_typed_value(logger: Logger, value: Any, parsing_method: Callable = str, default: Any = None): +def get_correct_typed_value(logger: Logger, value: Any, parsing_method: Callable = lambda x: x.strip(), + default: Any = None): if not value: return default if value == SPDX_NAMESPACE.noassertion or value.toPython() == SPDX_NO_ASSERTION_STRING: @@ -65,7 +67,7 @@ def get_correct_typed_value(logger: Logger, value: Any, parsing_method: Callable def parse_literal_or_no_assertion(logger: Logger, graph: Graph, subject: Node, predicate: Node, - parsing_method: Callable = str, default: Any = None): + parsing_method: Callable = lambda x: x.strip(), default: Any = None): value = get_unique_value(logger, graph, subject, predicate, default) if not value: return default From 5809feefdf5c07abba82b8ab55201c4bc2370c8f Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 10 Feb 2023 14:11:11 +0100 Subject: [PATCH 266/362] [issue-456] fix parsing of externalPackageRef Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/package_parser.py | 17 +- .../rdf/data/file_to_test_rdf_parser.rdf.xml | 413 ++++++++++-------- tests/spdx/parser/rdf/test_package_parser.py | 25 +- 3 files changed, 264 insertions(+), 191 deletions(-) diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index 43e651b1a..a266fa4fc 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -9,8 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from typing import Optional - -from rdflib import URIRef, Graph, RDFS, DOAP +from rdflib import URIRef, Graph, RDFS, DOAP, Literal from spdx.datetime_conversions import datetime_from_str from spdx.model.package import Package, PackagePurpose, ExternalPackageRef, PackageVerificationCode, \ @@ -47,7 +46,7 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac external_package_refs = [] for (_, _, external_package_ref_node) in graph.triples((package_node, SPDX_NAMESPACE.externalRef, None)): - external_package_refs.append(parse_external_package_ref(external_package_ref_node, graph)) + external_package_refs.append(parse_external_package_ref(external_package_ref_node, graph, doc_namespace)) files_analyzed = bool(graph.value(package_node, SPDX_NAMESPACE.filesAnalyzed, default=True)) license_concluded = parse_literal_or_no_assertion_or_none( logger, graph, package_node, SPDX_NAMESPACE.licenseConcluded, @@ -121,14 +120,14 @@ def parse_package_verification_code(package_verification_code_node: URIRef, grap return package_verification_code -def parse_external_package_ref(external_package_ref_node: URIRef, graph: Graph) -> ExternalPackageRef: +def parse_external_package_ref(external_package_ref_node: URIRef, graph: Graph, doc_namespace) -> ExternalPackageRef: logger = Logger() ref_locator = parse_literal(logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceLocator) ref_category = parse_literal( logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceCategory, parsing_method=lambda x: parse_enum_value(x, ExternalPackageRefCategory, SPDX_NAMESPACE.referenceCategory_, )) ref_type = parse_literal(logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceType, - parsing_method=lambda x: remove_prefix(x, REFERENCE_NAMESPACE)) + parsing_method=lambda x: parse_external_package_ref_type(x, doc_namespace)) comment = parse_literal(logger, graph, external_package_ref_node, RDFS.comment) raise_parsing_error_if_logger_has_messages(logger, "ExternalPackageRef") @@ -136,3 +135,11 @@ def parse_external_package_ref(external_package_ref_node: URIRef, graph: Graph) dict(category=ref_category, reference_type=ref_type, locator=ref_locator, comment=comment)) return external_package_ref + + +def parse_external_package_ref_type(external_package_ref_type_resource: URIRef, doc_namespace: str) -> str: + if external_package_ref_type_resource.startswith(doc_namespace): + return external_package_ref_type_resource.fragment + if external_package_ref_type_resource.startswith(REFERENCE_NAMESPACE): + return external_package_ref_type_resource.replace(REFERENCE_NAMESPACE, "") + return external_package_ref_type_resource.toPython() diff --git a/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml b/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml index f832f8f09..26e6cb534 100644 --- a/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml +++ b/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml @@ -1,186 +1,243 @@ - - - - - - - - - - - org.apache.tomcat:tomcat:9.0.0.M4 - - - externalPackageRefComment - - - - - - - - - https://homepage.com - Person: supplierName (some@mail.com) - packageSummary - true - 2022-12-03T00:00:00Z - packageAttributionText - packageDescription - packageComment - - - ./exclude.py - 85ed0817af83a24ad8da68c2b5094de69833983c - - - packageLicenseComment - packageName - 2022-12-01T00:00:00Z - 12.2 - Person: originatorName (some@mail.com) - - - packageCopyrightText - 2022-12-02T00:00:00Z - - - 71c4025dd9897b364f3ebbb42c484ff43d00791c - - - - ./packageFileName - sourceInfo - https://download.com - - - snippetComment - - - - - - - - - - - - - - - fileComment - - copyrightText - fileContributor - - - annotationComment - Person: annotatorName (some@mail.com) - 2022-12-01T00:00:00Z - - - - licenseComment + + SPDX-2.3 + + + https://see.also + extractedText + LicenseRef-1 + licenseComment + licenseName + + + documentComment + documentName + + + + + + fileNotice + + + annotationComment + Person: annotatorName (some@mail.com) + 2022-12-01T00:00:00Z + + + + fileAttributionText + + + + + + + + + fileContributor + + copyrightText + + + + 71c4025dd9897b364f3ebbb42c484ff43d00791c + + + fileComment + ./fileName.py + licenseComment + + + relationshipComment + + + + + + creatorComment + Person: creatorName (some@mail.com) + 3.19 + 2022-12-01T00:00:00Z + + + + - - - 71c4025dd9897b364f3ebbb42c484ff43d00791c - + + + 71c4025dd9897b364f3ebbb42c484ff43d00791c + - ./fileName.py - - - fileNotice - fileAttributionText - - - 3 - - - - - - 4 - - - - - - - - - - 2 - - - - - 1 - - - - - - snippetAttributionText - - snippetLicenseComment - licenseCopyrightText - snippetName - - - - - - - - - - SPDX-2.3 - documentComment - - - + + + + + + + + + + + + 12.2 + ./packageFileName + Person: supplierName (some@mail.com) + https://download.com + + + Person: originatorName (some@mail.com) + + + + + + + 2022-12-03T00:00:00Z + true + packageDescription + + sourceInfo + packageLicenseComment + + + ./exclude.py + 85ed0817af83a24ad8da68c2b5094de69833983c + + + + 2022-12-02T00:00:00Z + packageComment + packageAttributionText + https://homepage.com + 2022-12-01T00:00:00Z + packageName + packageSummary - - 71c4025dd9897b364f3ebbb42c484ff43d00791c - - + + 71c4025dd9897b364f3ebbb42c484ff43d00791c + + - - - documentName - - - extractedText - https://see.also - LicenseRef-1 - licenseComment - licenseName - - - - - - - relationshipComment - - - - - 2022-12-01T00:00:00Z - creatorComment - 3.19 - Person: creatorName (some@mail.com) - - - + packageCopyrightText + + + org.apache.tomcat:tomcat:9.0.0.M4 + + + externalPackageRefComment + + + + + packageName + 2022-12-01T00:00:00Z + Person: supplierName (some@mail.com) + + + + 71c4025dd9897b364f3ebbb42c484ff43d00791c + + + 12.2 + + + 2022-12-03T00:00:00Z + https://homepage.com + true + ./packageFileName + + + + + + + + + ./exclude.py + 85ed0817af83a24ad8da68c2b5094de69833983c + + + + packageCopyrightText + http://differentdownload.com + sourceInfo + packageSummary + packageAttributionText + packageDescription + + + + + + + Person: originatorName (some@mail.com) + + + + + This is the external ref for Acme + acmecorp/acmenator/4.1.3-alpha + + + + packageComment + 2022-12-02T00:00:00Z + packageLicenseComment + + + + + + + + 3 + + + + + + 4 + + + + + + + + + 1 + + + + + + + 2 + + + + + + + + snippetAttributionText + snippetName + snippetLicenseComment + snippetComment + licenseCopyrightText + + + + + + + diff --git a/tests/spdx/parser/rdf/test_package_parser.py b/tests/spdx/parser/rdf/test_package_parser.py index f1e9615af..5999e2ed8 100644 --- a/tests/spdx/parser/rdf/test_package_parser.py +++ b/tests/spdx/parser/rdf/test_package_parser.py @@ -11,8 +11,9 @@ import os from unittest import TestCase +import pytest from license_expression import get_spdx_licensing -from rdflib import RDF, Graph +from rdflib import RDF, Graph, Literal from spdx.model.actor import Actor, ActorType from spdx.model.checksum import ChecksumAlgorithm, Checksum @@ -55,14 +56,22 @@ def test_package_parser(): assert package.originator == Actor(ActorType.PERSON, "originatorName", "some@mail.com") -def test_external_package_ref_parser(): +@pytest.mark.parametrize("download_location,category,locator,type,comment", + [("https://download.com", ExternalPackageRefCategory.PACKAGE_MANAGER, + "org.apache.tomcat:tomcat:9.0.0.M4", "maven-central", "externalPackageRefComment"), + ("http://differentdownload.com", ExternalPackageRefCategory.OTHER, + "acmecorp/acmenator/4.1.3-alpha", "LocationRef-acmeforge","This is the external ref for Acme")]) +def test_external_package_ref_parser(download_location, category, locator, type, comment): graph = Graph().parse(os.path.join(os.path.dirname(__file__), "data/file_to_test_rdf_parser.rdf.xml")) - package_node = graph.value(predicate=RDF.type, object=SPDX_NAMESPACE.Package) + doc_namespace = "https://some.namespace" + # we use the download location to identify the package node + # in the test file we have two different external package refs depending on the package + package_node = graph.value(predicate=SPDX_NAMESPACE.downloadLocation, object=Literal(download_location)) external_package_ref_node = graph.value(package_node, SPDX_NAMESPACE.externalRef) - external_package_ref = parse_external_package_ref(external_package_ref_node, graph) + external_package_ref = parse_external_package_ref(external_package_ref_node, graph, doc_namespace) - assert external_package_ref.category == ExternalPackageRefCategory.PACKAGE_MANAGER - assert external_package_ref.locator == "org.apache.tomcat:tomcat:9.0.0.M4" - assert external_package_ref.reference_type == "maven-central" - assert external_package_ref.comment == "externalPackageRefComment" + assert external_package_ref.category == category + assert external_package_ref.locator == locator + assert external_package_ref.reference_type == type + assert external_package_ref.comment == comment From fa1630b40ed78c4a2f32371cbcb1c248be5a3974 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 15 Feb 2023 09:47:30 +0100 Subject: [PATCH 267/362] fix helper method The return value of err.args[0] is a string, so we need to use append to add this string to the list of messages in the logger. Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/graph_parsing_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spdx/parser/rdf/graph_parsing_functions.py b/src/spdx/parser/rdf/graph_parsing_functions.py index db874bcbc..7248f4a5a 100644 --- a/src/spdx/parser/rdf/graph_parsing_functions.py +++ b/src/spdx/parser/rdf/graph_parsing_functions.py @@ -39,7 +39,7 @@ def apply_parsing_method_or_log_error(logger: Logger, value: Any, parsing_method except SPDXParsingError as err: logger.extend(err.get_messages()) except (TypeError, ValueError) as err: - logger.extend(err.args[0]) + logger.append(err.args[0]) return default From 8392a2aec2c24f6db79a99bbe1ace9d0c9f14b1a Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 20 Feb 2023 12:27:56 +0100 Subject: [PATCH 268/362] [review] use helper method for enums also for FileType Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/file_parser.py | 15 +++++--------- tests/spdx/parser/rdf/test_file_parser.py | 25 +---------------------- 2 files changed, 6 insertions(+), 34 deletions(-) diff --git a/src/spdx/parser/rdf/file_parser.py b/src/spdx/parser/rdf/file_parser.py index ab3a2e936..1e90a57db 100644 --- a/src/spdx/parser/rdf/file_parser.py +++ b/src/spdx/parser/rdf/file_parser.py @@ -15,7 +15,7 @@ from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.rdf.checksum_parser import parse_checksum from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, parse_literal_or_no_assertion_or_none, \ - get_correct_typed_value, remove_prefix + get_correct_typed_value, apply_parsing_method_or_log_error, parse_enum_value from spdx.parser.rdf.license_expression_parser import parse_license_expression from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -30,10 +30,10 @@ def parse_file(file_node: URIRef, graph: Graph, doc_namespace: str) -> File: file_types = [] for (_, _, file_type_ref) in graph.triples((file_node, SPDX_NAMESPACE.fileType, None)): - try: - file_types.append(convert_uri_ref_to_file_type(file_type_ref)) - except KeyError: - logger.append(f"Invalid FileType: {file_type_ref}") + file_types.append( + apply_parsing_method_or_log_error(logger, file_type_ref, + parsing_method=lambda x: parse_enum_value(x, FileType, + SPDX_NAMESPACE.fileType_))) license_concluded = parse_literal_or_no_assertion_or_none( logger, graph, file_node, SPDX_NAMESPACE.licenseConcluded, parsing_method=lambda x: parse_license_expression(x, graph, doc_namespace)) @@ -64,8 +64,3 @@ def parse_file(file_node: URIRef, graph: Graph, doc_namespace: str) -> File: license_info_in_file=license_info_in_file, notice=notice_text)) return file - - -def convert_uri_ref_to_file_type(file_type_ref: URIRef) -> FileType: - file_type = remove_prefix(file_type_ref, SPDX_NAMESPACE.fileType_).upper() - return FileType[file_type] diff --git a/tests/spdx/parser/rdf/test_file_parser.py b/tests/spdx/parser/rdf/test_file_parser.py index eba8ec682..6ffed17a0 100644 --- a/tests/spdx/parser/rdf/test_file_parser.py +++ b/tests/spdx/parser/rdf/test_file_parser.py @@ -11,13 +11,12 @@ import os from unittest import TestCase -import pytest from license_expression import get_spdx_licensing from rdflib import Graph, RDF from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.model.file import FileType -from spdx.parser.rdf.file_parser import convert_uri_ref_to_file_type, parse_file +from spdx.parser.rdf.file_parser import parse_file from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -41,25 +40,3 @@ def test_parse_file(): assert file.license_comment == "licenseComment" assert file.notice == "fileNotice" assert file.attribution_texts == ["fileAttributionText"] - - -@pytest.mark.parametrize("uri_ref,expected", [(SPDX_NAMESPACE.fileType_source, FileType.SOURCE), - (SPDX_NAMESPACE.fileType_binary, FileType.BINARY), - (SPDX_NAMESPACE.fileType_archive, FileType.ARCHIVE), - (SPDX_NAMESPACE.fileType_application, FileType.APPLICATION), - (SPDX_NAMESPACE.fileType_audio, FileType.AUDIO), - (SPDX_NAMESPACE.fileType_image, FileType.IMAGE), - (SPDX_NAMESPACE.fileType_text, FileType.TEXT), - (SPDX_NAMESPACE.fileType_video, FileType.VIDEO), - (SPDX_NAMESPACE.fileType_documentation, FileType.DOCUMENTATION), - (SPDX_NAMESPACE.fileType_spdx, FileType.SPDX), - (SPDX_NAMESPACE.fileType_other, FileType.OTHER)]) -def test_convert_uri_ref_to_file_type(uri_ref, expected): - file_type = convert_uri_ref_to_file_type(uri_ref) - - assert file_type == expected - - -def test_convert_uri_ref_to_file_type_error(): - with pytest.raises(KeyError): - convert_uri_ref_to_file_type(SPDX_NAMESPACE.filetype_SPDX) From 2d34f2a722aff840a3e1c48aef030dd3eab33aed Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 20 Feb 2023 13:53:54 +0100 Subject: [PATCH 269/362] [review, refactor] strip copyright texts, delete trailing comma Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/file_parser.py | 3 +-- src/spdx/parser/rdf/package_parser.py | 7 +++---- src/spdx/parser/rdf/snippet_parser.py | 3 +-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/spdx/parser/rdf/file_parser.py b/src/spdx/parser/rdf/file_parser.py index 1e90a57db..05b1eb158 100644 --- a/src/spdx/parser/rdf/file_parser.py +++ b/src/spdx/parser/rdf/file_parser.py @@ -43,8 +43,7 @@ def parse_file(file_node: URIRef, graph: Graph, doc_namespace: str) -> File: get_correct_typed_value(logger, license_info_from_files_node, lambda x: parse_license_expression(x, graph, doc_namespace))) license_comment = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.licenseComments) - copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, file_node, SPDX_NAMESPACE.copyrightText, - parsing_method=str) + copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, file_node, SPDX_NAMESPACE.copyrightText) file_contributors = [] for (_, _, file_contributor) in graph.triples((file_node, SPDX_NAMESPACE.fileContributor, None)): file_contributors.append(file_contributor.toPython()) diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index a266fa4fc..64d29ec86 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -29,7 +29,7 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac spdx_id = parse_spdx_id(package_node, doc_namespace, graph) name = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.name) download_location = parse_literal_or_no_assertion_or_none(logger, graph, package_node, - SPDX_NAMESPACE.downloadLocation, parsing_method=str) + SPDX_NAMESPACE.downloadLocation) checksums = [] for (_, _, checksum_node) in graph.triples((package_node, SPDX_NAMESPACE.checksum, None)): checksums.append(parse_checksum(checksum_node, graph)) @@ -64,8 +64,7 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac comment = parse_literal(logger, graph, package_node, RDFS.comment) summary = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.summary) description = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.description) - copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, package_node, SPDX_NAMESPACE.copyrightText, - parsing_method=str) + copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, package_node, SPDX_NAMESPACE.copyrightText) source_info = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.sourceInfo) primary_package_purpose = parse_literal( logger, graph, package_node, SPDX_NAMESPACE.primaryPackagePurpose, @@ -125,7 +124,7 @@ def parse_external_package_ref(external_package_ref_node: URIRef, graph: Graph, ref_locator = parse_literal(logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceLocator) ref_category = parse_literal( logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceCategory, - parsing_method=lambda x: parse_enum_value(x, ExternalPackageRefCategory, SPDX_NAMESPACE.referenceCategory_, )) + parsing_method=lambda x: parse_enum_value(x, ExternalPackageRefCategory, SPDX_NAMESPACE.referenceCategory_)) ref_type = parse_literal(logger, graph, external_package_ref_node, SPDX_NAMESPACE.referenceType, parsing_method=lambda x: parse_external_package_ref_type(x, doc_namespace)) comment = parse_literal(logger, graph, external_package_ref_node, RDFS.comment) diff --git a/src/spdx/parser/rdf/snippet_parser.py b/src/spdx/parser/rdf/snippet_parser.py index e62074594..c9002cce4 100644 --- a/src/spdx/parser/rdf/snippet_parser.py +++ b/src/spdx/parser/rdf/snippet_parser.py @@ -39,8 +39,7 @@ def parse_snippet(snippet_node: URIRef, graph: Graph, doc_namespace: str) -> Sni get_correct_typed_value(logger, license_info_in_snippet_node, lambda x: parse_license_expression(x, graph, doc_namespace))) license_comment = parse_literal(logger, graph, snippet_node, SPDX_NAMESPACE.licenseComments) - copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, snippet_node, SPDX_NAMESPACE.copyrightText, - parsing_method=str) + copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, snippet_node, SPDX_NAMESPACE.copyrightText) comment = parse_literal(logger, graph, snippet_node, RDFS.comment) name = parse_literal(logger, graph, snippet_node, SPDX_NAMESPACE.name) attribution_texts = [] From 6ff9728248b72227c7260d4d364aabe5bf761308 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 20 Feb 2023 16:37:10 +0100 Subject: [PATCH 270/362] [review] rewrite parse_ranges Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/snippet_parser.py | 78 +++++++++++++---- tests/spdx/parser/rdf/test_snippet_parser.py | 91 +++++++++++++++++++- 2 files changed, 149 insertions(+), 20 deletions(-) diff --git a/src/spdx/parser/rdf/snippet_parser.py b/src/spdx/parser/rdf/snippet_parser.py index c9002cce4..c54a2cff4 100644 --- a/src/spdx/parser/rdf/snippet_parser.py +++ b/src/spdx/parser/rdf/snippet_parser.py @@ -8,16 +8,18 @@ # 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 typing import Tuple, Optional +from typing import Tuple, Optional, Dict from rdflib import Graph, RDF, RDFS +from rdflib.exceptions import UniquenessError from rdflib.term import URIRef, Node +from spdx.parser.error import SPDXParsingError from spdx.model.snippet import Snippet from spdx.parser.logger import Logger from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, parse_literal_or_no_assertion_or_none, \ - get_correct_typed_value + get_correct_typed_value, apply_parsing_method_or_log_error from spdx.parser.rdf.license_expression_parser import parse_license_expression from spdx.rdfschema.namespace import SPDX_NAMESPACE, POINTER_NAMESPACE @@ -27,8 +29,13 @@ def parse_snippet(snippet_node: URIRef, graph: Graph, doc_namespace: str) -> Sni spdx_id = parse_spdx_id(snippet_node, doc_namespace, graph) file_spdx_id_uri = graph.value(subject=snippet_node, predicate=SPDX_NAMESPACE.snippetFromFile) file_spdx_id = parse_spdx_id(file_spdx_id_uri, doc_namespace, graph) - byte_range = parse_ranges(snippet_node, graph, POINTER_NAMESPACE.ByteOffsetPointer, POINTER_NAMESPACE.offset) - line_range = parse_ranges(snippet_node, graph, POINTER_NAMESPACE.LineCharPointer, POINTER_NAMESPACE.lineNumber) + byte_range = None + line_range = None + for (_, _, start_end_pointer) in graph.triples((snippet_node, SPDX_NAMESPACE.range, None)): + parsed_range = apply_parsing_method_or_log_error(logger, start_end_pointer, + parsing_method=lambda x: parse_ranges(x, graph)) + byte_range, line_range = set_range_or_log_error(byte_range, line_range, logger, parsed_range) + license_concluded = parse_literal_or_no_assertion_or_none( logger, graph, snippet_node, SPDX_NAMESPACE.licenseConcluded, parsing_method=lambda x: parse_license_expression(x, graph, doc_namespace)) @@ -57,21 +64,58 @@ def parse_snippet(snippet_node: URIRef, graph: Graph, doc_namespace: str) -> Sni return snippet -def parse_ranges(snippet_node: URIRef, graph: Graph, pointer: Node, member: Node) -> Tuple[int, int]: - start_range = None - end_range = None - for (_, _, start_end_pointer) in graph.triples((snippet_node, SPDX_NAMESPACE.range, None)): - for (_, _, pointer_node) in graph.triples((start_end_pointer, POINTER_NAMESPACE.startPointer, None)): - for (typed_pointer_node, _, _) in graph.triples((pointer_node, RDF.type, pointer)): - start_range = parse_range_value(graph, typed_pointer_node, member) - for (_, _, pointer_node) in graph.triples((start_end_pointer, POINTER_NAMESPACE.endPointer, None)): - for (typed_pointer_node, _, _) in graph.triples((pointer_node, RDF.type, pointer)): - end_range = parse_range_value(graph, typed_pointer_node, member) - return start_range, end_range +def set_range_or_log_error( + byte_range: Optional[Tuple[int, int]], line_range: Optional[Tuple[int, int]], logger: Logger, + parsed_range: Dict[str, Tuple[int, int]]) -> Tuple[Optional[Tuple[int, int]], Optional[Tuple[int, int]]]: + if not parsed_range: + return byte_range, line_range + if "ByteOffsetPointer" in parsed_range.keys() and not byte_range: + byte_range = parsed_range["ByteOffsetPointer"] + elif "ByteOffsetPointer" in parsed_range.keys() and byte_range: + logger.append("Multiple ByteOffsetPointer found.") + elif "LineCharPointer" in parsed_range.keys() and not line_range: + line_range = parsed_range["LineCharPointer"] + elif "LineCharPointer" in parsed_range.keys() and line_range: + logger.append("Multiple LineCharPointer found.") + return byte_range, line_range + + +def parse_ranges(start_end_pointer: URIRef, graph: Graph) -> Dict[str, Tuple[int, int]]: + range_values = dict() + start_pointer_type, start_pointer_node = get_pointer_type(graph, POINTER_NAMESPACE.startPointer, start_end_pointer) + end_pointer_type, end_pointer_node = get_pointer_type(graph, POINTER_NAMESPACE.endPointer, start_end_pointer) + + if start_pointer_type != end_pointer_type: + raise SPDXParsingError([f"Types of startPointer and endPointer don't match"]) + + range_values["startPointer"] = parse_range_value(graph, start_pointer_node, POINTER_MATCHING[start_pointer_type]) + range_values["endPointer"] = parse_range_value(graph, end_pointer_node, POINTER_MATCHING[end_pointer_type]) + + return {str(start_pointer_type.fragment): ( + range_values["startPointer"], range_values["endPointer"])} + + +def get_pointer_type(graph: Graph, pointer: URIRef, start_end_pointer: URIRef) -> Tuple[URIRef, URIRef]: + try: + pointer_node = graph.value(start_end_pointer, pointer, any=False) + except UniquenessError: + raise SPDXParsingError([f"Multiple values for {pointer.fragment}"]) + if not pointer_node: + raise SPDXParsingError([f"Couldn't find pointer of type {pointer.fragment}."]) + pointer_type = graph.value(pointer_node, RDF.type) + return pointer_type, pointer_node + + +POINTER_MATCHING = { + POINTER_NAMESPACE.ByteOffsetPointer: POINTER_NAMESPACE.offset, + POINTER_NAMESPACE.LineCharPointer: POINTER_NAMESPACE.lineNumber} -def parse_range_value(graph: Graph, pointer_node: Node, predicate: Node) -> Optional[int]: - value = graph.value(pointer_node, predicate) +def parse_range_value(graph: Graph, pointer_node: Node, predicate: URIRef) -> Optional[int]: + try: + value = graph.value(pointer_node, predicate, any=False) + except UniquenessError: + raise SPDXParsingError([f"Multiple values for {predicate.fragment} found."]) if value: value = int(value) return value diff --git a/tests/spdx/parser/rdf/test_snippet_parser.py b/tests/spdx/parser/rdf/test_snippet_parser.py index c5ff277a5..1bf80c2ed 100644 --- a/tests/spdx/parser/rdf/test_snippet_parser.py +++ b/tests/spdx/parser/rdf/test_snippet_parser.py @@ -11,11 +11,13 @@ import os from unittest import TestCase +import pytest from license_expression import get_spdx_licensing -from rdflib import Graph, RDF +from rdflib import Graph, RDF, BNode, Literal -from spdx.parser.rdf.snippet_parser import parse_snippet -from spdx.rdfschema.namespace import SPDX_NAMESPACE +from spdx.parser.error import SPDXParsingError +from spdx.parser.rdf.snippet_parser import parse_snippet, parse_ranges +from spdx.rdfschema.namespace import SPDX_NAMESPACE, POINTER_NAMESPACE def test_parse_snippet(): @@ -37,3 +39,86 @@ def test_parse_snippet(): assert snippet.comment == "snippetComment" assert snippet.name == "snippetName" assert snippet.attribution_texts == ["snippetAttributionText"] + + +@pytest.mark.parametrize("predicate_value_class_member", + [([(POINTER_NAMESPACE.startPointer, 1, POINTER_NAMESPACE.ByteOffsetPointer, + POINTER_NAMESPACE.offset), + (POINTER_NAMESPACE.endPointer, 2, POINTER_NAMESPACE.ByteOffsetPointer, + POINTER_NAMESPACE.offset)]), + ([(POINTER_NAMESPACE.startPointer, 100, POINTER_NAMESPACE.LineCharPointer, + POINTER_NAMESPACE.lineNumber), + (POINTER_NAMESPACE.endPointer, 200, POINTER_NAMESPACE.LineCharPointer, + POINTER_NAMESPACE.lineNumber)]) + ]) +def test_parse_ranges(predicate_value_class_member): + graph = Graph() + pointer_class = predicate_value_class_member[0][2] + + add_range_to_graph_helper(graph, predicate_value_class_member) + + range_dict = parse_ranges(graph.value(predicate=RDF.type, object=POINTER_NAMESPACE.StartEndPointer), graph) + + assert pointer_class.fragment in range_dict.keys() + assert range_dict[pointer_class.fragment][0] == predicate_value_class_member[0][1] + assert range_dict[pointer_class.fragment][1] == predicate_value_class_member[1][1] + + +@pytest.mark.parametrize("predicate_value_class_member", + [([(POINTER_NAMESPACE.startPointer, 1, POINTER_NAMESPACE.ByteOffsetPointer, + POINTER_NAMESPACE.lineNumber), + (POINTER_NAMESPACE.endPointer, 2, POINTER_NAMESPACE.ByteOffsetPointer, + POINTER_NAMESPACE.lineNumber)]), + ([(POINTER_NAMESPACE.startPointer, 100, POINTER_NAMESPACE.LineCharPointer, + POINTER_NAMESPACE.offset), + (POINTER_NAMESPACE.endPointer, 200, POINTER_NAMESPACE.LineCharPointer, + POINTER_NAMESPACE.offset)]) + ]) +def test_parse_ranges_wrong_pair_of_pointer_classes(predicate_value_class_member): + graph = Graph() + pointer_class = predicate_value_class_member[0][2] + + add_range_to_graph_helper(graph, predicate_value_class_member) + + range_dict = parse_ranges(graph.value(predicate=RDF.type, object=POINTER_NAMESPACE.StartEndPointer), graph) + + assert pointer_class.fragment in range_dict.keys() + assert range_dict[pointer_class.fragment][0] is None + assert range_dict[pointer_class.fragment][1] is None + + +@pytest.mark.parametrize( + "predicate_value_class_member,expected_message", + [([(POINTER_NAMESPACE.endPointer, 1, POINTER_NAMESPACE.ByteOffsetPointer, POINTER_NAMESPACE.offset), + (POINTER_NAMESPACE.endPointer, 2, POINTER_NAMESPACE.ByteOffsetPointer, POINTER_NAMESPACE.offset)], + "Couldn't find pointer of type startPointer."), + ([(POINTER_NAMESPACE.startPointer, 1, POINTER_NAMESPACE.ByteOffsetPointer, POINTER_NAMESPACE.offset)], + "Couldn't find pointer of type endPointer."), + ([(POINTER_NAMESPACE.startPointer, 1, POINTER_NAMESPACE.ByteOffsetPointer, POINTER_NAMESPACE.offset), + (POINTER_NAMESPACE.endPointer, 2, POINTER_NAMESPACE.ByteOffsetPointer, POINTER_NAMESPACE.offset), + (POINTER_NAMESPACE.endPointer, 3, POINTER_NAMESPACE.ByteOffsetPointer, POINTER_NAMESPACE.offset)], + "Multiple values for endPointer."), + ([(POINTER_NAMESPACE.startPointer, 100, POINTER_NAMESPACE.LineCharPointer, POINTER_NAMESPACE.lineNumber), + (POINTER_NAMESPACE.startPointer, 200, POINTER_NAMESPACE.LineCharPointer, POINTER_NAMESPACE.lineNumber)], + "Multiple values for startPointer"), + ([(POINTER_NAMESPACE.startPointer, 100, POINTER_NAMESPACE.LineCharPointer, POINTER_NAMESPACE.lineNumber), + (POINTER_NAMESPACE.endPointer, 200, POINTER_NAMESPACE.ByteOffsetPointer, POINTER_NAMESPACE.offset)], + f"Types of startPointer and endPointer don't match") + ]) +def test_parse_ranges_error(predicate_value_class_member, expected_message): + graph = Graph() + + add_range_to_graph_helper(graph, predicate_value_class_member) + + with pytest.raises(SPDXParsingError, match=expected_message): + parse_ranges(graph.value(predicate=RDF.type, object=POINTER_NAMESPACE.StartEndPointer), graph) + + +def add_range_to_graph_helper(graph, predicate_value_class_member): + start_end_pointer = BNode() + graph.add((start_end_pointer, RDF.type, POINTER_NAMESPACE.StartEndPointer)) + for (predicate, value, pointer_class, pointer_member) in predicate_value_class_member: + pointer_node = BNode() + graph.add((pointer_node, RDF.type, pointer_class)) + graph.add((start_end_pointer, predicate, pointer_node)) + graph.add((pointer_node, pointer_member, Literal(value))) From b7e0a9950bdccb61ad93ef5caf918331d6b30c98 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 20 Feb 2023 17:20:00 +0100 Subject: [PATCH 271/362] [review, refactor] let Constructor handle None values and use the more general helper method Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/extracted_licensing_info_parser.py | 4 ++-- src/spdx/parser/rdf/graph_parsing_functions.py | 10 ---------- src/spdx/parser/rdf/package_parser.py | 10 +++++----- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/spdx/parser/rdf/extracted_licensing_info_parser.py b/src/spdx/parser/rdf/extracted_licensing_info_parser.py index f9c278953..edaf68b4a 100644 --- a/src/spdx/parser/rdf/extracted_licensing_info_parser.py +++ b/src/spdx/parser/rdf/extracted_licensing_info_parser.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from rdflib import URIRef, Graph, RDFS -from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_literal_or_no_assertion +from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_literal_or_no_assertion_or_none from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error @@ -23,7 +23,7 @@ def parse_extracted_licensing_info(extracted_licensing_info_node: URIRef, graph: license_id = parse_literal(logger, graph, extracted_licensing_info_node, SPDX_NAMESPACE.licenseId) extracted_text = parse_literal(logger, graph, extracted_licensing_info_node, SPDX_NAMESPACE.extractedText) comment = parse_literal(logger, graph, extracted_licensing_info_node, RDFS.comment) - license_name = parse_literal_or_no_assertion(logger, graph, extracted_licensing_info_node, SPDX_NAMESPACE.name) + license_name = parse_literal_or_no_assertion_or_none(logger, graph, extracted_licensing_info_node, SPDX_NAMESPACE.name) cross_references = [] for (_, _, cross_reference_node) in graph.triples((extracted_licensing_info_node, RDFS.seeAlso, None)): cross_references.append(cross_reference_node.toPython()) diff --git a/src/spdx/parser/rdf/graph_parsing_functions.py b/src/spdx/parser/rdf/graph_parsing_functions.py index 7248f4a5a..20d748a6d 100644 --- a/src/spdx/parser/rdf/graph_parsing_functions.py +++ b/src/spdx/parser/rdf/graph_parsing_functions.py @@ -66,16 +66,6 @@ def get_correct_typed_value(logger: Logger, value: Any, parsing_method: Callable return apply_parsing_method_or_log_error(logger, value, parsing_method, default) -def parse_literal_or_no_assertion(logger: Logger, graph: Graph, subject: Node, predicate: Node, - parsing_method: Callable = lambda x: x.strip(), default: Any = None): - value = get_unique_value(logger, graph, subject, predicate, default) - if not value: - return default - if value == SPDX_NAMESPACE.noassertion or value.toPython() == SPDX_NO_ASSERTION_STRING: - return SpdxNoAssertion() - return apply_parsing_method_or_log_error(logger, value, parsing_method, default) - - def get_unique_value(logger: Logger, graph: Graph, subject: Node, predicate: Node, default: Any) -> Any: try: value = graph.value(subject=subject, predicate=predicate, default=default, any=False) diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index 64d29ec86..e8ab287ce 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -19,7 +19,7 @@ from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error from spdx.parser.rdf.checksum_parser import parse_checksum from spdx.parser.rdf.graph_parsing_functions import parse_spdx_id, parse_literal, parse_enum_value, \ - parse_literal_or_no_assertion_or_none, get_correct_typed_value, parse_literal_or_no_assertion, remove_prefix + parse_literal_or_no_assertion_or_none, get_correct_typed_value, parse_literal_or_no_assertion_or_none, remove_prefix from spdx.parser.rdf.license_expression_parser import parse_license_expression from spdx.rdfschema.namespace import SPDX_NAMESPACE, REFERENCE_NAMESPACE @@ -37,10 +37,10 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac version_info = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.versionInfo) package_file_name = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.packageFileName) - supplier = parse_literal_or_no_assertion(logger, graph, package_node, SPDX_NAMESPACE.supplier, - parsing_method=ActorParser.parse_actor) - originator = parse_literal_or_no_assertion(logger, graph, package_node, SPDX_NAMESPACE.originator, - parsing_method=ActorParser.parse_actor) + supplier = parse_literal_or_no_assertion_or_none(logger, graph, package_node, SPDX_NAMESPACE.supplier, + parsing_method=ActorParser.parse_actor) + originator = parse_literal_or_no_assertion_or_none(logger, graph, package_node, SPDX_NAMESPACE.originator, + parsing_method=ActorParser.parse_actor) verification_code = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.packageVerificationCode, parsing_method=lambda x: parse_package_verification_code(x, graph)) From 872ab596202b6235f2c9ee9c319ccb36f1a0766c Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 20 Feb 2023 17:20:58 +0100 Subject: [PATCH 272/362] [review] refactor helper methods Signed-off-by: Meret Behrens --- src/spdx/parser/rdf/file_parser.py | 6 +++--- src/spdx/parser/rdf/graph_parsing_functions.py | 12 +++--------- src/spdx/parser/rdf/package_parser.py | 6 +++--- src/spdx/parser/rdf/snippet_parser.py | 6 +++--- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/spdx/parser/rdf/file_parser.py b/src/spdx/parser/rdf/file_parser.py index 05b1eb158..994f50660 100644 --- a/src/spdx/parser/rdf/file_parser.py +++ b/src/spdx/parser/rdf/file_parser.py @@ -15,7 +15,7 @@ from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.rdf.checksum_parser import parse_checksum from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, parse_literal_or_no_assertion_or_none, \ - get_correct_typed_value, apply_parsing_method_or_log_error, parse_enum_value + get_correctly_typed_value, apply_parsing_method_or_log_error, parse_enum_value from spdx.parser.rdf.license_expression_parser import parse_license_expression from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -40,8 +40,8 @@ def parse_file(file_node: URIRef, graph: Graph, doc_namespace: str) -> File: license_info_in_file = [] for (_, _, license_info_from_files_node) in graph.triples((file_node, SPDX_NAMESPACE.licenseInfoInFile, None)): license_info_in_file.append( - get_correct_typed_value(logger, license_info_from_files_node, - lambda x: parse_license_expression(x, graph, doc_namespace))) + get_correctly_typed_value(logger, license_info_from_files_node, + lambda x: parse_license_expression(x, graph, doc_namespace))) license_comment = parse_literal(logger, graph, file_node, SPDX_NAMESPACE.licenseComments) copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, file_node, SPDX_NAMESPACE.copyrightText) file_contributors = [] diff --git a/src/spdx/parser/rdf/graph_parsing_functions.py b/src/spdx/parser/rdf/graph_parsing_functions.py index 20d748a6d..08fe3aae0 100644 --- a/src/spdx/parser/rdf/graph_parsing_functions.py +++ b/src/spdx/parser/rdf/graph_parsing_functions.py @@ -46,17 +46,11 @@ def apply_parsing_method_or_log_error(logger: Logger, value: Any, parsing_method def parse_literal_or_no_assertion_or_none(logger: Logger, graph: Graph, subject: Node, predicate: Node, parsing_method: Callable = lambda x: x.strip(), default: Any = None): value = get_unique_value(logger, graph, subject, predicate, default) - if not value: - return default - if value == SPDX_NAMESPACE.noassertion or value.toPython() == SPDX_NO_ASSERTION_STRING: - return SpdxNoAssertion() - if value == SPDX_NAMESPACE.none or value.toPython() == SPDX_NONE_STRING: - return SpdxNone() - return apply_parsing_method_or_log_error(logger, value, parsing_method, default) + return get_correctly_typed_value(logger, value, parsing_method, default) -def get_correct_typed_value(logger: Logger, value: Any, parsing_method: Callable = lambda x: x.strip(), - default: Any = None): +def get_correctly_typed_value(logger: Logger, value: Any, parsing_method: Callable = lambda x: x.strip(), + default: Any = None): if not value: return default if value == SPDX_NAMESPACE.noassertion or value.toPython() == SPDX_NO_ASSERTION_STRING: diff --git a/src/spdx/parser/rdf/package_parser.py b/src/spdx/parser/rdf/package_parser.py index e8ab287ce..27977daba 100644 --- a/src/spdx/parser/rdf/package_parser.py +++ b/src/spdx/parser/rdf/package_parser.py @@ -19,7 +19,7 @@ from spdx.parser.parsing_functions import raise_parsing_error_if_logger_has_messages, construct_or_raise_parsing_error from spdx.parser.rdf.checksum_parser import parse_checksum from spdx.parser.rdf.graph_parsing_functions import parse_spdx_id, parse_literal, parse_enum_value, \ - parse_literal_or_no_assertion_or_none, get_correct_typed_value, parse_literal_or_no_assertion_or_none, remove_prefix + parse_literal_or_no_assertion_or_none, get_correctly_typed_value, parse_literal_or_no_assertion_or_none, remove_prefix from spdx.parser.rdf.license_expression_parser import parse_license_expression from spdx.rdfschema.namespace import SPDX_NAMESPACE, REFERENCE_NAMESPACE @@ -58,8 +58,8 @@ def parse_package(package_node: URIRef, graph: Graph, doc_namespace: str) -> Pac for (_, _, license_info_from_files_node) in graph.triples( (package_node, SPDX_NAMESPACE.licenseInfoFromFiles, None)): license_info_from_files.append( - get_correct_typed_value(logger, license_info_from_files_node, - lambda x: parse_license_expression(x, graph, doc_namespace))) + get_correctly_typed_value(logger, license_info_from_files_node, + lambda x: parse_license_expression(x, graph, doc_namespace))) license_comment = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.licenseComments) comment = parse_literal(logger, graph, package_node, RDFS.comment) summary = parse_literal(logger, graph, package_node, SPDX_NAMESPACE.summary) diff --git a/src/spdx/parser/rdf/snippet_parser.py b/src/spdx/parser/rdf/snippet_parser.py index c54a2cff4..096681fe0 100644 --- a/src/spdx/parser/rdf/snippet_parser.py +++ b/src/spdx/parser/rdf/snippet_parser.py @@ -19,7 +19,7 @@ from spdx.parser.logger import Logger from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.rdf.graph_parsing_functions import parse_literal, parse_spdx_id, parse_literal_or_no_assertion_or_none, \ - get_correct_typed_value, apply_parsing_method_or_log_error + get_correctly_typed_value, apply_parsing_method_or_log_error from spdx.parser.rdf.license_expression_parser import parse_license_expression from spdx.rdfschema.namespace import SPDX_NAMESPACE, POINTER_NAMESPACE @@ -43,8 +43,8 @@ def parse_snippet(snippet_node: URIRef, graph: Graph, doc_namespace: str) -> Sni for (_, _, license_info_in_snippet_node) in graph.triples( (snippet_node, SPDX_NAMESPACE.licenseInfoInSnippet, None)): license_info_in_snippet.append( - get_correct_typed_value(logger, license_info_in_snippet_node, - lambda x: parse_license_expression(x, graph, doc_namespace))) + get_correctly_typed_value(logger, license_info_in_snippet_node, + lambda x: parse_license_expression(x, graph, doc_namespace))) license_comment = parse_literal(logger, graph, snippet_node, SPDX_NAMESPACE.licenseComments) copyright_text = parse_literal_or_no_assertion_or_none(logger, graph, snippet_node, SPDX_NAMESPACE.copyrightText) comment = parse_literal(logger, graph, snippet_node, RDFS.comment) From 4d6cb59ac4f8843692c587810157b1739a41a88b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 2 Feb 2023 10:54:07 +0100 Subject: [PATCH 273/362] [issue-463] add validation of v2.3 only package fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/validation/package_validator.py | 28 +++++++++++++++---- .../spdx/validation/test_package_validator.py | 21 +++++++++++--- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/spdx/validation/package_validator.py b/src/spdx/validation/package_validator.py index f73f42eff..3138eaf86 100644 --- a/src/spdx/validation/package_validator.py +++ b/src/spdx/validation/package_validator.py @@ -24,19 +24,20 @@ from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_packages(packages: List[Package], document: Optional[Document] = None) -> List[ValidationMessage]: +def validate_packages(packages: List[Package], version: str, document: Optional[Document] = None) -> List[ + ValidationMessage]: validation_messages: List[ValidationMessage] = [] if document: for package in packages: - validation_messages.extend(validate_package_within_document(package, document)) + validation_messages.extend(validate_package_within_document(package, version, document)) else: for package in packages: - validation_messages.extend(validate_package(package)) + validation_messages.extend(validate_package(package, version)) return validation_messages -def validate_package_within_document(package: Package, document: Document) -> List[ValidationMessage]: +def validate_package_within_document(package: Package, version: str, document: Document) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] context = ValidationContext(spdx_id=package.spdx_id, parent_id=document.creation_info.spdx_id, element_type=SpdxElementType.PACKAGE, full_element=package) @@ -59,12 +60,13 @@ def validate_package_within_document(package: Package, document: Document) -> Li context) ) - validation_messages.extend(validate_package(package, context)) + validation_messages.extend(validate_package(package, version, context)) return validation_messages -def validate_package(package: Package, context: Optional[ValidationContext] = None) -> List[ValidationMessage]: +def validate_package(package: Package, version: str, context: Optional[ValidationContext] = None) -> List[ + ValidationMessage]: validation_messages: List[ValidationMessage] = [] if not context: context = ValidationContext(spdx_id=package.spdx_id, element_type=SpdxElementType.PACKAGE, full_element=package) @@ -108,4 +110,18 @@ def validate_package(package: Package, context: Optional[ValidationContext] = No validation_messages.extend(validate_external_package_refs(package.external_references, package.spdx_id)) + if version == "SPDX-2.2": + if package.primary_package_purpose is not None: + validation_messages.append( + ValidationMessage(f"primary_package_purpose is not supported in SPDX-2.2", context)) + if package.built_date is not None: + validation_messages.append( + ValidationMessage(f"built_date is not supported in SPDX-2.2", context)) + if package.release_date is not None: + validation_messages.append( + ValidationMessage(f"release_date is not supported in SPDX-2.2", context)) + if package.valid_until_date is not None: + validation_messages.append( + ValidationMessage(f"valid_until_date is not supported in SPDX-2.2", context)) + return validation_messages diff --git a/tests/spdx/validation/test_package_validator.py b/tests/spdx/validation/test_package_validator.py index fa7923982..7123a2a67 100644 --- a/tests/spdx/validation/test_package_validator.py +++ b/tests/spdx/validation/test_package_validator.py @@ -10,6 +10,7 @@ # limitations under the License. from typing import List +from unittest import TestCase import pytest from license_expression import Licensing @@ -17,14 +18,15 @@ from spdx.model.relationship import Relationship, RelationshipType from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone -from spdx.validation.package_validator import validate_package_within_document +from spdx.validation.package_validator import validate_package_within_document, validate_package from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.spdx.fixtures import package_fixture, package_verification_code_fixture, document_fixture, file_fixture def test_valid_package(): package = package_fixture() - validation_messages: List[ValidationMessage] = validate_package_within_document(package, document_fixture()) + validation_messages: List[ValidationMessage] = validate_package_within_document(package, "SPDX-2.3", + document_fixture()) assert validation_messages == [] @@ -46,7 +48,7 @@ def test_valid_package(): "is_exception=False)]") ]) def test_invalid_package(package_input, expected_message): - validation_messages: List[ValidationMessage] = validate_package_within_document(package_input, + validation_messages: List[ValidationMessage] = validate_package_within_document(package_input, "SPDX-2.3", document_fixture(relationships=[])) expected = ValidationMessage(expected_message, @@ -72,8 +74,19 @@ def test_invalid_package_with_contains(relationships): element_type=SpdxElementType.PACKAGE, full_element=package) - validation_messages: List[ValidationMessage] = validate_package_within_document(package, document) + validation_messages: List[ValidationMessage] = validate_package_within_document(package, "SPDX-2.3", document) assert validation_messages == [ ValidationMessage(f"package must contain no elements if files_analyzed is False, but found {relationships}", context)] + + +def test_v2_3only_fields(): + package = package_fixture() + validation_messages: List[ValidationMessage] = validate_package(package, "SPDX-2.2") + + context = ValidationContext(spdx_id=package.spdx_id, element_type=SpdxElementType.PACKAGE, full_element=package) + unsupported_fields = ["primary_package_purpose", "built_date", "release_date", "valid_until_date"] + expected = [ValidationMessage(f"{field} is not supported in SPDX-2.2", context) for field in unsupported_fields] + + TestCase().assertCountEqual(validation_messages, expected) From a5e8b6705337127dea6c0eaeae37280c64398eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 2 Feb 2023 11:06:41 +0100 Subject: [PATCH 274/362] [issue-463] add validation of v2.2 mandatory package fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/validation/package_validator.py | 10 ++++++++++ tests/spdx/validation/test_package_validator.py | 15 +++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/spdx/validation/package_validator.py b/src/spdx/validation/package_validator.py index 3138eaf86..f2070b3bf 100644 --- a/src/spdx/validation/package_validator.py +++ b/src/spdx/validation/package_validator.py @@ -124,4 +124,14 @@ def validate_package(package: Package, version: str, context: Optional[Validatio validation_messages.append( ValidationMessage(f"valid_until_date is not supported in SPDX-2.2", context)) + if package.license_concluded is None: + validation_messages.append( + ValidationMessage(f"license_concluded is mandatory in SPDX-2.2", context)) + if package.license_declared is None: + validation_messages.append( + ValidationMessage(f"license_declared is mandatory in SPDX-2.2", context)) + if package.copyright_text is None: + validation_messages.append( + ValidationMessage(f"copyright_text is mandatory in SPDX-2.2", context)) + return validation_messages diff --git a/tests/spdx/validation/test_package_validator.py b/tests/spdx/validation/test_package_validator.py index 7123a2a67..1e1ac3698 100644 --- a/tests/spdx/validation/test_package_validator.py +++ b/tests/spdx/validation/test_package_validator.py @@ -90,3 +90,18 @@ def test_v2_3only_fields(): expected = [ValidationMessage(f"{field} is not supported in SPDX-2.2", context) for field in unsupported_fields] TestCase().assertCountEqual(validation_messages, expected) + + +def test_v2_2mandatory_fields(): + package = package_fixture(license_concluded=None, license_declared=None, copyright_text=None, + primary_package_purpose=None, built_date=None, release_date=None, valid_until_date=None) + + assert validate_package(package, "SPDX-2.3") == [] + + validation_messages: List[ValidationMessage] = validate_package(package, "SPDX-2.2") + + context = ValidationContext(spdx_id=package.spdx_id, element_type=SpdxElementType.PACKAGE, full_element=package) + mandatory_fields = ["license_concluded", "license_declared", "copyright_text"] + expected = [ValidationMessage(f"{field} is mandatory in SPDX-2.2", context) for field in mandatory_fields] + + TestCase().assertCountEqual(validation_messages, expected) From b5e209676da60d8622324fe6170a340b62864174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 2 Feb 2023 12:07:27 +0100 Subject: [PATCH 275/362] [issue-463] add validation of v2.2 mandatory file fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer [issue-463] fix file validation tests Signed-off-by: Armin Tänzer --- src/spdx/validation/file_validator.py | 23 +++++++++++++++----- tests/spdx/validation/test_file_validator.py | 22 ++++++++++++++++--- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/spdx/validation/file_validator.py b/src/spdx/validation/file_validator.py index 16845e55d..5a48812f7 100644 --- a/src/spdx/validation/file_validator.py +++ b/src/spdx/validation/file_validator.py @@ -20,19 +20,19 @@ from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_files(files: List[File], document: Optional[Document] = None) -> List[ValidationMessage]: +def validate_files(files: List[File], version: str, document: Optional[Document] = None) -> List[ValidationMessage]: validation_messages = [] if document: for file in files: - validation_messages.extend(validate_file_within_document(file, document)) + validation_messages.extend(validate_file_within_document(file, version, document)) else: for file in files: - validation_messages.extend(validate_file(file)) + validation_messages.extend(validate_file(file, version)) return validation_messages -def validate_file_within_document(file: File, document: Document) -> List[ValidationMessage]: +def validate_file_within_document(file: File, version: str, document: Document) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] context = ValidationContext(spdx_id=file.spdx_id, parent_id=document.creation_info.spdx_id, element_type=SpdxElementType.FILE, full_element=file) @@ -40,12 +40,12 @@ def validate_file_within_document(file: File, document: Document) -> List[Valida for message in validate_spdx_id(file.spdx_id, document): validation_messages.append(ValidationMessage(message, context)) - validation_messages.extend(validate_file(file, context)) + validation_messages.extend(validate_file(file, version, context)) return validation_messages -def validate_file(file: File, context: Optional[ValidationContext] = None) -> List[ValidationMessage]: +def validate_file(file: File, version: str, context: Optional[ValidationContext] = None) -> List[ValidationMessage]: validation_messages = [] if not context: context = ValidationContext(spdx_id=file.spdx_id, element_type=SpdxElementType.FILE, full_element=file) @@ -69,4 +69,15 @@ def validate_file(file: File, context: Optional[ValidationContext] = None) -> Li validation_messages.extend(validate_license_expressions(file.license_info_in_file)) + if version == "SPDX-2.2": + if file.license_concluded is None: + validation_messages.append( + ValidationMessage(f"license_concluded is mandatory in SPDX-2.2", context)) + if not file.license_info_in_file: + validation_messages.append( + ValidationMessage(f"license_info_in_file is mandatory in SPDX-2.2", context)) + if file.copyright_text is None: + validation_messages.append( + ValidationMessage(f"copyright_text is mandatory in SPDX-2.2", context)) + return validation_messages diff --git a/tests/spdx/validation/test_file_validator.py b/tests/spdx/validation/test_file_validator.py index d06a24d63..48c1c36b9 100644 --- a/tests/spdx/validation/test_file_validator.py +++ b/tests/spdx/validation/test_file_validator.py @@ -10,18 +10,19 @@ # limitations under the License. from typing import List +from unittest import TestCase import pytest from spdx.model.checksum import Checksum, ChecksumAlgorithm -from spdx.validation.file_validator import validate_file_within_document +from spdx.validation.file_validator import validate_file_within_document, validate_file from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.spdx.fixtures import file_fixture, document_fixture def test_valid_file(): file = file_fixture() - validation_messages: List[ValidationMessage] = validate_file_within_document(file, document_fixture()) + validation_messages: List[ValidationMessage] = validate_file_within_document(file, "SPDX-2.3", document_fixture()) assert validation_messages == [] @@ -35,7 +36,7 @@ def test_valid_file(): f'checksums must contain a SHA1 algorithm checksum, but only contains: []') ]) def test_invalid_file(file_input, spdx_id, expected_message): - validation_messages: List[ValidationMessage] = validate_file_within_document(file_input, document_fixture()) + validation_messages: List[ValidationMessage] = validate_file_within_document(file_input, "SPDX-2.3", document_fixture()) expected = ValidationMessage(expected_message, ValidationContext(spdx_id=spdx_id, @@ -44,3 +45,18 @@ def test_invalid_file(file_input, spdx_id, expected_message): full_element=file_input)) assert validation_messages == [expected] + + +def test_v2_2mandatory_fields(): + file = file_fixture(license_concluded=None, license_info_in_file=[], copyright_text=None) + + assert validate_file(file, "SPDX-2.3") == [] + + validation_messages: List[ValidationMessage] = validate_file(file, "SPDX-2.2") + + context = ValidationContext(spdx_id=file.spdx_id, element_type=SpdxElementType.FILE, full_element=file) + mandatory_fields = ["license_concluded", "license_info_in_file", "copyright_text"] + expected = [ValidationMessage(f"{field} is mandatory in SPDX-2.2", context) for field in mandatory_fields] + + TestCase().assertCountEqual(validation_messages, expected) + From ebe47a15be38dfc8b12887fc3d3a6e14a8bc1534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 2 Feb 2023 12:13:15 +0100 Subject: [PATCH 276/362] [issue-463] add validation of v2.2 mandatory snippet fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer [issue-463] fix snippet validation tests Signed-off-by: Armin Tänzer --- src/spdx/validation/snippet_validator.py | 20 ++++++++++++------ .../spdx/validation/test_snippet_validator.py | 21 ++++++++++++++++--- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/spdx/validation/snippet_validator.py b/src/spdx/validation/snippet_validator.py index 4d350f353..ac27ec74c 100644 --- a/src/spdx/validation/snippet_validator.py +++ b/src/spdx/validation/snippet_validator.py @@ -19,19 +19,19 @@ from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_snippets(snippets: List[Snippet], document: Optional[Document] = None) -> List[ValidationMessage]: +def validate_snippets(snippets: List[Snippet], version: str, document: Optional[Document] = None) -> List[ValidationMessage]: validation_messages = [] if document: for snippet in snippets: - validation_messages.extend(validate_snippet_within_document(snippet, document)) + validation_messages.extend(validate_snippet_within_document(snippet, version, document)) else: for snippet in snippets: - validation_messages.extend(validate_snippet(snippet)) + validation_messages.extend(validate_snippet(snippet, version)) return validation_messages -def validate_snippet_within_document(snippet: Snippet, document: Document) -> List[ValidationMessage]: +def validate_snippet_within_document(snippet: Snippet, version: str, document: Document) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] context = ValidationContext(spdx_id=snippet.spdx_id, parent_id=document.creation_info.spdx_id, element_type=SpdxElementType.SNIPPET, full_element=snippet) @@ -44,12 +44,12 @@ def validate_snippet_within_document(snippet: Snippet, document: Document) -> Li for message in messages: validation_messages.append(ValidationMessage(message, context)) - validation_messages.extend(validate_snippet(snippet, context)) + validation_messages.extend(validate_snippet(snippet, version, context)) return validation_messages -def validate_snippet(snippet: Snippet, context: Optional[ValidationContext] = None) -> List[ValidationMessage]: +def validate_snippet(snippet: Snippet, version: str, context: Optional[ValidationContext] = None) -> List[ValidationMessage]: validation_messages = [] if not context: context = ValidationContext(spdx_id=snippet.spdx_id, element_type=SpdxElementType.SNIPPET, full_element=snippet) @@ -87,4 +87,12 @@ def validate_snippet(snippet: Snippet, context: Optional[ValidationContext] = No validation_messages.extend(validate_license_expressions(snippet.license_info_in_snippet)) + if version == "SPDX-2.2": + if snippet.license_concluded is None: + validation_messages.append( + ValidationMessage(f"license_concluded is mandatory in SPDX-2.2", context)) + if snippet.copyright_text is None: + validation_messages.append( + ValidationMessage(f"copyright_text is mandatory in SPDX-2.2", context)) + return validation_messages diff --git a/tests/spdx/validation/test_snippet_validator.py b/tests/spdx/validation/test_snippet_validator.py index f6594d93d..084a5e067 100644 --- a/tests/spdx/validation/test_snippet_validator.py +++ b/tests/spdx/validation/test_snippet_validator.py @@ -10,17 +10,18 @@ # limitations under the License. from typing import List +from unittest import TestCase import pytest -from spdx.validation.snippet_validator import validate_snippet_within_document +from spdx.validation.snippet_validator import validate_snippet_within_document, validate_snippet from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType from tests.spdx.fixtures import document_fixture, snippet_fixture def test_valid_snippet(): snippet = snippet_fixture() - validation_messages: List[ValidationMessage] = validate_snippet_within_document(snippet, document_fixture()) + validation_messages: List[ValidationMessage] = validate_snippet_within_document(snippet, "SPDX-2.3", document_fixture()) assert validation_messages == [] @@ -36,7 +37,7 @@ def test_valid_snippet(): "the first value of line_range must be less than or equal to the second, but is: (45, 23)") ]) def test_invalid_ranges(snippet_input, expected_message): - validation_messages: List[ValidationMessage] = validate_snippet_within_document(snippet_input, document_fixture()) + validation_messages: List[ValidationMessage] = validate_snippet_within_document(snippet_input, "SPDX-2.3", document_fixture()) expected = ValidationMessage(expected_message, ValidationContext(spdx_id=snippet_input.spdx_id, @@ -45,3 +46,17 @@ def test_invalid_ranges(snippet_input, expected_message): full_element=snippet_input)) assert validation_messages == [expected] + + +def test_v2_2mandatory_fields(): + snippet = snippet_fixture(license_concluded=None, copyright_text=None) + + assert validate_snippet(snippet, "SPDX-2.3") == [] + + validation_messages: List[ValidationMessage] = validate_snippet(snippet, "SPDX-2.2") + + context = ValidationContext(spdx_id=snippet.spdx_id, element_type=SpdxElementType.SNIPPET, full_element=snippet) + mandatory_fields = ["license_concluded", "copyright_text"] + expected = [ValidationMessage(f"{field} is mandatory in SPDX-2.2", context) for field in mandatory_fields] + + TestCase().assertCountEqual(validation_messages, expected) From d83275dc7a7c8cdda5a969d32747f92d7fdf403e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 2 Feb 2023 12:19:09 +0100 Subject: [PATCH 277/362] [issue-463] fix usage of spdx version during validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/validation/document_validator.py | 8 ++++---- src/spdx/validation/relationship_validator.py | 10 +++++----- tests/spdx/validation/test_relationship_validator.py | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/spdx/validation/document_validator.py b/src/spdx/validation/document_validator.py index fe6422a21..0695f5b8a 100644 --- a/src/spdx/validation/document_validator.py +++ b/src/spdx/validation/document_validator.py @@ -54,11 +54,11 @@ def validate_full_spdx_document(document: Document, spdx_version: str = None) -> return validation_messages validation_messages.extend(validate_creation_info(document.creation_info)) - validation_messages.extend(validate_packages(document.packages, document)) - validation_messages.extend(validate_files(document.files, document)) - validation_messages.extend(validate_snippets(document.snippets, document)) + validation_messages.extend(validate_packages(document.packages, spdx_version, document)) + validation_messages.extend(validate_files(document.files, spdx_version, document)) + validation_messages.extend(validate_snippets(document.snippets, spdx_version, document)) validation_messages.extend(validate_annotations(document.annotations, document)) - validation_messages.extend(validate_relationships(document.relationships, document, spdx_version)) + validation_messages.extend(validate_relationships(document.relationships, spdx_version, document)) validation_messages.extend(validate_extracted_licensing_infos(document.extracted_licensing_info)) document_id = document.creation_info.spdx_id diff --git a/src/spdx/validation/relationship_validator.py b/src/spdx/validation/relationship_validator.py index f919ebaae..4f37ba7ab 100644 --- a/src/spdx/validation/relationship_validator.py +++ b/src/spdx/validation/relationship_validator.py @@ -19,15 +19,15 @@ from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_relationships(relationships: List[Relationship], document: Document, spdx_version: str) -> List[ValidationMessage]: +def validate_relationships(relationships: List[Relationship], spdx_version: str, document: Document) -> List[ValidationMessage]: validation_messages = [] for relationship in relationships: - validation_messages.extend(validate_relationship(relationship, document, spdx_version)) + validation_messages.extend(validate_relationship(relationship, spdx_version, document)) return validation_messages -def validate_relationship(relationship: Relationship, document: Document, spdx_version: str) -> List[ValidationMessage]: +def validate_relationship(relationship: Relationship, spdx_version: str, document: Document) -> List[ValidationMessage]: validation_messages = [] context = ValidationContext(element_type=SpdxElementType.RELATIONSHIP, full_element=relationship) @@ -43,9 +43,9 @@ def validate_relationship(relationship: Relationship, document: Document, spdx_v for message in messages: validation_messages.append(ValidationMessage(message, context)) - if spdx_version != "SPDX-2.3": + if spdx_version == "SPDX-2.2": if relationship_type == RelationshipType.SPECIFICATION_FOR or relationship_type == RelationshipType.REQUIREMENT_DESCRIPTION_FOR: validation_messages.append( - ValidationMessage(f"{relationship_type} is not supported for SPDX versions below SPDX-2.3", context)) + ValidationMessage(f"{relationship_type} is not supported in SPDX-2.2", context)) return validation_messages diff --git a/tests/spdx/validation/test_relationship_validator.py b/tests/spdx/validation/test_relationship_validator.py index 89ea0883d..32e4c114b 100644 --- a/tests/spdx/validation/test_relationship_validator.py +++ b/tests/spdx/validation/test_relationship_validator.py @@ -26,7 +26,7 @@ ["SPDXRef-Package", SpdxNoAssertion(), SpdxNone()]) def test_valid_relationship(related_spdx_element): relationship = Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, related_spdx_element, comment="comment") - validation_messages: List[ValidationMessage] = validate_relationship(relationship, document_fixture(), "2.3") + validation_messages: List[ValidationMessage] = validate_relationship(relationship, "SPDX-2.3", document_fixture()) assert validation_messages == [] @@ -40,7 +40,7 @@ def test_valid_relationship(related_spdx_element): def test_unknown_spdx_id(spdx_element_id, related_spdx_element_id, expected_message): relationship: Relationship = relationship_fixture(spdx_element_id=spdx_element_id, related_spdx_element_id=related_spdx_element_id) - validation_messages: List[ValidationMessage] = validate_relationship(relationship, document_fixture(), "SPDX-2.3") + validation_messages: List[ValidationMessage] = validate_relationship(relationship, "SPDX-2.3", document_fixture()) expected = ValidationMessage(expected_message, ValidationContext(element_type=SpdxElementType.RELATIONSHIP, @@ -51,14 +51,14 @@ def test_unknown_spdx_id(spdx_element_id, related_spdx_element_id, expected_mess @pytest.mark.parametrize("relationship, expected_message", [(Relationship("SPDXRef-DOCUMENT", RelationshipType.SPECIFICATION_FOR, "SPDXRef-Package"), - "RelationshipType.SPECIFICATION_FOR is not supported for SPDX versions below SPDX-2.3"), + "RelationshipType.SPECIFICATION_FOR is not supported in SPDX-2.2"), (Relationship("SPDXRef-DOCUMENT", RelationshipType.REQUIREMENT_DESCRIPTION_FOR, "SPDXRef-Package"), - "RelationshipType.REQUIREMENT_DESCRIPTION_FOR is not supported for SPDX versions below SPDX-2.3")]) + "RelationshipType.REQUIREMENT_DESCRIPTION_FOR is not supported in SPDX-2.2")]) def test_v2_3_only_types(relationship, expected_message): document: Document = document_fixture() - validation_message: List[ValidationMessage] = validate_relationship(relationship, document, "SPDX-2.2") + validation_message: List[ValidationMessage] = validate_relationship(relationship, "SPDX-2.2", document) expected = [ValidationMessage(expected_message, ValidationContext(element_type=SpdxElementType.RELATIONSHIP, From 5904728c086998f07d5c079642f32b380069d5d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 22 Feb 2023 10:55:10 +0100 Subject: [PATCH 278/362] [issue-463] restrict possible SPDX versions to supported versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/validation/document_validator.py | 4 ++-- tests/spdx/validation/test_document_validator.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/spdx/validation/document_validator.py b/src/spdx/validation/document_validator.py index 0695f5b8a..19ecef799 100644 --- a/src/spdx/validation/document_validator.py +++ b/src/spdx/validation/document_validator.py @@ -34,10 +34,10 @@ def validate_full_spdx_document(document: Document, spdx_version: str = None) -> if not spdx_version: spdx_version = document_version - if not re.match(r"^SPDX-\d+.\d+$", document_version): + if document_version not in ["SPDX-2.2", "SPDX-2.3"]: validation_messages.append( ValidationMessage( - f'the document\'s spdx_version must be of the form "SPDX-[major].[minor]" but is: {document_version}', + f'only SPDX versions "SPDX-2.2" and "SPDX-2.3" are supported, but the document\'s spdx_version is: {document_version}', context ) ) diff --git a/tests/spdx/validation/test_document_validator.py b/tests/spdx/validation/test_document_validator.py index 0ee0aff9d..80cb999d0 100644 --- a/tests/spdx/validation/test_document_validator.py +++ b/tests/spdx/validation/test_document_validator.py @@ -35,11 +35,11 @@ def test_valid_document(): (creation_info_fixture(spdx_version="SPDX-2.3"), "SPDX2.3", "provided SPDX version SPDX2.3 does not match the document's SPDX version SPDX-2.3"), (creation_info_fixture(spdx_version="SPDX2.3"), "SPDX-2.3", - 'the document\'s spdx_version must be of the form "SPDX-[major].[minor]" but is: SPDX2.3'), + 'only SPDX versions "SPDX-2.2" and "SPDX-2.3" are supported, but the document\'s spdx_version is: SPDX2.3'), (creation_info_fixture(spdx_version="SPDX2.3"), None, - 'the document\'s spdx_version must be of the form "SPDX-[major].[minor]" but is: SPDX2.3'), + 'only SPDX versions "SPDX-2.2" and "SPDX-2.3" are supported, but the document\'s spdx_version is: SPDX2.3'), (creation_info_fixture(spdx_version="SPDX2.3"), "SPDX2.3", - 'the document\'s spdx_version must be of the form "SPDX-[major].[minor]" but is: SPDX2.3'), + 'only SPDX versions "SPDX-2.2" and "SPDX-2.3" are supported, but the document\'s spdx_version is: SPDX2.3'), ]) def test_spdx_version_handling(creation_info: CreationInfo, version_input: str, expected_message: Optional[str]): document: Document = document_fixture(creation_info=creation_info) From 3c932f6fe77828c9bb54c9b3afd9048f998991de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 22 Feb 2023 11:30:08 +0100 Subject: [PATCH 279/362] [issue-463] add validation of v2.3 only checksums MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/validation/checksum_validator.py | 19 ++++++++++-- .../validation/creation_info_validator.py | 4 +-- src/spdx/validation/document_validator.py | 2 +- .../external_document_ref_validator.py | 8 ++--- src/spdx/validation/file_validator.py | 2 +- src/spdx/validation/package_validator.py | 2 +- .../validation/test_checksum_validator.py | 31 +++++++++++++++++-- .../test_creation_info_validator.py | 4 +-- .../test_external_document_ref_validator.py | 2 +- 9 files changed, 57 insertions(+), 17 deletions(-) diff --git a/src/spdx/validation/checksum_validator.py b/src/spdx/validation/checksum_validator.py index b684ec772..bc0e12d25 100644 --- a/src/spdx/validation/checksum_validator.py +++ b/src/spdx/validation/checksum_validator.py @@ -37,20 +37,33 @@ } -def validate_checksums(checksums: List[Checksum], parent_id: str) -> List[ValidationMessage]: +def validate_checksums(checksums: List[Checksum], parent_id: str, spdx_version: str) -> List[ValidationMessage]: validation_messages = [] for checksum in checksums: - validation_messages.extend(validate_checksum(checksum, parent_id)) + validation_messages.extend(validate_checksum(checksum, parent_id, spdx_version)) return validation_messages -def validate_checksum(checksum: Checksum, parent_id: str) -> List[ValidationMessage]: +def validate_checksum(checksum: Checksum, parent_id: str, spdx_version: str) -> List[ValidationMessage]: validation_messages = [] algorithm = checksum.algorithm context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.CHECKSUM, full_element=checksum) + if spdx_version == "SPDX-2.2" and algorithm in [ChecksumAlgorithm.SHA3_512, + ChecksumAlgorithm.SHA3_384, + ChecksumAlgorithm.SHA3_256, + ChecksumAlgorithm.BLAKE3, + ChecksumAlgorithm.BLAKE2B_512, + ChecksumAlgorithm.BLAKE2B_384, + ChecksumAlgorithm.BLAKE2B_256, + ChecksumAlgorithm.ADLER32]: + validation_messages.append( + ValidationMessage( + f"{checksum.algorithm.name} is not supported in SPDX-2.2", context) + ) + if not re.match("^[0-9a-f]{" + algorithm_length[algorithm] + "}$", checksum.value): if algorithm == ChecksumAlgorithm.BLAKE3: length = "at least 256" diff --git a/src/spdx/validation/creation_info_validator.py b/src/spdx/validation/creation_info_validator.py index fc54740f6..e45f5818a 100644 --- a/src/spdx/validation/creation_info_validator.py +++ b/src/spdx/validation/creation_info_validator.py @@ -18,7 +18,7 @@ from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_creation_info(creation_info: CreationInfo) -> List[ValidationMessage]: +def validate_creation_info(creation_info: CreationInfo, spdx_version: str) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] context = ValidationContext(spdx_id=creation_info.spdx_id, element_type=SpdxElementType.DOCUMENT) @@ -48,6 +48,6 @@ def validate_creation_info(creation_info: CreationInfo) -> List[ValidationMessag validation_messages.extend(validate_actors(creation_info.creators, creation_info.spdx_id)) - validation_messages.extend(validate_external_document_refs(creation_info.external_document_refs, creation_info.spdx_id)) + validation_messages.extend(validate_external_document_refs(creation_info.external_document_refs, creation_info.spdx_id, spdx_version)) return validation_messages diff --git a/src/spdx/validation/document_validator.py b/src/spdx/validation/document_validator.py index 19ecef799..10d7e3489 100644 --- a/src/spdx/validation/document_validator.py +++ b/src/spdx/validation/document_validator.py @@ -53,7 +53,7 @@ def validate_full_spdx_document(document: Document, spdx_version: str = None) -> "the validation process has been cancelled.", context)) return validation_messages - validation_messages.extend(validate_creation_info(document.creation_info)) + validation_messages.extend(validate_creation_info(document.creation_info, spdx_version)) validation_messages.extend(validate_packages(document.packages, spdx_version, document)) validation_messages.extend(validate_files(document.files, spdx_version, document)) validation_messages.extend(validate_snippets(document.snippets, spdx_version, document)) diff --git a/src/spdx/validation/external_document_ref_validator.py b/src/spdx/validation/external_document_ref_validator.py index 3d35434a7..26fbb05ef 100644 --- a/src/spdx/validation/external_document_ref_validator.py +++ b/src/spdx/validation/external_document_ref_validator.py @@ -18,16 +18,16 @@ from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_external_document_refs(external_document_refs: List[ExternalDocumentRef], parent_id: str) -> List[ +def validate_external_document_refs(external_document_refs: List[ExternalDocumentRef], parent_id: str, spdx_version: str) -> List[ ValidationMessage]: validation_messages = [] for external_document_ref in external_document_refs: - validation_messages.extend(validate_external_document_ref(external_document_ref, parent_id)) + validation_messages.extend(validate_external_document_ref(external_document_ref, parent_id, spdx_version)) return validation_messages -def validate_external_document_ref(external_document_ref: ExternalDocumentRef, parent_id: str) -> List[ValidationMessage]: +def validate_external_document_ref(external_document_ref: ExternalDocumentRef, parent_id: str, spdx_version: str) -> List[ValidationMessage]: validation_messages = [] context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.EXTERNAL_DOCUMENT_REF, full_element=external_document_ref) @@ -47,6 +47,6 @@ def validate_external_document_ref(external_document_ref: ExternalDocumentRef, p ) ) - validation_messages.extend(validate_checksum(external_document_ref.checksum, parent_id)) + validation_messages.extend(validate_checksum(external_document_ref.checksum, parent_id, spdx_version)) return validation_messages diff --git a/src/spdx/validation/file_validator.py b/src/spdx/validation/file_validator.py index 5a48812f7..2d1ec19d9 100644 --- a/src/spdx/validation/file_validator.py +++ b/src/spdx/validation/file_validator.py @@ -63,7 +63,7 @@ def validate_file(file: File, version: str, context: Optional[ValidationContext] context) ) - validation_messages.extend(validate_checksums(file.checksums, file.spdx_id)) + validation_messages.extend(validate_checksums(file.checksums, file.spdx_id, version)) validation_messages.extend(validate_license_expression(file.license_concluded)) diff --git a/src/spdx/validation/package_validator.py b/src/spdx/validation/package_validator.py index f2070b3bf..afce5bc67 100644 --- a/src/spdx/validation/package_validator.py +++ b/src/spdx/validation/package_validator.py @@ -91,7 +91,7 @@ def validate_package(package: Package, version: str, context: Optional[Validatio else: validation_messages.extend(validate_verification_code(verification_code, package.spdx_id)) - validation_messages.extend(validate_checksums(package.checksums, package.spdx_id)) + validation_messages.extend(validate_checksums(package.checksums, package.spdx_id, version)) validation_messages.extend(validate_license_expression(package.license_concluded)) diff --git a/tests/spdx/validation/test_checksum_validator.py b/tests/spdx/validation/test_checksum_validator.py index 7fd5f56d2..995806a28 100644 --- a/tests/spdx/validation/test_checksum_validator.py +++ b/tests/spdx/validation/test_checksum_validator.py @@ -51,7 +51,7 @@ "af1eec2a1b18886c3f3cc244349d91d8d4c41ce30a517d6ce9d79c8c17bb4b660d7f61beb7018b3924c6b8f96549fa39"), Checksum(ChecksumAlgorithm.ADLER32, "02ec0130")]) def test_valid_checksum(checksum): - validation_messages: List[ValidationMessage] = validate_checksum(checksum, "parent_id") + validation_messages: List[ValidationMessage] = validate_checksum(checksum, "parent_id", "SPDX-2.3") assert validation_messages == [] @@ -97,10 +97,37 @@ def test_valid_checksum(checksum): ]) def test_invalid_checksum(checksum, expected_message): parent_id = "parent_id" - validation_messages: List[ValidationMessage] = validate_checksum(checksum, parent_id) + validation_messages: List[ValidationMessage] = validate_checksum(checksum, parent_id, "SPDX-2.3") expected = ValidationMessage(expected_message, ValidationContext(parent_id=parent_id, element_type=SpdxElementType.CHECKSUM, full_element=checksum)) assert validation_messages == [expected] + + +@pytest.mark.parametrize("checksum", + [Checksum(ChecksumAlgorithm.SHA3_256, + "1e772489c042f49aeaae32b00fc5ef170a25afa741cffaafadde597d4d1727ce"), + Checksum(ChecksumAlgorithm.SHA3_384, + "dd9e30747551865b483bd76bd967384dce0e5670d1b1c3f701cffac7f49b1c46791253493835136b3aa5f679e364c166"), + Checksum(ChecksumAlgorithm.SHA3_512, + "906bca5580be8c95ae44f775363fb69968ad568898dfb03e0ff96cd9445a0b75f817b68e5c1e80ad624031f851cfddd3a101e1d111310266a5d46e2bc1ffbb36"), + Checksum(ChecksumAlgorithm.BLAKE2B_256, + "a0eb3ddfa5807780a562b9c313b2537f1e8dc621e9a524f8c1ffcf07a79e35c7"), + Checksum(ChecksumAlgorithm.BLAKE2B_384, + "902511afc8939c0193d87857f45a19eddfd7e0413b0f8701a3baaf1b025f882b45a8fbf623fa0ad79b64850ac7a4d0b2"), + Checksum(ChecksumAlgorithm.BLAKE2B_512, + "72c23b0160e1af3cb159f0cc96210c5e9aecc5a65d4618566776fa6117bf84929dcef56c7f8b087691c23000c945470842d90b5e8c4af74dce531ca8ebd8824c"), + Checksum(ChecksumAlgorithm.BLAKE3, + "a872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafed"), + Checksum(ChecksumAlgorithm.ADLER32, "02ec0130") + ]) +def test_v2_3only_checksums(checksum): + parent_id = "parent_id" + validation_messages: List[ValidationMessage] = validate_checksum(checksum, parent_id, "SPDX-2.2") + + context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.CHECKSUM, full_element=checksum) + expected = ValidationMessage(f"{checksum.algorithm.name} is not supported in SPDX-2.2", context) + + assert validation_messages == [expected] diff --git a/tests/spdx/validation/test_creation_info_validator.py b/tests/spdx/validation/test_creation_info_validator.py index 0fb4fc746..4e62e6fbc 100644 --- a/tests/spdx/validation/test_creation_info_validator.py +++ b/tests/spdx/validation/test_creation_info_validator.py @@ -20,7 +20,7 @@ def test_valid_creation_info(): creation_info = creation_info_fixture() - validation_messages: List[ValidationMessage] = validate_creation_info(creation_info) + validation_messages: List[ValidationMessage] = validate_creation_info(creation_info, "SPDX-2.3") assert validation_messages == [] @@ -35,7 +35,7 @@ def test_valid_creation_info(): "document_namespace must be a valid URI specified in RFC-3986 and must contain no fragment (#), but is: some_namespace"), ]) def test_invalid_creation_info(creation_info_input, expected_message, spdx_id): - validation_messages: List[ValidationMessage] = validate_creation_info(creation_info_input) + validation_messages: List[ValidationMessage] = validate_creation_info(creation_info_input, "SPDX-2.3") expected = ValidationMessage(expected_message, ValidationContext(spdx_id, None, SpdxElementType.DOCUMENT)) diff --git a/tests/spdx/validation/test_external_document_ref_validator.py b/tests/spdx/validation/test_external_document_ref_validator.py index 5010f69cb..f2ca9c4b9 100644 --- a/tests/spdx/validation/test_external_document_ref_validator.py +++ b/tests/spdx/validation/test_external_document_ref_validator.py @@ -18,6 +18,6 @@ def test_valid_external_document_ref(): external_document_ref = external_document_ref_fixture() - validation_messages: List[ValidationMessage] = validate_external_document_ref(external_document_ref, "parent_id") + validation_messages: List[ValidationMessage] = validate_external_document_ref(external_document_ref, "parent_id", "SPDX-2.3") assert validation_messages == [] From f7f3b04c1fd54af1a29fad5983aac6c2335c74b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 22 Feb 2023 11:32:01 +0100 Subject: [PATCH 280/362] [issue-463] change "version" to "spdx_version" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/validation/file_validator.py | 16 ++++++++-------- src/spdx/validation/package_validator.py | 16 ++++++++-------- src/spdx/validation/snippet_validator.py | 14 +++++++------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/spdx/validation/file_validator.py b/src/spdx/validation/file_validator.py index 2d1ec19d9..bfa1758b5 100644 --- a/src/spdx/validation/file_validator.py +++ b/src/spdx/validation/file_validator.py @@ -20,19 +20,19 @@ from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_files(files: List[File], version: str, document: Optional[Document] = None) -> List[ValidationMessage]: +def validate_files(files: List[File], spdx_version: str, document: Optional[Document] = None) -> List[ValidationMessage]: validation_messages = [] if document: for file in files: - validation_messages.extend(validate_file_within_document(file, version, document)) + validation_messages.extend(validate_file_within_document(file, spdx_version, document)) else: for file in files: - validation_messages.extend(validate_file(file, version)) + validation_messages.extend(validate_file(file, spdx_version)) return validation_messages -def validate_file_within_document(file: File, version: str, document: Document) -> List[ValidationMessage]: +def validate_file_within_document(file: File, spdx_version: str, document: Document) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] context = ValidationContext(spdx_id=file.spdx_id, parent_id=document.creation_info.spdx_id, element_type=SpdxElementType.FILE, full_element=file) @@ -40,12 +40,12 @@ def validate_file_within_document(file: File, version: str, document: Document) for message in validate_spdx_id(file.spdx_id, document): validation_messages.append(ValidationMessage(message, context)) - validation_messages.extend(validate_file(file, version, context)) + validation_messages.extend(validate_file(file, spdx_version, context)) return validation_messages -def validate_file(file: File, version: str, context: Optional[ValidationContext] = None) -> List[ValidationMessage]: +def validate_file(file: File, spdx_version: str, context: Optional[ValidationContext] = None) -> List[ValidationMessage]: validation_messages = [] if not context: context = ValidationContext(spdx_id=file.spdx_id, element_type=SpdxElementType.FILE, full_element=file) @@ -63,13 +63,13 @@ def validate_file(file: File, version: str, context: Optional[ValidationContext] context) ) - validation_messages.extend(validate_checksums(file.checksums, file.spdx_id, version)) + validation_messages.extend(validate_checksums(file.checksums, file.spdx_id, spdx_version)) validation_messages.extend(validate_license_expression(file.license_concluded)) validation_messages.extend(validate_license_expressions(file.license_info_in_file)) - if version == "SPDX-2.2": + if spdx_version == "SPDX-2.2": if file.license_concluded is None: validation_messages.append( ValidationMessage(f"license_concluded is mandatory in SPDX-2.2", context)) diff --git a/src/spdx/validation/package_validator.py b/src/spdx/validation/package_validator.py index afce5bc67..6dcfd2b5e 100644 --- a/src/spdx/validation/package_validator.py +++ b/src/spdx/validation/package_validator.py @@ -24,20 +24,20 @@ from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_packages(packages: List[Package], version: str, document: Optional[Document] = None) -> List[ +def validate_packages(packages: List[Package], spdx_version: str, document: Optional[Document] = None) -> List[ ValidationMessage]: validation_messages: List[ValidationMessage] = [] if document: for package in packages: - validation_messages.extend(validate_package_within_document(package, version, document)) + validation_messages.extend(validate_package_within_document(package, spdx_version, document)) else: for package in packages: - validation_messages.extend(validate_package(package, version)) + validation_messages.extend(validate_package(package, spdx_version)) return validation_messages -def validate_package_within_document(package: Package, version: str, document: Document) -> List[ValidationMessage]: +def validate_package_within_document(package: Package, spdx_version: str, document: Document) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] context = ValidationContext(spdx_id=package.spdx_id, parent_id=document.creation_info.spdx_id, element_type=SpdxElementType.PACKAGE, full_element=package) @@ -60,12 +60,12 @@ def validate_package_within_document(package: Package, version: str, document: D context) ) - validation_messages.extend(validate_package(package, version, context)) + validation_messages.extend(validate_package(package, spdx_version, context)) return validation_messages -def validate_package(package: Package, version: str, context: Optional[ValidationContext] = None) -> List[ +def validate_package(package: Package, spdx_version: str, context: Optional[ValidationContext] = None) -> List[ ValidationMessage]: validation_messages: List[ValidationMessage] = [] if not context: @@ -91,7 +91,7 @@ def validate_package(package: Package, version: str, context: Optional[Validatio else: validation_messages.extend(validate_verification_code(verification_code, package.spdx_id)) - validation_messages.extend(validate_checksums(package.checksums, package.spdx_id, version)) + validation_messages.extend(validate_checksums(package.checksums, package.spdx_id, spdx_version)) validation_messages.extend(validate_license_expression(package.license_concluded)) @@ -110,7 +110,7 @@ def validate_package(package: Package, version: str, context: Optional[Validatio validation_messages.extend(validate_external_package_refs(package.external_references, package.spdx_id)) - if version == "SPDX-2.2": + if spdx_version == "SPDX-2.2": if package.primary_package_purpose is not None: validation_messages.append( ValidationMessage(f"primary_package_purpose is not supported in SPDX-2.2", context)) diff --git a/src/spdx/validation/snippet_validator.py b/src/spdx/validation/snippet_validator.py index ac27ec74c..8dc508abc 100644 --- a/src/spdx/validation/snippet_validator.py +++ b/src/spdx/validation/snippet_validator.py @@ -19,19 +19,19 @@ from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_snippets(snippets: List[Snippet], version: str, document: Optional[Document] = None) -> List[ValidationMessage]: +def validate_snippets(snippets: List[Snippet], spdx_version: str, document: Optional[Document] = None) -> List[ValidationMessage]: validation_messages = [] if document: for snippet in snippets: - validation_messages.extend(validate_snippet_within_document(snippet, version, document)) + validation_messages.extend(validate_snippet_within_document(snippet, spdx_version, document)) else: for snippet in snippets: - validation_messages.extend(validate_snippet(snippet, version)) + validation_messages.extend(validate_snippet(snippet, spdx_version)) return validation_messages -def validate_snippet_within_document(snippet: Snippet, version: str, document: Document) -> List[ValidationMessage]: +def validate_snippet_within_document(snippet: Snippet, spdx_version: str, document: Document) -> List[ValidationMessage]: validation_messages: List[ValidationMessage] = [] context = ValidationContext(spdx_id=snippet.spdx_id, parent_id=document.creation_info.spdx_id, element_type=SpdxElementType.SNIPPET, full_element=snippet) @@ -44,12 +44,12 @@ def validate_snippet_within_document(snippet: Snippet, version: str, document: D for message in messages: validation_messages.append(ValidationMessage(message, context)) - validation_messages.extend(validate_snippet(snippet, version, context)) + validation_messages.extend(validate_snippet(snippet, spdx_version, context)) return validation_messages -def validate_snippet(snippet: Snippet, version: str, context: Optional[ValidationContext] = None) -> List[ValidationMessage]: +def validate_snippet(snippet: Snippet, spdx_version: str, context: Optional[ValidationContext] = None) -> List[ValidationMessage]: validation_messages = [] if not context: context = ValidationContext(spdx_id=snippet.spdx_id, element_type=SpdxElementType.SNIPPET, full_element=snippet) @@ -87,7 +87,7 @@ def validate_snippet(snippet: Snippet, version: str, context: Optional[Validatio validation_messages.extend(validate_license_expressions(snippet.license_info_in_snippet)) - if version == "SPDX-2.2": + if spdx_version == "SPDX-2.2": if snippet.license_concluded is None: validation_messages.append( ValidationMessage(f"license_concluded is mandatory in SPDX-2.2", context)) From 284796955085374db4852cbeef0dfb33a5709e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 22 Feb 2023 12:06:12 +0100 Subject: [PATCH 281/362] [issue-463] add validation of v2.3 only externalPackageRef types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- .../external_package_ref_validator.py | 42 +++++++++++-------- src/spdx/validation/package_validator.py | 2 +- .../test_external_package_ref_validator.py | 28 +++++++++++-- 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/spdx/validation/external_package_ref_validator.py b/src/spdx/validation/external_package_ref_validator.py index bff504d97..901db0849 100644 --- a/src/spdx/validation/external_package_ref_validator.py +++ b/src/spdx/validation/external_package_ref_validator.py @@ -40,16 +40,17 @@ } -def validate_external_package_refs(external_package_refs: List[ExternalPackageRef], parent_id: str) -> List[ +def validate_external_package_refs(external_package_refs: List[ExternalPackageRef], parent_id: str, spdx_version: str) -> List[ ValidationMessage]: validation_messages = [] for external_package_ref in external_package_refs: - validation_messages.extend(validate_external_package_ref(external_package_ref, parent_id)) + validation_messages.extend(validate_external_package_ref(external_package_ref, parent_id, spdx_version)) return validation_messages -def validate_external_package_ref(external_package_ref: ExternalPackageRef, parent_id: str) -> List[ValidationMessage]: +def validate_external_package_ref(external_package_ref: ExternalPackageRef, parent_id: str, spdx_version: str) -> List[ValidationMessage]: + validation_messages = [] context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.EXTERNAL_PACKAGE_REF, full_element=external_package_ref) @@ -59,31 +60,36 @@ def validate_external_package_ref(external_package_ref: ExternalPackageRef, pare if category == ExternalPackageRefCategory.OTHER: if " " in locator: - return [ValidationMessage( + validation_messages.append(ValidationMessage( f"externalPackageRef locator in category OTHER must contain no spaces, but is: {locator}", - context)] - return [] + context)) - if reference_type not in CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES[category]: - return [ValidationMessage( + elif reference_type not in CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES[category]: + validation_messages.append(ValidationMessage( f"externalPackageRef type in category {category.name} must be one of {CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES[category]}, but is: {reference_type}", - context)] + context)) - if reference_type in ["advisory", "fix", "url"]: + elif reference_type in ["advisory", "fix", "url"]: if validate_url(locator): - return [ValidationMessage( + validation_messages.append(ValidationMessage( f'externalPackageRef locator of type "{reference_type}" must be a valid URL, but is: {locator}', - context)] - return [] + context)) - if reference_type == "swid": + elif reference_type == "swid": if not uritools.isuri(locator) or not locator.startswith("swid"): - return [ValidationMessage( + validation_messages.append(ValidationMessage( f'externalPackageRef locator of type "swid" must be a valid URI with scheme swid, but is: {locator}', - context)] - return [] + context)) - return validate_against_regex(locator, reference_type, context) + else: + validation_messages.extend(validate_against_regex(locator, reference_type, context)) + + if spdx_version == "SPDX-2.2" and reference_type in ["advisory", "fix", "url", "swid"]: + validation_messages.append( + ValidationMessage(f'externalPackageRef type "{reference_type}" is not supported in SPDX-2.2', context) + ) + + return validation_messages def validate_against_regex(string_to_validate: str, reference_type: str, context: ValidationContext) -> List[ diff --git a/src/spdx/validation/package_validator.py b/src/spdx/validation/package_validator.py index 6dcfd2b5e..9575f7355 100644 --- a/src/spdx/validation/package_validator.py +++ b/src/spdx/validation/package_validator.py @@ -108,7 +108,7 @@ def validate_package(package: Package, spdx_version: str, context: Optional[Vali validation_messages.extend(validate_license_expression(package.license_declared)) - validation_messages.extend(validate_external_package_refs(package.external_references, package.spdx_id)) + validation_messages.extend(validate_external_package_refs(package.external_references, package.spdx_id, spdx_version)) if spdx_version == "SPDX-2.2": if package.primary_package_purpose is not None: diff --git a/tests/spdx/validation/test_external_package_ref_validator.py b/tests/spdx/validation/test_external_package_ref_validator.py index f0085c868..556b68435 100644 --- a/tests/spdx/validation/test_external_package_ref_validator.py +++ b/tests/spdx/validation/test_external_package_ref_validator.py @@ -76,7 +76,7 @@ ]) def test_valid_external_package_ref(category, reference_type, locator): external_package_ref = ExternalPackageRef(category, reference_type, locator, "externalPackageRef comment") - validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, "parent_id") + validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, "parent_id", "SPDX-2.3") assert validation_messages == [] @@ -95,7 +95,7 @@ def test_valid_external_package_ref(category, reference_type, locator): def test_invalid_external_package_ref_types(category, reference_type, locator, expected_message): external_package_ref = ExternalPackageRef(category, reference_type, locator, "externalPackageRef comment") parent_id = "SPDXRef-Package" - validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, parent_id) + validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, parent_id, "SPDX-2.3") expected = ValidationMessage(expected_message, ValidationContext(parent_id=parent_id, @@ -145,7 +145,7 @@ def test_invalid_external_package_ref_types(category, reference_type, locator, e def test_invalid_external_package_ref_locators(category, reference_type, locator, expected_message): external_package_ref = ExternalPackageRef(category, reference_type, locator, "externalPackageRef comment") parent_id = "SPDXRef-Package" - validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, parent_id) + validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, parent_id, "SPDX-2.3") expected = ValidationMessage(expected_message, ValidationContext(parent_id=parent_id, @@ -153,3 +153,25 @@ def test_invalid_external_package_ref_locators(category, reference_type, locator full_element=external_package_ref)) assert validation_messages == [expected] + + +@pytest.mark.parametrize("category, reference_type, locator", + [(ExternalPackageRefCategory.SECURITY, "advisory", + "https://nvd.nist.gov/vuln/detail/CVE-2020-28498"), + (ExternalPackageRefCategory.SECURITY, "fix", + "https://github.com/indutny/elliptic/commit/441b7428"), + (ExternalPackageRefCategory.SECURITY, "url", + "https://github.com/christianlundkvist/blog/blob/master/2020_05_26_secp256k1_twist_attacks/secp256k1_twist_attacks.md"), + (ExternalPackageRefCategory.SECURITY, "swid", "swid:2df9de35-0aff-4a86-ace6-f7dddd1ade4c") + ]) +def test_v2_3only_external_package_ref_types(category, reference_type, locator): + external_package_ref = ExternalPackageRef(category, reference_type, locator, "externalPackageRef comment") + parent_id = "SPDXRef-Package" + validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, parent_id, "SPDX-2.2") + + expected = ValidationMessage(f'externalPackageRef type "{reference_type}" is not supported in SPDX-2.2', + ValidationContext(parent_id=parent_id, + element_type=SpdxElementType.EXTERNAL_PACKAGE_REF, + full_element=external_package_ref)) + + assert validation_messages == [expected] From 07b02ba5df83fe2a7a6dea012ed4797290d6124c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 22 Feb 2023 14:54:09 +0100 Subject: [PATCH 282/362] [issue-463, review] early return for 2.2-unsupported values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/validation/checksum_validator.py | 5 +---- src/spdx/validation/external_package_ref_validator.py | 8 +++----- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/spdx/validation/checksum_validator.py b/src/spdx/validation/checksum_validator.py index bc0e12d25..1f4830336 100644 --- a/src/spdx/validation/checksum_validator.py +++ b/src/spdx/validation/checksum_validator.py @@ -59,10 +59,7 @@ def validate_checksum(checksum: Checksum, parent_id: str, spdx_version: str) -> ChecksumAlgorithm.BLAKE2B_384, ChecksumAlgorithm.BLAKE2B_256, ChecksumAlgorithm.ADLER32]: - validation_messages.append( - ValidationMessage( - f"{checksum.algorithm.name} is not supported in SPDX-2.2", context) - ) + return [ValidationMessage(f"{checksum.algorithm.name} is not supported in SPDX-2.2", context)] if not re.match("^[0-9a-f]{" + algorithm_length[algorithm] + "}$", checksum.value): if algorithm == ChecksumAlgorithm.BLAKE3: diff --git a/src/spdx/validation/external_package_ref_validator.py b/src/spdx/validation/external_package_ref_validator.py index 901db0849..0205bb392 100644 --- a/src/spdx/validation/external_package_ref_validator.py +++ b/src/spdx/validation/external_package_ref_validator.py @@ -64,6 +64,9 @@ def validate_external_package_ref(external_package_ref: ExternalPackageRef, pare f"externalPackageRef locator in category OTHER must contain no spaces, but is: {locator}", context)) + elif spdx_version == "SPDX-2.2" and reference_type in ["advisory", "fix", "url", "swid"]: + return [ValidationMessage(f'externalPackageRef type "{reference_type}" is not supported in SPDX-2.2', context)] + elif reference_type not in CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES[category]: validation_messages.append(ValidationMessage( f"externalPackageRef type in category {category.name} must be one of {CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES[category]}, but is: {reference_type}", @@ -84,11 +87,6 @@ def validate_external_package_ref(external_package_ref: ExternalPackageRef, pare else: validation_messages.extend(validate_against_regex(locator, reference_type, context)) - if spdx_version == "SPDX-2.2" and reference_type in ["advisory", "fix", "url", "swid"]: - validation_messages.append( - ValidationMessage(f'externalPackageRef type "{reference_type}" is not supported in SPDX-2.2', context) - ) - return validation_messages From 474b2efd9316e39ef09d50c08f833ddfc19228eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 22 Feb 2023 14:57:33 +0100 Subject: [PATCH 283/362] [issue-463, review] add version handling testcase 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_document_validator.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/spdx/validation/test_document_validator.py b/tests/spdx/validation/test_document_validator.py index 80cb999d0..97a431d2e 100644 --- a/tests/spdx/validation/test_document_validator.py +++ b/tests/spdx/validation/test_document_validator.py @@ -40,6 +40,8 @@ def test_valid_document(): 'only SPDX versions "SPDX-2.2" and "SPDX-2.3" are supported, but the document\'s spdx_version is: SPDX2.3'), (creation_info_fixture(spdx_version="SPDX2.3"), "SPDX2.3", 'only SPDX versions "SPDX-2.2" and "SPDX-2.3" are supported, but the document\'s spdx_version is: SPDX2.3'), + (creation_info_fixture(spdx_version="SPDX-2.1"), "SPDX-2.1", + 'only SPDX versions "SPDX-2.2" and "SPDX-2.3" are supported, but the document\'s spdx_version is: SPDX-2.1'), ]) def test_spdx_version_handling(creation_info: CreationInfo, version_input: str, expected_message: Optional[str]): document: Document = document_fixture(creation_info=creation_info) From d1cb667dbe4b13f2a79dc14497be1f6d59ae5119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 22 Feb 2023 15:05:07 +0100 Subject: [PATCH 284/362] [autoformat] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- .../validation/creation_info_validator.py | 4 +-- src/spdx/validation/document_validator.py | 1 - .../external_document_ref_validator.py | 7 +++-- .../external_package_ref_validator.py | 9 +++--- .../extracted_licensing_info_validator.py | 3 +- src/spdx/validation/file_validator.py | 6 ++-- src/spdx/validation/package_validator.py | 6 ++-- src/spdx/validation/relationship_validator.py | 3 +- src/spdx/validation/snippet_validator.py | 9 ++++-- .../validation/test_document_validator.py | 6 ++-- .../test_external_document_ref_validator.py | 3 +- .../test_external_package_ref_validator.py | 31 +++++++++++-------- tests/spdx/validation/test_file_validator.py | 6 ++-- .../spdx/validation/test_package_validator.py | 6 ++-- ...est_package_verification_code_validator.py | 3 +- .../spdx/validation/test_snippet_validator.py | 6 ++-- .../validation/test_spdx_id_validators.py | 12 ++++--- 17 files changed, 74 insertions(+), 47 deletions(-) diff --git a/src/spdx/validation/creation_info_validator.py b/src/spdx/validation/creation_info_validator.py index e45f5818a..3511e1d89 100644 --- a/src/spdx/validation/creation_info_validator.py +++ b/src/spdx/validation/creation_info_validator.py @@ -47,7 +47,7 @@ def validate_creation_info(creation_info: CreationInfo, spdx_version: str) -> Li ) validation_messages.extend(validate_actors(creation_info.creators, creation_info.spdx_id)) - - validation_messages.extend(validate_external_document_refs(creation_info.external_document_refs, creation_info.spdx_id, spdx_version)) + validation_messages.extend( + validate_external_document_refs(creation_info.external_document_refs, creation_info.spdx_id, spdx_version)) return validation_messages diff --git a/src/spdx/validation/document_validator.py b/src/spdx/validation/document_validator.py index 10d7e3489..264e8d400 100644 --- a/src/spdx/validation/document_validator.py +++ b/src/spdx/validation/document_validator.py @@ -8,7 +8,6 @@ # 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 re from typing import List from spdx.model.document import Document diff --git a/src/spdx/validation/external_document_ref_validator.py b/src/spdx/validation/external_document_ref_validator.py index 26fbb05ef..99156566b 100644 --- a/src/spdx/validation/external_document_ref_validator.py +++ b/src/spdx/validation/external_document_ref_validator.py @@ -18,8 +18,8 @@ from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_external_document_refs(external_document_refs: List[ExternalDocumentRef], parent_id: str, spdx_version: str) -> List[ - ValidationMessage]: +def validate_external_document_refs(external_document_refs: List[ExternalDocumentRef], parent_id: str, + spdx_version: str) -> List[ValidationMessage]: validation_messages = [] for external_document_ref in external_document_refs: validation_messages.extend(validate_external_document_ref(external_document_ref, parent_id, spdx_version)) @@ -27,7 +27,8 @@ def validate_external_document_refs(external_document_refs: List[ExternalDocumen return validation_messages -def validate_external_document_ref(external_document_ref: ExternalDocumentRef, parent_id: str, spdx_version: str) -> List[ValidationMessage]: +def validate_external_document_ref(external_document_ref: ExternalDocumentRef, parent_id: str, spdx_version: str) -> \ +List[ValidationMessage]: validation_messages = [] context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.EXTERNAL_DOCUMENT_REF, full_element=external_document_ref) diff --git a/src/spdx/validation/external_package_ref_validator.py b/src/spdx/validation/external_package_ref_validator.py index 0205bb392..07a8f6bc4 100644 --- a/src/spdx/validation/external_package_ref_validator.py +++ b/src/spdx/validation/external_package_ref_validator.py @@ -14,7 +14,7 @@ import uritools from spdx.model.package import ExternalPackageRef, ExternalPackageRefCategory, CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES -from spdx.validation.uri_validators import validate_url, validate_uri +from spdx.validation.uri_validators import validate_url from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType CPE22TYPE_REGEX = r'^c[pP][eE]:/[AHOaho]?(:[A-Za-z0-9._\-~%]*){0,6}$' @@ -40,8 +40,8 @@ } -def validate_external_package_refs(external_package_refs: List[ExternalPackageRef], parent_id: str, spdx_version: str) -> List[ - ValidationMessage]: +def validate_external_package_refs(external_package_refs: List[ExternalPackageRef], parent_id: str, + spdx_version: str) -> List[ValidationMessage]: validation_messages = [] for external_package_ref in external_package_refs: validation_messages.extend(validate_external_package_ref(external_package_ref, parent_id, spdx_version)) @@ -49,7 +49,8 @@ def validate_external_package_refs(external_package_refs: List[ExternalPackageRe return validation_messages -def validate_external_package_ref(external_package_ref: ExternalPackageRef, parent_id: str, spdx_version: str) -> List[ValidationMessage]: +def validate_external_package_ref(external_package_ref: ExternalPackageRef, parent_id: str, spdx_version: str) -> List[ + ValidationMessage]: validation_messages = [] context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.EXTERNAL_PACKAGE_REF, full_element=external_package_ref) diff --git a/src/spdx/validation/extracted_licensing_info_validator.py b/src/spdx/validation/extracted_licensing_info_validator.py index 92cd7c395..8b769e13d 100644 --- a/src/spdx/validation/extracted_licensing_info_validator.py +++ b/src/spdx/validation/extracted_licensing_info_validator.py @@ -17,7 +17,8 @@ from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_extracted_licensing_infos(extracted_licensing_infos: Optional[List[ExtractedLicensingInfo]]) -> List[ValidationMessage]: +def validate_extracted_licensing_infos(extracted_licensing_infos: Optional[List[ExtractedLicensingInfo]]) -> List[ + ValidationMessage]: validation_messages = [] for extracted_licensing_info in extracted_licensing_infos: validation_messages.extend(validate_extracted_licensing_info(extracted_licensing_info)) diff --git a/src/spdx/validation/file_validator.py b/src/spdx/validation/file_validator.py index bfa1758b5..a21f28fd7 100644 --- a/src/spdx/validation/file_validator.py +++ b/src/spdx/validation/file_validator.py @@ -20,7 +20,8 @@ from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_files(files: List[File], spdx_version: str, document: Optional[Document] = None) -> List[ValidationMessage]: +def validate_files(files: List[File], spdx_version: str, document: Optional[Document] = None) -> List[ + ValidationMessage]: validation_messages = [] if document: for file in files: @@ -45,7 +46,8 @@ def validate_file_within_document(file: File, spdx_version: str, document: Docum return validation_messages -def validate_file(file: File, spdx_version: str, context: Optional[ValidationContext] = None) -> List[ValidationMessage]: +def validate_file(file: File, spdx_version: str, context: Optional[ValidationContext] = None) -> List[ + ValidationMessage]: validation_messages = [] if not context: context = ValidationContext(spdx_id=file.spdx_id, element_type=SpdxElementType.FILE, full_element=file) diff --git a/src/spdx/validation/package_validator.py b/src/spdx/validation/package_validator.py index 9575f7355..888911002 100644 --- a/src/spdx/validation/package_validator.py +++ b/src/spdx/validation/package_validator.py @@ -37,7 +37,8 @@ def validate_packages(packages: List[Package], spdx_version: str, document: Opti return validation_messages -def validate_package_within_document(package: Package, spdx_version: str, document: Document) -> List[ValidationMessage]: +def validate_package_within_document(package: Package, spdx_version: str, document: Document) -> List[ + ValidationMessage]: validation_messages: List[ValidationMessage] = [] context = ValidationContext(spdx_id=package.spdx_id, parent_id=document.creation_info.spdx_id, element_type=SpdxElementType.PACKAGE, full_element=package) @@ -108,7 +109,8 @@ def validate_package(package: Package, spdx_version: str, context: Optional[Vali validation_messages.extend(validate_license_expression(package.license_declared)) - validation_messages.extend(validate_external_package_refs(package.external_references, package.spdx_id, spdx_version)) + validation_messages.extend( + validate_external_package_refs(package.external_references, package.spdx_id, spdx_version)) if spdx_version == "SPDX-2.2": if package.primary_package_purpose is not None: diff --git a/src/spdx/validation/relationship_validator.py b/src/spdx/validation/relationship_validator.py index 4f37ba7ab..4fa310970 100644 --- a/src/spdx/validation/relationship_validator.py +++ b/src/spdx/validation/relationship_validator.py @@ -19,7 +19,8 @@ from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_relationships(relationships: List[Relationship], spdx_version: str, document: Document) -> List[ValidationMessage]: +def validate_relationships(relationships: List[Relationship], spdx_version: str, document: Document) -> List[ + ValidationMessage]: validation_messages = [] for relationship in relationships: validation_messages.extend(validate_relationship(relationship, spdx_version, document)) diff --git a/src/spdx/validation/snippet_validator.py b/src/spdx/validation/snippet_validator.py index 8dc508abc..21d2e0d44 100644 --- a/src/spdx/validation/snippet_validator.py +++ b/src/spdx/validation/snippet_validator.py @@ -19,7 +19,8 @@ from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_snippets(snippets: List[Snippet], spdx_version: str, document: Optional[Document] = None) -> List[ValidationMessage]: +def validate_snippets(snippets: List[Snippet], spdx_version: str, document: Optional[Document] = None) -> List[ + ValidationMessage]: validation_messages = [] if document: for snippet in snippets: @@ -31,7 +32,8 @@ def validate_snippets(snippets: List[Snippet], spdx_version: str, document: Opti return validation_messages -def validate_snippet_within_document(snippet: Snippet, spdx_version: str, document: Document) -> List[ValidationMessage]: +def validate_snippet_within_document(snippet: Snippet, spdx_version: str, document: Document) -> List[ + ValidationMessage]: validation_messages: List[ValidationMessage] = [] context = ValidationContext(spdx_id=snippet.spdx_id, parent_id=document.creation_info.spdx_id, element_type=SpdxElementType.SNIPPET, full_element=snippet) @@ -49,7 +51,8 @@ def validate_snippet_within_document(snippet: Snippet, spdx_version: str, docume return validation_messages -def validate_snippet(snippet: Snippet, spdx_version: str, context: Optional[ValidationContext] = None) -> List[ValidationMessage]: +def validate_snippet(snippet: Snippet, spdx_version: str, context: Optional[ValidationContext] = None) -> List[ + ValidationMessage]: validation_messages = [] if not context: context = ValidationContext(spdx_id=snippet.spdx_id, element_type=SpdxElementType.SNIPPET, full_element=snippet) diff --git a/tests/spdx/validation/test_document_validator.py b/tests/spdx/validation/test_document_validator.py index 97a431d2e..f9a66d1b5 100644 --- a/tests/spdx/validation/test_document_validator.py +++ b/tests/spdx/validation/test_document_validator.py @@ -70,7 +70,8 @@ def test_document_describes_at_least_one_element(relationships): def test_document_does_not_describe_an_element(): - document = document_fixture(relationships=[Relationship("SPDXRef-Package", RelationshipType.DESCRIBES, "SPDXRef-File")]) + document = document_fixture( + relationships=[Relationship("SPDXRef-Package", RelationshipType.DESCRIBES, "SPDXRef-File")]) validation_messages: List[ValidationMessage] = validate_full_spdx_document(document) assert validation_messages == [ValidationMessage( @@ -81,7 +82,8 @@ def test_document_does_not_describe_an_element(): def test_duplicated_spdx_ids(): document = document_fixture( - files=[file_fixture(spdx_id="SPDXRef-File"), file_fixture(spdx_id="SPDXRef-2"), file_fixture(spdx_id="SPDXRef-3")], + files=[file_fixture(spdx_id="SPDXRef-File"), file_fixture(spdx_id="SPDXRef-2"), + file_fixture(spdx_id="SPDXRef-3")], packages=[package_fixture(spdx_id="SPDXRef-2"), package_fixture(spdx_id="SPDXRef-DOCUMENT")], snippets=[snippet_fixture(spdx_id="SPDXRef-2"), snippet_fixture(spdx_id="SPDXRef-3")]) diff --git a/tests/spdx/validation/test_external_document_ref_validator.py b/tests/spdx/validation/test_external_document_ref_validator.py index f2ca9c4b9..a81bc7b9d 100644 --- a/tests/spdx/validation/test_external_document_ref_validator.py +++ b/tests/spdx/validation/test_external_document_ref_validator.py @@ -18,6 +18,7 @@ def test_valid_external_document_ref(): external_document_ref = external_document_ref_fixture() - validation_messages: List[ValidationMessage] = validate_external_document_ref(external_document_ref, "parent_id", "SPDX-2.3") + validation_messages: List[ValidationMessage] = validate_external_document_ref(external_document_ref, "parent_id", + "SPDX-2.3") assert validation_messages == [] diff --git a/tests/spdx/validation/test_external_package_ref_validator.py b/tests/spdx/validation/test_external_package_ref_validator.py index 556b68435..e9e50f7f5 100644 --- a/tests/spdx/validation/test_external_package_ref_validator.py +++ b/tests/spdx/validation/test_external_package_ref_validator.py @@ -76,26 +76,29 @@ ]) def test_valid_external_package_ref(category, reference_type, locator): external_package_ref = ExternalPackageRef(category, reference_type, locator, "externalPackageRef comment") - validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, "parent_id", "SPDX-2.3") + validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, "parent_id", + "SPDX-2.3") assert validation_messages == [] @pytest.mark.parametrize("category, reference_type, locator, expected_message", [( - ExternalPackageRefCategory.SECURITY, "cpe22Typo", "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", - "externalPackageRef type in category SECURITY must be one of ['cpe22Type', 'cpe23Type', 'advisory', 'fix', 'url', 'swid'], but is: cpe22Typo"), - (ExternalPackageRefCategory.PACKAGE_MANAGER, "nugat", - "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", - "externalPackageRef type in category PACKAGE_MANAGER must be one of ['maven-central', 'npm', 'nuget', 'bower', 'purl'], but is: nugat"), - (ExternalPackageRefCategory.PERSISTENT_ID, "git-oid", - "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", - "externalPackageRef type in category PERSISTENT_ID must be one of ['swh', 'gitoid'], but is: git-oid") - ]) + ExternalPackageRefCategory.SECURITY, "cpe22Typo", + "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", + "externalPackageRef type in category SECURITY must be one of ['cpe22Type', 'cpe23Type', 'advisory', 'fix', 'url', 'swid'], but is: cpe22Typo"), + (ExternalPackageRefCategory.PACKAGE_MANAGER, "nugat", + "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", + "externalPackageRef type in category PACKAGE_MANAGER must be one of ['maven-central', 'npm', 'nuget', 'bower', 'purl'], but is: nugat"), + (ExternalPackageRefCategory.PERSISTENT_ID, "git-oid", + "cpe:/o:canonical:ubuntu_linux:10.04:-:lts", + "externalPackageRef type in category PERSISTENT_ID must be one of ['swh', 'gitoid'], but is: git-oid") + ]) def test_invalid_external_package_ref_types(category, reference_type, locator, expected_message): external_package_ref = ExternalPackageRef(category, reference_type, locator, "externalPackageRef comment") parent_id = "SPDXRef-Package" - validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, parent_id, "SPDX-2.3") + validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, parent_id, + "SPDX-2.3") expected = ValidationMessage(expected_message, ValidationContext(parent_id=parent_id, @@ -145,7 +148,8 @@ def test_invalid_external_package_ref_types(category, reference_type, locator, e def test_invalid_external_package_ref_locators(category, reference_type, locator, expected_message): external_package_ref = ExternalPackageRef(category, reference_type, locator, "externalPackageRef comment") parent_id = "SPDXRef-Package" - validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, parent_id, "SPDX-2.3") + validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, parent_id, + "SPDX-2.3") expected = ValidationMessage(expected_message, ValidationContext(parent_id=parent_id, @@ -167,7 +171,8 @@ def test_invalid_external_package_ref_locators(category, reference_type, locator def test_v2_3only_external_package_ref_types(category, reference_type, locator): external_package_ref = ExternalPackageRef(category, reference_type, locator, "externalPackageRef comment") parent_id = "SPDXRef-Package" - validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, parent_id, "SPDX-2.2") + validation_messages: List[ValidationMessage] = validate_external_package_ref(external_package_ref, parent_id, + "SPDX-2.2") expected = ValidationMessage(f'externalPackageRef type "{reference_type}" is not supported in SPDX-2.2', ValidationContext(parent_id=parent_id, diff --git a/tests/spdx/validation/test_file_validator.py b/tests/spdx/validation/test_file_validator.py index 48c1c36b9..b1fb6b41d 100644 --- a/tests/spdx/validation/test_file_validator.py +++ b/tests/spdx/validation/test_file_validator.py @@ -30,13 +30,14 @@ def test_valid_file(): @pytest.mark.parametrize("file_input, spdx_id, expected_message", [(file_fixture(name="/invalid/file/name"), file_fixture().spdx_id, f'file name must not be an absolute path starting with "/", but is: /invalid/file/name'), - ( + ( file_fixture(checksums=[Checksum(ChecksumAlgorithm.MD2, "d4c41ce30a517d6ce9d79c8c17bb4b66")]), file_fixture().spdx_id, f'checksums must contain a SHA1 algorithm checksum, but only contains: []') ]) def test_invalid_file(file_input, spdx_id, expected_message): - validation_messages: List[ValidationMessage] = validate_file_within_document(file_input, "SPDX-2.3", document_fixture()) + validation_messages: List[ValidationMessage] = validate_file_within_document(file_input, "SPDX-2.3", + document_fixture()) expected = ValidationMessage(expected_message, ValidationContext(spdx_id=spdx_id, @@ -59,4 +60,3 @@ def test_v2_2mandatory_fields(): expected = [ValidationMessage(f"{field} is mandatory in SPDX-2.2", context) for field in mandatory_fields] TestCase().assertCountEqual(validation_messages, expected) - diff --git a/tests/spdx/validation/test_package_validator.py b/tests/spdx/validation/test_package_validator.py index 1e1ac3698..72391fc8e 100644 --- a/tests/spdx/validation/test_package_validator.py +++ b/tests/spdx/validation/test_package_validator.py @@ -61,9 +61,11 @@ def test_invalid_package(package_input, expected_message): @pytest.mark.parametrize("relationships", [[Relationship("SPDXRef-Package", RelationshipType.CONTAINS, "SPDXRef-File1")], - [Relationship("SPDXRef-Package", RelationshipType.CONTAINS, "DocumentRef-external:SPDXRef-File")], + [Relationship("SPDXRef-Package", RelationshipType.CONTAINS, + "DocumentRef-external:SPDXRef-File")], [Relationship("SPDXRef-File2", RelationshipType.CONTAINED_BY, "SPDXRef-Package")], - [Relationship("DocumentRef-external:SPDXRef-File", RelationshipType.CONTAINED_BY, "SPDXRef-Package")], + [Relationship("DocumentRef-external:SPDXRef-File", RelationshipType.CONTAINED_BY, + "SPDXRef-Package")], [Relationship("SPDXRef-Package", RelationshipType.CONTAINS, "SPDXRef-File2"), Relationship("SPDXRef-File1", RelationshipType.CONTAINED_BY, "SPDXRef-Package")]]) def test_invalid_package_with_contains(relationships): diff --git a/tests/spdx/validation/test_package_verification_code_validator.py b/tests/spdx/validation/test_package_verification_code_validator.py index 5ed3ae4ce..d94d56be3 100644 --- a/tests/spdx/validation/test_package_verification_code_validator.py +++ b/tests/spdx/validation/test_package_verification_code_validator.py @@ -28,7 +28,8 @@ def test_valid_package_verification_code(): "value of verification_code must consist of 40 lowercase hexadecimal digits, but is: 71c4025dd9897b364f3ebbb42c484ff43d00791cab (length: 42 digits)"), (PackageVerificationCode("CE9F343C4BA371746FD7EAD9B59031AE34D8AFC4", []), "value of verification_code must consist of 40 lowercase hexadecimal digits, but is: CE9F343C4BA371746FD7EAD9B59031AE34D8AFC4 (length: 40 digits)"), - (PackageVerificationCode("71c4025dd9897b364f3ebbb42c484ff43d00791c", ["/invalid/excluded/file"]), + (PackageVerificationCode("71c4025dd9897b364f3ebbb42c484ff43d00791c", + ["/invalid/excluded/file"]), 'file name must not be an absolute path starting with "/", but is: /invalid/excluded/file') ]) def test_invalid_package_verification_code(code, expected_message): diff --git a/tests/spdx/validation/test_snippet_validator.py b/tests/spdx/validation/test_snippet_validator.py index 084a5e067..90bd4aca1 100644 --- a/tests/spdx/validation/test_snippet_validator.py +++ b/tests/spdx/validation/test_snippet_validator.py @@ -21,7 +21,8 @@ def test_valid_snippet(): snippet = snippet_fixture() - validation_messages: List[ValidationMessage] = validate_snippet_within_document(snippet, "SPDX-2.3", document_fixture()) + validation_messages: List[ValidationMessage] = validate_snippet_within_document(snippet, "SPDX-2.3", + document_fixture()) assert validation_messages == [] @@ -37,7 +38,8 @@ def test_valid_snippet(): "the first value of line_range must be less than or equal to the second, but is: (45, 23)") ]) def test_invalid_ranges(snippet_input, expected_message): - validation_messages: List[ValidationMessage] = validate_snippet_within_document(snippet_input, "SPDX-2.3", document_fixture()) + validation_messages: List[ValidationMessage] = validate_snippet_within_document(snippet_input, "SPDX-2.3", + document_fixture()) expected = ValidationMessage(expected_message, ValidationContext(spdx_id=snippet_input.spdx_id, diff --git a/tests/spdx/validation/test_spdx_id_validators.py b/tests/spdx/validation/test_spdx_id_validators.py index ee8536cba..bcf8745c1 100644 --- a/tests/spdx/validation/test_spdx_id_validators.py +++ b/tests/spdx/validation/test_spdx_id_validators.py @@ -24,8 +24,9 @@ snippets=[snippet_fixture(spdx_id="SPDXRef-Snippet1"), snippet_fixture(spdx_id="SPDXRef-Snippet2")], creation_info=creation_info_fixture( - external_document_refs=[external_document_ref_fixture(document_ref_id="DocumentRef-external"), - external_document_ref_fixture(document_ref_id="DocumentRef-1.2-ext")])) + external_document_refs=[ + external_document_ref_fixture(document_ref_id="DocumentRef-external"), + external_document_ref_fixture(document_ref_id="DocumentRef-1.2-ext")])) @pytest.mark.parametrize("spdx_id", ["SPDXRef-DOCUMENT", "SPDXRef-File1", "SPDXRef-1.3-3.7"]) @@ -62,6 +63,7 @@ def test_is_external_doc_ref_present_in_document(): assert is_external_doc_ref_present_in_document("DocumentRef-1.2-ext", DOCUMENT) assert not is_external_doc_ref_present_in_document("DocumentRef-External1", DOCUMENT) + def test_list_of_all_spdx_ids(): TestCase().assertCountEqual(get_list_of_all_spdx_ids(DOCUMENT), ["SPDXRef-DOCUMENT", "SPDXRef-File1", "SPDXRef-File2", "SPDXRef-Package1", @@ -98,7 +100,8 @@ def test_invalid_spdx_id(spdx_id, expected_messages): @pytest.mark.parametrize("spdx_id", - ["DocumentRef-external:SPDXRef-File", "SPDXRef-DOCUMENT", "SPDXRef-File1", "SPDXRef-Package1", "SPDXRef-Snippet1"]) + ["DocumentRef-external:SPDXRef-File", "SPDXRef-DOCUMENT", "SPDXRef-File1", "SPDXRef-Package1", + "SPDXRef-Snippet1"]) def test_valid_spdx_id_with_check_document(spdx_id): validation_messages = validate_spdx_id(spdx_id, DOCUMENT, check_document=True) assert validation_messages == [] @@ -118,5 +121,6 @@ def test_valid_spdx_id_with_check_files(spdx_id): def test_invalid_spdx_id_with_check_files(): validation_messages = validate_spdx_id("SPDXRef-Package1", DOCUMENT, check_files=True) - assert validation_messages == ['did not find the referenced spdx_id "SPDXRef-Package1" in the SPDX document\'s files'] + assert validation_messages == [ + 'did not find the referenced spdx_id "SPDXRef-Package1" in the SPDX document\'s files'] From c8fd2d7ac7547e1db1c4aef5556238fa9c430d48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 22 Feb 2023 15:14:02 +0100 Subject: [PATCH 285/362] [issue-463, review] cli-tool: catch unsupported SPDX versions early MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/clitools/pyspdxtools.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/spdx/clitools/pyspdxtools.py b/src/spdx/clitools/pyspdxtools.py index 695cf99e8..ed32dbb77 100644 --- a/src/spdx/clitools/pyspdxtools.py +++ b/src/spdx/clitools/pyspdxtools.py @@ -27,7 +27,7 @@ @click.command() @click.option("--infile", "-i", 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"). Will be read from the document if not provided.', default=None) +@click.option("--version", help='The SPDX version to be used during parsing and validation ("SPDX-2.2" or "SPDX-2.3"). Will be read from the document if not provided.', default=None) @click.option("--novalidation", is_flag=True, help="Don't validate the provided document.") def main(infile: str, outfile: str, version: str, novalidation: bool): """ @@ -45,6 +45,11 @@ def main(infile: str, outfile: str, version: str, novalidation: bool): if not version: version = document.creation_info.spdx_version + if not version in ["SPDX-2.2", "SPDX-2.3"]: + print(f"This tool only supports SPDX versions SPDX-2.2 and SPDX-2.3, but got: {version}", + file=sys.stderr) + sys.exit(1) + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, version) if validation_messages: print("The document is invalid. The following issues have been found:", file=sys.stderr) From 2d71c1097573dc0fa209010012b80959b9b111da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 1 Feb 2023 09:25:30 +0100 Subject: [PATCH 286/362] [issue-374] implement license expression validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- .../license_expression_validator.py | 51 ++++++++-- src/spdx/validation/validation_message.py | 1 + .../test_license_expression_validator.py | 92 +++++++++++++++++-- 3 files changed, 129 insertions(+), 15 deletions(-) diff --git a/src/spdx/validation/license_expression_validator.py b/src/spdx/validation/license_expression_validator.py index 0d6d0e687..0beafb86c 100644 --- a/src/spdx/validation/license_expression_validator.py +++ b/src/spdx/validation/license_expression_validator.py @@ -11,25 +11,58 @@ from typing import List, Optional, Union -from license_expression import LicenseExpression +from license_expression import LicenseExpression, get_spdx_licensing, ExpressionError, ExpressionParseError +from spdx.model.document import Document + from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone -from spdx.validation.validation_message import ValidationMessage +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType def validate_license_expressions(license_expressions: Optional[ - Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]]) -> List[ValidationMessage]: + Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]], document: Document, parent_id: str) -> List[ValidationMessage]: if license_expressions in [SpdxNoAssertion(), SpdxNone(), None]: return [] - error_messages = [] + context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.LICENSE_EXPRESSION, full_element=license_expressions) + validation_messages = [] for license_expression in license_expressions: - error_messages.extend(validate_license_expression(license_expression)) + validation_messages.extend(validate_license_expression(license_expression, document, parent_id, context)) + + return validation_messages + + +def validate_license_expression(license_expression: Optional[ + Union[LicenseExpression, SpdxNoAssertion, SpdxNone]], document: Document, parent_id: str, context: ValidationContext = None) -> List[ValidationMessage]: + if license_expression in [SpdxNoAssertion(), SpdxNone(), None]: + return [] + + if context is None: + context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.LICENSE_EXPRESSION, full_element=license_expression) + + validation_messages = [] + license_ref_ids: List[str] = [license_ref.license_id for license_ref in document.extracted_licensing_info] - return error_messages + for non_spdx_token in get_spdx_licensing().validate(license_expression).invalid_symbols: + if non_spdx_token not in license_ref_ids: + validation_messages.append( + ValidationMessage( + f"Unrecognized license reference: {non_spdx_token}. license_expression must only use IDs from the license list or extracted licensing info, but is: {license_expression}", + context) + ) + try: + get_spdx_licensing().parse(str(license_expression), validate=True, strict=True) + except ExpressionParseError as err: + validation_messages.append( + ValidationMessage( + f"{err}. for license_expression: {license_expression}", + context) + ) + except ExpressionError: + # This happens for invalid symbols but the error provides only a string of these. On the other hand, + # get_spdx_licensing().validate() gives an actual list of invalid symbols, so this is handled above. + pass -def validate_license_expression(license_expression: LicenseExpression) -> List[ValidationMessage]: - # TODO: implement this once we have a better license expression model: https://github.com/spdx/tools-python/issues/374 - return [] + return validation_messages diff --git a/src/spdx/validation/validation_message.py b/src/spdx/validation/validation_message.py index bf1490126..a86fc2aa7 100644 --- a/src/spdx/validation/validation_message.py +++ b/src/spdx/validation/validation_message.py @@ -15,6 +15,7 @@ class SpdxElementType(Enum): + LICENSE_EXPRESSION = auto() PACKAGE_VERIFICATION_CODE = auto() EXTERNAL_DOCUMENT_REF = auto() CHECKSUM = auto() diff --git a/tests/spdx/validation/test_license_expression_validator.py b/tests/spdx/validation/test_license_expression_validator.py index 458a98acc..b6b89ba25 100644 --- a/tests/spdx/validation/test_license_expression_validator.py +++ b/tests/spdx/validation/test_license_expression_validator.py @@ -10,14 +10,94 @@ # limitations under the License. from typing import List +from unittest import TestCase -from license_expression import Licensing -from spdx.validation.license_expression_validator import validate_license_expression -from spdx.validation.validation_message import ValidationMessage +import pytest +from license_expression import get_spdx_licensing, LicenseExpression +from spdx.model.document import Document +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone +from spdx.validation.license_expression_validator import validate_license_expression, validate_license_expressions +from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType +from tests.spdx.fixtures import document_fixture, extracted_licensing_info_fixture -def test_valid_license_expression(): - license_expression = Licensing().parse("something") - validation_messages: List[ValidationMessage] = validate_license_expression(license_expression) +FIXTURE_LICENSE_ID = extracted_licensing_info_fixture().license_id + + +@pytest.mark.parametrize("expression_string", + ["MIT", FIXTURE_LICENSE_ID, + f"GPL-2.0-only with GPL-CC-1.0 and {FIXTURE_LICENSE_ID} with 389-exception or Beerware"]) +def test_valid_license_expression(expression_string): + document: Document = document_fixture() + license_expression: LicenseExpression = get_spdx_licensing().parse(expression_string) + validation_messages: List[ValidationMessage] = validate_license_expression(license_expression, document, + parent_id="SPDXRef-File") + + assert validation_messages == [] + + +@pytest.mark.parametrize("expression", [SpdxNone(), SpdxNoAssertion()]) +def test_none_and_no_assertion(expression): + document: Document = document_fixture() + validation_messages: List[ValidationMessage] = validate_license_expression(expression, document, + parent_id="SPDXRef-File") + assert validation_messages == [] + + +@pytest.mark.parametrize("expression_list", + [SpdxNone(), SpdxNoAssertion(), + [get_spdx_licensing().parse("MIT and GPL-3.0-only"), + get_spdx_licensing().parse(FIXTURE_LICENSE_ID)] + ]) +def test_valid_license_expressions(expression_list): + document: Document = document_fixture() + validation_messages: List[ValidationMessage] = validate_license_expressions(expression_list, document, + parent_id="SPDXRef-File") assert validation_messages == [] + + +@pytest.mark.parametrize("expression_string, unknown_symbols", + [(f"{FIXTURE_LICENSE_ID} or LicenseRef-22", ["LicenseRef-22"]), + ("nope with 389-exception and _.- or LicenseRef-10", ["nope", "_.-", "LicenseRef-10"]) + ]) +def test_invalid_license_expression_with_unknown_symbols(expression_string, unknown_symbols): + document: Document = document_fixture() + license_expression: LicenseExpression = get_spdx_licensing().parse(expression_string) + parent_id = "SPDXRef-File" + context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.LICENSE_EXPRESSION, + full_element=license_expression) + + validation_messages: List[ValidationMessage] = validate_license_expression(license_expression, document, parent_id) + expected_messages = [ValidationMessage( + f"Unrecognized license reference: {symbol}. license_expression must only use IDs from the license list or extracted licensing info, but is: {license_expression}", + context + ) for symbol in unknown_symbols] + + TestCase().assertCountEqual(validation_messages, expected_messages) + + +@pytest.mark.parametrize("expression_string, expected_message", + [("MIT with MIT", + 'A plain license symbol cannot be used as an exception in a "WITH symbol" statement. for token: "MIT" at position: 9. for license_expression: MIT WITH MIT'), + (f"GPL-2.0-or-later and {FIXTURE_LICENSE_ID} with {FIXTURE_LICENSE_ID}", + f'A plain license symbol cannot be used as an exception in a "WITH symbol" statement. for token: "{FIXTURE_LICENSE_ID}" at position: 39. for license_expression: GPL-2.0-or-later AND {FIXTURE_LICENSE_ID} WITH {FIXTURE_LICENSE_ID}'), + (f"GPL-2.0-or-later with MIT and {FIXTURE_LICENSE_ID} with GPL-2.0-or-later", + f'A plain license symbol cannot be used as an exception in a "WITH symbol" statement. for token: "MIT" at position: 22. for license_expression: GPL-2.0-or-later WITH MIT AND {FIXTURE_LICENSE_ID} WITH GPL-2.0-or-later'), + ("389-exception with 389-exception", + 'A license exception symbol can only be used as an exception in a "WITH exception" statement. for token: "389-exception". for license_expression: 389-exception WITH 389-exception'), + ("389-exception with MIT", + 'A license exception symbol can only be used as an exception in a "WITH exception" statement. for token: "389-exception". for license_expression: 389-exception WITH MIT'), + ]) +def test_invalid_license_expression_with_invalid_exceptions(expression_string, expected_message): + document: Document = document_fixture() + license_expression: LicenseExpression = get_spdx_licensing().parse(expression_string) + parent_id = "SPDXRef-File" + context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.LICENSE_EXPRESSION, + full_element=license_expression) + + validation_messages: List[ValidationMessage] = validate_license_expression(license_expression, document, parent_id) + expected_messages = [ValidationMessage(expected_message, context)] + + assert validation_messages == expected_messages From e428de0d5b0e678468dd99548dcdf9f132867967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Thu, 23 Feb 2023 13:05:13 +0100 Subject: [PATCH 287/362] [issue-374] adapt validate_license_expression calls to new usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/validation/file_validator.py | 8 +++---- src/spdx/validation/package_validator.py | 30 ++++++++++++------------ src/spdx/validation/snippet_validator.py | 8 +++---- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/spdx/validation/file_validator.py b/src/spdx/validation/file_validator.py index a21f28fd7..745131bdc 100644 --- a/src/spdx/validation/file_validator.py +++ b/src/spdx/validation/file_validator.py @@ -41,6 +41,10 @@ def validate_file_within_document(file: File, spdx_version: str, document: Docum for message in validate_spdx_id(file.spdx_id, document): validation_messages.append(ValidationMessage(message, context)) + validation_messages.extend(validate_license_expression(file.license_concluded, document, file.spdx_id)) + + validation_messages.extend(validate_license_expressions(file.license_info_in_file, document, file.spdx_id)) + validation_messages.extend(validate_file(file, spdx_version, context)) return validation_messages @@ -67,10 +71,6 @@ def validate_file(file: File, spdx_version: str, context: Optional[ValidationCon validation_messages.extend(validate_checksums(file.checksums, file.spdx_id, spdx_version)) - validation_messages.extend(validate_license_expression(file.license_concluded)) - - validation_messages.extend(validate_license_expressions(file.license_info_in_file)) - if spdx_version == "SPDX-2.2": if file.license_concluded is None: validation_messages.append( diff --git a/src/spdx/validation/package_validator.py b/src/spdx/validation/package_validator.py index 888911002..4cd850fc8 100644 --- a/src/spdx/validation/package_validator.py +++ b/src/spdx/validation/package_validator.py @@ -61,6 +61,21 @@ def validate_package_within_document(package: Package, spdx_version: str, docume context) ) + validation_messages.extend(validate_license_expression(package.license_concluded, document, package.spdx_id)) + + license_info_from_files = package.license_info_from_files + if license_info_from_files: + if not package.files_analyzed: + validation_messages.append( + ValidationMessage( + f"license_info_from_files must be None if files_analyzed is False, but is: {license_info_from_files}", + context) + ) + else: + validation_messages.extend(validate_license_expressions(license_info_from_files, document, package.spdx_id)) + + validation_messages.extend(validate_license_expression(package.license_declared, document, package.spdx_id)) + validation_messages.extend(validate_package(package, spdx_version, context)) return validation_messages @@ -94,21 +109,6 @@ def validate_package(package: Package, spdx_version: str, context: Optional[Vali validation_messages.extend(validate_checksums(package.checksums, package.spdx_id, spdx_version)) - validation_messages.extend(validate_license_expression(package.license_concluded)) - - license_info_from_files = package.license_info_from_files - if license_info_from_files: - if not package.files_analyzed: - validation_messages.append( - ValidationMessage( - f"license_info_from_files must be None if files_analyzed is False, but is: {license_info_from_files}", - context) - ) - else: - validation_messages.extend(validate_license_expressions(license_info_from_files)) - - validation_messages.extend(validate_license_expression(package.license_declared)) - validation_messages.extend( validate_external_package_refs(package.external_references, package.spdx_id, spdx_version)) diff --git a/src/spdx/validation/snippet_validator.py b/src/spdx/validation/snippet_validator.py index 21d2e0d44..90a110fa3 100644 --- a/src/spdx/validation/snippet_validator.py +++ b/src/spdx/validation/snippet_validator.py @@ -46,6 +46,10 @@ def validate_snippet_within_document(snippet: Snippet, spdx_version: str, docume for message in messages: validation_messages.append(ValidationMessage(message, context)) + validation_messages.extend(validate_license_expression(snippet.license_concluded, document, snippet.spdx_id)) + + validation_messages.extend(validate_license_expressions(snippet.license_info_in_snippet, document, snippet.spdx_id)) + validation_messages.extend(validate_snippet(snippet, spdx_version, context)) return validation_messages @@ -86,10 +90,6 @@ def validate_snippet(snippet: Snippet, spdx_version: str, context: Optional[Vali context) ) - validation_messages.extend(validate_license_expression(snippet.license_concluded)) - - validation_messages.extend(validate_license_expressions(snippet.license_info_in_snippet)) - if spdx_version == "SPDX-2.2": if snippet.license_concluded is None: validation_messages.append( From 354faa6bf0cca927cd2d15e890c4f00a0c574c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Mon, 27 Feb 2023 11:56:47 +0100 Subject: [PATCH 288/362] [issue-374, review] more descriptive comments, remove unused LICENSE from Enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/validation/license_expression_validator.py | 8 +++++--- src/spdx/validation/validation_message.py | 1 - 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/spdx/validation/license_expression_validator.py b/src/spdx/validation/license_expression_validator.py index 0beafb86c..f2c8ddfbf 100644 --- a/src/spdx/validation/license_expression_validator.py +++ b/src/spdx/validation/license_expression_validator.py @@ -38,7 +38,7 @@ def validate_license_expression(license_expression: Optional[ if license_expression in [SpdxNoAssertion(), SpdxNone(), None]: return [] - if context is None: + if not context: context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.LICENSE_EXPRESSION, full_element=license_expression) validation_messages = [] @@ -55,14 +55,16 @@ def validate_license_expression(license_expression: Optional[ try: get_spdx_licensing().parse(str(license_expression), validate=True, strict=True) except ExpressionParseError as err: + # This error is raised when an exception symbol is used as a license symbol and vice versa. + # So far, it only catches the first such error in the provided string. validation_messages.append( ValidationMessage( f"{err}. for license_expression: {license_expression}", context) ) except ExpressionError: - # This happens for invalid symbols but the error provides only a string of these. On the other hand, - # get_spdx_licensing().validate() gives an actual list of invalid symbols, so this is handled above. + # This error is raised for invalid symbols within the license_expression, but it provides only a string of these. + # On the other hand, get_spdx_licensing().validate() gives an actual list of invalid symbols, so this is handled above. pass return validation_messages diff --git a/src/spdx/validation/validation_message.py b/src/spdx/validation/validation_message.py index a86fc2aa7..70001b78a 100644 --- a/src/spdx/validation/validation_message.py +++ b/src/spdx/validation/validation_message.py @@ -26,7 +26,6 @@ class SpdxElementType(Enum): PACKAGE = auto() FILE = auto() SNIPPET = auto() - LICENSE = auto() ANNOTATION = auto() RELATIONSHIP = auto() EXTRACTED_LICENSING_INFO = auto() From 3171f1f17459f5fef431523c0d8374a2c6552067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Mon, 27 Feb 2023 09:31:07 +0100 Subject: [PATCH 289/362] [issue-391] update readme to reflect the new usage after refactoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- README.md | 111 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 84 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index cb8e9d3c2..adebb3a88 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ CI status (Linux, macOS and Windows): [![Install and Test][1]][2] # CURRENT STATE A major refactoring of large parts of the codebase is currently in progress. It is expected that functionality on `main` -is severely limited during this process. Please check out +is limited during this process. Please check out the [latest release](https://github.com/spdx/tools-python/releases/tag/v0.7.0) if you are looking for a working version. # Information @@ -21,10 +21,6 @@ This library implements SPDX parsers, convertors, validators and handlers in Pyt - Issues: https://github.com/spdx/tools-python/issues - PyPI: https://pypi.python.org/pypi/spdx-tools -# 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. # License @@ -32,18 +28,20 @@ This is the result of an initial GSoC contribution by @[ah450](https://github.co # Features -* API to create and manipulate SPDX v2.3 documents. -* Parse, convert, create and validate Tag/Value, RDF, JSON, YAML, XML format SPDX files +* 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 -### Known Limitations +# Planned features -* No full 2.3 support for RDF format [#295](https://github.com/spdx/tools-python/issues/295) -* No full license expression support [#10](https://github.com/spdx/tools-python/issues/10) -* Output of the CLI parser is incomplete [#268](https://github.com/spdx/tools-python/issues/268) +* up-to-date support of SPDX v3.0 as soon as it releases -# TODOs +# Installation -* Include specialized validation for SPDX v2.2.1(ISO 5962:2021) +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 with +`yourenv/bin/pip install spdx-tools`. Note that on Windows it would be `Scripts` +instead of `bin`. # How to use @@ -51,35 +49,88 @@ This is the result of an initial GSoC contribution by @[ah450](https://github.co 1. **PARSING/VALIDATING** (for parsing any format): -* 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`. +* Use `pyspdxtools -i ` where `` is the location of the file. The input format is inferred automatically from the file ending. -* Or you can use `pyspdxtools` only, and it will automatically prompt/ask for the `input file path`. +* If you are using a source distribution, try running: + `pyspdxtools -i tests/data/formats/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 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` + 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/formats/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/formats/SPDXJSONExample-v2.3.spdx.json -o output.tag --novalidation` + `pyspdxtools -i tests/data/formats/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` -# 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 with -`yourenv/bin/pip install spdx-tools`. Note that on Windows it would be `Scripts` -instead of `bin`. +## Library usage: +1. **DATA MODEL** + * The `src.spdx.model` package constitutes the internal SPDX v2.3 data model (v2.2 is a simply a subset of this). + * 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. + * Type checking is enforced from the type hints 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, 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. During serialization, they will be written again where appropriate. +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. +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. +4. **WRITING** + * 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)`. + Caution: Only valid documents can be serialized reliably; serialization of invalid documents is not supported. + +## Example +Here are some examples of possible use cases to quickly get you started with the spdx-tools: +```python +# read in an SPDX document from a file +document = parse_file("spdx_document.json") + +# change the document's name +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_type=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 (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] + +# validate the edited document and log the validation messages (depending on your use case, you might also want to utilize the provided validation_message.context) +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 and we can safely serialize it without validating again +if not validation_messages: + write_file(document, "new_spdx_document.rdf") +``` # Dependencies * PyYAML: https://pypi.org/project/PyYAML/ for handling YAML. * xmltodict: https://pypi.org/project/xmltodict/ for handling XML. -* click: https://pypi.org/project/click/ for creating the CLI interface. * rdflib: https://pypi.python.org/pypi/rdflib/ for handling RDF. -* typeguard: https://pypi.org/project/typeguard/ for using typehints. +* click: https://pypi.org/project/click/ for creating the CLI interface. +* typeguard: https://pypi.org/project/typeguard/ for type checking. * uritools: https://pypi.org/project/uritools/ for validation of URIs. # Support @@ -93,3 +144,9 @@ instead of `bin`. Contributions are very welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md) for instructions on how to contribute to the codebase. + +# 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. +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 03/2023. From 9315e42d8ea064bae47e69fe07b79934ef6dddb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Mon, 27 Feb 2023 12:40:58 +0100 Subject: [PATCH 290/362] [issue-391, review] update readme: "Current state", add license_expression to dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- README.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index adebb3a88..9416994b9 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,13 @@ CI status (Linux, macOS and Windows): [![Install and Test][1]][2] [2]: https://github.com/spdx/tools-python/actions/workflows/install_and_test.yml -# CURRENT STATE +# Current state -A major refactoring of large parts of the codebase is currently in progress. It is expected that functionality on `main` -is limited during this process. Please check out -the [latest release](https://github.com/spdx/tools-python/releases/tag/v0.7.0) if you are looking for a working version. +This repository was subject to a major refactoring recently to get ready for the upcoming SPDX v3.0 release. +Therefore, we'd like to encourage you to post any and all issues you find at https://github.com/spdx/tools-python/issues. +If you prefer a version that has been longer in use, please check out +the [latest release](https://github.com/spdx/tools-python/releases/tag/v0.7.0). +Note, though, that this will only receive bug fixes but no new features. # Information @@ -34,7 +36,7 @@ This library implements SPDX parsers, convertors, validators and handlers in Pyt # Planned features -* up-to-date support of SPDX v3.0 as soon as it releases +* up-to-date support of SPDX v3.0 as soon as it is released # Installation @@ -45,7 +47,7 @@ instead of `bin`. # How to use -## Command-line usage: +## Command-line usage 1. **PARSING/VALIDATING** (for parsing any format): @@ -68,12 +70,12 @@ instead of `bin`. * For help use `pyspdxtools --help` -## Library usage: +## Library usage 1. **DATA MODEL** * The `src.spdx.model` package constitutes the internal SPDX v2.3 data model (v2.2 is a simply a subset of this). * 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. - * Type checking is enforced from the type hints when initializing a new instance or setting/getting a property on an instance + * 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, which links to all other classes. @@ -121,7 +123,7 @@ for validation_message in validation_messages: # 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") + write_file(document, "new_spdx_document.rdf", validate=False) ``` # Dependencies @@ -132,6 +134,7 @@ if not validation_messages: * click: https://pypi.org/project/click/ for creating the CLI interface. * typeguard: https://pypi.org/project/typeguard/ 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. # Support From 956cf637ec903cb616b455fdddb1c9f84b384111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 24 Feb 2023 12:29:05 +0100 Subject: [PATCH 291/362] [issue-441] replace print() with logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/clitools/pyspdxtools.py | 36 ++++++++++++++++------------- src/spdx/writer/rdf/writer_utils.py | 5 ++-- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/spdx/clitools/pyspdxtools.py b/src/spdx/clitools/pyspdxtools.py index ed32dbb77..44a4a3bac 100644 --- a/src/spdx/clitools/pyspdxtools.py +++ b/src/spdx/clitools/pyspdxtools.py @@ -10,6 +10,7 @@ # 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 logging import sys from typing import List @@ -26,8 +27,11 @@ @click.command() @click.option("--infile", "-i", 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 ("SPDX-2.2" or "SPDX-2.3"). Will be read from the document if not provided.', default=None) +@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 ("SPDX-2.2" or "SPDX-2.3"). Will be read from the document if not provided.', + default=None) @click.option("--novalidation", is_flag=True, help="Don't validate the provided document.") def main(infile: str, outfile: str, version: str, novalidation: bool): """ @@ -46,34 +50,34 @@ def main(infile: str, outfile: str, version: str, novalidation: bool): version = document.creation_info.spdx_version if not version in ["SPDX-2.2", "SPDX-2.3"]: - print(f"This tool only supports SPDX versions SPDX-2.2 and SPDX-2.3, but got: {version}", - file=sys.stderr) + logging.error(f"This tool only supports SPDX versions SPDX-2.2 and SPDX-2.3, but got: {version}") sys.exit(1) validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, version) if validation_messages: - print("The document is invalid. The following issues have been found:", file=sys.stderr) - for message in validation_messages: - print(message.validation_message, file=sys.stderr) + log_string = "\n".join( + ["The document is invalid. The following issues have been found:"] + + [message.validation_message for message in validation_messages]) + logging.error(log_string) sys.exit(1) else: - print("The document is valid.", file=sys.stderr) + logging.info("The document is valid.") if outfile and outfile != "-": write_file(document, outfile, validate=False) except NotImplementedError as err: - print(err.args[0], file=sys.stderr) - print("Please note that this project is currently undergoing a major refactoring and therefore missing " - "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.", file=sys.stderr) + logging.error(err.args[0] + + "\nPlease note that this project is currently undergoing a major refactoring and therefore missing " + "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 current PyPI release version.") sys.exit(1) except SPDXParsingError as err: - print("There have been issues while parsing the provided document:", file=sys.stderr) - for message in err.get_messages(): - print(message, file=sys.stderr) + log_string = "\n".join(["There have been issues while parsing the provided document:"] + + [message for message in err.get_messages()]) + logging.error(log_string) sys.exit(1) diff --git a/src/spdx/writer/rdf/writer_utils.py b/src/spdx/writer/rdf/writer_utils.py index 772f80f6e..1498b75a7 100644 --- a/src/spdx/writer/rdf/writer_utils.py +++ b/src/spdx/writer/rdf/writer_utils.py @@ -8,7 +8,7 @@ # 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 +import logging from datetime import datetime from typing import Any, Optional, Dict @@ -59,8 +59,7 @@ def add_namespace_to_spdx_id(spdx_id: str, doc_namespace: str, external_doc_name if ":" in spdx_id: external_doc_ref_id = spdx_id.split(":")[0] if external_doc_ref_id not in external_doc_namespaces.keys(): - print(f"No namespace for external document reference with id {external_doc_ref_id} provided.", - file=sys.stderr) + logging.warning(f"No namespace for external document reference with id {external_doc_ref_id} provided.") return spdx_id return f"{external_doc_namespaces[external_doc_ref_id]}#{spdx_id.split(':')[1]}" From 7a6ee59c134d9299e03f2a1c35531b0858d6e8c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Mon, 27 Feb 2023 15:10:29 +0100 Subject: [PATCH 292/362] [issue-441, review] use logging in rdf creation_info_parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/parser/rdf/creation_info_parser.py | 15 ++++++++++----- .../spdx/parser/rdf/test_creation_info_parser.py | 6 ++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/spdx/parser/rdf/creation_info_parser.py b/src/spdx/parser/rdf/creation_info_parser.py index 522b21e53..7ca2215e1 100644 --- a/src/spdx/parser/rdf/creation_info_parser.py +++ b/src/spdx/parser/rdf/creation_info_parser.py @@ -8,6 +8,7 @@ # 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 logging import sys from typing import Tuple from urllib.parse import urldefrag @@ -72,19 +73,23 @@ def parse_namespace_and_spdx_id(graph: Graph) -> (str, str): try: subject = graph.value(predicate=RDF.type, object=SPDX_NAMESPACE.SpdxDocument, any=False) except UniquenessError: - sys.exit("Multiple SpdxDocuments found, can't parse rdf file.") + logging.error("Multiple SpdxDocuments found, can't parse rdf file.") + sys.exit(1) if not subject: - sys.exit("No SpdxDocument found, can't parse rdf file.") + logging.error("No SpdxDocument found, can't parse rdf file.") + sys.exit(1) if not "#" in subject: - sys.exit("No '#' found in the URI of SpdxDocument, " - "the URI for the SpdxDocument should be the namespace appended by '#SPDXRef-DOCUMENT.") + logging.error("No '#' found in the URI of SpdxDocument, " + "the URI for the SpdxDocument should be the namespace appended by '#SPDXRef-DOCUMENT.") + sys.exit(1) namespace, spdx_id = urldefrag(subject) if not namespace: - sys.exit( + logging.error( "No namespace found, the URI for the SpdxDocument should be the namespace appended by '#SPDXRef-DOCUMENT.") + sys.exit(1) if not spdx_id: spdx_id = None diff --git a/tests/spdx/parser/rdf/test_creation_info_parser.py b/tests/spdx/parser/rdf/test_creation_info_parser.py index 516b0ffdf..71ee6ccd6 100644 --- a/tests/spdx/parser/rdf/test_creation_info_parser.py +++ b/tests/spdx/parser/rdf/test_creation_info_parser.py @@ -60,14 +60,16 @@ def test_parse_namespace_and_spdx_id(): ([(URIRef("docNamespace1"), RDF.type, SPDX_NAMESPACE.SpdxDocument), (URIRef("docNamespace2"), RDF.type, SPDX_NAMESPACE.SpdxDocument)], "Multiple SpdxDocuments found")]) -def test_parse_namespace_and_spdx_id_with_system_exit(triples: List[Tuple[Node, Node, Node]], error_message: str): +def test_parse_namespace_and_spdx_id_with_system_exit(triples: List[Tuple[Node, Node, Node]], error_message: str, caplog): graph = Graph() for triple in triples: graph = graph.add(triple) - with pytest.raises(SystemExit, match=error_message): + with pytest.raises(SystemExit): parse_namespace_and_spdx_id(graph) + assert error_message in caplog.text + def test_parse_external_document_refs(): graph = Graph().parse(os.path.join(os.path.dirname(__file__), "data/file_to_test_rdf_parser.rdf.xml")) From 20cc1d8ac0027b3988e66b1deaccea84a2665351 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 2 Mar 2023 14:58:48 +0100 Subject: [PATCH 293/362] [fix] use correct value for LicenseListVersion Signed-off-by: Meret Behrens --- src/spdx/writer/tagvalue/creation_info_writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spdx/writer/tagvalue/creation_info_writer.py b/src/spdx/writer/tagvalue/creation_info_writer.py index 45788410b..e03c222f2 100644 --- a/src/spdx/writer/tagvalue/creation_info_writer.py +++ b/src/spdx/writer/tagvalue/creation_info_writer.py @@ -32,7 +32,7 @@ def write_creation_info(creation_info: CreationInfo, text_output: TextIO): write_separator(text_output) text_output.write("## Creation Information\n") - write_value("LicenseListVersion", str(creation_info.spdx_version), text_output) + write_value("LicenseListVersion", str(creation_info.license_list_version), text_output) for creator in creation_info.creators: write_value("Creator", creator.to_serialized_string(), text_output) write_value("Created", datetime_to_iso_string(creation_info.created), text_output) From 27a4f61e4a8b1265cb05f35651c1d52022c50e8f Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 20 Feb 2023 16:52:20 +0100 Subject: [PATCH 294/362] fix assertion to test that the dataLicense is correctly added to the graph Signed-off-by: Meret Behrens --- tests/spdx/writer/rdf/test_creation_info_writer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/spdx/writer/rdf/test_creation_info_writer.py b/tests/spdx/writer/rdf/test_creation_info_writer.py index b7592555c..6237208ff 100644 --- a/tests/spdx/writer/rdf/test_creation_info_writer.py +++ b/tests/spdx/writer/rdf/test_creation_info_writer.py @@ -12,7 +12,7 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.writer.rdf.creation_info_writer import add_creation_info_to_graph -from spdx.rdfschema.namespace import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE, LICENSE_NAMESPACE from tests.spdx.fixtures import creation_info_fixture @@ -24,7 +24,7 @@ def test_add_creation_info_to_graph(): assert (None, RDF.type, SPDX_NAMESPACE.SpdxDocument) in graph assert (URIRef(f"{creation_info.document_namespace}#{creation_info.spdx_id}"), None, None) in graph - assert (None, SPDX_NAMESPACE.dataLicense, URIRef(f"https://spdx.org/licenses/{creation_info.data_license}")) + assert (None, SPDX_NAMESPACE.dataLicense, LICENSE_NAMESPACE[creation_info.data_license]) in graph assert (None, SPDX_NAMESPACE.name, Literal(creation_info.name)) in graph assert (None, SPDX_NAMESPACE.specVersion, Literal(creation_info.spdx_version)) in graph assert (None, SPDX_NAMESPACE.creationInfo, None) in graph From eb5382672b33e957c8c4a6415517fc05f757d77f Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 21 Feb 2023 11:21:27 +0100 Subject: [PATCH 295/362] [issue-382] add lexer Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/lexer/__init__.py | 0 src/spdx/parser/tagvalue/lexer/tagvalue.py | 242 +++++++++++++++ tests/spdx/parser/tagvalue/__init__.py | 0 .../parser/tagvalue/test_tag_value_lexer.py | 288 ++++++++++++++++++ 4 files changed, 530 insertions(+) create mode 100644 src/spdx/parser/tagvalue/lexer/__init__.py create mode 100644 src/spdx/parser/tagvalue/lexer/tagvalue.py create mode 100644 tests/spdx/parser/tagvalue/__init__.py create mode 100644 tests/spdx/parser/tagvalue/test_tag_value_lexer.py diff --git a/src/spdx/parser/tagvalue/lexer/__init__.py b/src/spdx/parser/tagvalue/lexer/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/spdx/parser/tagvalue/lexer/tagvalue.py b/src/spdx/parser/tagvalue/lexer/tagvalue.py new file mode 100644 index 000000000..e6737db47 --- /dev/null +++ b/src/spdx/parser/tagvalue/lexer/tagvalue.py @@ -0,0 +1,242 @@ +# Copyright (c) 2014 Ahmed H. Ismail +# Copyright (c) 2023 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 ply import lex +from ply.lex import TOKEN + + +class SPDXLexer(object): + reserved = { + # Top level fields + "SPDXVersion": "DOC_VERSION", + "DataLicense": "DOC_LICENSE", + "DocumentName": "DOC_NAME", + "SPDXID": "SPDX_ID", + "DocumentComment": "DOC_COMMENT", + "DocumentNamespace": "DOC_NAMESPACE", + "ExternalDocumentRef": "EXT_DOC_REF", + # Creation info fields + "Creator": "CREATOR", + "Created": "CREATED", + "CreatorComment": "CREATOR_COMMENT", + "LicenseListVersion": "LIC_LIST_VER", + # Annotation fields + "Annotator": "ANNOTATOR", + "AnnotationDate": "ANNOTATION_DATE", + "AnnotationComment": "ANNOTATION_COMMENT", + "AnnotationType": "ANNOTATION_TYPE", + "SPDXREF": "ANNOTATION_SPDX_ID", + # Relationship fields + "Relationship": "RELATIONSHIP", + "RelationshipComment": "RELATIONSHIP_COMMENT", + # Package fields + "PackageName": "PKG_NAME", + "PackageVersion": "PKG_VERSION", + "PackageDownloadLocation": "PKG_DOWN", + "FilesAnalyzed": "PKG_FILES_ANALYZED", + "PackageSummary": "PKG_SUM", + "PackageSourceInfo": "PKG_SRC_INFO", + "PackageFileName": "PKG_FILE_NAME", + "PackageSupplier": "PKG_SUPPL", + "PackageOriginator": "PKG_ORIG", + "PackageChecksum": "PKG_CHECKSUM", + "PackageVerificationCode": "PKG_VERF_CODE", + "PackageDescription": "PKG_DESC", + "PackageComment": "PKG_COMMENT", + "PackageLicenseDeclared": "PKG_LICS_DECL", + "PackageLicenseConcluded": "PKG_LICS_CONC", + "PackageLicenseInfoFromFiles": "PKG_LICS_FFILE", + "PackageLicenseComments": "PKG_LICS_COMMENT", + "PackageCopyrightText": "PKG_CPY_TEXT", + "PackageHomePage": "PKG_HOME", + "ExternalRef": "PKG_EXT_REF", + "ExternalRefComment": "PKG_EXT_REF_COMMENT", + "PackageAttributionText": "PKG_ATTRIBUTION_TEXT", + "PrimaryPackagePurpose": "PRIMARY_PACKAGE_PURPOSE", + "BuiltDate": "BUILT_DATE", + "ReleaseDate": "RELEASE_DATE", + "ValidUntilDate": "VALID_UNTIL_DATE", + # File fields + "FileName": "FILE_NAME", + "FileType": "FILE_TYPE", + "FileChecksum": "FILE_CHECKSUM", + "LicenseConcluded": "FILE_LICS_CONC", + "LicenseInfoInFile": "FILE_LICS_INFO", + "FileCopyrightText": "FILE_CR_TEXT", + "LicenseComments": "FILE_LICS_COMMENT", + "FileComment": "FILE_COMMENT", + "FileNotice": "FILE_NOTICE", + "FileContributor": "FILE_CONTRIB", + "FileAttributionText": "FILE_ATTRIBUTION_TEXT", + # ExtractedLicensingInfo fields + "LicenseID": "LICS_ID", + "ExtractedText": "LICS_TEXT", + "LicenseName": "LICS_NAME", + "LicenseCrossReference": "LICS_CRS_REF", + "LicenseComment": "LICS_COMMENT", + # Snippet fields + "SnippetSPDXID": "SNIPPET_SPDX_ID", + "SnippetName": "SNIPPET_NAME", + "SnippetComment": "SNIPPET_COMMENT", + "SnippetCopyrightText": "SNIPPET_CR_TEXT", + "SnippetLicenseComments": "SNIPPET_LICS_COMMENT", + "SnippetFromFileSPDXID": "SNIPPET_FILE_SPDXID", + "SnippetLicenseConcluded": "SNIPPET_LICS_CONC", + "LicenseInfoInSnippet": "SNIPPET_LICS_INFO", + "SnippetAttributionText": "SNIPPET_ATTRIBUTION_TEXT", + "SnippetByteRange": "SNIPPET_BYTE_RANGE", + "SnippetLineRange": "SNIPPET_LINE_RANGE", + # Common fields + "NOASSERTION": "NO_ASSERTION", + "NONE": "NONE", + "SOURCE": "SOURCE", + "BINARY": "BINARY", + "ARCHIVE": "ARCHIVE", + "APPLICATION": "APPLICATION", + "AUDIO": "AUDIO", + "IMAGE": "IMAGE", + "TEXT": "FILETYPE_TEXT", + "VIDEO": "VIDEO", + "DOCUMENTATION": "DOCUMENTATION", + "SPDX": "SPDX", + "OTHER": "OTHER", + "REVIEW": "REVIEW", + "FRAMEWORK": "FRAMEWORK", + "LIBRARY": "LIBRARY", + "CONTAINER": "CONTAINER", + "OPERATING-SYSTEM": "OPERATING_SYSTEM", + "DEVICE": "DEVICE", + "FIRMWARE": "FIRMWARE", + "FILE": "FILE", + "INSTALL": "INSTALL" + } + states = (("text", "exclusive"),) + + tokens = [ + "TEXT", + "TOOL_VALUE", + "UNKNOWN_TAG", + "ORG_VALUE", + "PERSON_VALUE", + "DATE", + "LINE", + "CHECKSUM", + "DOC_REF_ID", + "DOC_URI", + "EXT_DOC_REF_CHECKSUM", + ] + list(reserved.values()) + + def __init__(self): + self.lexer = None + + @TOKEN(r":\s*") + def t_text(self, t): + t.lexer.text_start = t.lexer.lexpos - len("") + t.lexer.begin("text") + + @TOKEN(r"\s*") + def t_text_end(self, t): + t.type = "TEXT" + t.value = t.lexer.lexdata[t.lexer.text_start: t.lexer.lexpos] + t.lexer.lineno += t.value.count("\n") + t.value = t.value.strip() + t.lexer.begin("INITIAL") + return t + + @TOKEN(r".|\n") + def t_text_any(self, t): + pass + + def t_text_error(self, t): + print("Lexer error in text state") + + @TOKEN( + r":\s*(ADLER32|BLAKE2b-256|BLAKE2b-384|BLAKE2b-512|BLAKE3|MD2|MD4|MD5|MD6|SHA1|SHA224|SHA256|SHA384|SHA512|SHA3-256|SHA3-384|SHA3-512):\s*([a-f0-9]*)") + def t_CHECKSUM(self, t): + t.value = t.value[1:].strip() + return t + + @TOKEN(r":\s*DocumentRef-([A-Za-z0-9\+\.\-]+)") + def t_DOC_REF_ID(self, t): + t.value = t.value[1:].strip() + return t + + @TOKEN(r"\s*((ht|f)tps?:\/\/\S*)") + def t_DOC_URI(self, t): + t.value = t.value.strip() + return t + + @TOKEN(r"\s*SHA1:\s*[a-f0-9]{40}") + def t_EXT_DOC_REF_CHECKSUM(self, t): + t.value = t.value[1:].strip() + return t + + @TOKEN(r":\s*Tool:.+") + def t_TOOL_VALUE(self, t): + t.value = t.value[1:].strip() + return t + + @TOKEN(r":\s*Organization:.+") + def t_ORG_VALUE(self, t): + t.value = t.value[1:].strip() + return t + + @TOKEN(r":\s*Person:.+") + def t_PERSON_VALUE(self, t): + t.value = t.value[1:].strip() + return t + + @TOKEN(r":\s*\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ") + def t_DATE(self, t): + t.value = t.value[1:].strip() + return t + + @TOKEN(r"[a-zA-Z]+") + def t_KEYWORD_AS_TAG(self, t): + t.type = self.reserved.get(t.value, "UNKNOWN_TAG") + t.value = t.value.strip() + return t + + @TOKEN(r":.+") + def t_LINE_OR_KEYWORD_VALUE(self, t): + t.value = t.value[1:].strip() + if t.value in self.reserved.keys(): + t.type = self.reserved[t.value] + else: + t.type = "LINE" + return t + + @TOKEN(r"\#.*") + def t_comment(self, t): + pass + + @TOKEN(r"\n+") + def t_newline(self, t): + t.lexer.lineno += len(t.value) + + @TOKEN(r"[ \t]+") + def t_whitespace(self, t): + pass + + def build(self, **kwargs): + self.lexer = lex.lex(module=self, **kwargs) + + def token(self): + return self.lexer.token() + + def input(self, data): + self.lexer.input(data) + + def t_error(self, t): + t.lexer.skip(1) + t.value = "Lexer error" + return t diff --git a/tests/spdx/parser/tagvalue/__init__.py b/tests/spdx/parser/tagvalue/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/spdx/parser/tagvalue/test_tag_value_lexer.py b/tests/spdx/parser/tagvalue/test_tag_value_lexer.py new file mode 100644 index 000000000..ce6b9a159 --- /dev/null +++ b/tests/spdx/parser/tagvalue/test_tag_value_lexer.py @@ -0,0 +1,288 @@ +# Copyright (c) 2014 Ahmed H. Ismail +# Copyright (c) 2023 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 unittest import TestCase + +import pytest + +from spdx.parser.tagvalue.lexer.tagvalue import SPDXLexer + + +@pytest.fixture +def lexer(): + lexer = SPDXLexer() + lexer.build() + return lexer + + +def token_assert_helper(token, token_type, value, line_number): + assert token.type == token_type + assert token.value == value + assert token.lineno == line_number + + +def test_tokenization_of_document(lexer): + document_str = '\n'.join([ + 'SPDXVersion: SPDX-2.1', + 'DataLicense: CC0-1.0', + 'DocumentName: Sample_Document-V2.1', + 'SPDXID: SPDXRef-DOCUMENT', + 'DocumentComment: Sample Comment', + 'DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301' + ]) + lexer.input(document_str) + token_assert_helper(lexer.token(), 'DOC_VERSION', 'SPDXVersion', 1) + token_assert_helper(lexer.token(), 'LINE', 'SPDX-2.1', 1) + token_assert_helper(lexer.token(), 'DOC_LICENSE', 'DataLicense', 2) + token_assert_helper(lexer.token(), 'LINE', 'CC0-1.0', 2) + token_assert_helper(lexer.token(), 'DOC_NAME', 'DocumentName', 3) + token_assert_helper(lexer.token(), 'LINE', 'Sample_Document-V2.1', 3) + token_assert_helper(lexer.token(), 'SPDX_ID', 'SPDXID', 4) + token_assert_helper(lexer.token(), 'LINE', 'SPDXRef-DOCUMENT', 4) + token_assert_helper(lexer.token(), 'DOC_COMMENT', 'DocumentComment', 5) + token_assert_helper(lexer.token(), 'TEXT', 'Sample Comment', 5) + token_assert_helper(lexer.token(), 'DOC_NAMESPACE', 'DocumentNamespace', 6) + token_assert_helper(lexer.token(), 'LINE', + 'https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301', 6) + + +def test_tokenization_of_external_document_references(lexer): + data = ''' + ExternalDocumentRef:DocumentRef-spdx-tool-2.1 http://spdx.org/spdxdocs/spdx-tools-v2.1-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759 + ''' + lexer.input(data) + token_assert_helper(lexer.token(), 'EXT_DOC_REF', 'ExternalDocumentRef', 2) + token_assert_helper(lexer.token(), 'DOC_REF_ID', 'DocumentRef-spdx-tool-2.1', 2) + token_assert_helper(lexer.token(), 'DOC_URI', 'http://spdx.org/spdxdocs/spdx-tools-v2.1-3F25' + '04E0-4F89-41D3-9A0C-0305E82C3301', 2) + token_assert_helper(lexer.token(), 'EXT_DOC_REF_CHECKSUM', 'SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759', 2) + + +def test_tokenization_of_file(lexer): + file_str = '\n'.join([ + 'FileName: testfile.java', + 'SPDXID: SPDXRef-File', + 'FileType: SOURCE', + 'FileType: TEXT', + 'FileChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', + 'LicenseConcluded: Apache-2.0', + 'LicenseInfoInFile: Apache-2.0', + 'FileCopyrightText: Copyright 2014 Acme Inc.', + 'FileComment: Very long file', + 'FileAttributionText: Acknowledgements that might be required to be communicated in some contexts.' + ]) + + lexer.input(file_str) + token_assert_helper(lexer.token(), 'FILE_NAME', 'FileName', 1) + token_assert_helper(lexer.token(), 'LINE', 'testfile.java', 1) + token_assert_helper(lexer.token(), 'SPDX_ID', 'SPDXID', 2) + token_assert_helper(lexer.token(), 'LINE', 'SPDXRef-File', 2) + token_assert_helper(lexer.token(), 'FILE_TYPE', 'FileType', 3) + token_assert_helper(lexer.token(), 'SOURCE', 'SOURCE', 3) + token_assert_helper(lexer.token(), 'FILE_TYPE', 'FileType', 4) + token_assert_helper(lexer.token(), 'FILETYPE_TEXT', 'TEXT', 4) + token_assert_helper(lexer.token(), 'FILE_CHECKSUM', 'FileChecksum', 5) + token_assert_helper(lexer.token(), 'CHECKSUM', 'SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', 5) + token_assert_helper(lexer.token(), 'FILE_LICS_CONC', 'LicenseConcluded', 6) + token_assert_helper(lexer.token(), 'LINE', 'Apache-2.0', 6) + token_assert_helper(lexer.token(), 'FILE_LICS_INFO', 'LicenseInfoInFile', 7) + token_assert_helper(lexer.token(), 'LINE', 'Apache-2.0', 7) + token_assert_helper(lexer.token(), 'FILE_CR_TEXT', 'FileCopyrightText', 8) + token_assert_helper(lexer.token(), 'TEXT', 'Copyright 2014 Acme Inc.', 8) + token_assert_helper(lexer.token(), 'FILE_COMMENT', 'FileComment', 9) + token_assert_helper(lexer.token(), 'TEXT', 'Very long file', 9) + token_assert_helper(lexer.token(), 'FILE_ATTRIBUTION_TEXT', 'FileAttributionText', 10) + token_assert_helper(lexer.token(), 'TEXT', + 'Acknowledgements that might be required to be communicated in some contexts.', + 10) + + +def test_tokenization_of_creation_info(lexer): + creation_str = '\n'.join([ + 'Creator: Person: Bob (bob@example.com)', + 'Creator: Organization: Acme.', + 'Created: 2010-02-03T00:00:00Z', + 'CreatorComment: Sample Comment' + ]) + + lexer.input(creation_str) + token_assert_helper(lexer.token(), 'CREATOR', 'Creator', 1) + token_assert_helper(lexer.token(), 'PERSON_VALUE', "Person: Bob (bob@example.com)", 1) + token_assert_helper(lexer.token(), 'CREATOR', 'Creator', 2) + token_assert_helper(lexer.token(), 'ORG_VALUE', 'Organization: Acme.', 2) + token_assert_helper(lexer.token(), 'CREATED', 'Created', 3) + token_assert_helper(lexer.token(), 'DATE', '2010-02-03T00:00:00Z', 3) + token_assert_helper(lexer.token(), 'CREATOR_COMMENT', 'CreatorComment', 4) + token_assert_helper(lexer.token(), 'TEXT', 'Sample Comment', 4) + + +def test_tokenization_of_package(lexer): + package_str = '\n'.join([ + 'PackageName: Test', + 'SPDXID: SPDXRef-Package', + 'PackageVersion: Version 0.9.2', + 'PackageDownloadLocation: http://example.com/test', + 'FilesAnalyzed: True', + 'PackageSummary: Test package', + 'PackageSourceInfo: Version 1.0 of test', + 'PackageFileName: test-1.0.zip', + 'PackageSupplier: Organization:ACME', + 'PackageOriginator: Organization:ACME', + 'PackageChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', + 'PackageVerificationCode: 4e3211c67a2d28fced849ee1bb76e7391b93feba (something.rdf, something.txt)', + 'PackageDescription: A package.', + 'PackageComment: Comment on the package.', + 'PackageCopyrightText: Copyright 2014 Acme Inc.', + 'PackageLicenseDeclared: Apache-2.0', + 'PackageLicenseConcluded: (LicenseRef-2.0 and Apache-2.0)', + 'PackageLicenseInfoFromFiles: Apache-1.0', + 'PackageLicenseInfoFromFiles: Apache-2.0', + 'PackageLicenseComments: License Comments', + 'ExternalRef: SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:', + 'ExternalRefComment: Some comment about the package.', + 'PrimaryPackagePurpose: OPERATING-SYSTEM', + 'BuiltDate: 2020-01-01T12:00:00Z', + 'ReleaseDate: 2021-01-01T12:00:00Z', + 'ValidUntilDate: 2022-01-01T12:00:00Z' + ]) + + lexer.input(package_str) + token_assert_helper(lexer.token(), 'PKG_NAME', 'PackageName', 1) + token_assert_helper(lexer.token(), 'LINE', 'Test', 1) + token_assert_helper(lexer.token(), 'SPDX_ID', 'SPDXID', 2) + token_assert_helper(lexer.token(), 'LINE', 'SPDXRef-Package', 2) + token_assert_helper(lexer.token(), 'PKG_VERSION', 'PackageVersion', 3) + token_assert_helper(lexer.token(), 'LINE', 'Version 0.9.2', 3) + token_assert_helper(lexer.token(), 'PKG_DOWN', 'PackageDownloadLocation', 4) + token_assert_helper(lexer.token(), 'LINE', 'http://example.com/test', 4) + token_assert_helper(lexer.token(), 'PKG_FILES_ANALYZED', 'FilesAnalyzed', 5) + token_assert_helper(lexer.token(), 'LINE', 'True', 5) + token_assert_helper(lexer.token(), 'PKG_SUM', 'PackageSummary', 6) + token_assert_helper(lexer.token(), 'TEXT', 'Test package', 6) + token_assert_helper(lexer.token(), 'PKG_SRC_INFO', 'PackageSourceInfo', 7) + token_assert_helper(lexer.token(), 'TEXT', 'Version 1.0 of test', 7) + token_assert_helper(lexer.token(), 'PKG_FILE_NAME', 'PackageFileName', 8) + token_assert_helper(lexer.token(), 'LINE', 'test-1.0.zip', 8) + token_assert_helper(lexer.token(), 'PKG_SUPPL', 'PackageSupplier', 9) + token_assert_helper(lexer.token(), 'ORG_VALUE', 'Organization:ACME', 9) + token_assert_helper(lexer.token(), 'PKG_ORIG', 'PackageOriginator', 10) + token_assert_helper(lexer.token(), 'ORG_VALUE', 'Organization:ACME', 10) + token_assert_helper(lexer.token(), 'PKG_CHECKSUM', 'PackageChecksum', 11) + token_assert_helper(lexer.token(), 'CHECKSUM', 'SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', 11) + token_assert_helper(lexer.token(), 'PKG_VERF_CODE', 'PackageVerificationCode', 12) + token_assert_helper(lexer.token(), 'LINE', + '4e3211c67a2d28fced849ee1bb76e7391b93feba (something.rdf, something.txt)', 12) + token_assert_helper(lexer.token(), 'PKG_DESC', 'PackageDescription', 13) + token_assert_helper(lexer.token(), 'TEXT', 'A package.', 13) + token_assert_helper(lexer.token(), 'PKG_COMMENT', 'PackageComment', 14) + token_assert_helper(lexer.token(), 'TEXT', 'Comment on the package.', 14) + token_assert_helper(lexer.token(), 'PKG_CPY_TEXT', 'PackageCopyrightText', 15) + token_assert_helper(lexer.token(), 'TEXT', ' Copyright 2014 Acme Inc.', 15) + token_assert_helper(lexer.token(), 'PKG_LICS_DECL', 'PackageLicenseDeclared', 16) + token_assert_helper(lexer.token(), 'LINE', 'Apache-2.0', 16) + token_assert_helper(lexer.token(), 'PKG_LICS_CONC', 'PackageLicenseConcluded', 17) + token_assert_helper(lexer.token(), 'LINE', '(LicenseRef-2.0 and Apache-2.0)', 17) + token_assert_helper(lexer.token(), 'PKG_LICS_FFILE', 'PackageLicenseInfoFromFiles', 18) + token_assert_helper(lexer.token(), 'LINE', 'Apache-1.0', 18) + token_assert_helper(lexer.token(), 'PKG_LICS_FFILE', 'PackageLicenseInfoFromFiles', 19) + token_assert_helper(lexer.token(), 'LINE', 'Apache-2.0', 19) + token_assert_helper(lexer.token(), 'PKG_LICS_COMMENT', 'PackageLicenseComments', 20) + token_assert_helper(lexer.token(), 'TEXT', 'License Comments', 20) + token_assert_helper(lexer.token(), 'PKG_EXT_REF', 'ExternalRef', 21) + token_assert_helper(lexer.token(), 'LINE', + 'SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:', 21) + token_assert_helper(lexer.token(), 'PKG_EXT_REF_COMMENT', 'ExternalRefComment', 22) + token_assert_helper(lexer.token(), 'TEXT', 'Some comment about the package.', 22) + token_assert_helper(lexer.token(), 'PRIMARY_PACKAGE_PURPOSE', 'PrimaryPackagePurpose', 23) + token_assert_helper(lexer.token(), 'OPERATING_SYSTEM', 'OPERATING-SYSTEM', 23) + token_assert_helper(lexer.token(), 'BUILT_DATE', 'BuiltDate', 24) + token_assert_helper(lexer.token(), 'DATE', '2020-01-01T12:00:00Z', 24) + token_assert_helper(lexer.token(), 'RELEASE_DATE', 'ReleaseDate', 25) + token_assert_helper(lexer.token(), 'DATE', '2021-01-01T12:00:00Z', 25) + token_assert_helper(lexer.token(), 'VALID_UNTIL_DATE', 'ValidUntilDate', 26) + token_assert_helper(lexer.token(), 'DATE', '2022-01-01T12:00:00Z', 26) + + +def test_tokenization_of_unknown_tag(lexer): + unknown_tag_str = 'SomeUnknownTag: SomeUnknownValue' + lexer.input(unknown_tag_str) + token_assert_helper(lexer.token(), 'UNKNOWN_TAG', 'SomeUnknownTag', 1) + token_assert_helper(lexer.token(), 'LINE', 'SomeUnknownValue', 1) + + +def test_tokenization_of_snippet(lexer): + snippet_str = '\n'.join([ + 'SnippetSPDXID: SPDXRef-Snippet', + 'SnippetLicenseComments: Some lic comment.', + 'SnippetCopyrightText: Copyright 2008-2010 John Smith ', + 'SnippetComment: Some snippet comment.', + 'SnippetName: from linux kernel', + 'SnippetFromFileSPDXID: SPDXRef-DoapSource', + 'SnippetLicenseConcluded: Apache-2.0', + 'LicenseInfoInSnippet: Apache-2.0', + 'SnippetByteRange: 310:420', + 'SnippetLineRange: 5:23', + ]) + lexer.input(snippet_str) + token_assert_helper(lexer.token(), 'SNIPPET_SPDX_ID', 'SnippetSPDXID', 1) + token_assert_helper(lexer.token(), 'LINE', 'SPDXRef-Snippet', 1) + token_assert_helper(lexer.token(), 'SNIPPET_LICS_COMMENT', 'SnippetLicenseComments', 2) + token_assert_helper(lexer.token(), 'TEXT', 'Some lic comment.', 2) + token_assert_helper(lexer.token(), 'SNIPPET_CR_TEXT', 'SnippetCopyrightText', 3) + token_assert_helper(lexer.token(), 'TEXT', ' Copyright 2008-2010 John Smith ', 3) + token_assert_helper(lexer.token(), 'SNIPPET_COMMENT', 'SnippetComment', 4) + token_assert_helper(lexer.token(), 'TEXT', 'Some snippet comment.', 4) + token_assert_helper(lexer.token(), 'SNIPPET_NAME', 'SnippetName', 5) + token_assert_helper(lexer.token(), 'LINE', 'from linux kernel', 5) + token_assert_helper(lexer.token(), 'SNIPPET_FILE_SPDXID', 'SnippetFromFileSPDXID', 6) + token_assert_helper(lexer.token(), 'LINE', 'SPDXRef-DoapSource', 6) + token_assert_helper(lexer.token(), 'SNIPPET_LICS_CONC', + 'SnippetLicenseConcluded', 7) + token_assert_helper(lexer.token(), 'LINE', 'Apache-2.0', 7) + token_assert_helper(lexer.token(), 'SNIPPET_LICS_INFO', 'LicenseInfoInSnippet', 8) + token_assert_helper(lexer.token(), 'LINE', 'Apache-2.0', 8) + token_assert_helper(lexer.token(), 'SNIPPET_BYTE_RANGE', 'SnippetByteRange', 9) + token_assert_helper(lexer.token(), 'LINE', '310:420', 9) + token_assert_helper(lexer.token(), 'SNIPPET_LINE_RANGE', 'SnippetLineRange', 10) + token_assert_helper(lexer.token(), 'LINE', '5:23', 10) + + +def test_tokenization_of_annotation(lexer): + annotation_str = '\n'.join([ + 'Annotator: Person: Jane Doe()', + 'AnnotationDate: 2010-01-29T18:30:22Z', + 'AnnotationComment: Document level annotation', + 'AnnotationType: OTHER', + 'SPDXREF: SPDXRef-DOCUMENT' + ]) + + lexer.input(annotation_str) + token_assert_helper(lexer.token(), 'ANNOTATOR', 'Annotator', 1) + token_assert_helper(lexer.token(), 'PERSON_VALUE', 'Person: Jane Doe()', 1) + token_assert_helper(lexer.token(), 'ANNOTATION_DATE', 'AnnotationDate', 2) + token_assert_helper(lexer.token(), 'DATE', '2010-01-29T18:30:22Z', 2) + token_assert_helper(lexer.token(), 'ANNOTATION_COMMENT', 'AnnotationComment', 3) + token_assert_helper(lexer.token(), 'TEXT', 'Document level annotation', 3) + token_assert_helper(lexer.token(), 'ANNOTATION_TYPE', 'AnnotationType', 4) + token_assert_helper(lexer.token(), 'OTHER', 'OTHER', 4) + token_assert_helper(lexer.token(), 'ANNOTATION_SPDX_ID', 'SPDXREF', 5) + token_assert_helper(lexer.token(), 'LINE', 'SPDXRef-DOCUMENT', 5) + + +def test_tokenization_of_relationship(lexer): + relationship_str = '\n'.join(['Relationship: SPDXRef-DOCUMENT DESCRIBES NONE', + 'RelationshipComment: This is a comment.']) + + lexer.input(relationship_str) + token_assert_helper(lexer.token(), 'RELATIONSHIP', 'Relationship', 1) + token_assert_helper(lexer.token(), 'LINE', 'SPDXRef-DOCUMENT DESCRIBES NONE', 1) + token_assert_helper(lexer.token(), 'RELATIONSHIP_COMMENT', 'RelationshipComment', 2) + token_assert_helper(lexer.token(), 'LINE', 'This is a comment.', 2) From b823fbc419c0dec124763209e35dedacd66f9ea2 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 27 Feb 2023 12:02:11 +0100 Subject: [PATCH 296/362] [issue-382] add tag-value parser The code is taken from the current implementation, I added a decorator function to use instead of docstrings and adapted the code according to the new data model. Signed-off-by: Meret Behrens --- .gitignore | 2 +- README.md | 1 + pyproject.toml | 2 +- src/spdx/model/package.py | 6 +- src/spdx/parser/tagvalue/parser/__init__.py | 0 .../parser/tagvalue/parser/helper_methods.py | 31 + src/spdx/parser/tagvalue/parser/tagvalue.py | 907 ++++++++++++++++++ .../parser/tagvalue/test_tag_value_parser.py | 246 +++++ 8 files changed, 1190 insertions(+), 5 deletions(-) create mode 100644 src/spdx/parser/tagvalue/parser/__init__.py create mode 100644 src/spdx/parser/tagvalue/parser/helper_methods.py create mode 100644 src/spdx/parser/tagvalue/parser/tagvalue.py create mode 100644 tests/spdx/parser/tagvalue/test_tag_value_parser.py diff --git a/.gitignore b/.gitignore index cc02c2461..5ef28e630 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ __pycache__/ /build/ /dist/ /tmp/ -spdx/parsers/parsetab.py +src/spdx/parser/tagvalue/parser/parsetab.py /.cache/ .tox diff --git a/README.md b/README.md index 9416994b9..e1681a7c2 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,7 @@ if not validation_messages: * 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. * typeguard: https://pypi.org/project/typeguard/ for type checking. * uritools: https://pypi.org/project/uritools/ for validation of URIs. diff --git a/pyproject.toml b/pyproject.toml index 282f56d38..933f1d264 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ ] urls = { Homepage = "https://github.com/spdx/tools-python" } requires-python = ">=3.7" -dependencies = ["click", "pyyaml", "xmltodict", "rdflib", "typeguard", "uritools", "license_expression"] +dependencies = ["click", "pyyaml", "xmltodict", "rdflib", "typeguard", "uritools", "license_expression", "ply"] dynamic = ["version"] [project.optional-dependencies] diff --git a/src/spdx/model/package.py b/src/spdx/model/package.py index 7f4e32296..a6a4eaf4c 100644 --- a/src/spdx/model/package.py +++ b/src/spdx/model/package.py @@ -55,9 +55,9 @@ class ExternalPackageRefCategory(Enum): CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES: Dict[ExternalPackageRefCategory, List[str]] = { - ExternalPackageRefCategory.SECURITY : ["cpe22Type", "cpe23Type", "advisory", "fix", "url", "swid"], - ExternalPackageRefCategory.PACKAGE_MANAGER : ["maven-central", "npm", "nuget", "bower", "purl"], - ExternalPackageRefCategory.PERSISTENT_ID : ["swh", "gitoid"], + ExternalPackageRefCategory.SECURITY: ["cpe22Type", "cpe23Type", "advisory", "fix", "url", "swid"], + ExternalPackageRefCategory.PACKAGE_MANAGER: ["maven-central", "npm", "nuget", "bower", "purl"], + ExternalPackageRefCategory.PERSISTENT_ID: ["swh", "gitoid"], ExternalPackageRefCategory.OTHER: [] } diff --git a/src/spdx/parser/tagvalue/parser/__init__.py b/src/spdx/parser/tagvalue/parser/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/spdx/parser/tagvalue/parser/helper_methods.py b/src/spdx/parser/tagvalue/parser/helper_methods.py new file mode 100644 index 000000000..32090810a --- /dev/null +++ b/src/spdx/parser/tagvalue/parser/helper_methods.py @@ -0,0 +1,31 @@ +# Copyright (c) 2023 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. +import re +from typing import Optional + + +def grammar_rule(doc): + # this is a helper method to use decorators for the parsing methods instead of docstrings + def decorate(func): + func.__doc__ = doc + return func + return decorate + + +def str_from_text(text: Optional[str]) -> Optional[str]: + regex = re.compile("((.|\n)+)", re.UNICODE) + match = regex.match(text) + if match: + return match.group(1) + elif isinstance(text, str): + return text + else: + return None diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py new file mode 100644 index 000000000..7909f35d9 --- /dev/null +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -0,0 +1,907 @@ +# Copyright (c) 2014 Ahmed H. Ismail +# Copyright (c) 2023 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. + +import re +from typing import Optional + +from license_expression import get_spdx_licensing +from ply import yacc + +from spdx.datetime_conversions import datetime_from_str +from spdx.model.annotation import AnnotationType, Annotation +from spdx.model.checksum import ChecksumAlgorithm, Checksum +from spdx.model.external_document_ref import ExternalDocumentRef +from spdx.model.extracted_licensing_info import ExtractedLicensingInfo +from spdx.model.package import Package, PackageVerificationCode, PackagePurpose, ExternalPackageRef, \ + ExternalPackageRefCategory +from spdx.model.relationship import Relationship, RelationshipType +from spdx.model.snippet import Snippet +from spdx.model.version import Version +from spdx.parser.jsonlikedict.actor_parser import ActorParser + +from spdx.model.document import Document, CreationInfo +from spdx.model.file import File, FileType +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone +from spdx.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error +from spdx.parser.logger import Logger +from spdx.parser.tagvalue.lexer.tagvalue import SPDXLexer +from spdx.parser.tagvalue.parser.helper_methods import grammar_rule, str_from_text + + +class Parser(object): + def __init__(self): + self.lex = None + self.yacc = None + self.tokens = SPDXLexer.tokens + self.logger = Logger() + self.element_stack = [] + self.current_element = dict() + self.creation_info = dict() + self.elements_build = dict() + + @grammar_rule("start : start attrib ") + def p_start_1(self, p): + pass + + @grammar_rule("start : attrib ") + def p_start_2(self, p): + pass + + @grammar_rule("attrib : spdx_version\n| spdx_id\n| data_lics\n| doc_name\n| doc_comment\n| doc_namespace\n| " + "creator\n| created\n| creator_comment\n| lics_list_ver\n| ext_doc_ref\n" + # attributes for file + "| file_name\n| file_type\n| file_checksum\n| file_conc\n| file_lics_info\n| file_cr_text\n" + "| file_lics_comment\n| file_attribution_text\n| file_notice\n| file_comment\n| file_contrib\n" + # attributes for annotation + "| annotator\n| annotation_date\n| annotation_comment\n| annotation_type\n| annotation_spdx_id\n" + # attributes for relationship + "| relationship\n| relationship_comment\n" + # attributes for snippet + "| snip_spdx_id\n| snip_name\n| snip_comment\n| snippet_attribution_text\n| snip_cr_text\n" + "| snip_lic_comment\n| snip_file_spdx_id\n| snip_lics_conc\n| snip_lics_info\n| snip_byte_range\n" + "| snip_line_range\n" + # attributes for package + "| package_name\n| package_version\n| pkg_down_location\n| pkg_files_analyzed\n| pkg_home\n" + "| pkg_summary\n| pkg_src_info\n| pkg_file_name\n| pkg_supplier\n| pkg_orig\n| pkg_checksum\n" + "| pkg_verif\n| pkg_desc\n| pkg_comment\n| pkg_attribution_text\n| pkg_lic_decl\n| pkg_lic_conc\n" + "| pkg_lic_ff\n| pkg_lic_comment\n| pkg_cr_text\n| pkg_ext_ref\n| primary_package_purpose\n" + "| built_date\n| release_date\n| valid_until_date\n" + # attributes for extracted licensing info + "| extr_lic_id\n| extr_lic_text\n| extr_lic_name\n| lic_xref\n| lic_comment\n" + "| unknown_tag ") + def p_attrib(self, p): + pass + + # general parsing methods + @grammar_rule("unknown_tag : UNKNOWN_TAG text_or_line\n | UNKNOWN_TAG DATE\n | UNKNOWN_TAG PERSON_VALUE") + def p_unknown_tag(self, p): + self.logger.append(f"Unknown tag provided in line {p.lineno(1)}") + + @grammar_rule("text_or_line : TEXT") + def p_text_or_line_value_1(self, p): + p[0] = str_from_text(p[1]) + + @grammar_rule("text_or_line : LINE") + def p_text_or_line_value_2(self, p): + p[0] = p[1] + + @grammar_rule("license_or_no_assertion_or_none : NO_ASSERTION") + def p_license_or_no_assertion_or_none_1(self, p): + p[0] = SpdxNoAssertion() + + @grammar_rule("license_or_no_assertion_or_none : NONE") + def p_license_or_no_assertion_or_none_2(self, p): + p[0] = SpdxNone() + + @grammar_rule("license_or_no_assertion_or_none : LINE") + def p_license_or_no_assertion_or_none_3(self, p): + p[0] = get_spdx_licensing().parse(p[1]) + + @grammar_rule("line_or_no_assertion : LINE") + def p_line_or_no_assertion_1(self, p): + p[0] = p[1] + + @grammar_rule("line_or_no_assertion : NO_ASSERTION") + def p_line_or_no_assertion_2(self, p): + p[0] = SpdxNoAssertion() + + @grammar_rule("line_or_no_assertion_or_none : text_or_line") + def p_line_1(self, p): + p[0] = p[1] + + @grammar_rule("line_or_no_assertion_or_none : NO_ASSERTION") + def p_no_assertion_2(self, p): + p[0] = SpdxNoAssertion() + + @grammar_rule("line_or_no_assertion_or_none : NONE") + def p_none_2(self, p): + p[0] = SpdxNoAssertion() + + @grammar_rule("spdx_id : SPDX_ID LINE") + def p_spdx_id(self, p): + # We assume that the documents spdx_id is defined first in the SPDXDocument, before any package or file + # information. If this is not the case the parser will behave unexpectedly as the spdx_ids are assigned falsy. + if "spdx_id" in self.creation_info: + self.current_element["spdx_id"] = p[2] + else: + self.creation_info["spdx_id"] = p[2] + + # parsing methods for creation info / document level + + @grammar_rule("lics_list_ver : LIC_LIST_VER LINE") + def p_lics_list_ver_1(self, p): + self.creation_info["license_list_version"] = Version.from_string(p[2]) + + @grammar_rule("lics_list_ver : LIC_LIST_VER error") + def p_lics_list_ver_2(self, p): + self.logger.append( + f"Error while parsing LicenseListVersion: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("doc_comment : DOC_COMMENT text_or_line") + def p_doc_comment_1(self, p): + self.creation_info["document_comment"] = p[2] + + @grammar_rule("doc_comment : DOC_COMMENT error") + def p_doc_comment_2(self, p): + self.logger.append( + f"Error while parsing DocumentComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("doc_namespace : DOC_NAMESPACE LINE") + def p_doc_namespace_1(self, p): + self.creation_info["document_namespace"] = p[2] + + @grammar_rule("doc_namespace : DOC_NAMESPACE error") + def p_doc_namespace_2(self, p): + self.logger.append( + f"Error while parsing DocumentNamespace: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("data_lics : DOC_LICENSE LINE") + def p_data_license_1(self, p): + self.creation_info["data_license"] = p[2] + + @grammar_rule("data_lics : DOC_LICENSE error") + def p_data_license_2(self, p): + self.logger.append( + f"Error while parsing DataLicense: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("doc_name : DOC_NAME LINE") + def p_doc_name_1(self, p): + self.creation_info["name"] = p[2] + + @grammar_rule("doc_name : DOC_NAME error") + def p_doc_name_2(self, p): + self.logger.append( + f"Error while parsing DocumentName: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("ext_doc_ref : EXT_DOC_REF DOC_REF_ID DOC_URI EXT_DOC_REF_CHECKSUM") + def p_ext_doc_refs_1(self, p): + + document_ref_id = p[2] + document_uri = p[3] + splitted_checksum = p[4].split(":") + algorithm = ChecksumAlgorithm[splitted_checksum[0]] + value = splitted_checksum[1].strip() + external_document_ref = ExternalDocumentRef(document_ref_id, document_uri, Checksum(algorithm, value)) + self.creation_info.setdefault("external_document_refs", []).append(external_document_ref) + + @grammar_rule("ext_doc_ref : EXT_DOC_REF error") + def p_ext_doc_refs_2(self, p): + self.logger.append( + f"Error while parsing ExternalDocumentRef: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("spdx_version : DOC_VERSION LINE") + def p_spdx_version_1(self, p): + self.creation_info["spdx_version"] = p[2] + + @grammar_rule("spdx_version : DOC_VERSION error") + def p_spdx_version_2(self, p): + self.logger.append( + f"Error while parsing SPDXVersion: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("creator_comment : CREATOR_COMMENT text_or_line") + def p_creator_comment_1(self, p): + self.creation_info["creator_comment"] = p[2] + + @grammar_rule("creator_comment : CREATOR_COMMENT error") + def p_creator_comment_2(self, p): + self.logger.append( + f"Error while parsing CreatorComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + def p_creator_1(self, p): + """creator : CREATOR PERSON_VALUE\n| CREATOR TOOL_VALUE\n| CREATOR ORG_VALUE""" + self.creation_info.setdefault("creators", []).append(ActorParser.parse_actor(p[2])) + + @grammar_rule("creator : CREATOR error") + def p_creator_2(self, p): + self.logger.append( + f"Error while parsing Creator: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("created : CREATED DATE") + def p_created_1(self, p): + self.creation_info["created"] = datetime_from_str(p[2]) + + @grammar_rule("created : CREATED error") + def p_created_2(self, p): + self.logger.append( + f"Error while parsing Created: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + # parsing methods for extracted licensing info + + @grammar_rule("extr_lic_id : LICS_ID LINE") + def p_extr_lic_id_1(self, p): + self.construct_current_element() + self.current_element["class"] = ExtractedLicensingInfo + self.current_element["license_id"] = p[2] + + @grammar_rule("extr_lic_id : LICS_ID error") + def p_extr_lic_id_2(self, p): + self.logger.append( + f"Error while parsing LicenseID: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("lic_xref : LICS_CRS_REF LINE") + def p_lic_xref_1(self, p): + self.current_element.setdefault("cross_references", []).append(p[2]) + + @grammar_rule("lic_xref : LICS_CRS_REF error") + def p_lic_xref_2(self, p): + self.logger.append(f"Error while parsing LicenseCrossReference: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("lic_comment : LICS_COMMENT text_or_line") + def p_lic_comment_1(self, p): + self.current_element["comment"] = p[2] + + @grammar_rule("lic_comment : LICS_COMMENT error") + def p_lic_comment_2(self, p): + self.logger.append( + f"Error while parsing LicenseComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("extr_lic_name : LICS_NAME line_or_no_assertion") + def p_extr_lic_name_1(self, p): + self.current_element["license_name"] = p[2] + + @grammar_rule("extr_lic_name : LICS_NAME error") + def p_extr_lic_name_2(self, p): + self.logger.append( + f"Error while parsing LicenseName: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("extr_lic_text : LICS_TEXT text_or_line") + def p_extr_lic_text_1(self, p): + self.current_element["extracted_text"] = p[2] + + @grammar_rule("extr_lic_text : LICS_TEXT error") + def p_extr_lic_text_2(self, p): + self.logger.append( + f"Error while parsing ExtractedText: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + # parsing methods for file + + @grammar_rule("file_name : FILE_NAME LINE") + def p_file_name_1(self, p): + self.construct_current_element() + self.element_stack.append(p[2]) + self.current_element = dict() + self.current_element["name"] = p[2] + self.current_element["class"] = File + + @grammar_rule("file_name : FILE_NAME error") + def p_file_name_2(self, p): + self.logger.append( + f"Error while parsing FileName: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("file_contrib : FILE_CONTRIB LINE") + def p_file_contrib_1(self, p): + self.current_element.setdefault("contributors", []).append(p[2]) + + @grammar_rule("file_contrib : FILE_CONTRIB error") + def p_file_contrib_2(self, p): + self.logger.append( + f"Error while parsing FileContributor: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("file_notice : FILE_NOTICE text_or_line") + def p_file_notice_1(self, p): + self.current_element["notice"] = p[2] + + @grammar_rule("file_notice : FILE_NOTICE error") + def p_file_notice_2(self, p): + self.logger.append( + f"Error while parsing FileNotice: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("file_cr_text : FILE_CR_TEXT line_or_no_assertion_or_none") + def p_file_cr_text_1(self, p): + + self.current_element["copyright_text"] = p[2] + + @grammar_rule("file_cr_text : FILE_CR_TEXT error") + def p_file_cr_text_2(self, p): + self.logger.append( + f"Error while parsing FileCopyrightText: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("file_lics_comment : FILE_LICS_COMMENT text_or_line") + def p_file_lics_comment_1(self, p): + self.current_element["license_comment"] = p[2] + + @grammar_rule("file_lics_comment : FILE_LICS_COMMENT error") + def p_file_lics_comment_2(self, p): + self.logger.append(f"Error while parsing LicenseComments in file: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("file_attribution_text : FILE_ATTRIBUTION_TEXT text_or_line") + def p_file_attribution_text_1(self, p): + self.current_element.setdefault("attribution_texts", []).append(p[2]) + + @grammar_rule("file_attribution_text : FILE_ATTRIBUTION_TEXT error") + def p_file_attribution_text_2(self, p): + self.logger.append( + f"Error while parsing FileAttributionText: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("file_lics_info : FILE_LICS_INFO license_or_no_assertion_or_none") + def p_file_lics_info_1(self, p): + if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): + self.current_element["license_info_in_file"] = p[2] + return + self.current_element.setdefault("license_info_in_file", []).append(p[2]) + + @grammar_rule("file_lics_info : FILE_LICS_INFO error") + def p_file_lics_info_2(self, p): + self.logger.append( + f"Error while parsing LicenseInfoInFile: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("file_comment : FILE_COMMENT text_or_line") + def p_file_comment_1(self, p): + self.current_element["comment"] = p[2] + + @grammar_rule("file_comment : FILE_COMMENT error") + def p_file_comment_2(self, p): + self.logger.append( + f"Error while parsing FileComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("file_type : FILE_TYPE file_type_value") + def p_file_type_1(self, p): + self.current_element.setdefault("file_type", []).append(FileType[p[2]]) + + @grammar_rule("file_type : FILE_TYPE error") + def p_file_type_2(self, p): + self.logger.append( + f"Error while parsing FileType: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("file_checksum : FILE_CHECKSUM CHECKSUM") + def p_file_checksum_1(self, p): + splitted_checksum = p[2].split(":") + algorithm = ChecksumAlgorithm[splitted_checksum[0]] + value = splitted_checksum[1] + self.current_element.setdefault("checksums", []).append(Checksum(algorithm, value)) + + @grammar_rule("file_checksum : FILE_CHECKSUM error") + def p_file_checksum_2(self, p): + self.logger.append( + f"Error while parsing Checksum in file: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("file_conc : FILE_LICS_CONC license_or_no_assertion_or_none") + def p_file_conc_1(self, p): + self.current_element["license_concluded"] = p[2] + + @grammar_rule("file_conc : FILE_LICS_CONC error") + def p_file_conc_2(self, p): + self.logger.append(f"Error while parsing LicenseConcluded in file: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule( + "file_type_value : SOURCE\n| BINARY\n| ARCHIVE\n | APPLICATION\n | AUDIO\n | IMAGE\n | FILETYPE_TEXT\n| VIDEO\n" + " | DOCUMENTATION\n| SPDX \n| OTHER ") + def p_file_type_value(self, p): + + p[0] = p[1] + + # parsing methods for package + + @grammar_rule("package_name : PKG_NAME LINE") + def p_package_name(self, p): + self.construct_current_element() + self.current_element["class"] = Package + self.current_element["name"] = p[2] + + @grammar_rule("package_name : PKG_NAME error") + def p_package_name_1(self, p): + self.logger.append( + f"Error while parsing PackageName: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("pkg_desc : PKG_DESC text_or_line") + def p_pkg_desc_1(self, p): + self.current_element["description"] = p[2] + + @grammar_rule("pkg_desc : PKG_DESC error") + def p_pkg_desc_2(self, p): + self.logger.append( + f"Error while parsing PackageDescription: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("pkg_comment : PKG_COMMENT text_or_line") + def p_pkg_comment_1(self, p): + self.current_element["comment"] = p[2] + + @grammar_rule("pkg_comment : PKG_COMMENT error") + def p_pkg_comment_2(self, p): + self.logger.append( + f"Error while parsing PackageComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("pkg_attribution_text : PKG_ATTRIBUTION_TEXT text_or_line") + def p_pkg_attribution_text_1(self, p): + self.current_element.setdefault("attribution_texts", []).append(p[2]) + + @grammar_rule("pkg_attribution_text : PKG_ATTRIBUTION_TEXT error") + def p_pkg_attribution_text_2(self, p): + self.logger.append(f"Error while parsing PackageAttributionText: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("pkg_summary : PKG_SUM text_or_line") + def p_pkg_summary_1(self, p): + self.current_element["summary"] = p[2] + + @grammar_rule("pkg_summary : PKG_SUM error") + def p_pkg_summary_2(self, p): + self.logger.append( + f"Error while parsing PackageSummary: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("pkg_cr_text : PKG_CPY_TEXT line_or_no_assertion_or_none") + def p_pkg_cr_text_1(self, p): + self.current_element["copyright_text"] = p[2] + + @grammar_rule("pkg_cr_text : PKG_CPY_TEXT error") + def p_pkg_cr_text_2(self, p): + self.logger.append(f"Error while parsing PackageCopyrightText: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("pkg_ext_ref : PKG_EXT_REF LINE PKG_EXT_REF_COMMENT text_or_line\n | PKG_EXT_REF LINE") + def p_pkg_ext_refs_1(self, p): + category, reference_type, locator = p[2].split(" ") + comment = None + if len(p) == 5: + comment = p[4] + external_package_ref = ExternalPackageRef(ExternalPackageRefCategory[category], reference_type, locator, + comment) + self.current_element.setdefault("external_references", []).append(external_package_ref) + + @grammar_rule("pkg_ext_ref : PKG_EXT_REF error") + def p_pkg_ext_refs_2(self, p): + self.logger.append( + f"Error while parsing ExternalRef in package: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("pkg_lic_comment : PKG_LICS_COMMENT text_or_line") + def p_pkg_lic_comment_1(self, p): + self.current_element["license_comment"] = p[2] + + @grammar_rule("pkg_lic_comment : PKG_LICS_COMMENT error") + def p_pkg_lic_comment_2(self, p): + self.logger.append(f"Error while parsing PackageLicenseComments: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("pkg_lic_decl : PKG_LICS_DECL license_or_no_assertion_or_none") + def p_pkg_lic_decl_1(self, p): + self.current_element["license_declared"] = p[2] + + @grammar_rule("pkg_lic_decl : PKG_LICS_DECL error") + def p_pkg_lic_decl_2(self, p): + self.logger.append( + f"Error while parsing LicenseDeclared in package: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("pkg_lic_ff : PKG_LICS_FFILE license_or_no_assertion_or_none") + def p_pkg_lic_ff_1(self, p): + if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): + self.current_element["license_info_from_files"] = p[2] + else: + self.current_element.setdefault("license_info_from_files", []).append(p[2]) + + @grammar_rule("pkg_lic_ff : PKG_LICS_FFILE error") + def p_pkg_lic_ff_error(self, p): + self.logger.append( + f"Error while parsing LicenseInfoFromFiles in package: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("pkg_lic_conc : PKG_LICS_CONC license_or_no_assertion_or_none") + def p_pkg_lic_conc_1(self, p): + self.current_element["license_concluded"] = p[2] + + @grammar_rule("pkg_lic_conc : PKG_LICS_CONC error") + def p_pkg_lic_conc_2(self, p): + self.logger.append( + f"Error while parsing LicenseConcluded in package: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("pkg_src_info : PKG_SRC_INFO text_or_line") + def p_pkg_src_info_1(self, p): + self.current_element["source_info"] = p[2] + + @grammar_rule("pkg_src_info : PKG_SRC_INFO error") + def p_pkg_src_info_2(self, p): + self.logger.append( + f"Error while parsing PackageSourceInfo: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("pkg_checksum : PKG_CHECKSUM CHECKSUM") + def p_pkg_checksum_1(self, p): + split_checksum = p[2].split(":") + algorithm = ChecksumAlgorithm[split_checksum[0]] + value = split_checksum[1].strip() + checksum = Checksum(algorithm, value) + self.current_element.setdefault("checksums", []).append(checksum) + + @grammar_rule("pkg_checksum : PKG_CHECKSUM error") + def p_pkg_checksum_2(self, p): + self.logger.append( + f"Error while parsing PackageChecksum: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("pkg_verif : PKG_VERF_CODE LINE") + def p_pkg_verif_1(self, p): + verif_code_regex = re.compile(r"([0-9a-f]+)\s*(\(excludes:\s*(.+)\))?", re.UNICODE) + verif_code_code_grp = 1 + verif_code_exc_files_grp = 3 + match = verif_code_regex.match(p[2]) + value = match.group(verif_code_code_grp) + excluded_files = None + if match.group(verif_code_exc_files_grp): + excluded_files = match.group(verif_code_exc_files_grp).split(",") + self.current_element["verification_code"] = PackageVerificationCode(value, excluded_files) + + @grammar_rule("pkg_verif : PKG_VERF_CODE error") + def p_pkg_verif_2(self, p): + self.logger.append(f"Error while parsing PackageVerificationCode: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("pkg_home : PKG_HOME line_or_no_assertion_or_none") + def p_pkg_home_1(self, p): + self.current_element["homepage"] = p[2] + + @grammar_rule("pkg_home : PKG_HOME error") + def p_pkg_home_2(self, p): + self.logger.append( + f"Error while parsing PackageHomePage: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("pkg_down_location : PKG_DOWN line_or_no_assertion_or_none") + def p_pkg_down_location_1(self, p): + self.current_element["download_location"] = p[2] + + @grammar_rule("pkg_down_location : PKG_DOWN error") + def p_pkg_down_location_2(self, p): + self.logger.append(f"Error while parsing PackageDownloadLocation: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("pkg_files_analyzed : PKG_FILES_ANALYZED LINE") + def p_pkg_files_analyzed_1(self, p): + if p[2] in ['false', 'False']: + self.current_element["files_analyzed"] = False + if p[2] in ['true', 'True']: + self.current_element["files_analyzed"] = True + + @grammar_rule("pkg_files_analyzed : PKG_FILES_ANALYZED error") + def p_pkg_files_analyzed_2(self, p): + self.logger.append(f"Error while parsing FilesAnalyzed in package: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("pkg_orig : PKG_ORIG pkg_supplier_values") + def p_pkg_orig_1(self, p): + self.current_element["originator"] = p[2] + + @grammar_rule("pkg_orig : PKG_ORIG error") + def p_pkg_orig_2(self, p): + self.logger.append( + f"Error while parsing PackageOriginator: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("pkg_supplier : PKG_SUPPL pkg_supplier_values") + def p_pkg_supplier_1(self, p): + self.current_element["supplier"] = p[2] + + @grammar_rule("pkg_supplier : PKG_SUPPL error") + def p_pkg_supplier_2(self, p): + self.logger.append( + f"Error while parsing PackageSupplier: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("pkg_supplier_values : NO_ASSERTION") + def p_pkg_supplier_values_1(self, p): + p[0] = SpdxNoAssertion() + + @grammar_rule("pkg_supplier_values : PERSON_VALUE\n | ORG_VALUE\n | TOOL_VALUE") + def p_pkg_supplier_values_2(self, p): + p[0] = ActorParser.parse_actor(p[1]) + + @grammar_rule("pkg_file_name : PKG_FILE_NAME LINE") + def p_pkg_file_name(self, p): + self.current_element["file_name"] = p[2] + + @grammar_rule("pkg_file_name : PKG_FILE_NAME error") + def p_pkg_file_name_1(self, p): + self.logger.append( + f"Error while parsing PackageFileName: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("package_version : PKG_VERSION LINE") + def p_package_version_1(self, p): + self.current_element["version"] = p[2] + + @grammar_rule("package_version : PKG_VERSION error") + def p_package_version_2(self, p): + self.logger.append( + f"Error while parsing PackageVersion: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("primary_package_purpose : PRIMARY_PACKAGE_PURPOSE primary_package_purpose_value") + def p_primary_package_purpose_1(self, p): + + self.current_element["primary_package_purpose"] = PackagePurpose[p[2].replace("-", "_")] + + @grammar_rule("primary_package_purpose : PRIMARY_PACKAGE_PURPOSE error") + def p_primary_package_purpose_2(self, p): + self.logger.append(f"Error while parsing PrimaryPackagePurpose: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("primary_package_purpose_value : APPLICATION\n | FRAMEWORK\n | LIBRARY\n | CONTAINER\n " + "| OPERATING_SYSTEM \n | DEVICE \n| FIRMWARE\n | SOURCE\n | ARCHIVE\n | FILE\n | INSTALL\n | OTHER") + def p_primary_package_purpose_value(self, p): + p[0] = p[1] + + @grammar_rule("built_date : BUILT_DATE DATE") + def p_built_date_1(self, p): + self.current_element["built_date"] = datetime_from_str(p[2]) + + @grammar_rule("built_date : BUILT_DATE error") + def p_built_date_2(self, p): + self.logger.append( + f"Error while parsing BuiltDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("release_date : RELEASE_DATE DATE") + def p_release_date_1(self, p): + self.current_element["release_date"] = datetime_from_str(p[2]) + + @grammar_rule("release_date : RELEASE_DATE error") + def p_release_date_2(self, p): + self.logger.append( + f"Error while parsing ReleaseDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("valid_until_date : VALID_UNTIL_DATE DATE") + def p_valid_until_date_1(self, p): + self.current_element["valid_until_date"] = datetime_from_str(p[2]) + + @grammar_rule("valid_until_date : VALID_UNTIL_DATE error") + def p_valid_until_date_2(self, p): + self.logger.append( + f"Error while parsing ValidUntilDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + # parsing methods for snippet + @grammar_rule("snip_spdx_id : SNIPPET_SPDX_ID LINE") + def p_snip_spdx_id(self, p): + self.construct_current_element() + self.current_element["class"] = Snippet + self.current_element["spdx_id"] = p[2] + + @grammar_rule("snip_spdx_id : SNIPPET_SPDX_ID error") + def p_snip_spdx_id_1(self, p): + self.logger.append( + f"Error while parsing SnippetSPDXID: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("snip_name : SNIPPET_NAME LINE") + def p_snippet_name(self, p): + self.current_element["name"] = p[2] + + @grammar_rule("snip_name : SNIPPET_NAME error") + def p_snippet_name_1(self, p): + self.logger.append( + f"Error while parsing SnippetName: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("snip_comment : SNIPPET_COMMENT text_or_line") + def p_snippet_comment(self, p): + self.current_element["comment"] = p[2] + + @grammar_rule("snip_comment : SNIPPET_COMMENT error") + def p_snippet_comment_1(self, p): + self.logger.append( + f"Error while parsing SnippetComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT text_or_line") + def p_snippet_attribution_text_1(self, p): + self.current_element.setdefault("attribution_texts", []).append(p[2]) + + @grammar_rule("snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT error") + def p_snippet_attribution_text_2(self, p): + self.logger.append(f"Error while parsing SnippetAttributionText: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("snip_cr_text : SNIPPET_CR_TEXT line_or_no_assertion_or_none") + def p_snippet_cr_text(self, p): + self.current_element["copyright_text"] = p[2] + + @grammar_rule("snip_cr_text : SNIPPET_CR_TEXT error") + def p_snippet_cr_text_1(self, p): + self.logger.append(f"Error while parsing SnippetCopyrightText: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("snip_lic_comment : SNIPPET_LICS_COMMENT text_or_line") + def p_snippet_lic_comment(self, p): + self.current_element["license_comment"] = p[2] + + @grammar_rule("snip_lic_comment : SNIPPET_LICS_COMMENT error") + def p_snippet_lic_comment_1(self, p): + self.logger.append(f"Error while parsing SnippetLicenseComments: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("snip_file_spdx_id : SNIPPET_FILE_SPDXID LINE") + def p_snip_from_file_spdxid(self, p): + self.current_element["file_spdx_id"] = p[2] + + @grammar_rule("snip_file_spdx_id : SNIPPET_FILE_SPDXID error") + def p_snip_from_file_spdxid_1(self, p): + self.logger.append(f"Error while parsing SnippetFromFileSPDXID: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("snip_lics_conc : SNIPPET_LICS_CONC license_or_no_assertion_or_none") + def p_snippet_concluded_license(self, p): + self.current_element["license_concluded"] = p[2] + + @grammar_rule("snip_lics_conc : SNIPPET_LICS_CONC error") + def p_snippet_concluded_license_1(self, p): + self.logger.append(f"Error while parsing SnippetLicenseConcluded: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("snip_lics_info : SNIPPET_LICS_INFO license_or_no_assertion_or_none") + def p_snippet_lics_info(self, p): + if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): + self.current_element["license_info_in_snippet"] = p[2] + else: + self.current_element.setdefault("license_info_in_snippet", []).append(p[2]) + + @grammar_rule("snip_lics_info : SNIPPET_LICS_INFO error") + def p_snippet_lics_info_1(self, p): + + self.logger.append(f"Error while parsing LicenseInfoInSnippet: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + @grammar_rule("snip_byte_range : SNIPPET_BYTE_RANGE LINE") + def p_snippet_byte_range(self, p): + range_re = re.compile(r"^(\d+):(\d+)$", re.UNICODE) + if not range_re.match(p[2].strip()): + self.current_element["logger"].append("Value for SnippetByteRange doesn't match valid range pattern.") + return + startpoint = int(p[2].split(":")[0]) + endpoint = int(p[2].split(":")[-1]) + self.current_element["byte_range"] = startpoint, endpoint + + @grammar_rule("snip_byte_range : SNIPPET_BYTE_RANGE error") + def p_snippet_byte_range_1(self, p): + + self.logger.append( + f"Error while parsing SnippetByteRange: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("snip_line_range : SNIPPET_LINE_RANGE LINE") + def p_snippet_line_range(self, p): + range_re = re.compile(r"^(\d+):(\d+)$", re.UNICODE) + if not range_re.match(p[2].strip()): + self.current_element["logger"].append("Value for SnippetLineRange doesn't match valid range pattern.") + return + startpoint = int(p[2].split(":")[0]) + endpoint = int(p[2].split(":")[1]) + self.current_element["line_range"] = startpoint, endpoint + + @grammar_rule("snip_line_range : SNIPPET_LINE_RANGE error") + def p_snippet_line_range_1(self, p): + self.logger.append( + f"Error while parsing SnippetLineRange: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + # parsing methods for annotation + def p_annotator_1(self, p): + """annotator : ANNOTATOR PERSON_VALUE\n| TOOL_VALUE\n| ORG_VALUE""" + self.construct_current_element() + self.current_element["annotator"] = ActorParser.parse_actor(p[2]) + self.current_element["class"] = Annotation + + @grammar_rule("annotator : ANNOTATOR error") + def p_annotator_2(self, p): + self.logger.append( + f"Error while parsing Annotator: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("annotation_date : ANNOTATION_DATE DATE") + def p_annotation_date_1(self, p): + self.current_element["annotation_date"] = datetime_from_str(p[2]) + + @grammar_rule("annotation_date : ANNOTATION_DATE error") + def p_annotation_date_2(self, p): + self.logger.append( + f"Error while parsing AnnotationDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("annotation_comment : ANNOTATION_COMMENT text_or_line") + def p_annotation_comment_1(self, p): + self.current_element["annotation_comment"] = p[2] + + @grammar_rule("annotation_comment : ANNOTATION_COMMENT error") + def p_annotation_comment_2(self, p): + self.logger.append( + f"Error while parsing AnnotationComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("annotation_type : ANNOTATION_TYPE annotation_type_value") + def p_annotation_type_1(self, p): + self.current_element["annotation_type"] = AnnotationType[p[2]] + + @grammar_rule("annotation_type : ANNOTATION_TYPE error") + def p_annotation_type_2(self, p): + self.logger.append( + f"Error while parsing AnnotationType: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("annotation_type_value : OTHER\n| REVIEW") + def p_annotation_type_value(self, p): + p[0] = p[1] + + @grammar_rule("annotation_spdx_id : ANNOTATION_SPDX_ID LINE") + def p_annotation_spdx_id_1(self, p): + self.current_element["spdx_id"] = p[2] + + @grammar_rule("annotation_spdx_id : ANNOTATION_SPDX_ID error") + def p_annotation_spdx_id_2(self, p): + self.logger.append(f"Error while parsing SPDXREF in annotation: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") + + # parsing methods for relationship + @grammar_rule("relationship : RELATIONSHIP relationship_value") + def p_relationship_1(self, p): + splitted_relationship = p[2].split(" ") + + self.construct_current_element() + self.current_element["class"] = Relationship + self.current_element["relationship_type"] = RelationshipType[splitted_relationship[1]] + self.current_element["related_spdx_element_id"] = splitted_relationship[2] + self.current_element["spdx_element_id"] = splitted_relationship[0] + + @grammar_rule("relationship : RELATIONSHIP error") + def p_relationship_2(self, p): + self.logger.append( + f"Error while parsing Relationship: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("relationship_value : DOC_REF_ID LINE") + def p_relationship_value_with_doc_ref(self, p): + + p[0] = p[1] + ":" + p[2] + + @grammar_rule("relationship_value : LINE") + def p_relationship_value_without_doc_ref(self, p): + + p[0] = p[1] + + @grammar_rule("relationship_comment : RELATIONSHIP_COMMENT text_or_line") + def p_relationship_comment_1(self, p): + self.current_element["comment"] = p[2] + + @grammar_rule("relationship_comment : RELATIONSHIP_COMMENT error") + def p_relationship_comment_2(self, p): + self.logger.append( + f"Error while parsing RelationshipComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + def p_error(self, p): + pass + + def build(self, **kwargs): + self.lex = SPDXLexer() + self.lex.build(reflags=re.UNICODE) + self.yacc = yacc.yacc(module=self, **kwargs) + + def parse(self, text): + self.yacc.parse(text, lexer=self.lex) + self.construct_current_element() + document = Document(creation_info=CreationInfo(**self.creation_info), **self.elements_build) + print(self.logger.get_messages()) + return document, self.logger.get_messages() + + def construct_current_element(self): + if "class" in self.current_element: + class_name = self.current_element.pop("class") + self.elements_build.setdefault(CLASS_MAPPING[class_name.__name__], []).append( + construct_or_raise_parsing_error(class_name, self.current_element)) + self.current_element = dict() + + +CLASS_MAPPING = dict(File="files", Annotation="annotations", Relationship="relationships", Snippet="snippets", + Package="packages", ExtractedLicensingInfo="extracted_licensing_info") diff --git a/tests/spdx/parser/tagvalue/test_tag_value_parser.py b/tests/spdx/parser/tagvalue/test_tag_value_parser.py new file mode 100644 index 000000000..3692571a2 --- /dev/null +++ b/tests/spdx/parser/tagvalue/test_tag_value_parser.py @@ -0,0 +1,246 @@ +# Copyright (c) 2014 Ahmed H. Ismail +# Copyright (c) 2023 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. +import sys +from datetime import datetime +from unittest import TestCase + +from license_expression import get_spdx_licensing + +from spdx.model.actor import Actor, ActorType +from spdx.model.annotation import AnnotationType +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.model.external_document_ref import ExternalDocumentRef +from spdx.model.file import FileType +from spdx.model.package import PackagePurpose, ExternalPackageRefCategory, ExternalPackageRef +from spdx.model.relationship import RelationshipType +from spdx.model.version import Version +from spdx.parser.tagvalue.parser.tagvalue import Parser + +document_str = '\n'.join([ + 'SPDXVersion: SPDX-2.3', + 'DataLicense: CC0-1.0', + 'DocumentName: Sample_Document-V2.3', + 'SPDXID: SPDXRef-DOCUMENT', + 'DocumentComment: Sample Comment', + 'DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301', + 'ExternalDocumentRef: DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759' +]) + +creation_str = '\n'.join([ + 'Creator: Person: Bob (bob@example.com)', + 'Creator: Organization: Acme.', + 'Created: 2010-02-03T00:00:00Z', + 'CreatorComment: Sample Comment', + 'LicenseListVersion: 3.17' +]) + +package_str = '\n'.join([ + 'PackageName: Test', + 'SPDXID: SPDXRef-Package', + 'PackageVersion: Version 0.9.2', + 'PackageDownloadLocation: http://example.com/test', + 'FilesAnalyzed: True', + 'PackageSummary: Test package', + 'PackageSourceInfo: Version 1.0 of test', + 'PackageFileName: test-1.0.zip', + 'PackageSupplier: Organization:ACME', + 'PackageOriginator: Organization:ACME', + 'PackageChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', + 'PackageVerificationCode: 4e3211c67a2d28fced849ee1bb76e7391b93feba (something.rdf, something.txt)', + 'PackageDescription: A package.', + 'PackageComment: Comment on the package.', + 'PackageCopyrightText: Copyright 2014 Acme Inc.', + 'PackageLicenseDeclared: Apache-2.0', + 'PackageLicenseConcluded: (LicenseRef-2.0 and Apache-2.0)', + 'PackageLicenseInfoFromFiles: Apache-1.0', + 'PackageLicenseInfoFromFiles: Apache-2.0', + 'PackageLicenseComments: License Comments', + 'ExternalRef: SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:', + 'ExternalRefComment: Some comment about the package.', + 'ExternalRef: OTHER LocationRef-acmeforge acmecorp/acmenator/4.1.3-alpha', + 'PrimaryPackagePurpose: OPERATING-SYSTEM', + 'BuiltDate: 2020-01-01T12:00:00Z', + 'ReleaseDate: 2021-01-01T12:00:00Z', + 'ValidUntilDate: 2022-01-01T12:00:00Z' +]) + +file_str = '\n'.join([ + 'FileName: testfile.java', + 'SPDXID: SPDXRef-File', + 'FileType: SOURCE', + 'FileType: TEXT', + 'FileChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', + 'LicenseConcluded: Apache-2.0', + 'LicenseInfoInFile: Apache-2.0', + 'FileCopyrightText: Copyright 2014 Acme Inc.', + 'FileComment: Very long file', + 'FileAttributionText: Acknowledgements that might be required to be communicated in some contexts.' +]) + +snippet_str = '\n'.join([ + 'SnippetSPDXID: SPDXRef-Snippet', + 'SnippetLicenseComments: Some lic comment.', + 'SnippetCopyrightText: Copyright 2008-2010 John Smith ', + 'SnippetComment: Some snippet comment.', + 'SnippetName: from linux kernel', + 'SnippetFromFileSPDXID: SPDXRef-DoapSource', + 'SnippetLicenseConcluded: Apache-2.0', + 'LicenseInfoInSnippet: Apache-2.0', + 'SnippetByteRange: 310:420', + 'SnippetLineRange: 5:23', +]) + +annotation_str = '\n'.join([ + 'Annotator: Person: Jane Doe()', + 'AnnotationDate: 2010-01-29T18:30:22Z', + 'AnnotationComment: Document level annotation', + 'AnnotationType: OTHER', + 'SPDXREF: SPDXRef-DOCUMENT' +]) + +relationship_str = '\n'.join([ + 'Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File', + 'RelationshipComment: This is a comment.']) + +extracted_licensing_info_str = '\n'.join([ + 'LicenseID: LicenseRef-Beerware-4.2', + 'ExtractedText: "THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' + 'LicenseName: Beer-Ware License (Version 42)', + 'LicenseCrossReference: http://people.freebsd.org/~phk/', + 'LicenseComment: The beerware license has a couple of other standard variants.' +]) + +unknown_tag_str = 'UnknownTag: This is an example for an unknown tag.' + + +class TestParser(TestCase): + maxDiff = None + complete_str = '{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}\n{7}\n{8}\n'.format(document_str, creation_str, file_str, + annotation_str, + relationship_str, snippet_str, package_str, + extracted_licensing_info_str, unknown_tag_str) + + def setUp(self): + self.p = Parser() + self.p.build() + + def test_creation_info(self): + document, _ = self.p.parse(self.complete_str) + assert document is not None + creation_info = document.creation_info + assert creation_info is not None + assert creation_info.spdx_version == "SPDX-2.3" + assert creation_info.data_license == 'CC0-1.0' + assert creation_info.name == 'Sample_Document-V2.3' + assert creation_info.spdx_id == 'SPDXRef-DOCUMENT' + assert creation_info.document_comment == 'Sample Comment' + assert creation_info.document_namespace == 'https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301' + TestCase().assertCountEqual(creation_info.creators, + [Actor(ActorType.PERSON, "Bob", "bob@example.com"), + Actor(ActorType.ORGANIZATION, "Acme.")]) + assert creation_info.creator_comment == 'Sample Comment' + assert creation_info.created == datetime(2010, 2, 3, 0, 0) + assert creation_info.license_list_version == Version(3, 17) + self.assertCountEqual(creation_info.external_document_refs, [ExternalDocumentRef("DocumentRef-spdx-tool-1.2", + "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301", + Checksum(ChecksumAlgorithm.SHA1, + "d6a770ba38583ed4bb4525bd96e50461655d2759"))]) + + def test_extracted_licensing_info(self): + document, _ = self.p.parse(self.complete_str) + assert document is not None + assert len(document.extracted_licensing_info) == 1 + extracted_licensing_info = document.extracted_licensing_info[0] + assert extracted_licensing_info.license_id == "LicenseRef-Beerware-4.2" + assert extracted_licensing_info.extracted_text == '"THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' + assert extracted_licensing_info.license_name == "Beer-Ware License (Version 42)" + assert extracted_licensing_info.cross_references == ["http://people.freebsd.org/~phk/"] + assert extracted_licensing_info.comment == "The beerware license has a couple of other standard variants." + + def test_unknown_tag(self): + document, messages = self.p.parse(self.complete_str) + assert len(messages) == 1 + + def test_package(self): + document, _ = self.p.parse(self.complete_str) + assert document is not None + package = document.packages[0] + assert package.name == 'Test' + assert package.spdx_id == 'SPDXRef-Package' + assert package.version == 'Version 0.9.2' + assert len(package.license_info_from_files) == 2 + assert package.license_concluded == get_spdx_licensing().parse('LicenseRef-2.0 AND Apache-2.0') + assert package.files_analyzed is True + assert package.comment == 'Comment on the package.' + assert len(package.external_references) == 2 + self.assertCountEqual(package.external_references, + [ExternalPackageRef(ExternalPackageRefCategory.SECURITY, "cpe23Type", + "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:", + "Some comment about the package."), + ExternalPackageRef(ExternalPackageRefCategory.OTHER, "LocationRef-acmeforge", + "acmecorp/acmenator/4.1.3-alpha")]) + assert package.primary_package_purpose == PackagePurpose.OPERATING_SYSTEM + assert package.built_date == datetime(2020, 1, 1, 12, 0, 0) + assert package.release_date == datetime(2021, 1, 1, 12, 0, 0) + assert package.valid_until_date == datetime(2022, 1, 1, 12, 0, 0) + + def test_file(self): + document, _ = self.p.parse(self.complete_str) + assert document is not None + assert len(document.files) == 1 + spdx_file = document.files[0] + assert spdx_file.name == 'testfile.java' + assert spdx_file.spdx_id == 'SPDXRef-File' + assert spdx_file.file_type == [FileType.SOURCE, FileType.TEXT] + assert spdx_file.comment == 'Very long file' + assert spdx_file.attribution_texts == ['Acknowledgements that might be required to be communicated in ' \ + 'some contexts.'] + assert spdx_file.license_info_in_file == [get_spdx_licensing().parse("Apache-2.0")] + assert spdx_file.license_concluded == get_spdx_licensing().parse("Apache-2.0") + + def test_annotation(self): + document, _ = self.p.parse(self.complete_str) + assert document is not None + assert len(document.annotations) == 1 + annotation = document.annotations[0] + assert annotation.annotator.name == 'Jane Doe' + assert annotation.annotation_date == datetime(2010, 1, 29, 18, 30, 22) + assert annotation.annotation_comment == 'Document level annotation' + assert annotation.annotation_type == AnnotationType.OTHER + assert annotation.spdx_id == 'SPDXRef-DOCUMENT' + + def test_relationship(self): + document, _ = self.p.parse(self.complete_str) + assert document is not None + relationship = document.relationships[0] + assert relationship.relationship_type == RelationshipType.DESCRIBES + assert relationship.related_spdx_element_id == "SPDXRef-File" + assert relationship.spdx_element_id == "SPDXRef-DOCUMENT" + assert relationship.comment == "This is a comment." + + def test_snippet(self): + document, _ = self.p.parse(self.complete_str) + assert document is not None + assert len(document.snippets) == 1 + snippet = document.snippets[0] + assert snippet.spdx_id == 'SPDXRef-Snippet' + assert snippet.name == 'from linux kernel' + assert snippet.comment == 'Some snippet comment.' + assert snippet.copyright_text == ' Copyright 2008-2010 John Smith ' + assert snippet.license_comment == 'Some lic comment.' + assert snippet.file_spdx_id == 'SPDXRef-DoapSource' + assert snippet.license_concluded == get_spdx_licensing().parse('Apache-2.0') + assert snippet.license_info_in_snippet == [get_spdx_licensing().parse('Apache-2.0')] + assert snippet.byte_range[0] == 310 + assert snippet.byte_range[1] == 420 + assert snippet.line_range[0] == 5 + assert snippet.line_range[1] == 23 From f0c7ede6b9d7cf8978f9e824d5cec894e3f6b2ae Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Mon, 27 Feb 2023 14:12:01 +0100 Subject: [PATCH 297/362] [refactor] use pytest fixture instead of class Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 11 +- .../parser/tagvalue/test_tag_value_parser.py | 255 +++++++++--------- 2 files changed, 140 insertions(+), 126 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 7909f35d9..8fcdff335 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -26,13 +26,13 @@ from spdx.model.relationship import Relationship, RelationshipType from spdx.model.snippet import Snippet from spdx.model.version import Version -from spdx.parser.jsonlikedict.actor_parser import ActorParser +from spdx.parser.actor_parser import ActorParser from spdx.model.document import Document, CreationInfo from spdx.model.file import File, FileType from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone -from spdx.parser.jsonlikedict.dict_parsing_functions import construct_or_raise_parsing_error +from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.logger import Logger from spdx.parser.tagvalue.lexer.tagvalue import SPDXLexer from spdx.parser.tagvalue.parser.helper_methods import grammar_rule, str_from_text @@ -891,9 +891,12 @@ def build(self, **kwargs): def parse(self, text): self.yacc.parse(text, lexer=self.lex) self.construct_current_element() - document = Document(creation_info=CreationInfo(**self.creation_info), **self.elements_build) + raise_parsing_error_if_logger_has_messages(self.logger) + creation_info = construct_or_raise_parsing_error(CreationInfo, self.creation_info) + self.elements_build["creation_info"] = creation_info + document = construct_or_raise_parsing_error(Document, self.elements_build) print(self.logger.get_messages()) - return document, self.logger.get_messages() + return document def construct_current_element(self): if "class" in self.current_element: diff --git a/tests/spdx/parser/tagvalue/test_tag_value_parser.py b/tests/spdx/parser/tagvalue/test_tag_value_parser.py index 3692571a2..3893b829c 100644 --- a/tests/spdx/parser/tagvalue/test_tag_value_parser.py +++ b/tests/spdx/parser/tagvalue/test_tag_value_parser.py @@ -13,6 +13,7 @@ from datetime import datetime from unittest import TestCase +import pytest from license_expression import get_spdx_licensing from spdx.model.actor import Actor, ActorType @@ -23,6 +24,7 @@ from spdx.model.package import PackagePurpose, ExternalPackageRefCategory, ExternalPackageRef from spdx.model.relationship import RelationshipType from spdx.model.version import Version +from spdx.parser.error import SPDXParsingError from spdx.parser.tagvalue.parser.tagvalue import Parser document_str = '\n'.join([ @@ -121,126 +123,135 @@ unknown_tag_str = 'UnknownTag: This is an example for an unknown tag.' +complete_str = '{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}\n{7}\n'.format(document_str, creation_str, file_str, + annotation_str, + relationship_str, snippet_str, package_str, + extracted_licensing_info_str) -class TestParser(TestCase): - maxDiff = None - complete_str = '{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}\n{7}\n{8}\n'.format(document_str, creation_str, file_str, - annotation_str, - relationship_str, snippet_str, package_str, - extracted_licensing_info_str, unknown_tag_str) - - def setUp(self): - self.p = Parser() - self.p.build() - - def test_creation_info(self): - document, _ = self.p.parse(self.complete_str) - assert document is not None - creation_info = document.creation_info - assert creation_info is not None - assert creation_info.spdx_version == "SPDX-2.3" - assert creation_info.data_license == 'CC0-1.0' - assert creation_info.name == 'Sample_Document-V2.3' - assert creation_info.spdx_id == 'SPDXRef-DOCUMENT' - assert creation_info.document_comment == 'Sample Comment' - assert creation_info.document_namespace == 'https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301' - TestCase().assertCountEqual(creation_info.creators, - [Actor(ActorType.PERSON, "Bob", "bob@example.com"), - Actor(ActorType.ORGANIZATION, "Acme.")]) - assert creation_info.creator_comment == 'Sample Comment' - assert creation_info.created == datetime(2010, 2, 3, 0, 0) - assert creation_info.license_list_version == Version(3, 17) - self.assertCountEqual(creation_info.external_document_refs, [ExternalDocumentRef("DocumentRef-spdx-tool-1.2", - "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301", - Checksum(ChecksumAlgorithm.SHA1, - "d6a770ba38583ed4bb4525bd96e50461655d2759"))]) - - def test_extracted_licensing_info(self): - document, _ = self.p.parse(self.complete_str) - assert document is not None - assert len(document.extracted_licensing_info) == 1 - extracted_licensing_info = document.extracted_licensing_info[0] - assert extracted_licensing_info.license_id == "LicenseRef-Beerware-4.2" - assert extracted_licensing_info.extracted_text == '"THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' - assert extracted_licensing_info.license_name == "Beer-Ware License (Version 42)" - assert extracted_licensing_info.cross_references == ["http://people.freebsd.org/~phk/"] - assert extracted_licensing_info.comment == "The beerware license has a couple of other standard variants." - - def test_unknown_tag(self): - document, messages = self.p.parse(self.complete_str) - assert len(messages) == 1 - - def test_package(self): - document, _ = self.p.parse(self.complete_str) - assert document is not None - package = document.packages[0] - assert package.name == 'Test' - assert package.spdx_id == 'SPDXRef-Package' - assert package.version == 'Version 0.9.2' - assert len(package.license_info_from_files) == 2 - assert package.license_concluded == get_spdx_licensing().parse('LicenseRef-2.0 AND Apache-2.0') - assert package.files_analyzed is True - assert package.comment == 'Comment on the package.' - assert len(package.external_references) == 2 - self.assertCountEqual(package.external_references, - [ExternalPackageRef(ExternalPackageRefCategory.SECURITY, "cpe23Type", - "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:", - "Some comment about the package."), - ExternalPackageRef(ExternalPackageRefCategory.OTHER, "LocationRef-acmeforge", - "acmecorp/acmenator/4.1.3-alpha")]) - assert package.primary_package_purpose == PackagePurpose.OPERATING_SYSTEM - assert package.built_date == datetime(2020, 1, 1, 12, 0, 0) - assert package.release_date == datetime(2021, 1, 1, 12, 0, 0) - assert package.valid_until_date == datetime(2022, 1, 1, 12, 0, 0) - - def test_file(self): - document, _ = self.p.parse(self.complete_str) - assert document is not None - assert len(document.files) == 1 - spdx_file = document.files[0] - assert spdx_file.name == 'testfile.java' - assert spdx_file.spdx_id == 'SPDXRef-File' - assert spdx_file.file_type == [FileType.SOURCE, FileType.TEXT] - assert spdx_file.comment == 'Very long file' - assert spdx_file.attribution_texts == ['Acknowledgements that might be required to be communicated in ' \ - 'some contexts.'] - assert spdx_file.license_info_in_file == [get_spdx_licensing().parse("Apache-2.0")] - assert spdx_file.license_concluded == get_spdx_licensing().parse("Apache-2.0") - - def test_annotation(self): - document, _ = self.p.parse(self.complete_str) - assert document is not None - assert len(document.annotations) == 1 - annotation = document.annotations[0] - assert annotation.annotator.name == 'Jane Doe' - assert annotation.annotation_date == datetime(2010, 1, 29, 18, 30, 22) - assert annotation.annotation_comment == 'Document level annotation' - assert annotation.annotation_type == AnnotationType.OTHER - assert annotation.spdx_id == 'SPDXRef-DOCUMENT' - - def test_relationship(self): - document, _ = self.p.parse(self.complete_str) - assert document is not None - relationship = document.relationships[0] - assert relationship.relationship_type == RelationshipType.DESCRIBES - assert relationship.related_spdx_element_id == "SPDXRef-File" - assert relationship.spdx_element_id == "SPDXRef-DOCUMENT" - assert relationship.comment == "This is a comment." - - def test_snippet(self): - document, _ = self.p.parse(self.complete_str) - assert document is not None - assert len(document.snippets) == 1 - snippet = document.snippets[0] - assert snippet.spdx_id == 'SPDXRef-Snippet' - assert snippet.name == 'from linux kernel' - assert snippet.comment == 'Some snippet comment.' - assert snippet.copyright_text == ' Copyright 2008-2010 John Smith ' - assert snippet.license_comment == 'Some lic comment.' - assert snippet.file_spdx_id == 'SPDXRef-DoapSource' - assert snippet.license_concluded == get_spdx_licensing().parse('Apache-2.0') - assert snippet.license_info_in_snippet == [get_spdx_licensing().parse('Apache-2.0')] - assert snippet.byte_range[0] == 310 - assert snippet.byte_range[1] == 420 - assert snippet.line_range[0] == 5 - assert snippet.line_range[1] == 23 + +@pytest.fixture +def parser(): + spdx_parser = Parser() + spdx_parser.build() + return spdx_parser + + +def test_creation_info(parser): + document = parser.parse(complete_str) + assert document is not None + creation_info = document.creation_info + assert creation_info is not None + assert creation_info.spdx_version == "SPDX-2.3" + assert creation_info.data_license == 'CC0-1.0' + assert creation_info.name == 'Sample_Document-V2.3' + assert creation_info.spdx_id == 'SPDXRef-DOCUMENT' + assert creation_info.document_comment == 'Sample Comment' + assert creation_info.document_namespace == 'https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301' + TestCase().assertCountEqual(creation_info.creators, + [Actor(ActorType.PERSON, "Bob", "bob@example.com"), + Actor(ActorType.ORGANIZATION, "Acme.")]) + assert creation_info.creator_comment == 'Sample Comment' + assert creation_info.created == datetime(2010, 2, 3, 0, 0) + assert creation_info.license_list_version == Version(3, 17) + TestCase().assertCountEqual(creation_info.external_document_refs, + [ExternalDocumentRef("DocumentRef-spdx-tool-1.2", + "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301", + Checksum(ChecksumAlgorithm.SHA1, + "d6a770ba38583ed4bb4525bd96e50461655d2759"))]) + + +def test_extracted_licensing_info(parser): + document = parser.parse(complete_str) + assert document is not None + assert len(document.extracted_licensing_info) == 1 + extracted_licensing_info = document.extracted_licensing_info[0] + assert extracted_licensing_info.license_id == "LicenseRef-Beerware-4.2" + assert extracted_licensing_info.extracted_text == '"THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' + assert extracted_licensing_info.license_name == "Beer-Ware License (Version 42)" + assert extracted_licensing_info.cross_references == ["http://people.freebsd.org/~phk/"] + assert extracted_licensing_info.comment == "The beerware license has a couple of other standard variants." + + +def test_package(parser): + document = parser.parse(complete_str) + assert document is not None + package = document.packages[0] + assert package.name == 'Test' + assert package.spdx_id == 'SPDXRef-Package' + assert package.version == 'Version 0.9.2' + assert len(package.license_info_from_files) == 2 + assert package.license_concluded == get_spdx_licensing().parse('LicenseRef-2.0 AND Apache-2.0') + assert package.files_analyzed is True + assert package.comment == 'Comment on the package.' + assert len(package.external_references) == 2 + TestCase().assertCountEqual(package.external_references, + [ExternalPackageRef(ExternalPackageRefCategory.SECURITY, "cpe23Type", + "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:", + "Some comment about the package."), + ExternalPackageRef(ExternalPackageRefCategory.OTHER, "LocationRef-acmeforge", + "acmecorp/acmenator/4.1.3-alpha")]) + assert package.primary_package_purpose == PackagePurpose.OPERATING_SYSTEM + assert package.built_date == datetime(2020, 1, 1, 12, 0, 0) + assert package.release_date == datetime(2021, 1, 1, 12, 0, 0) + assert package.valid_until_date == datetime(2022, 1, 1, 12, 0, 0) + + +def test_file(parser): + document = parser.parse(complete_str) + assert document is not None + assert len(document.files) == 1 + spdx_file = document.files[0] + assert spdx_file.name == 'testfile.java' + assert spdx_file.spdx_id == 'SPDXRef-File' + assert spdx_file.file_type == [FileType.SOURCE, FileType.TEXT] + assert spdx_file.comment == 'Very long file' + assert spdx_file.attribution_texts == ['Acknowledgements that might be required to be communicated in ' \ + 'some contexts.'] + assert spdx_file.license_info_in_file == [get_spdx_licensing().parse("Apache-2.0")] + assert spdx_file.license_concluded == get_spdx_licensing().parse("Apache-2.0") + + +def test_annotation(parser): + document = parser.parse(complete_str) + assert document is not None + assert len(document.annotations) == 1 + annotation = document.annotations[0] + assert annotation.annotator.name == 'Jane Doe' + assert annotation.annotation_date == datetime(2010, 1, 29, 18, 30, 22) + assert annotation.annotation_comment == 'Document level annotation' + assert annotation.annotation_type == AnnotationType.OTHER + assert annotation.spdx_id == 'SPDXRef-DOCUMENT' + + +def test_relationship(parser): + document = parser.parse(complete_str) + assert document is not None + relationship = document.relationships[0] + assert relationship.relationship_type == RelationshipType.DESCRIBES + assert relationship.related_spdx_element_id == "SPDXRef-File" + assert relationship.spdx_element_id == "SPDXRef-DOCUMENT" + assert relationship.comment == "This is a comment." + + +def test_snippet(parser): + document = parser.parse(complete_str) + assert document is not None + assert len(document.snippets) == 1 + snippet = document.snippets[0] + assert snippet.spdx_id == 'SPDXRef-Snippet' + assert snippet.name == 'from linux kernel' + assert snippet.comment == 'Some snippet comment.' + assert snippet.copyright_text == ' Copyright 2008-2010 John Smith ' + assert snippet.license_comment == 'Some lic comment.' + assert snippet.file_spdx_id == 'SPDXRef-DoapSource' + assert snippet.license_concluded == get_spdx_licensing().parse('Apache-2.0') + assert snippet.license_info_in_snippet == [get_spdx_licensing().parse('Apache-2.0')] + assert snippet.byte_range[0] == 310 + assert snippet.byte_range[1] == 420 + assert snippet.line_range[0] == 5 + assert snippet.line_range[1] == 23 + + +def test_unknown_str(parser): + with pytest.raises(SPDXParsingError, match="Unknown tag"): + parser.parse(unknown_tag_str) From 23afa898c71bfaaef6971cfb2ed6e052f37cd83d Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 28 Feb 2023 08:52:55 +0100 Subject: [PATCH 298/362] [issue-382] implement error handling Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 36 +++++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 8fcdff335..a0198f149 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -11,7 +11,6 @@ # limitations under the License. import re -from typing import Optional from license_expression import get_spdx_licensing from ply import yacc @@ -32,6 +31,7 @@ from spdx.model.file import File, FileType from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone +from spdx.parser.error import SPDXParsingError from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.logger import Logger from spdx.parser.tagvalue.lexer.tagvalue import SPDXLexer @@ -250,6 +250,7 @@ def p_extr_lic_id_2(self, p): @grammar_rule("lic_xref : LICS_CRS_REF LINE") def p_lic_xref_1(self, p): + self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) self.current_element.setdefault("cross_references", []).append(p[2]) @grammar_rule("lic_xref : LICS_CRS_REF error") @@ -848,13 +849,20 @@ def p_annotation_spdx_id_2(self, p): # parsing methods for relationship @grammar_rule("relationship : RELATIONSHIP relationship_value") def p_relationship_1(self, p): - splitted_relationship = p[2].split(" ") - + try: + spdx_element_id, relationship_type, related_spdx_element_id = p[2].split(" ") + except ValueError: + self.logger.append(f"Relationship couldn't be split in spdx_element_id, relationship_type and " + f"related_spdx_element. Line: {p.lineno(1)}") + return self.construct_current_element() self.current_element["class"] = Relationship - self.current_element["relationship_type"] = RelationshipType[splitted_relationship[1]] - self.current_element["related_spdx_element_id"] = splitted_relationship[2] - self.current_element["spdx_element_id"] = splitted_relationship[0] + try: + self.current_element["relationship_type"] = RelationshipType[relationship_type] + except KeyError: + self.logger.append(f"Invalid RelationshipType {relationship_type}. Line: {p.lineno(1)}") + self.current_element["related_spdx_element_id"] = related_spdx_element_id + self.current_element["spdx_element_id"] = spdx_element_id @grammar_rule("relationship : RELATIONSHIP error") def p_relationship_2(self, p): @@ -895,15 +903,23 @@ def parse(self, text): creation_info = construct_or_raise_parsing_error(CreationInfo, self.creation_info) self.elements_build["creation_info"] = creation_info document = construct_or_raise_parsing_error(Document, self.elements_build) - print(self.logger.get_messages()) return document def construct_current_element(self): - if "class" in self.current_element: - class_name = self.current_element.pop("class") + if "class" not in self.current_element: + return + class_name = self.current_element.pop("class") + try: self.elements_build.setdefault(CLASS_MAPPING[class_name.__name__], []).append( construct_or_raise_parsing_error(class_name, self.current_element)) - self.current_element = dict() + except SPDXParsingError as err: + self.logger.append(err.get_messages()) + self.current_element = dict() + + def check_that_current_element_matches_class_for_value(self, expected_class): + if expected_class != self.current_element["class"]: + raise SPDXParsingError(["Unexpected current element for value"]) + # what to do now? exit parsing CLASS_MAPPING = dict(File="files", Annotation="annotations", Relationship="relationships", Snippet="snippets", From 20f0a411e19027816ff42348b67e6148d7b61aa2 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 28 Feb 2023 08:57:23 +0100 Subject: [PATCH 299/362] [issue-382] catch exception if not all required arguments are provided for construction Signed-off-by: Meret Behrens --- src/spdx/parser/parsing_functions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/spdx/parser/parsing_functions.py b/src/spdx/parser/parsing_functions.py index 5beaef5f1..f8cd3ce25 100644 --- a/src/spdx/parser/parsing_functions.py +++ b/src/spdx/parser/parsing_functions.py @@ -20,6 +20,8 @@ def construct_or_raise_parsing_error(object_to_construct: Any, args_for_construc constructed_object = object_to_construct(**args_for_construction) except ConstructorTypeErrors as err: raise SPDXParsingError([f"Error while constructing {object_to_construct.__name__}: {err.get_messages()}"]) + except TypeError as err: + raise SPDXParsingError([f"Error while constructing {object_to_construct.__name__}: {err.args[0]}"]) return constructed_object From 5b6437940f49045f1ace4c07f4a944f58c0a25e6 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Mar 2023 14:29:20 +0100 Subject: [PATCH 300/362] [refactor] tests Signed-off-by: Meret Behrens --- .../parser/tagvalue/test_annotation_parser.py | 44 ++++ .../tagvalue/test_creation_info_parser.py | 66 +++++ .../test_extracted_licensing_info_parser.py | 40 ++++ .../spdx/parser/tagvalue/test_file_parser.py | 51 ++++ .../parser/tagvalue/test_package_parser.py | 79 ++++++ .../tagvalue/test_relationship_parser.py | 45 ++++ .../parser/tagvalue/test_snippet_parser.py | 54 +++++ .../parser/tagvalue/test_tag_value_parser.py | 225 +----------------- 8 files changed, 381 insertions(+), 223 deletions(-) create mode 100644 tests/spdx/parser/tagvalue/test_annotation_parser.py create mode 100644 tests/spdx/parser/tagvalue/test_creation_info_parser.py create mode 100644 tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py create mode 100644 tests/spdx/parser/tagvalue/test_file_parser.py create mode 100644 tests/spdx/parser/tagvalue/test_package_parser.py create mode 100644 tests/spdx/parser/tagvalue/test_relationship_parser.py create mode 100644 tests/spdx/parser/tagvalue/test_snippet_parser.py diff --git a/tests/spdx/parser/tagvalue/test_annotation_parser.py b/tests/spdx/parser/tagvalue/test_annotation_parser.py new file mode 100644 index 000000000..8fbcc2f9b --- /dev/null +++ b/tests/spdx/parser/tagvalue/test_annotation_parser.py @@ -0,0 +1,44 @@ +# Copyright (c) 2023 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 datetime import datetime + +import pytest + +from spdx.parser.tagvalue.parser.tagvalue import Parser +from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR + +from spdx.model.annotation import AnnotationType + + +@pytest.fixture +def parser(): + spdx_parser = Parser() + spdx_parser.build() + return spdx_parser + + +def test_annotation(parser): + annotation_str = '\n'.join([ + 'Annotator: Person: Jane Doe()', + 'AnnotationDate: 2010-01-29T18:30:22Z', + 'AnnotationComment: Document level annotation', + 'AnnotationType: OTHER', + 'SPDXREF: SPDXRef-DOCUMENT' + ]) + document = parser.parse("\n".join([DOCUMENT_STR, annotation_str])) + assert document is not None + assert len(document.annotations) == 1 + annotation = document.annotations[0] + assert annotation.annotator.name == 'Jane Doe' + assert annotation.annotation_date == datetime(2010, 1, 29, 18, 30, 22) + assert annotation.annotation_comment == 'Document level annotation' + assert annotation.annotation_type == AnnotationType.OTHER + assert annotation.spdx_id == 'SPDXRef-DOCUMENT' diff --git a/tests/spdx/parser/tagvalue/test_creation_info_parser.py b/tests/spdx/parser/tagvalue/test_creation_info_parser.py new file mode 100644 index 000000000..3aca7aca9 --- /dev/null +++ b/tests/spdx/parser/tagvalue/test_creation_info_parser.py @@ -0,0 +1,66 @@ +# Copyright (c) 2023 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 datetime import datetime +from unittest import TestCase + +import pytest + +from spdx.model.actor import Actor, ActorType +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.model.external_document_ref import ExternalDocumentRef +from spdx.model.version import Version +from spdx.parser.tagvalue.parser.tagvalue import Parser + +DOCUMENT_STR = '\n'.join([ + 'SPDXVersion: SPDX-2.3', + 'DataLicense: CC0-1.0', + 'DocumentName: Sample_Document-V2.3', + 'SPDXID: SPDXRef-DOCUMENT', + 'DocumentComment: Sample Comment', + 'DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301', + 'ExternalDocumentRef: DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759', + 'Creator: Person: Bob (bob@example.com)', + 'Creator: Organization: Acme.', + 'Created: 2010-02-03T00:00:00Z', + 'CreatorComment: Sample Comment', + 'LicenseListVersion: 3.17' +]) + + +@pytest.fixture +def parser(): + spdx_parser = Parser() + spdx_parser.build() + return spdx_parser + + +def test_creation_info(parser): + document = parser.parse(DOCUMENT_STR) + assert document is not None + creation_info = document.creation_info + assert creation_info is not None + assert creation_info.spdx_version == "SPDX-2.3" + assert creation_info.data_license == 'CC0-1.0' + assert creation_info.name == 'Sample_Document-V2.3' + assert creation_info.spdx_id == 'SPDXRef-DOCUMENT' + assert creation_info.document_comment == 'Sample Comment' + assert creation_info.document_namespace == 'https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301' + TestCase().assertCountEqual(creation_info.creators, + [Actor(ActorType.PERSON, "Bob", "bob@example.com"), + Actor(ActorType.ORGANIZATION, "Acme.")]) + assert creation_info.creator_comment == 'Sample Comment' + assert creation_info.created == datetime(2010, 2, 3, 0, 0) + assert creation_info.license_list_version == Version(3, 17) + TestCase().assertCountEqual(creation_info.external_document_refs, + [ExternalDocumentRef("DocumentRef-spdx-tool-1.2", + "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301", + Checksum(ChecksumAlgorithm.SHA1, + "d6a770ba38583ed4bb4525bd96e50461655d2759"))]) diff --git a/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py b/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py new file mode 100644 index 000000000..7197b0676 --- /dev/null +++ b/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py @@ -0,0 +1,40 @@ +# Copyright (c) 2023 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. +import pytest + +from spdx.parser.tagvalue.parser.tagvalue import Parser +from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR + + +@pytest.fixture +def parser(): + spdx_parser = Parser() + spdx_parser.build() + return spdx_parser + + +def test_extracted_licensing_info(parser): + extracted_licensing_info_str = '\n'.join([ + 'LicenseID: LicenseRef-Beerware-4.2', + 'ExtractedText: "THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' + 'LicenseName: Beer-Ware License (Version 42)', + 'LicenseCrossReference: http://people.freebsd.org/~phk/', + 'LicenseComment: The beerware license has a couple of other standard variants.' + ]) + document = parser.parse("\n".join([DOCUMENT_STR, extracted_licensing_info_str])) + assert document is not None + assert len(document.extracted_licensing_info) == 1 + extracted_licensing_info = document.extracted_licensing_info[0] + assert extracted_licensing_info.license_id == "LicenseRef-Beerware-4.2" + assert extracted_licensing_info.extracted_text == '"THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' + assert extracted_licensing_info.license_name == "Beer-Ware License (Version 42)" + assert extracted_licensing_info.cross_references == ["http://people.freebsd.org/~phk/"] + assert extracted_licensing_info.comment == "The beerware license has a couple of other standard variants." diff --git a/tests/spdx/parser/tagvalue/test_file_parser.py b/tests/spdx/parser/tagvalue/test_file_parser.py new file mode 100644 index 000000000..dab3eeedc --- /dev/null +++ b/tests/spdx/parser/tagvalue/test_file_parser.py @@ -0,0 +1,51 @@ +# Copyright (c) 2023 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. +import pytest +from license_expression import get_spdx_licensing + +from spdx.parser.tagvalue.parser.tagvalue import Parser +from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR + +from spdx.model.file import FileType + + +@pytest.fixture +def parser(): + spdx_parser = Parser() + spdx_parser.build() + return spdx_parser + + +def test_file(parser): + file_str = '\n'.join([ + 'FileName: testfile.java', + 'SPDXID: SPDXRef-File', + 'FileType: SOURCE', + 'FileType: TEXT', + 'FileChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', + 'LicenseConcluded: Apache-2.0', + 'LicenseInfoInFile: Apache-2.0', + 'FileCopyrightText: Copyright 2014 Acme Inc.', + 'FileComment: Very long file', + 'FileAttributionText: Acknowledgements that might be required to be communicated in some contexts.' + ]) + document = parser.parse("\n".join([DOCUMENT_STR, file_str])) + assert document is not None + assert len(document.files) == 1 + spdx_file = document.files[0] + assert spdx_file.name == 'testfile.java' + assert spdx_file.spdx_id == 'SPDXRef-File' + assert spdx_file.file_type == [FileType.SOURCE, FileType.TEXT] + assert spdx_file.comment == 'Very long file' + assert spdx_file.attribution_texts == [ + 'Acknowledgements that might be required to be communicated in some contexts.'] + assert spdx_file.license_info_in_file == [get_spdx_licensing().parse("Apache-2.0")] + assert spdx_file.license_concluded == get_spdx_licensing().parse("Apache-2.0") diff --git a/tests/spdx/parser/tagvalue/test_package_parser.py b/tests/spdx/parser/tagvalue/test_package_parser.py new file mode 100644 index 000000000..4de7ffd41 --- /dev/null +++ b/tests/spdx/parser/tagvalue/test_package_parser.py @@ -0,0 +1,79 @@ +# Copyright (c) 2023 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 datetime import datetime +from unittest import TestCase + +import pytest +from license_expression import get_spdx_licensing + +from spdx.model.package import ExternalPackageRef, ExternalPackageRefCategory, PackagePurpose +from spdx.parser.tagvalue.parser.tagvalue import Parser +from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR + + +@pytest.fixture +def parser(): + spdx_parser = Parser() + spdx_parser.build() + return spdx_parser + + +def test_package(parser): + package_str = '\n'.join([ + 'PackageName: Test', + 'SPDXID: SPDXRef-Package', + 'PackageVersion: Version 0.9.2', + 'PackageDownloadLocation: http://example.com/test', + 'FilesAnalyzed: True', + 'PackageSummary: Test package', + 'PackageSourceInfo: Version 1.0 of test', + 'PackageFileName: test-1.0.zip', + 'PackageSupplier: Organization:ACME', + 'PackageOriginator: Organization:ACME', + 'PackageChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', + 'PackageVerificationCode: 4e3211c67a2d28fced849ee1bb76e7391b93feba (something.rdf, something.txt)', + 'PackageDescription: A package.', + 'PackageComment: Comment on the package.', + 'PackageCopyrightText: Copyright 2014 Acme Inc.', + 'PackageLicenseDeclared: Apache-2.0', + 'PackageLicenseConcluded: (LicenseRef-2.0 and Apache-2.0)', + 'PackageLicenseInfoFromFiles: Apache-1.0', + 'PackageLicenseInfoFromFiles: Apache-2.0', + 'PackageLicenseComments: License Comments', + 'ExternalRef: SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:', + 'ExternalRefComment: Some comment about the package.', + 'ExternalRef: OTHER LocationRef-acmeforge acmecorp/acmenator/4.1.3-alpha', + 'PrimaryPackagePurpose: OPERATING-SYSTEM', + 'BuiltDate: 2020-01-01T12:00:00Z', + 'ReleaseDate: 2021-01-01T12:00:00Z', + 'ValidUntilDate: 2022-01-01T12:00:00Z' + ]) + document = parser.parse("\n".join([DOCUMENT_STR, package_str])) + assert document is not None + package = document.packages[0] + assert package.name == 'Test' + assert package.spdx_id == 'SPDXRef-Package' + assert package.version == 'Version 0.9.2' + assert len(package.license_info_from_files) == 2 + assert package.license_concluded == get_spdx_licensing().parse('LicenseRef-2.0 AND Apache-2.0') + assert package.files_analyzed is True + assert package.comment == 'Comment on the package.' + assert len(package.external_references) == 2 + TestCase().assertCountEqual(package.external_references, + [ExternalPackageRef(ExternalPackageRefCategory.SECURITY, "cpe23Type", + "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:", + "Some comment about the package."), + ExternalPackageRef(ExternalPackageRefCategory.OTHER, "LocationRef-acmeforge", + "acmecorp/acmenator/4.1.3-alpha")]) + assert package.primary_package_purpose == PackagePurpose.OPERATING_SYSTEM + assert package.built_date == datetime(2020, 1, 1, 12, 0, 0) + assert package.release_date == datetime(2021, 1, 1, 12, 0, 0) + assert package.valid_until_date == datetime(2022, 1, 1, 12, 0, 0) diff --git a/tests/spdx/parser/tagvalue/test_relationship_parser.py b/tests/spdx/parser/tagvalue/test_relationship_parser.py new file mode 100644 index 000000000..ba18ea88c --- /dev/null +++ b/tests/spdx/parser/tagvalue/test_relationship_parser.py @@ -0,0 +1,45 @@ +# Copyright (c) 2023 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. +import pytest + +from spdx.model.relationship import RelationshipType +from spdx.parser.error import SPDXParsingError +from spdx.parser.tagvalue.parser.tagvalue import Parser +from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR + + +@pytest.fixture +def parser(): + spdx_parser = Parser() + spdx_parser.build() + return spdx_parser + + +def test_relationship(parser): + relationship_str = '\n'.join([ + 'Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File', + 'RelationshipComment: This is a comment.']) + + document = parser.parse("\n".join([DOCUMENT_STR, relationship_str])) + assert document is not None + relationship = document.relationships[0] + assert relationship.relationship_type == RelationshipType.DESCRIBES + assert relationship.related_spdx_element_id == "SPDXRef-File" + assert relationship.spdx_element_id == "SPDXRef-DOCUMENT" + assert relationship.comment == "This is a comment." + + +@pytest.mark.parametrize("relationship_str, expected_message", + [("Relationship: spdx_id DESCRIBES", "Relationship couldn't be split"), + ("Relationship: spdx_id IS spdx_id", "Invalid RelationshipType IS. Line: 1")]) +def test_falsy_relationship(parser, relationship_str, expected_message): + with pytest.raises(SPDXParsingError, match=expected_message): + parser.parse(relationship_str) diff --git a/tests/spdx/parser/tagvalue/test_snippet_parser.py b/tests/spdx/parser/tagvalue/test_snippet_parser.py new file mode 100644 index 000000000..95f766e52 --- /dev/null +++ b/tests/spdx/parser/tagvalue/test_snippet_parser.py @@ -0,0 +1,54 @@ +# Copyright (c) 2023 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. +import pytest +from license_expression import get_spdx_licensing + +from spdx.parser.tagvalue.parser.tagvalue import Parser +from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR + + +@pytest.fixture +def parser(): + spdx_parser = Parser() + spdx_parser.build() + return spdx_parser + + +def test_snippet(parser): + snippet_str = '\n'.join([ + 'SnippetSPDXID: SPDXRef-Snippet', + 'SnippetLicenseComments: Some lic comment.', + 'SnippetCopyrightText: Copyright 2008-2010 John Smith ', + 'SnippetComment: Some snippet comment.', + 'SnippetName: from linux kernel', + 'SnippetFromFileSPDXID: SPDXRef-DoapSource', + 'SnippetLicenseConcluded: Apache-2.0', + 'LicenseInfoInSnippet: Apache-2.0', + 'SnippetByteRange: 310:420', + 'SnippetLineRange: 5:23', + ]) + + document = parser.parse("\n".join([DOCUMENT_STR, snippet_str])) + assert document is not None + assert len(document.snippets) == 1 + snippet = document.snippets[0] + assert snippet.spdx_id == 'SPDXRef-Snippet' + assert snippet.name == 'from linux kernel' + assert snippet.comment == 'Some snippet comment.' + assert snippet.copyright_text == ' Copyright 2008-2010 John Smith ' + assert snippet.license_comment == 'Some lic comment.' + assert snippet.file_spdx_id == 'SPDXRef-DoapSource' + assert snippet.license_concluded == get_spdx_licensing().parse('Apache-2.0') + assert snippet.license_info_in_snippet == [get_spdx_licensing().parse('Apache-2.0')] + assert snippet.byte_range[0] == 310 + assert snippet.byte_range[1] == 420 + assert snippet.line_range[0] == 5 + assert snippet.line_range[1] == 23 diff --git a/tests/spdx/parser/tagvalue/test_tag_value_parser.py b/tests/spdx/parser/tagvalue/test_tag_value_parser.py index 3893b829c..b4228ebc7 100644 --- a/tests/spdx/parser/tagvalue/test_tag_value_parser.py +++ b/tests/spdx/parser/tagvalue/test_tag_value_parser.py @@ -9,125 +9,18 @@ # 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 datetime import datetime from unittest import TestCase import pytest -from license_expression import get_spdx_licensing from spdx.model.actor import Actor, ActorType -from spdx.model.annotation import AnnotationType from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.model.external_document_ref import ExternalDocumentRef -from spdx.model.file import FileType -from spdx.model.package import PackagePurpose, ExternalPackageRefCategory, ExternalPackageRef -from spdx.model.relationship import RelationshipType from spdx.model.version import Version from spdx.parser.error import SPDXParsingError from spdx.parser.tagvalue.parser.tagvalue import Parser -document_str = '\n'.join([ - 'SPDXVersion: SPDX-2.3', - 'DataLicense: CC0-1.0', - 'DocumentName: Sample_Document-V2.3', - 'SPDXID: SPDXRef-DOCUMENT', - 'DocumentComment: Sample Comment', - 'DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301', - 'ExternalDocumentRef: DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759' -]) - -creation_str = '\n'.join([ - 'Creator: Person: Bob (bob@example.com)', - 'Creator: Organization: Acme.', - 'Created: 2010-02-03T00:00:00Z', - 'CreatorComment: Sample Comment', - 'LicenseListVersion: 3.17' -]) - -package_str = '\n'.join([ - 'PackageName: Test', - 'SPDXID: SPDXRef-Package', - 'PackageVersion: Version 0.9.2', - 'PackageDownloadLocation: http://example.com/test', - 'FilesAnalyzed: True', - 'PackageSummary: Test package', - 'PackageSourceInfo: Version 1.0 of test', - 'PackageFileName: test-1.0.zip', - 'PackageSupplier: Organization:ACME', - 'PackageOriginator: Organization:ACME', - 'PackageChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', - 'PackageVerificationCode: 4e3211c67a2d28fced849ee1bb76e7391b93feba (something.rdf, something.txt)', - 'PackageDescription: A package.', - 'PackageComment: Comment on the package.', - 'PackageCopyrightText: Copyright 2014 Acme Inc.', - 'PackageLicenseDeclared: Apache-2.0', - 'PackageLicenseConcluded: (LicenseRef-2.0 and Apache-2.0)', - 'PackageLicenseInfoFromFiles: Apache-1.0', - 'PackageLicenseInfoFromFiles: Apache-2.0', - 'PackageLicenseComments: License Comments', - 'ExternalRef: SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:', - 'ExternalRefComment: Some comment about the package.', - 'ExternalRef: OTHER LocationRef-acmeforge acmecorp/acmenator/4.1.3-alpha', - 'PrimaryPackagePurpose: OPERATING-SYSTEM', - 'BuiltDate: 2020-01-01T12:00:00Z', - 'ReleaseDate: 2021-01-01T12:00:00Z', - 'ValidUntilDate: 2022-01-01T12:00:00Z' -]) - -file_str = '\n'.join([ - 'FileName: testfile.java', - 'SPDXID: SPDXRef-File', - 'FileType: SOURCE', - 'FileType: TEXT', - 'FileChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', - 'LicenseConcluded: Apache-2.0', - 'LicenseInfoInFile: Apache-2.0', - 'FileCopyrightText: Copyright 2014 Acme Inc.', - 'FileComment: Very long file', - 'FileAttributionText: Acknowledgements that might be required to be communicated in some contexts.' -]) - -snippet_str = '\n'.join([ - 'SnippetSPDXID: SPDXRef-Snippet', - 'SnippetLicenseComments: Some lic comment.', - 'SnippetCopyrightText: Copyright 2008-2010 John Smith ', - 'SnippetComment: Some snippet comment.', - 'SnippetName: from linux kernel', - 'SnippetFromFileSPDXID: SPDXRef-DoapSource', - 'SnippetLicenseConcluded: Apache-2.0', - 'LicenseInfoInSnippet: Apache-2.0', - 'SnippetByteRange: 310:420', - 'SnippetLineRange: 5:23', -]) - -annotation_str = '\n'.join([ - 'Annotator: Person: Jane Doe()', - 'AnnotationDate: 2010-01-29T18:30:22Z', - 'AnnotationComment: Document level annotation', - 'AnnotationType: OTHER', - 'SPDXREF: SPDXRef-DOCUMENT' -]) - -relationship_str = '\n'.join([ - 'Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File', - 'RelationshipComment: This is a comment.']) - -extracted_licensing_info_str = '\n'.join([ - 'LicenseID: LicenseRef-Beerware-4.2', - 'ExtractedText: "THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' - 'LicenseName: Beer-Ware License (Version 42)', - 'LicenseCrossReference: http://people.freebsd.org/~phk/', - 'LicenseComment: The beerware license has a couple of other standard variants.' -]) - -unknown_tag_str = 'UnknownTag: This is an example for an unknown tag.' - -complete_str = '{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}\n{7}\n'.format(document_str, creation_str, file_str, - annotation_str, - relationship_str, snippet_str, package_str, - extracted_licensing_info_str) - @pytest.fixture def parser(): @@ -136,122 +29,8 @@ def parser(): return spdx_parser -def test_creation_info(parser): - document = parser.parse(complete_str) - assert document is not None - creation_info = document.creation_info - assert creation_info is not None - assert creation_info.spdx_version == "SPDX-2.3" - assert creation_info.data_license == 'CC0-1.0' - assert creation_info.name == 'Sample_Document-V2.3' - assert creation_info.spdx_id == 'SPDXRef-DOCUMENT' - assert creation_info.document_comment == 'Sample Comment' - assert creation_info.document_namespace == 'https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301' - TestCase().assertCountEqual(creation_info.creators, - [Actor(ActorType.PERSON, "Bob", "bob@example.com"), - Actor(ActorType.ORGANIZATION, "Acme.")]) - assert creation_info.creator_comment == 'Sample Comment' - assert creation_info.created == datetime(2010, 2, 3, 0, 0) - assert creation_info.license_list_version == Version(3, 17) - TestCase().assertCountEqual(creation_info.external_document_refs, - [ExternalDocumentRef("DocumentRef-spdx-tool-1.2", - "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301", - Checksum(ChecksumAlgorithm.SHA1, - "d6a770ba38583ed4bb4525bd96e50461655d2759"))]) - - -def test_extracted_licensing_info(parser): - document = parser.parse(complete_str) - assert document is not None - assert len(document.extracted_licensing_info) == 1 - extracted_licensing_info = document.extracted_licensing_info[0] - assert extracted_licensing_info.license_id == "LicenseRef-Beerware-4.2" - assert extracted_licensing_info.extracted_text == '"THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' - assert extracted_licensing_info.license_name == "Beer-Ware License (Version 42)" - assert extracted_licensing_info.cross_references == ["http://people.freebsd.org/~phk/"] - assert extracted_licensing_info.comment == "The beerware license has a couple of other standard variants." - - -def test_package(parser): - document = parser.parse(complete_str) - assert document is not None - package = document.packages[0] - assert package.name == 'Test' - assert package.spdx_id == 'SPDXRef-Package' - assert package.version == 'Version 0.9.2' - assert len(package.license_info_from_files) == 2 - assert package.license_concluded == get_spdx_licensing().parse('LicenseRef-2.0 AND Apache-2.0') - assert package.files_analyzed is True - assert package.comment == 'Comment on the package.' - assert len(package.external_references) == 2 - TestCase().assertCountEqual(package.external_references, - [ExternalPackageRef(ExternalPackageRefCategory.SECURITY, "cpe23Type", - "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:", - "Some comment about the package."), - ExternalPackageRef(ExternalPackageRefCategory.OTHER, "LocationRef-acmeforge", - "acmecorp/acmenator/4.1.3-alpha")]) - assert package.primary_package_purpose == PackagePurpose.OPERATING_SYSTEM - assert package.built_date == datetime(2020, 1, 1, 12, 0, 0) - assert package.release_date == datetime(2021, 1, 1, 12, 0, 0) - assert package.valid_until_date == datetime(2022, 1, 1, 12, 0, 0) - - -def test_file(parser): - document = parser.parse(complete_str) - assert document is not None - assert len(document.files) == 1 - spdx_file = document.files[0] - assert spdx_file.name == 'testfile.java' - assert spdx_file.spdx_id == 'SPDXRef-File' - assert spdx_file.file_type == [FileType.SOURCE, FileType.TEXT] - assert spdx_file.comment == 'Very long file' - assert spdx_file.attribution_texts == ['Acknowledgements that might be required to be communicated in ' \ - 'some contexts.'] - assert spdx_file.license_info_in_file == [get_spdx_licensing().parse("Apache-2.0")] - assert spdx_file.license_concluded == get_spdx_licensing().parse("Apache-2.0") - - -def test_annotation(parser): - document = parser.parse(complete_str) - assert document is not None - assert len(document.annotations) == 1 - annotation = document.annotations[0] - assert annotation.annotator.name == 'Jane Doe' - assert annotation.annotation_date == datetime(2010, 1, 29, 18, 30, 22) - assert annotation.annotation_comment == 'Document level annotation' - assert annotation.annotation_type == AnnotationType.OTHER - assert annotation.spdx_id == 'SPDXRef-DOCUMENT' - - -def test_relationship(parser): - document = parser.parse(complete_str) - assert document is not None - relationship = document.relationships[0] - assert relationship.relationship_type == RelationshipType.DESCRIBES - assert relationship.related_spdx_element_id == "SPDXRef-File" - assert relationship.spdx_element_id == "SPDXRef-DOCUMENT" - assert relationship.comment == "This is a comment." - - -def test_snippet(parser): - document = parser.parse(complete_str) - assert document is not None - assert len(document.snippets) == 1 - snippet = document.snippets[0] - assert snippet.spdx_id == 'SPDXRef-Snippet' - assert snippet.name == 'from linux kernel' - assert snippet.comment == 'Some snippet comment.' - assert snippet.copyright_text == ' Copyright 2008-2010 John Smith ' - assert snippet.license_comment == 'Some lic comment.' - assert snippet.file_spdx_id == 'SPDXRef-DoapSource' - assert snippet.license_concluded == get_spdx_licensing().parse('Apache-2.0') - assert snippet.license_info_in_snippet == [get_spdx_licensing().parse('Apache-2.0')] - assert snippet.byte_range[0] == 310 - assert snippet.byte_range[1] == 420 - assert snippet.line_range[0] == 5 - assert snippet.line_range[1] == 23 - - def test_unknown_str(parser): + unknown_tag_str = 'UnknownTag: This is an example for an unknown tag.' + with pytest.raises(SPDXParsingError, match="Unknown tag"): parser.parse(unknown_tag_str) From 092a067c2da5c98352774ad4f4999428b305cec1 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 28 Feb 2023 09:21:42 +0100 Subject: [PATCH 301/362] [issue-382] add test and specify error message Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 2 +- .../spdx/parser/tagvalue/test_file_parser.py | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index a0198f149..248fb5ed8 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -374,7 +374,7 @@ def p_file_type_1(self, p): @grammar_rule("file_type : FILE_TYPE error") def p_file_type_2(self, p): self.logger.append( - f"Error while parsing FileType: Token did not match specified grammar rule. Line: {p.lineno(1)}") + f"Error while parsing FileType: Token did not match any of the valid values. Line: {p.lineno(1)}") @grammar_rule("file_checksum : FILE_CHECKSUM CHECKSUM") def p_file_checksum_1(self, p): diff --git a/tests/spdx/parser/tagvalue/test_file_parser.py b/tests/spdx/parser/tagvalue/test_file_parser.py index dab3eeedc..90eb1e6a8 100644 --- a/tests/spdx/parser/tagvalue/test_file_parser.py +++ b/tests/spdx/parser/tagvalue/test_file_parser.py @@ -11,6 +11,7 @@ import pytest from license_expression import get_spdx_licensing +from spdx.parser.error import SPDXParsingError from spdx.parser.tagvalue.parser.tagvalue import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR @@ -49,3 +50,26 @@ def test_file(parser): 'Acknowledgements that might be required to be communicated in some contexts.'] assert spdx_file.license_info_in_file == [get_spdx_licensing().parse("Apache-2.0")] assert spdx_file.license_concluded == get_spdx_licensing().parse("Apache-2.0") + + +def test_invalid_file(parser): + file_str = '\n'.join([ + 'FileName: testfile.java', + 'SPDXID: SPDXRef-File', + 'FileType: SOUCE', + 'FileType: TEXT', + 'FileChecksum: SHA3: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', + 'LicenseConcluded: Apache-2.0', + 'LicenseInfoInFile: Apache-2.0', + 'FileCopyrightText: Copyright 2014 Acme Inc.', + 'FileComment: Very long file', + 'FileAttributionText: Acknowledgements that might be required to be communicated in some contexts.' + ]) + + with pytest.raises(SPDXParsingError) as err: + parser.parse(file_str) + + assert err.value.get_messages() == ['Error while parsing FileType: Token did not match specified grammar rule. ' + 'Line: 3', + 'Error while parsing Checksum in file: Token did not match specified grammar ' + 'rule. Line: 5'] From 0efa7b2e7cebb5a1c2579f23513ec570b3d2b81f Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 28 Feb 2023 12:20:00 +0100 Subject: [PATCH 302/362] [issue-382] refactor relationship_parser Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 22 ++++++------ .../tagvalue/test_relationship_parser.py | 34 +++++++++++++------ 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 248fb5ed8..579a16d81 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -65,7 +65,7 @@ def p_start_2(self, p): # attributes for annotation "| annotator\n| annotation_date\n| annotation_comment\n| annotation_type\n| annotation_spdx_id\n" # attributes for relationship - "| relationship\n| relationship_comment\n" + "| relationship\n" # attributes for snippet "| snip_spdx_id\n| snip_name\n| snip_comment\n| snippet_attribution_text\n| snip_cr_text\n" "| snip_lic_comment\n| snip_file_spdx_id\n| snip_lics_conc\n| snip_lics_info\n| snip_byte_range\n" @@ -847,22 +847,29 @@ def p_annotation_spdx_id_2(self, p): f"Line: {p.lineno(1)}") # parsing methods for relationship - @grammar_rule("relationship : RELATIONSHIP relationship_value") + @grammar_rule("relationship : RELATIONSHIP relationship_value RELATIONSHIP_COMMENT text_or_line\n " + "| RELATIONSHIP relationship_value") def p_relationship_1(self, p): + self.construct_current_element() try: spdx_element_id, relationship_type, related_spdx_element_id = p[2].split(" ") except ValueError: self.logger.append(f"Relationship couldn't be split in spdx_element_id, relationship_type and " f"related_spdx_element. Line: {p.lineno(1)}") return - self.construct_current_element() self.current_element["class"] = Relationship try: self.current_element["relationship_type"] = RelationshipType[relationship_type] except KeyError: self.logger.append(f"Invalid RelationshipType {relationship_type}. Line: {p.lineno(1)}") + if related_spdx_element_id == "NONE": + related_spdx_element_id = SpdxNone() + if related_spdx_element_id == "NOASSERTION": + related_spdx_element_id = SpdxNoAssertion() self.current_element["related_spdx_element_id"] = related_spdx_element_id self.current_element["spdx_element_id"] = spdx_element_id + if len(p) == 5: + self.current_element["comment"] = p[4] @grammar_rule("relationship : RELATIONSHIP error") def p_relationship_2(self, p): @@ -879,15 +886,6 @@ def p_relationship_value_without_doc_ref(self, p): p[0] = p[1] - @grammar_rule("relationship_comment : RELATIONSHIP_COMMENT text_or_line") - def p_relationship_comment_1(self, p): - self.current_element["comment"] = p[2] - - @grammar_rule("relationship_comment : RELATIONSHIP_COMMENT error") - def p_relationship_comment_2(self, p): - self.logger.append( - f"Error while parsing RelationshipComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") - def p_error(self, p): pass diff --git a/tests/spdx/parser/tagvalue/test_relationship_parser.py b/tests/spdx/parser/tagvalue/test_relationship_parser.py index ba18ea88c..12fc37ecd 100644 --- a/tests/spdx/parser/tagvalue/test_relationship_parser.py +++ b/tests/spdx/parser/tagvalue/test_relationship_parser.py @@ -10,7 +10,9 @@ # limitations under the License. import pytest -from spdx.model.relationship import RelationshipType +from spdx.model.relationship import RelationshipType, Relationship +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone from spdx.parser.error import SPDXParsingError from spdx.parser.tagvalue.parser.tagvalue import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR @@ -23,23 +25,33 @@ def parser(): return spdx_parser -def test_relationship(parser): - relationship_str = '\n'.join([ - 'Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File', - 'RelationshipComment: This is a comment.']) - +@pytest.mark.parametrize("relationship_str, expected_relationship", + [('\n'.join(['Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File', + 'RelationshipComment: This is a comment.']), + Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, + "SPDXRef-File", "This is a comment.")), + ('Relationship: SPDXRef-DOCUMENT PATCH_FOR NOASSERTION', + Relationship("SPDXRef-DOCUMENT", RelationshipType.PATCH_FOR, + SpdxNoAssertion())), + ('Relationship: SPDXRef-CarolCompression DEPENDS_ON NONE', + Relationship("SPDXRef-CarolCompression", RelationshipType.DEPENDS_ON, SpdxNone())), + ('Relationship: DocumentRef-ExternalDocument: SPDXRef-Test DEPENDS_ON DocumentRef:AnotherRef', + Relationship("DocumentRef-ExternalDocument:SPDXRef-Test", RelationshipType.DEPENDS_ON, + "DocumentRef:AnotherRef")) + ]) +def test_relationship(parser, relationship_str, expected_relationship): document = parser.parse("\n".join([DOCUMENT_STR, relationship_str])) assert document is not None relationship = document.relationships[0] - assert relationship.relationship_type == RelationshipType.DESCRIBES - assert relationship.related_spdx_element_id == "SPDXRef-File" - assert relationship.spdx_element_id == "SPDXRef-DOCUMENT" - assert relationship.comment == "This is a comment." + assert relationship == expected_relationship @pytest.mark.parametrize("relationship_str, expected_message", [("Relationship: spdx_id DESCRIBES", "Relationship couldn't be split"), - ("Relationship: spdx_id IS spdx_id", "Invalid RelationshipType IS. Line: 1")]) + ("Relationship: spdx_id IS spdx_id", "Invalid RelationshipType IS. Line: 1"), + ("Relationship: spdx_id IS spdx_id\nRelationshipComment: SOURCE", + "Error while parsing Relationship: Token did not match specified grammar rule. Line: 1") + ]) def test_falsy_relationship(parser, relationship_str, expected_message): with pytest.raises(SPDXParsingError, match=expected_message): parser.parse(relationship_str) From 6312af631fcc23e376e7cc05a6b40959f1414a14 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 28 Feb 2023 14:32:53 +0100 Subject: [PATCH 303/362] [issue-382] use individual logger for current_element Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 220 ++++++++++-------- .../spdx/parser/tagvalue/test_file_parser.py | 7 +- 2 files changed, 128 insertions(+), 99 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 579a16d81..30b4ac368 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -45,8 +45,8 @@ def __init__(self): self.tokens = SPDXLexer.tokens self.logger = Logger() self.element_stack = [] - self.current_element = dict() - self.creation_info = dict() + self.current_element = {"logger": Logger()} + self.creation_info = {"logger": Logger()} self.elements_build = dict() @grammar_rule("start : start attrib ") @@ -144,7 +144,7 @@ def p_lics_list_ver_1(self, p): @grammar_rule("lics_list_ver : LIC_LIST_VER error") def p_lics_list_ver_2(self, p): - self.logger.append( + self.creation_info["logger"].append( f"Error while parsing LicenseListVersion: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("doc_comment : DOC_COMMENT text_or_line") @@ -153,7 +153,7 @@ def p_doc_comment_1(self, p): @grammar_rule("doc_comment : DOC_COMMENT error") def p_doc_comment_2(self, p): - self.logger.append( + self.creation_info["logger"].append( f"Error while parsing DocumentComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("doc_namespace : DOC_NAMESPACE LINE") @@ -162,7 +162,7 @@ def p_doc_namespace_1(self, p): @grammar_rule("doc_namespace : DOC_NAMESPACE error") def p_doc_namespace_2(self, p): - self.logger.append( + self.creation_info["logger"].append( f"Error while parsing DocumentNamespace: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("data_lics : DOC_LICENSE LINE") @@ -171,7 +171,7 @@ def p_data_license_1(self, p): @grammar_rule("data_lics : DOC_LICENSE error") def p_data_license_2(self, p): - self.logger.append( + self.creation_info["logger"].append( f"Error while parsing DataLicense: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("doc_name : DOC_NAME LINE") @@ -180,7 +180,7 @@ def p_doc_name_1(self, p): @grammar_rule("doc_name : DOC_NAME error") def p_doc_name_2(self, p): - self.logger.append( + self.creation_info["logger"].append( f"Error while parsing DocumentName: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("ext_doc_ref : EXT_DOC_REF DOC_REF_ID DOC_URI EXT_DOC_REF_CHECKSUM") @@ -196,7 +196,7 @@ def p_ext_doc_refs_1(self, p): @grammar_rule("ext_doc_ref : EXT_DOC_REF error") def p_ext_doc_refs_2(self, p): - self.logger.append( + self.creation_info["logger"].append( f"Error while parsing ExternalDocumentRef: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("spdx_version : DOC_VERSION LINE") @@ -205,7 +205,7 @@ def p_spdx_version_1(self, p): @grammar_rule("spdx_version : DOC_VERSION error") def p_spdx_version_2(self, p): - self.logger.append( + self.creation_info["logger"].append( f"Error while parsing SPDXVersion: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("creator_comment : CREATOR_COMMENT text_or_line") @@ -214,7 +214,7 @@ def p_creator_comment_1(self, p): @grammar_rule("creator_comment : CREATOR_COMMENT error") def p_creator_comment_2(self, p): - self.logger.append( + self.creation_info["logger"].append( f"Error while parsing CreatorComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") def p_creator_1(self, p): @@ -223,7 +223,7 @@ def p_creator_1(self, p): @grammar_rule("creator : CREATOR error") def p_creator_2(self, p): - self.logger.append( + self.creation_info["logger"].append( f"Error while parsing Creator: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("created : CREATED DATE") @@ -232,7 +232,7 @@ def p_created_1(self, p): @grammar_rule("created : CREATED error") def p_created_2(self, p): - self.logger.append( + self.creation_info["logger"].append( f"Error while parsing Created: Token did not match specified grammar rule. Line: {p.lineno(1)}") # parsing methods for extracted licensing info @@ -245,7 +245,7 @@ def p_extr_lic_id_1(self, p): @grammar_rule("extr_lic_id : LICS_ID error") def p_extr_lic_id_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing LicenseID: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("lic_xref : LICS_CRS_REF LINE") @@ -255,8 +255,9 @@ def p_lic_xref_1(self, p): @grammar_rule("lic_xref : LICS_CRS_REF error") def p_lic_xref_2(self, p): - self.logger.append(f"Error while parsing LicenseCrossReference: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing LicenseCrossReference: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") @grammar_rule("lic_comment : LICS_COMMENT text_or_line") def p_lic_comment_1(self, p): @@ -264,7 +265,7 @@ def p_lic_comment_1(self, p): @grammar_rule("lic_comment : LICS_COMMENT error") def p_lic_comment_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing LicenseComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("extr_lic_name : LICS_NAME line_or_no_assertion") @@ -273,7 +274,7 @@ def p_extr_lic_name_1(self, p): @grammar_rule("extr_lic_name : LICS_NAME error") def p_extr_lic_name_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing LicenseName: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("extr_lic_text : LICS_TEXT text_or_line") @@ -282,7 +283,7 @@ def p_extr_lic_text_1(self, p): @grammar_rule("extr_lic_text : LICS_TEXT error") def p_extr_lic_text_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing ExtractedText: Token did not match specified grammar rule. Line: {p.lineno(1)}") # parsing methods for file @@ -291,14 +292,13 @@ def p_extr_lic_text_2(self, p): def p_file_name_1(self, p): self.construct_current_element() self.element_stack.append(p[2]) - self.current_element = dict() self.current_element["name"] = p[2] self.current_element["class"] = File @grammar_rule("file_name : FILE_NAME error") def p_file_name_2(self, p): - self.logger.append( - f"Error while parsing FileName: Token did not match specified grammar rule. Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("file_contrib : FILE_CONTRIB LINE") def p_file_contrib_1(self, p): @@ -306,7 +306,7 @@ def p_file_contrib_1(self, p): @grammar_rule("file_contrib : FILE_CONTRIB error") def p_file_contrib_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing FileContributor: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("file_notice : FILE_NOTICE text_or_line") @@ -315,7 +315,7 @@ def p_file_notice_1(self, p): @grammar_rule("file_notice : FILE_NOTICE error") def p_file_notice_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing FileNotice: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("file_cr_text : FILE_CR_TEXT line_or_no_assertion_or_none") @@ -325,7 +325,7 @@ def p_file_cr_text_1(self, p): @grammar_rule("file_cr_text : FILE_CR_TEXT error") def p_file_cr_text_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing FileCopyrightText: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("file_lics_comment : FILE_LICS_COMMENT text_or_line") @@ -334,8 +334,9 @@ def p_file_lics_comment_1(self, p): @grammar_rule("file_lics_comment : FILE_LICS_COMMENT error") def p_file_lics_comment_2(self, p): - self.logger.append(f"Error while parsing LicenseComments in file: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing LicenseComments in file: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") @grammar_rule("file_attribution_text : FILE_ATTRIBUTION_TEXT text_or_line") def p_file_attribution_text_1(self, p): @@ -343,7 +344,7 @@ def p_file_attribution_text_1(self, p): @grammar_rule("file_attribution_text : FILE_ATTRIBUTION_TEXT error") def p_file_attribution_text_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing FileAttributionText: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("file_lics_info : FILE_LICS_INFO license_or_no_assertion_or_none") @@ -355,7 +356,7 @@ def p_file_lics_info_1(self, p): @grammar_rule("file_lics_info : FILE_LICS_INFO error") def p_file_lics_info_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing LicenseInfoInFile: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("file_comment : FILE_COMMENT text_or_line") @@ -364,7 +365,7 @@ def p_file_comment_1(self, p): @grammar_rule("file_comment : FILE_COMMENT error") def p_file_comment_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing FileComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("file_type : FILE_TYPE file_type_value") @@ -373,7 +374,7 @@ def p_file_type_1(self, p): @grammar_rule("file_type : FILE_TYPE error") def p_file_type_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing FileType: Token did not match any of the valid values. Line: {p.lineno(1)}") @grammar_rule("file_checksum : FILE_CHECKSUM CHECKSUM") @@ -385,7 +386,7 @@ def p_file_checksum_1(self, p): @grammar_rule("file_checksum : FILE_CHECKSUM error") def p_file_checksum_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing Checksum in file: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("file_conc : FILE_LICS_CONC license_or_no_assertion_or_none") @@ -394,8 +395,9 @@ def p_file_conc_1(self, p): @grammar_rule("file_conc : FILE_LICS_CONC error") def p_file_conc_2(self, p): - self.logger.append(f"Error while parsing LicenseConcluded in file: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing LicenseConcluded in file: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") @grammar_rule( "file_type_value : SOURCE\n| BINARY\n| ARCHIVE\n | APPLICATION\n | AUDIO\n | IMAGE\n | FILETYPE_TEXT\n| VIDEO\n" @@ -409,13 +411,14 @@ def p_file_type_value(self, p): @grammar_rule("package_name : PKG_NAME LINE") def p_package_name(self, p): self.construct_current_element() + self.element_stack.push("package") self.current_element["class"] = Package self.current_element["name"] = p[2] @grammar_rule("package_name : PKG_NAME error") def p_package_name_1(self, p): - self.logger.append( - f"Error while parsing PackageName: Token did not match specified grammar rule. Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("pkg_desc : PKG_DESC text_or_line") def p_pkg_desc_1(self, p): @@ -423,7 +426,7 @@ def p_pkg_desc_1(self, p): @grammar_rule("pkg_desc : PKG_DESC error") def p_pkg_desc_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing PackageDescription: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("pkg_comment : PKG_COMMENT text_or_line") @@ -432,7 +435,7 @@ def p_pkg_comment_1(self, p): @grammar_rule("pkg_comment : PKG_COMMENT error") def p_pkg_comment_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing PackageComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("pkg_attribution_text : PKG_ATTRIBUTION_TEXT text_or_line") @@ -441,8 +444,9 @@ def p_pkg_attribution_text_1(self, p): @grammar_rule("pkg_attribution_text : PKG_ATTRIBUTION_TEXT error") def p_pkg_attribution_text_2(self, p): - self.logger.append(f"Error while parsing PackageAttributionText: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing PackageAttributionText: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") @grammar_rule("pkg_summary : PKG_SUM text_or_line") def p_pkg_summary_1(self, p): @@ -450,7 +454,7 @@ def p_pkg_summary_1(self, p): @grammar_rule("pkg_summary : PKG_SUM error") def p_pkg_summary_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing PackageSummary: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("pkg_cr_text : PKG_CPY_TEXT line_or_no_assertion_or_none") @@ -459,8 +463,9 @@ def p_pkg_cr_text_1(self, p): @grammar_rule("pkg_cr_text : PKG_CPY_TEXT error") def p_pkg_cr_text_2(self, p): - self.logger.append(f"Error while parsing PackageCopyrightText: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing PackageCopyrightText: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") @grammar_rule("pkg_ext_ref : PKG_EXT_REF LINE PKG_EXT_REF_COMMENT text_or_line\n | PKG_EXT_REF LINE") def p_pkg_ext_refs_1(self, p): @@ -474,7 +479,7 @@ def p_pkg_ext_refs_1(self, p): @grammar_rule("pkg_ext_ref : PKG_EXT_REF error") def p_pkg_ext_refs_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing ExternalRef in package: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @@ -484,8 +489,9 @@ def p_pkg_lic_comment_1(self, p): @grammar_rule("pkg_lic_comment : PKG_LICS_COMMENT error") def p_pkg_lic_comment_2(self, p): - self.logger.append(f"Error while parsing PackageLicenseComments: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing PackageLicenseComments: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") @grammar_rule("pkg_lic_decl : PKG_LICS_DECL license_or_no_assertion_or_none") def p_pkg_lic_decl_1(self, p): @@ -493,7 +499,7 @@ def p_pkg_lic_decl_1(self, p): @grammar_rule("pkg_lic_decl : PKG_LICS_DECL error") def p_pkg_lic_decl_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing LicenseDeclared in package: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @@ -506,7 +512,7 @@ def p_pkg_lic_ff_1(self, p): @grammar_rule("pkg_lic_ff : PKG_LICS_FFILE error") def p_pkg_lic_ff_error(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing LicenseInfoFromFiles in package: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @@ -516,7 +522,7 @@ def p_pkg_lic_conc_1(self, p): @grammar_rule("pkg_lic_conc : PKG_LICS_CONC error") def p_pkg_lic_conc_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing LicenseConcluded in package: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @@ -526,7 +532,7 @@ def p_pkg_src_info_1(self, p): @grammar_rule("pkg_src_info : PKG_SRC_INFO error") def p_pkg_src_info_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing PackageSourceInfo: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("pkg_checksum : PKG_CHECKSUM CHECKSUM") @@ -539,7 +545,7 @@ def p_pkg_checksum_1(self, p): @grammar_rule("pkg_checksum : PKG_CHECKSUM error") def p_pkg_checksum_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing PackageChecksum: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("pkg_verif : PKG_VERF_CODE LINE") @@ -556,8 +562,9 @@ def p_pkg_verif_1(self, p): @grammar_rule("pkg_verif : PKG_VERF_CODE error") def p_pkg_verif_2(self, p): - self.logger.append(f"Error while parsing PackageVerificationCode: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing PackageVerificationCode: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") @grammar_rule("pkg_home : PKG_HOME line_or_no_assertion_or_none") def p_pkg_home_1(self, p): @@ -565,7 +572,7 @@ def p_pkg_home_1(self, p): @grammar_rule("pkg_home : PKG_HOME error") def p_pkg_home_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing PackageHomePage: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("pkg_down_location : PKG_DOWN line_or_no_assertion_or_none") @@ -574,8 +581,9 @@ def p_pkg_down_location_1(self, p): @grammar_rule("pkg_down_location : PKG_DOWN error") def p_pkg_down_location_2(self, p): - self.logger.append(f"Error while parsing PackageDownloadLocation: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing PackageDownloadLocation: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") @grammar_rule("pkg_files_analyzed : PKG_FILES_ANALYZED LINE") def p_pkg_files_analyzed_1(self, p): @@ -586,8 +594,9 @@ def p_pkg_files_analyzed_1(self, p): @grammar_rule("pkg_files_analyzed : PKG_FILES_ANALYZED error") def p_pkg_files_analyzed_2(self, p): - self.logger.append(f"Error while parsing FilesAnalyzed in package: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing FilesAnalyzed in package: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") @grammar_rule("pkg_orig : PKG_ORIG pkg_supplier_values") def p_pkg_orig_1(self, p): @@ -595,7 +604,7 @@ def p_pkg_orig_1(self, p): @grammar_rule("pkg_orig : PKG_ORIG error") def p_pkg_orig_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing PackageOriginator: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("pkg_supplier : PKG_SUPPL pkg_supplier_values") @@ -604,7 +613,7 @@ def p_pkg_supplier_1(self, p): @grammar_rule("pkg_supplier : PKG_SUPPL error") def p_pkg_supplier_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing PackageSupplier: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("pkg_supplier_values : NO_ASSERTION") @@ -621,7 +630,7 @@ def p_pkg_file_name(self, p): @grammar_rule("pkg_file_name : PKG_FILE_NAME error") def p_pkg_file_name_1(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing PackageFileName: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("package_version : PKG_VERSION LINE") @@ -630,7 +639,7 @@ def p_package_version_1(self, p): @grammar_rule("package_version : PKG_VERSION error") def p_package_version_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing PackageVersion: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("primary_package_purpose : PRIMARY_PACKAGE_PURPOSE primary_package_purpose_value") @@ -640,8 +649,9 @@ def p_primary_package_purpose_1(self, p): @grammar_rule("primary_package_purpose : PRIMARY_PACKAGE_PURPOSE error") def p_primary_package_purpose_2(self, p): - self.logger.append(f"Error while parsing PrimaryPackagePurpose: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing PrimaryPackagePurpose: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") @grammar_rule("primary_package_purpose_value : APPLICATION\n | FRAMEWORK\n | LIBRARY\n | CONTAINER\n " "| OPERATING_SYSTEM \n | DEVICE \n| FIRMWARE\n | SOURCE\n | ARCHIVE\n | FILE\n | INSTALL\n | OTHER") @@ -654,7 +664,7 @@ def p_built_date_1(self, p): @grammar_rule("built_date : BUILT_DATE error") def p_built_date_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing BuiltDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("release_date : RELEASE_DATE DATE") @@ -663,7 +673,7 @@ def p_release_date_1(self, p): @grammar_rule("release_date : RELEASE_DATE error") def p_release_date_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing ReleaseDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("valid_until_date : VALID_UNTIL_DATE DATE") @@ -672,7 +682,7 @@ def p_valid_until_date_1(self, p): @grammar_rule("valid_until_date : VALID_UNTIL_DATE error") def p_valid_until_date_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing ValidUntilDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") # parsing methods for snippet @@ -684,7 +694,7 @@ def p_snip_spdx_id(self, p): @grammar_rule("snip_spdx_id : SNIPPET_SPDX_ID error") def p_snip_spdx_id_1(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing SnippetSPDXID: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("snip_name : SNIPPET_NAME LINE") @@ -693,7 +703,7 @@ def p_snippet_name(self, p): @grammar_rule("snip_name : SNIPPET_NAME error") def p_snippet_name_1(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing SnippetName: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("snip_comment : SNIPPET_COMMENT text_or_line") @@ -702,7 +712,7 @@ def p_snippet_comment(self, p): @grammar_rule("snip_comment : SNIPPET_COMMENT error") def p_snippet_comment_1(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing SnippetComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT text_or_line") @@ -711,8 +721,9 @@ def p_snippet_attribution_text_1(self, p): @grammar_rule("snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT error") def p_snippet_attribution_text_2(self, p): - self.logger.append(f"Error while parsing SnippetAttributionText: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing SnippetAttributionText: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") @grammar_rule("snip_cr_text : SNIPPET_CR_TEXT line_or_no_assertion_or_none") def p_snippet_cr_text(self, p): @@ -720,8 +731,9 @@ def p_snippet_cr_text(self, p): @grammar_rule("snip_cr_text : SNIPPET_CR_TEXT error") def p_snippet_cr_text_1(self, p): - self.logger.append(f"Error while parsing SnippetCopyrightText: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing SnippetCopyrightText: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") @grammar_rule("snip_lic_comment : SNIPPET_LICS_COMMENT text_or_line") def p_snippet_lic_comment(self, p): @@ -729,8 +741,9 @@ def p_snippet_lic_comment(self, p): @grammar_rule("snip_lic_comment : SNIPPET_LICS_COMMENT error") def p_snippet_lic_comment_1(self, p): - self.logger.append(f"Error while parsing SnippetLicenseComments: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing SnippetLicenseComments: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") @grammar_rule("snip_file_spdx_id : SNIPPET_FILE_SPDXID LINE") def p_snip_from_file_spdxid(self, p): @@ -738,8 +751,9 @@ def p_snip_from_file_spdxid(self, p): @grammar_rule("snip_file_spdx_id : SNIPPET_FILE_SPDXID error") def p_snip_from_file_spdxid_1(self, p): - self.logger.append(f"Error while parsing SnippetFromFileSPDXID: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing SnippetFromFileSPDXID: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") @grammar_rule("snip_lics_conc : SNIPPET_LICS_CONC license_or_no_assertion_or_none") def p_snippet_concluded_license(self, p): @@ -747,8 +761,9 @@ def p_snippet_concluded_license(self, p): @grammar_rule("snip_lics_conc : SNIPPET_LICS_CONC error") def p_snippet_concluded_license_1(self, p): - self.logger.append(f"Error while parsing SnippetLicenseConcluded: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing SnippetLicenseConcluded: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") @grammar_rule("snip_lics_info : SNIPPET_LICS_INFO license_or_no_assertion_or_none") def p_snippet_lics_info(self, p): @@ -760,8 +775,9 @@ def p_snippet_lics_info(self, p): @grammar_rule("snip_lics_info : SNIPPET_LICS_INFO error") def p_snippet_lics_info_1(self, p): - self.logger.append(f"Error while parsing LicenseInfoInSnippet: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing LicenseInfoInSnippet: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") @grammar_rule("snip_byte_range : SNIPPET_BYTE_RANGE LINE") def p_snippet_byte_range(self, p): @@ -776,7 +792,7 @@ def p_snippet_byte_range(self, p): @grammar_rule("snip_byte_range : SNIPPET_BYTE_RANGE error") def p_snippet_byte_range_1(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing SnippetByteRange: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("snip_line_range : SNIPPET_LINE_RANGE LINE") @@ -791,7 +807,7 @@ def p_snippet_line_range(self, p): @grammar_rule("snip_line_range : SNIPPET_LINE_RANGE error") def p_snippet_line_range_1(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing SnippetLineRange: Token did not match specified grammar rule. Line: {p.lineno(1)}") # parsing methods for annotation @@ -803,8 +819,8 @@ def p_annotator_1(self, p): @grammar_rule("annotator : ANNOTATOR error") def p_annotator_2(self, p): - self.logger.append( - f"Error while parsing Annotator: Token did not match specified grammar rule. Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("annotation_date : ANNOTATION_DATE DATE") def p_annotation_date_1(self, p): @@ -812,7 +828,7 @@ def p_annotation_date_1(self, p): @grammar_rule("annotation_date : ANNOTATION_DATE error") def p_annotation_date_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing AnnotationDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("annotation_comment : ANNOTATION_COMMENT text_or_line") @@ -821,7 +837,7 @@ def p_annotation_comment_1(self, p): @grammar_rule("annotation_comment : ANNOTATION_COMMENT error") def p_annotation_comment_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing AnnotationComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("annotation_type : ANNOTATION_TYPE annotation_type_value") @@ -830,7 +846,7 @@ def p_annotation_type_1(self, p): @grammar_rule("annotation_type : ANNOTATION_TYPE error") def p_annotation_type_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing AnnotationType: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("annotation_type_value : OTHER\n| REVIEW") @@ -843,8 +859,9 @@ def p_annotation_spdx_id_1(self, p): @grammar_rule("annotation_spdx_id : ANNOTATION_SPDX_ID error") def p_annotation_spdx_id_2(self, p): - self.logger.append(f"Error while parsing SPDXREF in annotation: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Error while parsing SPDXREF in annotation: Token did not match specified grammar rule. " + f"Line: {p.lineno(1)}") # parsing methods for relationship @grammar_rule("relationship : RELATIONSHIP relationship_value RELATIONSHIP_COMMENT text_or_line\n " @@ -854,14 +871,15 @@ def p_relationship_1(self, p): try: spdx_element_id, relationship_type, related_spdx_element_id = p[2].split(" ") except ValueError: - self.logger.append(f"Relationship couldn't be split in spdx_element_id, relationship_type and " - f"related_spdx_element. Line: {p.lineno(1)}") + self.current_element["logger"].append( + f"Relationship couldn't be split in spdx_element_id, relationship_type and " + f"related_spdx_element. Line: {p.lineno(1)}") return self.current_element["class"] = Relationship try: self.current_element["relationship_type"] = RelationshipType[relationship_type] except KeyError: - self.logger.append(f"Invalid RelationshipType {relationship_type}. Line: {p.lineno(1)}") + self.current_element["logger"].append(f"Invalid RelationshipType {relationship_type}. Line: {p.lineno(1)}") if related_spdx_element_id == "NONE": related_spdx_element_id = SpdxNone() if related_spdx_element_id == "NOASSERTION": @@ -873,7 +891,7 @@ def p_relationship_1(self, p): @grammar_rule("relationship : RELATIONSHIP error") def p_relationship_2(self, p): - self.logger.append( + self.current_element["logger"].append( f"Error while parsing Relationship: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("relationship_value : DOC_REF_ID LINE") @@ -897,6 +915,10 @@ def build(self, **kwargs): def parse(self, text): self.yacc.parse(text, lexer=self.lex) self.construct_current_element() + try: + raise_parsing_error_if_logger_has_messages(self.creation_info.pop("logger"), "CreationInfo") + except SPDXParsingError as err: + self.logger.append(err.get_messages()) raise_parsing_error_if_logger_has_messages(self.logger) creation_info = construct_or_raise_parsing_error(CreationInfo, self.creation_info) self.elements_build["creation_info"] = creation_info @@ -905,14 +927,21 @@ def parse(self, text): def construct_current_element(self): if "class" not in self.current_element: + self.current_element = {"logger": Logger()} return class_name = self.current_element.pop("class") + try: + raise_parsing_error_if_logger_has_messages(self.current_element.pop("logger"), class_name.__name__) + except SPDXParsingError as err: + self.logger.append(err.get_messages()) + self.current_element = {"logger": Logger()} + return try: self.elements_build.setdefault(CLASS_MAPPING[class_name.__name__], []).append( construct_or_raise_parsing_error(class_name, self.current_element)) except SPDXParsingError as err: self.logger.append(err.get_messages()) - self.current_element = dict() + self.current_element = {"logger": Logger()} def check_that_current_element_matches_class_for_value(self, expected_class): if expected_class != self.current_element["class"]: @@ -922,3 +951,4 @@ def check_that_current_element_matches_class_for_value(self, expected_class): CLASS_MAPPING = dict(File="files", Annotation="annotations", Relationship="relationships", Snippet="snippets", Package="packages", ExtractedLicensingInfo="extracted_licensing_info") + diff --git a/tests/spdx/parser/tagvalue/test_file_parser.py b/tests/spdx/parser/tagvalue/test_file_parser.py index 90eb1e6a8..15ca0fa59 100644 --- a/tests/spdx/parser/tagvalue/test_file_parser.py +++ b/tests/spdx/parser/tagvalue/test_file_parser.py @@ -69,7 +69,6 @@ def test_invalid_file(parser): with pytest.raises(SPDXParsingError) as err: parser.parse(file_str) - assert err.value.get_messages() == ['Error while parsing FileType: Token did not match specified grammar rule. ' - 'Line: 3', - 'Error while parsing Checksum in file: Token did not match specified grammar ' - 'rule. Line: 5'] + assert err.value.get_messages() == [["Error while parsing File: ['Error while parsing FileType: Token did not " + "match any of the valid values. Line: 3', 'Error while parsing Checksum in " + "file: Token did not match specified grammar rule. Line: 5']"]] From e913a985aa137f812137fb027c576c96d3adbc6f Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 28 Feb 2023 15:09:16 +0100 Subject: [PATCH 304/362] [issue-382] add helper method to start a new current element Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 41 +++++++++++-------- .../tagvalue/test_relationship_parser.py | 14 +++++-- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 30b4ac368..1f6bc1566 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -11,6 +11,7 @@ # limitations under the License. import re +from typing import Any from license_expression import get_spdx_licensing from ply import yacc @@ -239,12 +240,12 @@ def p_created_2(self, p): @grammar_rule("extr_lic_id : LICS_ID LINE") def p_extr_lic_id_1(self, p): - self.construct_current_element() - self.current_element["class"] = ExtractedLicensingInfo + self.initialize_new_current_element(ExtractedLicensingInfo) self.current_element["license_id"] = p[2] @grammar_rule("extr_lic_id : LICS_ID error") def p_extr_lic_id_2(self, p): + self.initialize_new_current_element(ExtractedLicensingInfo) self.current_element["logger"].append( f"Error while parsing LicenseID: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -290,13 +291,12 @@ def p_extr_lic_text_2(self, p): @grammar_rule("file_name : FILE_NAME LINE") def p_file_name_1(self, p): - self.construct_current_element() - self.element_stack.append(p[2]) + self.initialize_new_current_element(File) self.current_element["name"] = p[2] - self.current_element["class"] = File @grammar_rule("file_name : FILE_NAME error") def p_file_name_2(self, p): + self.initialize_new_current_element(File) self.current_element["logger"].append( f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -320,7 +320,6 @@ def p_file_notice_2(self, p): @grammar_rule("file_cr_text : FILE_CR_TEXT line_or_no_assertion_or_none") def p_file_cr_text_1(self, p): - self.current_element["copyright_text"] = p[2] @grammar_rule("file_cr_text : FILE_CR_TEXT error") @@ -410,13 +409,14 @@ def p_file_type_value(self, p): @grammar_rule("package_name : PKG_NAME LINE") def p_package_name(self, p): - self.construct_current_element() - self.element_stack.push("package") - self.current_element["class"] = Package + self.initialize_new_current_element(Package) self.current_element["name"] = p[2] @grammar_rule("package_name : PKG_NAME error") def p_package_name_1(self, p): + self.initialize_new_current_element(Package) + self.construct_current_element() + self.current_element["class"] = Package self.current_element["logger"].append( f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -688,12 +688,12 @@ def p_valid_until_date_2(self, p): # parsing methods for snippet @grammar_rule("snip_spdx_id : SNIPPET_SPDX_ID LINE") def p_snip_spdx_id(self, p): - self.construct_current_element() - self.current_element["class"] = Snippet + self.initialize_new_current_element(Snippet) self.current_element["spdx_id"] = p[2] @grammar_rule("snip_spdx_id : SNIPPET_SPDX_ID error") def p_snip_spdx_id_1(self, p): + self.initialize_new_current_element(Snippet) self.current_element["logger"].append( f"Error while parsing SnippetSPDXID: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -813,12 +813,15 @@ def p_snippet_line_range_1(self, p): # parsing methods for annotation def p_annotator_1(self, p): """annotator : ANNOTATOR PERSON_VALUE\n| TOOL_VALUE\n| ORG_VALUE""" - self.construct_current_element() - self.current_element["annotator"] = ActorParser.parse_actor(p[2]) - self.current_element["class"] = Annotation + self.initialize_new_current_element(Annotation) + try: + self.current_element["annotator"] = ActorParser.parse_actor(p[2]) + except SPDXParsingError as err: + self.current_element["logger"].append(err.get_messages()) @grammar_rule("annotator : ANNOTATOR error") def p_annotator_2(self, p): + self.initialize_new_current_element(Annotation) self.current_element["logger"].append( f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -867,7 +870,7 @@ def p_annotation_spdx_id_2(self, p): @grammar_rule("relationship : RELATIONSHIP relationship_value RELATIONSHIP_COMMENT text_or_line\n " "| RELATIONSHIP relationship_value") def p_relationship_1(self, p): - self.construct_current_element() + self.initialize_new_current_element(Relationship) try: spdx_element_id, relationship_type, related_spdx_element_id = p[2].split(" ") except ValueError: @@ -875,7 +878,6 @@ def p_relationship_1(self, p): f"Relationship couldn't be split in spdx_element_id, relationship_type and " f"related_spdx_element. Line: {p.lineno(1)}") return - self.current_element["class"] = Relationship try: self.current_element["relationship_type"] = RelationshipType[relationship_type] except KeyError: @@ -891,6 +893,7 @@ def p_relationship_1(self, p): @grammar_rule("relationship : RELATIONSHIP error") def p_relationship_2(self, p): + self.initialize_new_current_element(Relationship) self.current_element["logger"].append( f"Error while parsing Relationship: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -948,6 +951,12 @@ def check_that_current_element_matches_class_for_value(self, expected_class): raise SPDXParsingError(["Unexpected current element for value"]) # what to do now? exit parsing + def initialize_new_current_element(self, class_name: Any): + if "class" in self.current_element and "spdx_id" in self.current_element: + self.element_stack.append({self.current_element["class"]: self.current_element["spdx_id"]}) + self.construct_current_element() + self.current_element["class"] = class_name + CLASS_MAPPING = dict(File="files", Annotation="annotations", Relationship="relationships", Snippet="snippets", Package="packages", ExtractedLicensingInfo="extracted_licensing_info") diff --git a/tests/spdx/parser/tagvalue/test_relationship_parser.py b/tests/spdx/parser/tagvalue/test_relationship_parser.py index 12fc37ecd..5247ad4c5 100644 --- a/tests/spdx/parser/tagvalue/test_relationship_parser.py +++ b/tests/spdx/parser/tagvalue/test_relationship_parser.py @@ -47,11 +47,17 @@ def test_relationship(parser, relationship_str, expected_relationship): @pytest.mark.parametrize("relationship_str, expected_message", - [("Relationship: spdx_id DESCRIBES", "Relationship couldn't be split"), - ("Relationship: spdx_id IS spdx_id", "Invalid RelationshipType IS. Line: 1"), + [("Relationship: spdx_id DESCRIBES", + [['Error while parsing Relationship: ["Relationship couldn\'t be split in spdx_element_id, ' + 'relationship_type and related_spdx_element. Line: 1"]']]), + ("Relationship: spdx_id IS spdx_id", + [["Error while parsing Relationship: ['Invalid RelationshipType IS. Line: 1']"]]), ("Relationship: spdx_id IS spdx_id\nRelationshipComment: SOURCE", - "Error while parsing Relationship: Token did not match specified grammar rule. Line: 1") + [["Error while parsing Relationship: ['Error while parsing Relationship: Token " + "did not match specified grammar rule. Line: 1']"]]) ]) def test_falsy_relationship(parser, relationship_str, expected_message): - with pytest.raises(SPDXParsingError, match=expected_message): + with pytest.raises(SPDXParsingError) as err: parser.parse(relationship_str) + + assert err.value.get_messages() == expected_message From c18d45f111dd134b271a059feb0d2fb1517c8d26 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 28 Feb 2023 15:35:47 +0100 Subject: [PATCH 305/362] [issue-382] add test to parse whole document Signed-off-by: Meret Behrens --- .../parser/tagvalue/test_tag_value_parser.py | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tests/spdx/parser/tagvalue/test_tag_value_parser.py b/tests/spdx/parser/tagvalue/test_tag_value_parser.py index b4228ebc7..edd0e8aef 100644 --- a/tests/spdx/parser/tagvalue/test_tag_value_parser.py +++ b/tests/spdx/parser/tagvalue/test_tag_value_parser.py @@ -9,15 +9,11 @@ # 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 datetime import datetime -from unittest import TestCase +import os import pytest -from spdx.model.actor import Actor, ActorType -from spdx.model.checksum import Checksum, ChecksumAlgorithm -from spdx.model.external_document_ref import ExternalDocumentRef -from spdx.model.version import Version +from spdx.model.document import Document from spdx.parser.error import SPDXParsingError from spdx.parser.tagvalue.parser.tagvalue import Parser @@ -34,3 +30,18 @@ def test_unknown_str(parser): with pytest.raises(SPDXParsingError, match="Unknown tag"): parser.parse(unknown_tag_str) + + +def test_parse_file(parser): + fn = os.path.join(os.path.dirname(__file__), "../../data/formats/SPDXTagExample-v2.3.spdx") + + with open(fn) as f: + data = f.read() + doc = parser.parse(data) + assert type(doc) == Document + assert len(doc.annotations) == 5 + assert len(doc.files) == 5 + assert len(doc.packages) == 4 + assert len(doc.snippets) == 1 + assert len(doc.relationships) == 13 + assert len(doc.extracted_licensing_info) == 5 From 817207958e63459b4bd4d63d18e0814f011af01b Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 28 Feb 2023 15:37:31 +0100 Subject: [PATCH 306/362] [issue-382] add function to parse_checksum Signed-off-by: Meret Behrens --- .../parser/tagvalue/parser/helper_methods.py | 22 +++++++++++ src/spdx/parser/tagvalue/parser/tagvalue.py | 37 ++++++++++--------- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/helper_methods.py b/src/spdx/parser/tagvalue/parser/helper_methods.py index 32090810a..63690a829 100644 --- a/src/spdx/parser/tagvalue/parser/helper_methods.py +++ b/src/spdx/parser/tagvalue/parser/helper_methods.py @@ -11,6 +11,11 @@ import re from typing import Optional +from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.parser.error import SPDXParsingError +from spdx.parser.logger import Logger +from spdx.parser.parsing_functions import construct_or_raise_parsing_error + def grammar_rule(doc): # this is a helper method to use decorators for the parsing methods instead of docstrings @@ -29,3 +34,20 @@ def str_from_text(text: Optional[str]) -> Optional[str]: return text else: return None + + +def parse_checksum(logger: Logger, checksum_str: str) -> Optional[Checksum]: + try: + algorithm, value = checksum_str.split(":") + except ValueError: + logger.append( + f"Couldn't split value for checksum in algorithm and value.") + return None + algorithm = ChecksumAlgorithm[algorithm.upper().replace("-", "_")] + value = value.strip() + try: + checksum = construct_or_raise_parsing_error(Checksum, {"algorithm": algorithm, "value": value}) + except SPDXParsingError as err: + logger.append(err.get_messages()) + checksum = None + return checksum diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 1f6bc1566..316d8ba71 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -18,7 +18,6 @@ from spdx.datetime_conversions import datetime_from_str from spdx.model.annotation import AnnotationType, Annotation -from spdx.model.checksum import ChecksumAlgorithm, Checksum from spdx.model.external_document_ref import ExternalDocumentRef from spdx.model.extracted_licensing_info import ExtractedLicensingInfo from spdx.model.package import Package, PackageVerificationCode, PackagePurpose, ExternalPackageRef, \ @@ -36,7 +35,7 @@ from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.logger import Logger from spdx.parser.tagvalue.lexer.tagvalue import SPDXLexer -from spdx.parser.tagvalue.parser.helper_methods import grammar_rule, str_from_text +from spdx.parser.tagvalue.parser.helper_methods import grammar_rule, str_from_text, parse_checksum class Parser(object): @@ -189,10 +188,8 @@ def p_ext_doc_refs_1(self, p): document_ref_id = p[2] document_uri = p[3] - splitted_checksum = p[4].split(":") - algorithm = ChecksumAlgorithm[splitted_checksum[0]] - value = splitted_checksum[1].strip() - external_document_ref = ExternalDocumentRef(document_ref_id, document_uri, Checksum(algorithm, value)) + checksum = parse_checksum(self.current_element["logger"], p[4]) + external_document_ref = ExternalDocumentRef(document_ref_id, document_uri, checksum) self.creation_info.setdefault("external_document_refs", []).append(external_document_ref) @grammar_rule("ext_doc_ref : EXT_DOC_REF error") @@ -378,10 +375,8 @@ def p_file_type_2(self, p): @grammar_rule("file_checksum : FILE_CHECKSUM CHECKSUM") def p_file_checksum_1(self, p): - splitted_checksum = p[2].split(":") - algorithm = ChecksumAlgorithm[splitted_checksum[0]] - value = splitted_checksum[1] - self.current_element.setdefault("checksums", []).append(Checksum(algorithm, value)) + checksum = parse_checksum(self.current_element["logger"], p[2]) + self.current_element.setdefault("checksums", []).append(checksum) @grammar_rule("file_checksum : FILE_CHECKSUM error") def p_file_checksum_2(self, p): @@ -473,8 +468,20 @@ def p_pkg_ext_refs_1(self, p): comment = None if len(p) == 5: comment = p[4] - external_package_ref = ExternalPackageRef(ExternalPackageRefCategory[category], reference_type, locator, - comment) + try: + category = ExternalPackageRefCategory[category.replace("-", "_")] + except KeyError: + self.current_element["logger"].append(f"Invalid ExternalPackageRefCategory: {category}") + return + try: + external_package_ref = construct_or_raise_parsing_error(ExternalPackageRef, + {"category": category, + "reference_type": reference_type, + "locator": locator, + "comment": comment}) + except SPDXParsingError as err: + self.current_element["logger"].append(err.get_messages()) + return self.current_element.setdefault("external_references", []).append(external_package_ref) @grammar_rule("pkg_ext_ref : PKG_EXT_REF error") @@ -537,10 +544,7 @@ def p_pkg_src_info_2(self, p): @grammar_rule("pkg_checksum : PKG_CHECKSUM CHECKSUM") def p_pkg_checksum_1(self, p): - split_checksum = p[2].split(":") - algorithm = ChecksumAlgorithm[split_checksum[0]] - value = split_checksum[1].strip() - checksum = Checksum(algorithm, value) + checksum = parse_checksum(self.current_element["logger"], p[2]) self.current_element.setdefault("checksums", []).append(checksum) @grammar_rule("pkg_checksum : PKG_CHECKSUM error") @@ -960,4 +964,3 @@ def initialize_new_current_element(self, class_name: Any): CLASS_MAPPING = dict(File="files", Annotation="annotations", Relationship="relationships", Snippet="snippets", Package="packages", ExtractedLicensingInfo="extracted_licensing_info") - From 89a0f82909813e0875d57204f9efd22c72546d60 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 28 Feb 2023 15:59:49 +0100 Subject: [PATCH 307/362] [issue-382] add error handling and tests for creation_info_parser Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 7 +++-- .../tagvalue/test_creation_info_parser.py | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 316d8ba71..4ce6b8e03 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -140,8 +140,10 @@ def p_spdx_id(self, p): @grammar_rule("lics_list_ver : LIC_LIST_VER LINE") def p_lics_list_ver_1(self, p): - self.creation_info["license_list_version"] = Version.from_string(p[2]) - + try: + self.creation_info["license_list_version"] = Version.from_string(p[2]) + except ValueError as err: + self.creation_info["logger"].append(err.args[0]) @grammar_rule("lics_list_ver : LIC_LIST_VER error") def p_lics_list_ver_2(self, p): self.creation_info["logger"].append( @@ -185,7 +187,6 @@ def p_doc_name_2(self, p): @grammar_rule("ext_doc_ref : EXT_DOC_REF DOC_REF_ID DOC_URI EXT_DOC_REF_CHECKSUM") def p_ext_doc_refs_1(self, p): - document_ref_id = p[2] document_uri = p[3] checksum = parse_checksum(self.current_element["logger"], p[4]) diff --git a/tests/spdx/parser/tagvalue/test_creation_info_parser.py b/tests/spdx/parser/tagvalue/test_creation_info_parser.py index 3aca7aca9..bd430f020 100644 --- a/tests/spdx/parser/tagvalue/test_creation_info_parser.py +++ b/tests/spdx/parser/tagvalue/test_creation_info_parser.py @@ -17,6 +17,7 @@ from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.model.external_document_ref import ExternalDocumentRef from spdx.model.version import Version +from spdx.parser.error import SPDXParsingError from spdx.parser.tagvalue.parser.tagvalue import Parser DOCUMENT_STR = '\n'.join([ @@ -64,3 +65,30 @@ def test_creation_info(parser): "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301", Checksum(ChecksumAlgorithm.SHA1, "d6a770ba38583ed4bb4525bd96e50461655d2759"))]) + + +@pytest.mark.parametrize("document_str, expected_message", + ([('\n'.join( + ['SPDXVersion: SPDX-2.3', 'DataLicense: CC0-1.0', 'DocumentName: Sample_Document-V2.3', + 'SPDXID: SPDXRef-DOCUMENT', 'DocumentComment: Sample Comment', + 'DocumentNamespace: Sample Comment', + 'ExternalDocumentRef: DocumentRef-spdx-tool-1.2:htp://spdx.org:SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759', + 'Creator: Person Bob (bob@example.com)', 'Creator: Organization: Acme [email]', + 'Created: 2010-02-03T00:00:0Z', 'CreatorComment: Sample Comment', + 'LicenseListVersion: 7']), + [["Error while parsing CreationInfo: " + "['Error while parsing DocumentNamespace: Token did not match specified grammar rule. " + "Line: 6', 'Error while parsing ExternalDocumentRef: " + "Token did not match specified grammar rule. Line: 7', 'Error while parsing Creator: " + "Token did not match specified grammar rule. Line: 8', 'Error while parsing Created: " + "Token did not match specified grammar rule. Line: 10', '7 is not a valid version string']"]]), + ('\n'.join( + ['SPDXVersion: SPDX-2.3', 'DataLicense: CC0-1.0', 'DocumentName: Sample_Document-V2.3', + 'SPDXID: SPDXRef-DOCUMENT']), + ['Error while constructing CreationInfo: CreationInfo.__init__() missing 3 ' + "required positional arguments: 'document_namespace', 'creators', and " + "'created'"])])) +def test_invalid_creation_info(parser, document_str, expected_message): + with pytest.raises(SPDXParsingError) as err: + parser.parse(document_str) + assert err.value.get_messages() == expected_message From 74da757638c8d879b6fd7a2fcc2185ef587629c4 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 28 Feb 2023 16:15:16 +0100 Subject: [PATCH 308/362] [issue-382] add contains relationships Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 4ce6b8e03..0cd3dafbc 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -144,6 +144,7 @@ def p_lics_list_ver_1(self, p): self.creation_info["license_list_version"] = Version.from_string(p[2]) except ValueError as err: self.creation_info["logger"].append(err.args[0]) + @grammar_rule("lics_list_ver : LIC_LIST_VER error") def p_lics_list_ver_2(self, p): self.creation_info["logger"].append( @@ -398,7 +399,6 @@ def p_file_conc_2(self, p): "file_type_value : SOURCE\n| BINARY\n| ARCHIVE\n | APPLICATION\n | AUDIO\n | IMAGE\n | FILETYPE_TEXT\n| VIDEO\n" " | DOCUMENTATION\n| SPDX \n| OTHER ") def p_file_type_value(self, p): - p[0] = p[1] # parsing methods for package @@ -947,6 +947,8 @@ def construct_current_element(self): try: self.elements_build.setdefault(CLASS_MAPPING[class_name.__name__], []).append( construct_or_raise_parsing_error(class_name, self.current_element)) + if class_name == File: + self.check_for_preceding_package_and_build_contains_relationship() except SPDXParsingError as err: self.logger.append(err.get_messages()) self.current_element = {"logger": Logger()} @@ -962,6 +964,15 @@ def initialize_new_current_element(self, class_name: Any): self.construct_current_element() self.current_element["class"] = class_name + def check_for_preceding_package_and_build_contains_relationship(self): + file_spdx_id = self.current_element["spdx_id"] + if "packages" not in self.elements_build: + return + package_spdx_id = self.elements_build["packages"][-1].spdx_id + relationship = Relationship(package_spdx_id, RelationshipType.CONTAINS, file_spdx_id) + if relationship not in self.elements_build["relationships"]: + self.elements_build.setdefault("relationships", []).append(relationship) + CLASS_MAPPING = dict(File="files", Annotation="annotations", Relationship="relationships", Snippet="snippets", Package="packages", ExtractedLicensingInfo="extracted_licensing_info") From dd7d499e206fbd00789421542580a1629370c84d Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Mar 2023 08:49:52 +0100 Subject: [PATCH 309/362] [issue-382] fix example Signed-off-by: Meret Behrens --- tests/spdx/data/formats/SPDXTagExample-v2.3.spdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/spdx/data/formats/SPDXTagExample-v2.3.spdx b/tests/spdx/data/formats/SPDXTagExample-v2.3.spdx index ca3906159..b84a97a76 100644 --- a/tests/spdx/data/formats/SPDXTagExample-v2.3.spdx +++ b/tests/spdx/data/formats/SPDXTagExample-v2.3.spdx @@ -334,6 +334,7 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. LicenseName: CyberNeko License -LicenseCrossReference: http://people.apache.org/~andyc/neko/LICENSE, http://justasample.url.com +LicenseCrossReference: http://people.apache.org/~andyc/neko/LICENSE +LicenseCrossReference: http://justasample.url.com LicenseComment: This is tye CyperNeko License From 8d1b351fe946d2c24709b078fa428a82a9273864 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Mar 2023 08:56:47 +0100 Subject: [PATCH 310/362] [refactor] build parser when initializing class Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 35 +++++++++++-------- .../parser/tagvalue/test_annotation_parser.py | 15 ++------ .../tagvalue/test_creation_info_parser.py | 13 +++---- .../test_extracted_licensing_info_parser.py | 12 ++----- .../spdx/parser/tagvalue/test_file_parser.py | 16 +++------ .../parser/tagvalue/test_package_parser.py | 11 ++---- .../tagvalue/test_relationship_parser.py | 13 +++---- .../parser/tagvalue/test_snippet_parser.py | 11 ++---- .../parser/tagvalue/test_tag_value_parser.py | 13 +++---- 9 files changed, 46 insertions(+), 93 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 0cd3dafbc..a38c9855f 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -11,43 +11,53 @@ # limitations under the License. import re -from typing import Any +from typing import Any, List, Dict from license_expression import get_spdx_licensing from ply import yacc +from ply.yacc import LRParser from spdx.datetime_conversions import datetime_from_str from spdx.model.annotation import AnnotationType, Annotation +from spdx.model.document import Document, CreationInfo from spdx.model.external_document_ref import ExternalDocumentRef from spdx.model.extracted_licensing_info import ExtractedLicensingInfo +from spdx.model.file import File, FileType from spdx.model.package import Package, PackageVerificationCode, PackagePurpose, ExternalPackageRef, \ ExternalPackageRefCategory from spdx.model.relationship import Relationship, RelationshipType from spdx.model.snippet import Snippet -from spdx.model.version import Version -from spdx.parser.actor_parser import ActorParser - -from spdx.model.document import Document, CreationInfo -from spdx.model.file import File, FileType from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone +from spdx.model.version import Version +from spdx.parser.actor_parser import ActorParser from spdx.parser.error import SPDXParsingError -from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.logger import Logger +from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.tagvalue.lexer.tagvalue import SPDXLexer from spdx.parser.tagvalue.parser.helper_methods import grammar_rule, str_from_text, parse_checksum class Parser(object): - def __init__(self): - self.lex = None - self.yacc = None + tokens: List[str] + logger: Logger + element_stack: List[Dict[str, str]] + current_element: Dict[str, Any] + creation_info: Dict[str, Any] + elements_build: Dict[str, Any] + lex: SPDXLexer + yacc: LRParser + + def __init__(self, **kwargs): self.tokens = SPDXLexer.tokens self.logger = Logger() self.element_stack = [] self.current_element = {"logger": Logger()} self.creation_info = {"logger": Logger()} self.elements_build = dict() + self.lex = SPDXLexer() + self.lex.build(reflags=re.UNICODE) + self.yacc = yacc.yacc(module=self, **kwargs) @grammar_rule("start : start attrib ") def p_start_1(self, p): @@ -915,11 +925,6 @@ def p_relationship_value_without_doc_ref(self, p): def p_error(self, p): pass - def build(self, **kwargs): - self.lex = SPDXLexer() - self.lex.build(reflags=re.UNICODE) - self.yacc = yacc.yacc(module=self, **kwargs) - def parse(self, text): self.yacc.parse(text, lexer=self.lex) self.construct_current_element() diff --git a/tests/spdx/parser/tagvalue/test_annotation_parser.py b/tests/spdx/parser/tagvalue/test_annotation_parser.py index 8fbcc2f9b..b40cd6575 100644 --- a/tests/spdx/parser/tagvalue/test_annotation_parser.py +++ b/tests/spdx/parser/tagvalue/test_annotation_parser.py @@ -10,22 +10,13 @@ # limitations under the License. from datetime import datetime -import pytest - +from spdx.model.annotation import AnnotationType from spdx.parser.tagvalue.parser.tagvalue import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR -from spdx.model.annotation import AnnotationType - - -@pytest.fixture -def parser(): - spdx_parser = Parser() - spdx_parser.build() - return spdx_parser - -def test_annotation(parser): +def test_annotation(): + parser = Parser() annotation_str = '\n'.join([ 'Annotator: Person: Jane Doe()', 'AnnotationDate: 2010-01-29T18:30:22Z', diff --git a/tests/spdx/parser/tagvalue/test_creation_info_parser.py b/tests/spdx/parser/tagvalue/test_creation_info_parser.py index bd430f020..6d9e4d427 100644 --- a/tests/spdx/parser/tagvalue/test_creation_info_parser.py +++ b/tests/spdx/parser/tagvalue/test_creation_info_parser.py @@ -36,14 +36,8 @@ ]) -@pytest.fixture -def parser(): - spdx_parser = Parser() - spdx_parser.build() - return spdx_parser - - -def test_creation_info(parser): +def test_creation_info(): + parser = Parser() document = parser.parse(DOCUMENT_STR) assert document is not None creation_info = document.creation_info @@ -88,7 +82,8 @@ def test_creation_info(parser): ['Error while constructing CreationInfo: CreationInfo.__init__() missing 3 ' "required positional arguments: 'document_namespace', 'creators', and " "'created'"])])) -def test_invalid_creation_info(parser, document_str, expected_message): +def test_invalid_creation_info(document_str, expected_message): + parser = Parser() with pytest.raises(SPDXParsingError) as err: parser.parse(document_str) assert err.value.get_messages() == expected_message diff --git a/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py b/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py index 7197b0676..7761a202e 100644 --- a/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py +++ b/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py @@ -8,20 +8,12 @@ # 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 pytest - from spdx.parser.tagvalue.parser.tagvalue import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR -@pytest.fixture -def parser(): - spdx_parser = Parser() - spdx_parser.build() - return spdx_parser - - -def test_extracted_licensing_info(parser): +def test_extracted_licensing_info(): + parser = Parser() extracted_licensing_info_str = '\n'.join([ 'LicenseID: LicenseRef-Beerware-4.2', 'ExtractedText: "THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' diff --git a/tests/spdx/parser/tagvalue/test_file_parser.py b/tests/spdx/parser/tagvalue/test_file_parser.py index 15ca0fa59..ec5a9df5d 100644 --- a/tests/spdx/parser/tagvalue/test_file_parser.py +++ b/tests/spdx/parser/tagvalue/test_file_parser.py @@ -11,21 +11,14 @@ import pytest from license_expression import get_spdx_licensing +from spdx.model.file import FileType from spdx.parser.error import SPDXParsingError from spdx.parser.tagvalue.parser.tagvalue import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR -from spdx.model.file import FileType - - -@pytest.fixture -def parser(): - spdx_parser = Parser() - spdx_parser.build() - return spdx_parser - -def test_file(parser): +def test_file(): + parser = Parser() file_str = '\n'.join([ 'FileName: testfile.java', 'SPDXID: SPDXRef-File', @@ -52,7 +45,8 @@ def test_file(parser): assert spdx_file.license_concluded == get_spdx_licensing().parse("Apache-2.0") -def test_invalid_file(parser): +def test_invalid_file(): + parser = Parser() file_str = '\n'.join([ 'FileName: testfile.java', 'SPDXID: SPDXRef-File', diff --git a/tests/spdx/parser/tagvalue/test_package_parser.py b/tests/spdx/parser/tagvalue/test_package_parser.py index 4de7ffd41..c17516610 100644 --- a/tests/spdx/parser/tagvalue/test_package_parser.py +++ b/tests/spdx/parser/tagvalue/test_package_parser.py @@ -11,7 +11,6 @@ from datetime import datetime from unittest import TestCase -import pytest from license_expression import get_spdx_licensing from spdx.model.package import ExternalPackageRef, ExternalPackageRefCategory, PackagePurpose @@ -19,14 +18,8 @@ from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR -@pytest.fixture -def parser(): - spdx_parser = Parser() - spdx_parser.build() - return spdx_parser - - -def test_package(parser): +def test_package(): + parser = Parser() package_str = '\n'.join([ 'PackageName: Test', 'SPDXID: SPDXRef-Package', diff --git a/tests/spdx/parser/tagvalue/test_relationship_parser.py b/tests/spdx/parser/tagvalue/test_relationship_parser.py index 5247ad4c5..c60e7eb76 100644 --- a/tests/spdx/parser/tagvalue/test_relationship_parser.py +++ b/tests/spdx/parser/tagvalue/test_relationship_parser.py @@ -18,13 +18,6 @@ from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR -@pytest.fixture -def parser(): - spdx_parser = Parser() - spdx_parser.build() - return spdx_parser - - @pytest.mark.parametrize("relationship_str, expected_relationship", [('\n'.join(['Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File', 'RelationshipComment: This is a comment.']), @@ -39,7 +32,8 @@ def parser(): Relationship("DocumentRef-ExternalDocument:SPDXRef-Test", RelationshipType.DEPENDS_ON, "DocumentRef:AnotherRef")) ]) -def test_relationship(parser, relationship_str, expected_relationship): +def test_relationship(relationship_str, expected_relationship): + parser = Parser() document = parser.parse("\n".join([DOCUMENT_STR, relationship_str])) assert document is not None relationship = document.relationships[0] @@ -56,7 +50,8 @@ def test_relationship(parser, relationship_str, expected_relationship): [["Error while parsing Relationship: ['Error while parsing Relationship: Token " "did not match specified grammar rule. Line: 1']"]]) ]) -def test_falsy_relationship(parser, relationship_str, expected_message): +def test_falsy_relationship(relationship_str, expected_message): + parser = Parser() with pytest.raises(SPDXParsingError) as err: parser.parse(relationship_str) diff --git a/tests/spdx/parser/tagvalue/test_snippet_parser.py b/tests/spdx/parser/tagvalue/test_snippet_parser.py index 95f766e52..80a10bd40 100644 --- a/tests/spdx/parser/tagvalue/test_snippet_parser.py +++ b/tests/spdx/parser/tagvalue/test_snippet_parser.py @@ -8,21 +8,14 @@ # 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 pytest from license_expression import get_spdx_licensing from spdx.parser.tagvalue.parser.tagvalue import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR -@pytest.fixture -def parser(): - spdx_parser = Parser() - spdx_parser.build() - return spdx_parser - - -def test_snippet(parser): +def test_snippet(): + parser = Parser() snippet_str = '\n'.join([ 'SnippetSPDXID: SPDXRef-Snippet', 'SnippetLicenseComments: Some lic comment.', diff --git a/tests/spdx/parser/tagvalue/test_tag_value_parser.py b/tests/spdx/parser/tagvalue/test_tag_value_parser.py index edd0e8aef..af88aab53 100644 --- a/tests/spdx/parser/tagvalue/test_tag_value_parser.py +++ b/tests/spdx/parser/tagvalue/test_tag_value_parser.py @@ -18,21 +18,16 @@ from spdx.parser.tagvalue.parser.tagvalue import Parser -@pytest.fixture -def parser(): - spdx_parser = Parser() - spdx_parser.build() - return spdx_parser - - -def test_unknown_str(parser): +def test_unknown_str(): + parser = Parser() unknown_tag_str = 'UnknownTag: This is an example for an unknown tag.' with pytest.raises(SPDXParsingError, match="Unknown tag"): parser.parse(unknown_tag_str) -def test_parse_file(parser): +def test_parse_file(): + parser = Parser() fn = os.path.join(os.path.dirname(__file__), "../../data/formats/SPDXTagExample-v2.3.spdx") with open(fn) as f: From 9c9b08d5715a158f3c2da3484b3ab2d24fb89275 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Mar 2023 09:05:40 +0100 Subject: [PATCH 311/362] [issue-382] add tag-value parser to cli tool Signed-off-by: Meret Behrens --- src/spdx/parser/parse_anything.py | 3 ++- .../parser/tagvalue/parser/tagvalue_parser.py | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/spdx/parser/tagvalue/parser/tagvalue_parser.py diff --git a/src/spdx/parser/parse_anything.py b/src/spdx/parser/parse_anything.py index 34b15d1dd..b2d1dfd87 100644 --- a/src/spdx/parser/parse_anything.py +++ b/src/spdx/parser/parse_anything.py @@ -11,6 +11,7 @@ from spdx.formats import file_name_to_format, FileFormat from spdx.parser.json import json_parser from spdx.parser.rdf import rdf_parser +from spdx.parser.tagvalue.parser import tagvalue_parser from spdx.parser.xml import xml_parser from spdx.parser.yaml import yaml_parser @@ -20,7 +21,7 @@ def parse_file(file_name: str): if input_format == FileFormat.RDF_XML: return rdf_parser.parse_from_file(file_name) elif input_format == FileFormat.TAG_VALUE: - raise NotImplementedError("Currently, the tag-value parser is not implemented") + return tagvalue_parser.parse_from_file(file_name) elif input_format == FileFormat.JSON: return json_parser.parse_from_file(file_name) elif input_format == FileFormat.XML: diff --git a/src/spdx/parser/tagvalue/parser/tagvalue_parser.py b/src/spdx/parser/tagvalue/parser/tagvalue_parser.py new file mode 100644 index 000000000..ba4a53ead --- /dev/null +++ b/src/spdx/parser/tagvalue/parser/tagvalue_parser.py @@ -0,0 +1,20 @@ +# Copyright (c) 2023 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 spdx.model.document import Document +from spdx.parser.tagvalue.parser.tagvalue import Parser + + +def parse_from_file(file_name: str) -> Document: + parser = Parser() + with open(file_name) as file: + data = file.read() + document: Document = parser.parse(data) + return document From 50004b5d38fd0fa28a14ca65c0db9f70f8339be4 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Mar 2023 09:32:49 +0100 Subject: [PATCH 312/362] [fix] raise error if name is an empty string in actor_parser Signed-off-by: Meret Behrens --- src/spdx/parser/actor_parser.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/spdx/parser/actor_parser.py b/src/spdx/parser/actor_parser.py index 28c5ad960..7d6406eba 100644 --- a/src/spdx/parser/actor_parser.py +++ b/src/spdx/parser/actor_parser.py @@ -29,14 +29,20 @@ def parse_actor(actor: str) -> Actor: if tool_match: name: str = tool_match.group(1).strip() + if not name: + raise SPDXParsingError([f"No name for Tool provided: {actor}."]) creator = construct_or_raise_parsing_error(Actor, dict(actor_type=ActorType.TOOL, name=name)) elif person_match: name: str = person_match.group(1).strip() + if not name: + raise SPDXParsingError([f"No name for Person provided: {actor}."]) email: Optional[str] = ActorParser.get_email_or_none(person_match) creator = construct_or_raise_parsing_error(Actor, dict(actor_type=ActorType.PERSON, name=name, email=email)) elif org_match: name: str = org_match.group(1).strip() + if not name: + raise SPDXParsingError([f"No name for Organization provided: {actor}."]) email: Optional[str] = ActorParser.get_email_or_none(org_match) creator = construct_or_raise_parsing_error(Actor, dict(actor_type=ActorType.ORGANIZATION, name=name, email=email)) From 46731ffc5bc8b41e9bf59a39f78eaf451623e651 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Mar 2023 11:25:06 +0100 Subject: [PATCH 313/362] [issue-382] check that current element matches class of parsed value Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 84 +++++++++++++++++---- 1 file changed, 69 insertions(+), 15 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index a38c9855f..78caa36ac 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -37,6 +37,11 @@ from spdx.parser.tagvalue.lexer.tagvalue import SPDXLexer from spdx.parser.tagvalue.parser.helper_methods import grammar_rule, str_from_text, parse_checksum +CLASS_MAPPING = dict(File="files", Annotation="annotations", Relationship="relationships", Snippet="snippets", + Package="packages", ExtractedLicensingInfo="extracted_licensing_info") +ELEMENT_EXPECTED_START_TAG = dict(File="FileName", Annotation="Annotator", Relationship="Relationship", + Snippet="SnippetSPDXID", Package="PackageName", ExtractedLicensingInfo="LicenseID") + class Parser(object): tokens: List[str] @@ -254,7 +259,6 @@ def p_extr_lic_id_1(self, p): @grammar_rule("extr_lic_id : LICS_ID error") def p_extr_lic_id_2(self, p): - self.initialize_new_current_element(ExtractedLicensingInfo) self.current_element["logger"].append( f"Error while parsing LicenseID: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -271,6 +275,7 @@ def p_lic_xref_2(self, p): @grammar_rule("lic_comment : LICS_COMMENT text_or_line") def p_lic_comment_1(self, p): + self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) self.current_element["comment"] = p[2] @grammar_rule("lic_comment : LICS_COMMENT error") @@ -280,6 +285,7 @@ def p_lic_comment_2(self, p): @grammar_rule("extr_lic_name : LICS_NAME line_or_no_assertion") def p_extr_lic_name_1(self, p): + self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) self.current_element["license_name"] = p[2] @grammar_rule("extr_lic_name : LICS_NAME error") @@ -289,6 +295,7 @@ def p_extr_lic_name_2(self, p): @grammar_rule("extr_lic_text : LICS_TEXT text_or_line") def p_extr_lic_text_1(self, p): + self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) self.current_element["extracted_text"] = p[2] @grammar_rule("extr_lic_text : LICS_TEXT error") @@ -311,6 +318,7 @@ def p_file_name_2(self, p): @grammar_rule("file_contrib : FILE_CONTRIB LINE") def p_file_contrib_1(self, p): + self.check_that_current_element_matches_class_for_value(File) self.current_element.setdefault("contributors", []).append(p[2]) @grammar_rule("file_contrib : FILE_CONTRIB error") @@ -320,6 +328,7 @@ def p_file_contrib_2(self, p): @grammar_rule("file_notice : FILE_NOTICE text_or_line") def p_file_notice_1(self, p): + self.check_that_current_element_matches_class_for_value(File) self.current_element["notice"] = p[2] @grammar_rule("file_notice : FILE_NOTICE error") @@ -329,6 +338,7 @@ def p_file_notice_2(self, p): @grammar_rule("file_cr_text : FILE_CR_TEXT line_or_no_assertion_or_none") def p_file_cr_text_1(self, p): + self.check_that_current_element_matches_class_for_value(File) self.current_element["copyright_text"] = p[2] @grammar_rule("file_cr_text : FILE_CR_TEXT error") @@ -338,6 +348,7 @@ def p_file_cr_text_2(self, p): @grammar_rule("file_lics_comment : FILE_LICS_COMMENT text_or_line") def p_file_lics_comment_1(self, p): + self.check_that_current_element_matches_class_for_value(File) self.current_element["license_comment"] = p[2] @grammar_rule("file_lics_comment : FILE_LICS_COMMENT error") @@ -348,6 +359,7 @@ def p_file_lics_comment_2(self, p): @grammar_rule("file_attribution_text : FILE_ATTRIBUTION_TEXT text_or_line") def p_file_attribution_text_1(self, p): + self.check_that_current_element_matches_class_for_value(File) self.current_element.setdefault("attribution_texts", []).append(p[2]) @grammar_rule("file_attribution_text : FILE_ATTRIBUTION_TEXT error") @@ -357,6 +369,7 @@ def p_file_attribution_text_2(self, p): @grammar_rule("file_lics_info : FILE_LICS_INFO license_or_no_assertion_or_none") def p_file_lics_info_1(self, p): + self.check_that_current_element_matches_class_for_value(File) if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): self.current_element["license_info_in_file"] = p[2] return @@ -369,6 +382,7 @@ def p_file_lics_info_2(self, p): @grammar_rule("file_comment : FILE_COMMENT text_or_line") def p_file_comment_1(self, p): + self.check_that_current_element_matches_class_for_value(File) self.current_element["comment"] = p[2] @grammar_rule("file_comment : FILE_COMMENT error") @@ -378,6 +392,7 @@ def p_file_comment_2(self, p): @grammar_rule("file_type : FILE_TYPE file_type_value") def p_file_type_1(self, p): + self.check_that_current_element_matches_class_for_value(File) self.current_element.setdefault("file_type", []).append(FileType[p[2]]) @grammar_rule("file_type : FILE_TYPE error") @@ -385,8 +400,15 @@ def p_file_type_2(self, p): self.current_element["logger"].append( f"Error while parsing FileType: Token did not match any of the valid values. Line: {p.lineno(1)}") + @grammar_rule( + "file_type_value : SOURCE\n| BINARY\n| ARCHIVE\n | APPLICATION\n | AUDIO\n | IMAGE\n | FILETYPE_TEXT\n| VIDEO\n" + " | DOCUMENTATION\n| SPDX \n| OTHER ") + def p_file_type_value(self, p): + p[0] = p[1] + @grammar_rule("file_checksum : FILE_CHECKSUM CHECKSUM") def p_file_checksum_1(self, p): + self.check_that_current_element_matches_class_for_value(File) checksum = parse_checksum(self.current_element["logger"], p[2]) self.current_element.setdefault("checksums", []).append(checksum) @@ -397,6 +419,7 @@ def p_file_checksum_2(self, p): @grammar_rule("file_conc : FILE_LICS_CONC license_or_no_assertion_or_none") def p_file_conc_1(self, p): + self.check_that_current_element_matches_class_for_value(File) self.current_element["license_concluded"] = p[2] @grammar_rule("file_conc : FILE_LICS_CONC error") @@ -405,12 +428,6 @@ def p_file_conc_2(self, p): f"Error while parsing LicenseConcluded in file: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") - @grammar_rule( - "file_type_value : SOURCE\n| BINARY\n| ARCHIVE\n | APPLICATION\n | AUDIO\n | IMAGE\n | FILETYPE_TEXT\n| VIDEO\n" - " | DOCUMENTATION\n| SPDX \n| OTHER ") - def p_file_type_value(self, p): - p[0] = p[1] - # parsing methods for package @grammar_rule("package_name : PKG_NAME LINE") @@ -428,6 +445,7 @@ def p_package_name_1(self, p): @grammar_rule("pkg_desc : PKG_DESC text_or_line") def p_pkg_desc_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element["description"] = p[2] @grammar_rule("pkg_desc : PKG_DESC error") @@ -437,6 +455,7 @@ def p_pkg_desc_2(self, p): @grammar_rule("pkg_comment : PKG_COMMENT text_or_line") def p_pkg_comment_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element["comment"] = p[2] @grammar_rule("pkg_comment : PKG_COMMENT error") @@ -446,6 +465,7 @@ def p_pkg_comment_2(self, p): @grammar_rule("pkg_attribution_text : PKG_ATTRIBUTION_TEXT text_or_line") def p_pkg_attribution_text_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element.setdefault("attribution_texts", []).append(p[2]) @grammar_rule("pkg_attribution_text : PKG_ATTRIBUTION_TEXT error") @@ -456,6 +476,7 @@ def p_pkg_attribution_text_2(self, p): @grammar_rule("pkg_summary : PKG_SUM text_or_line") def p_pkg_summary_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element["summary"] = p[2] @grammar_rule("pkg_summary : PKG_SUM error") @@ -465,6 +486,7 @@ def p_pkg_summary_2(self, p): @grammar_rule("pkg_cr_text : PKG_CPY_TEXT line_or_no_assertion_or_none") def p_pkg_cr_text_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element["copyright_text"] = p[2] @grammar_rule("pkg_cr_text : PKG_CPY_TEXT error") @@ -475,6 +497,7 @@ def p_pkg_cr_text_2(self, p): @grammar_rule("pkg_ext_ref : PKG_EXT_REF LINE PKG_EXT_REF_COMMENT text_or_line\n | PKG_EXT_REF LINE") def p_pkg_ext_refs_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) category, reference_type, locator = p[2].split(" ") comment = None if len(p) == 5: @@ -503,6 +526,7 @@ def p_pkg_ext_refs_2(self, p): @grammar_rule("pkg_lic_comment : PKG_LICS_COMMENT text_or_line") def p_pkg_lic_comment_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element["license_comment"] = p[2] @grammar_rule("pkg_lic_comment : PKG_LICS_COMMENT error") @@ -513,6 +537,7 @@ def p_pkg_lic_comment_2(self, p): @grammar_rule("pkg_lic_decl : PKG_LICS_DECL license_or_no_assertion_or_none") def p_pkg_lic_decl_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element["license_declared"] = p[2] @grammar_rule("pkg_lic_decl : PKG_LICS_DECL error") @@ -523,6 +548,7 @@ def p_pkg_lic_decl_2(self, p): @grammar_rule("pkg_lic_ff : PKG_LICS_FFILE license_or_no_assertion_or_none") def p_pkg_lic_ff_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): self.current_element["license_info_from_files"] = p[2] else: @@ -536,6 +562,7 @@ def p_pkg_lic_ff_error(self, p): @grammar_rule("pkg_lic_conc : PKG_LICS_CONC license_or_no_assertion_or_none") def p_pkg_lic_conc_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element["license_concluded"] = p[2] @grammar_rule("pkg_lic_conc : PKG_LICS_CONC error") @@ -546,6 +573,7 @@ def p_pkg_lic_conc_2(self, p): @grammar_rule("pkg_src_info : PKG_SRC_INFO text_or_line") def p_pkg_src_info_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element["source_info"] = p[2] @grammar_rule("pkg_src_info : PKG_SRC_INFO error") @@ -555,6 +583,7 @@ def p_pkg_src_info_2(self, p): @grammar_rule("pkg_checksum : PKG_CHECKSUM CHECKSUM") def p_pkg_checksum_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) checksum = parse_checksum(self.current_element["logger"], p[2]) self.current_element.setdefault("checksums", []).append(checksum) @@ -565,6 +594,7 @@ def p_pkg_checksum_2(self, p): @grammar_rule("pkg_verif : PKG_VERF_CODE LINE") def p_pkg_verif_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) verif_code_regex = re.compile(r"([0-9a-f]+)\s*(\(excludes:\s*(.+)\))?", re.UNICODE) verif_code_code_grp = 1 verif_code_exc_files_grp = 3 @@ -583,6 +613,7 @@ def p_pkg_verif_2(self, p): @grammar_rule("pkg_home : PKG_HOME line_or_no_assertion_or_none") def p_pkg_home_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element["homepage"] = p[2] @grammar_rule("pkg_home : PKG_HOME error") @@ -592,6 +623,7 @@ def p_pkg_home_2(self, p): @grammar_rule("pkg_down_location : PKG_DOWN line_or_no_assertion_or_none") def p_pkg_down_location_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element["download_location"] = p[2] @grammar_rule("pkg_down_location : PKG_DOWN error") @@ -602,6 +634,7 @@ def p_pkg_down_location_2(self, p): @grammar_rule("pkg_files_analyzed : PKG_FILES_ANALYZED LINE") def p_pkg_files_analyzed_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) if p[2] in ['false', 'False']: self.current_element["files_analyzed"] = False if p[2] in ['true', 'True']: @@ -615,6 +648,7 @@ def p_pkg_files_analyzed_2(self, p): @grammar_rule("pkg_orig : PKG_ORIG pkg_supplier_values") def p_pkg_orig_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element["originator"] = p[2] @grammar_rule("pkg_orig : PKG_ORIG error") @@ -624,6 +658,7 @@ def p_pkg_orig_2(self, p): @grammar_rule("pkg_supplier : PKG_SUPPL pkg_supplier_values") def p_pkg_supplier_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element["supplier"] = p[2] @grammar_rule("pkg_supplier : PKG_SUPPL error") @@ -641,6 +676,7 @@ def p_pkg_supplier_values_2(self, p): @grammar_rule("pkg_file_name : PKG_FILE_NAME LINE") def p_pkg_file_name(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element["file_name"] = p[2] @grammar_rule("pkg_file_name : PKG_FILE_NAME error") @@ -650,6 +686,7 @@ def p_pkg_file_name_1(self, p): @grammar_rule("package_version : PKG_VERSION LINE") def p_package_version_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element["version"] = p[2] @grammar_rule("package_version : PKG_VERSION error") @@ -659,7 +696,7 @@ def p_package_version_2(self, p): @grammar_rule("primary_package_purpose : PRIMARY_PACKAGE_PURPOSE primary_package_purpose_value") def p_primary_package_purpose_1(self, p): - + self.check_that_current_element_matches_class_for_value(Package) self.current_element["primary_package_purpose"] = PackagePurpose[p[2].replace("-", "_")] @grammar_rule("primary_package_purpose : PRIMARY_PACKAGE_PURPOSE error") @@ -675,6 +712,7 @@ def p_primary_package_purpose_value(self, p): @grammar_rule("built_date : BUILT_DATE DATE") def p_built_date_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element["built_date"] = datetime_from_str(p[2]) @grammar_rule("built_date : BUILT_DATE error") @@ -684,6 +722,7 @@ def p_built_date_2(self, p): @grammar_rule("release_date : RELEASE_DATE DATE") def p_release_date_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element["release_date"] = datetime_from_str(p[2]) @grammar_rule("release_date : RELEASE_DATE error") @@ -693,6 +732,7 @@ def p_release_date_2(self, p): @grammar_rule("valid_until_date : VALID_UNTIL_DATE DATE") def p_valid_until_date_1(self, p): + self.check_that_current_element_matches_class_for_value(Package) self.current_element["valid_until_date"] = datetime_from_str(p[2]) @grammar_rule("valid_until_date : VALID_UNTIL_DATE error") @@ -714,6 +754,7 @@ def p_snip_spdx_id_1(self, p): @grammar_rule("snip_name : SNIPPET_NAME LINE") def p_snippet_name(self, p): + self.check_that_current_element_matches_class_for_value(Snippet) self.current_element["name"] = p[2] @grammar_rule("snip_name : SNIPPET_NAME error") @@ -723,6 +764,7 @@ def p_snippet_name_1(self, p): @grammar_rule("snip_comment : SNIPPET_COMMENT text_or_line") def p_snippet_comment(self, p): + self.check_that_current_element_matches_class_for_value(Snippet) self.current_element["comment"] = p[2] @grammar_rule("snip_comment : SNIPPET_COMMENT error") @@ -732,6 +774,7 @@ def p_snippet_comment_1(self, p): @grammar_rule("snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT text_or_line") def p_snippet_attribution_text_1(self, p): + self.check_that_current_element_matches_class_for_value(Snippet) self.current_element.setdefault("attribution_texts", []).append(p[2]) @grammar_rule("snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT error") @@ -742,6 +785,7 @@ def p_snippet_attribution_text_2(self, p): @grammar_rule("snip_cr_text : SNIPPET_CR_TEXT line_or_no_assertion_or_none") def p_snippet_cr_text(self, p): + self.check_that_current_element_matches_class_for_value(Snippet) self.current_element["copyright_text"] = p[2] @grammar_rule("snip_cr_text : SNIPPET_CR_TEXT error") @@ -752,6 +796,7 @@ def p_snippet_cr_text_1(self, p): @grammar_rule("snip_lic_comment : SNIPPET_LICS_COMMENT text_or_line") def p_snippet_lic_comment(self, p): + self.check_that_current_element_matches_class_for_value(Snippet) self.current_element["license_comment"] = p[2] @grammar_rule("snip_lic_comment : SNIPPET_LICS_COMMENT error") @@ -762,6 +807,7 @@ def p_snippet_lic_comment_1(self, p): @grammar_rule("snip_file_spdx_id : SNIPPET_FILE_SPDXID LINE") def p_snip_from_file_spdxid(self, p): + self.check_that_current_element_matches_class_for_value(Snippet) self.current_element["file_spdx_id"] = p[2] @grammar_rule("snip_file_spdx_id : SNIPPET_FILE_SPDXID error") @@ -772,6 +818,7 @@ def p_snip_from_file_spdxid_1(self, p): @grammar_rule("snip_lics_conc : SNIPPET_LICS_CONC license_or_no_assertion_or_none") def p_snippet_concluded_license(self, p): + self.check_that_current_element_matches_class_for_value(Snippet) self.current_element["license_concluded"] = p[2] @grammar_rule("snip_lics_conc : SNIPPET_LICS_CONC error") @@ -782,6 +829,7 @@ def p_snippet_concluded_license_1(self, p): @grammar_rule("snip_lics_info : SNIPPET_LICS_INFO license_or_no_assertion_or_none") def p_snippet_lics_info(self, p): + self.check_that_current_element_matches_class_for_value(Snippet) if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): self.current_element["license_info_in_snippet"] = p[2] else: @@ -796,6 +844,7 @@ def p_snippet_lics_info_1(self, p): @grammar_rule("snip_byte_range : SNIPPET_BYTE_RANGE LINE") def p_snippet_byte_range(self, p): + self.check_that_current_element_matches_class_for_value(Snippet) range_re = re.compile(r"^(\d+):(\d+)$", re.UNICODE) if not range_re.match(p[2].strip()): self.current_element["logger"].append("Value for SnippetByteRange doesn't match valid range pattern.") @@ -812,6 +861,7 @@ def p_snippet_byte_range_1(self, p): @grammar_rule("snip_line_range : SNIPPET_LINE_RANGE LINE") def p_snippet_line_range(self, p): + self.check_that_current_element_matches_class_for_value(Snippet) range_re = re.compile(r"^(\d+):(\d+)$", re.UNICODE) if not range_re.match(p[2].strip()): self.current_element["logger"].append("Value for SnippetLineRange doesn't match valid range pattern.") @@ -842,6 +892,7 @@ def p_annotator_2(self, p): @grammar_rule("annotation_date : ANNOTATION_DATE DATE") def p_annotation_date_1(self, p): + self.check_that_current_element_matches_class_for_value(Annotation) self.current_element["annotation_date"] = datetime_from_str(p[2]) @grammar_rule("annotation_date : ANNOTATION_DATE error") @@ -851,6 +902,7 @@ def p_annotation_date_2(self, p): @grammar_rule("annotation_comment : ANNOTATION_COMMENT text_or_line") def p_annotation_comment_1(self, p): + self.check_that_current_element_matches_class_for_value(Annotation) self.current_element["annotation_comment"] = p[2] @grammar_rule("annotation_comment : ANNOTATION_COMMENT error") @@ -860,6 +912,7 @@ def p_annotation_comment_2(self, p): @grammar_rule("annotation_type : ANNOTATION_TYPE annotation_type_value") def p_annotation_type_1(self, p): + self.check_that_current_element_matches_class_for_value(Annotation) self.current_element["annotation_type"] = AnnotationType[p[2]] @grammar_rule("annotation_type : ANNOTATION_TYPE error") @@ -959,9 +1012,14 @@ def construct_current_element(self): self.current_element = {"logger": Logger()} def check_that_current_element_matches_class_for_value(self, expected_class): - if expected_class != self.current_element["class"]: - raise SPDXParsingError(["Unexpected current element for value"]) - # what to do now? exit parsing + if "class" not in self.current_element: + self.logger.append( + f"Element {expected_class.__name__} is not the current element in scope, probably the expected tag to " + f"start the element ({ELEMENT_EXPECTED_START_TAG[expected_class.__name__]}) is missing.") + elif expected_class != self.current_element["class"]: + self.logger.append( + f"Element {expected_class.__name__} is not the current element in scope, probably the expected tag to " + f"start the element ({ELEMENT_EXPECTED_START_TAG[expected_class.__name__]}) is missing.") def initialize_new_current_element(self, class_name: Any): if "class" in self.current_element and "spdx_id" in self.current_element: @@ -977,7 +1035,3 @@ def check_for_preceding_package_and_build_contains_relationship(self): relationship = Relationship(package_spdx_id, RelationshipType.CONTAINS, file_spdx_id) if relationship not in self.elements_build["relationships"]: self.elements_build.setdefault("relationships", []).append(relationship) - - -CLASS_MAPPING = dict(File="files", Annotation="annotations", Relationship="relationships", Snippet="snippets", - Package="packages", ExtractedLicensingInfo="extracted_licensing_info") From 43e8dd3de2c03261ddd44235ad82f8ca9b33c591 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Mar 2023 09:33:47 +0100 Subject: [PATCH 314/362] [issue-382] add negative test for annotation_parser Signed-off-by: Meret Behrens --- .../parser/tagvalue/test_annotation_parser.py | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/spdx/parser/tagvalue/test_annotation_parser.py b/tests/spdx/parser/tagvalue/test_annotation_parser.py index b40cd6575..e0b7b515d 100644 --- a/tests/spdx/parser/tagvalue/test_annotation_parser.py +++ b/tests/spdx/parser/tagvalue/test_annotation_parser.py @@ -10,12 +10,15 @@ # limitations under the License. from datetime import datetime +import pytest + from spdx.model.annotation import AnnotationType +from spdx.parser.error import SPDXParsingError from spdx.parser.tagvalue.parser.tagvalue import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR -def test_annotation(): +def test_parse_annotation(): parser = Parser() annotation_str = '\n'.join([ 'Annotator: Person: Jane Doe()', @@ -33,3 +36,27 @@ def test_annotation(): assert annotation.annotation_comment == 'Document level annotation' assert annotation.annotation_type == AnnotationType.OTHER assert annotation.spdx_id == 'SPDXRef-DOCUMENT' + + +@pytest.mark.parametrize("annotation_str, expected_message", [ + ('Annotator: Person: Jane Doe()', [['Error while constructing Annotation: Annotation.__init__() missing 4 ' + "required positional arguments: 'spdx_id', 'annotation_type', " + "'annotation_date', and 'annotation_comment'"]]), + ('Annotator: Person: Jane Doe()\nAnnotationType: SOURCE\nAnnotationDate: 201001-2912:23', + [["Error while parsing Annotation: ['Error while parsing AnnotationType: Token " + "did not match specified grammar rule. Line: 2', 'Error while parsing " + "AnnotationDate: Token did not match specified grammar rule. Line: 3']"]]), + ('Annotator: Jane Doe()\nAnnotationDate: 201001-29T18:30:22Z\n' + 'AnnotationComment: Document level annotation\nAnnotationType: OTHER\nSPDXREF: SPDXRef-DOCUMENT', + [["Error while parsing Annotation: ['Error while parsing Annotator: Token did " + "not match specified grammar rule. Line: 1', 'Error while parsing " + "AnnotationDate: Token did not match specified grammar rule. Line: 2']"]]), + ('Annotator: Person: ()', [["Error while parsing Annotation: [['No name for Person provided: Person: ().']]"]]), + ('AnnotationType: REVIEW', ['Element Annotation is not the current element in scope, probably the ' + 'expected tag to start the element (Annotator) is missing.'])]) +def test_parse_invalid_annotation(annotation_str, expected_message): + parser = Parser() + with pytest.raises(SPDXParsingError) as err: + parser.parse(annotation_str) + + assert err.value.get_messages() == expected_message From c39e516002691e2947a7913576a5e727e3b66d33 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Mar 2023 11:25:19 +0100 Subject: [PATCH 315/362] [issue-382] add negative tests for extracted_licensing_info Signed-off-by: Meret Behrens --- .../test_extracted_licensing_info_parser.py | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py b/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py index 7761a202e..753577cb2 100644 --- a/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py +++ b/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py @@ -8,6 +8,11 @@ # 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 spdx.parser.error import SPDXParsingError from spdx.parser.tagvalue.parser.tagvalue import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR @@ -16,9 +21,12 @@ def test_extracted_licensing_info(): parser = Parser() extracted_licensing_info_str = '\n'.join([ 'LicenseID: LicenseRef-Beerware-4.2', - 'ExtractedText: "THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' + 'ExtractedText: "THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you ' + 'retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this ' + 'stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' 'LicenseName: Beer-Ware License (Version 42)', 'LicenseCrossReference: http://people.freebsd.org/~phk/', + 'LicenseCrossReference: http://another.cross.reference/', 'LicenseComment: The beerware license has a couple of other standard variants.' ]) document = parser.parse("\n".join([DOCUMENT_STR, extracted_licensing_info_str])) @@ -26,7 +34,31 @@ def test_extracted_licensing_info(): assert len(document.extracted_licensing_info) == 1 extracted_licensing_info = document.extracted_licensing_info[0] assert extracted_licensing_info.license_id == "LicenseRef-Beerware-4.2" - assert extracted_licensing_info.extracted_text == '"THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' + assert extracted_licensing_info.extracted_text == '"THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. ' \ + 'As long as you retain this notice you can do whatever you want with this stuff. ' \ + 'If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' assert extracted_licensing_info.license_name == "Beer-Ware License (Version 42)" - assert extracted_licensing_info.cross_references == ["http://people.freebsd.org/~phk/"] + TestCase().assertCountEqual(extracted_licensing_info.cross_references, + ["http://people.freebsd.org/~phk/", "http://another.cross.reference/"]) assert extracted_licensing_info.comment == "The beerware license has a couple of other standard variants." + + +def test_parse_invalid_licensing_info(): + parser = Parser() + extracted_licensing_info_str = '\n'.join([ + 'ExtractedText: "THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' + 'LicenseName: Beer-Ware License (Version 42)', + 'LicenseCrossReference: http://people.freebsd.org/~phk/', + 'LicenseComment: The beerware license has a couple of other standard variants.']) + + with pytest.raises(SPDXParsingError) as err: + parser.parse(extracted_licensing_info_str) + + assert err.value.get_messages() == ['Element ExtractedLicensingInfo is not the current element in scope, probably ' + 'the expected tag to start the element (LicenseID) is missing.', + 'Element ExtractedLicensingInfo is not the current element in scope, probably ' + 'the expected tag to start the element (LicenseID) is missing.', + 'Element ExtractedLicensingInfo is not the current element in scope, probably ' + 'the expected tag to start the element (LicenseID) is missing.', + 'Element ExtractedLicensingInfo is not the current element in scope, probably ' + 'the expected tag to start the element (LicenseID) is missing.'] From ce2687d38d684d98814a52aad63ea114bd4c90b9 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Mar 2023 11:55:13 +0100 Subject: [PATCH 316/362] [issue-382] rename parsing methods Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 283 ++++++++++---------- 1 file changed, 141 insertions(+), 142 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 78caa36ac..6a905e6dd 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -65,11 +65,11 @@ def __init__(self, **kwargs): self.yacc = yacc.yacc(module=self, **kwargs) @grammar_rule("start : start attrib ") - def p_start_1(self, p): + def p_start_start_attrib(self, p): pass @grammar_rule("start : attrib ") - def p_start_2(self, p): + def p_start_attrib(self, p): pass @grammar_rule("attrib : spdx_version\n| spdx_id\n| data_lics\n| doc_name\n| doc_comment\n| doc_namespace\n| " @@ -103,19 +103,19 @@ def p_unknown_tag(self, p): self.logger.append(f"Unknown tag provided in line {p.lineno(1)}") @grammar_rule("text_or_line : TEXT") - def p_text_or_line_value_1(self, p): + def p_text_or_line_value(self, p): p[0] = str_from_text(p[1]) @grammar_rule("text_or_line : LINE") - def p_text_or_line_value_2(self, p): + def p_text_or_line_value_error(self, p): p[0] = p[1] @grammar_rule("license_or_no_assertion_or_none : NO_ASSERTION") - def p_license_or_no_assertion_or_none_1(self, p): + def p_license_or_no_assertion_or_none(self, p): p[0] = SpdxNoAssertion() @grammar_rule("license_or_no_assertion_or_none : NONE") - def p_license_or_no_assertion_or_none_2(self, p): + def p_license_or_no_assertion_or_none_error(self, p): p[0] = SpdxNone() @grammar_rule("license_or_no_assertion_or_none : LINE") @@ -123,23 +123,23 @@ def p_license_or_no_assertion_or_none_3(self, p): p[0] = get_spdx_licensing().parse(p[1]) @grammar_rule("line_or_no_assertion : LINE") - def p_line_or_no_assertion_1(self, p): + def p_line_or_no_assertion(self, p): p[0] = p[1] @grammar_rule("line_or_no_assertion : NO_ASSERTION") - def p_line_or_no_assertion_2(self, p): + def p_line_or_no_assertion_error(self, p): p[0] = SpdxNoAssertion() @grammar_rule("line_or_no_assertion_or_none : text_or_line") - def p_line_1(self, p): + def p_line(self, p): p[0] = p[1] @grammar_rule("line_or_no_assertion_or_none : NO_ASSERTION") - def p_no_assertion_2(self, p): + def p_no_assertion_error(self, p): p[0] = SpdxNoAssertion() @grammar_rule("line_or_no_assertion_or_none : NONE") - def p_none_2(self, p): + def p_none_error(self, p): p[0] = SpdxNoAssertion() @grammar_rule("spdx_id : SPDX_ID LINE") @@ -154,221 +154,221 @@ def p_spdx_id(self, p): # parsing methods for creation info / document level @grammar_rule("lics_list_ver : LIC_LIST_VER LINE") - def p_lics_list_ver_1(self, p): + def p_license_list_version(self, p): try: self.creation_info["license_list_version"] = Version.from_string(p[2]) except ValueError as err: self.creation_info["logger"].append(err.args[0]) @grammar_rule("lics_list_ver : LIC_LIST_VER error") - def p_lics_list_ver_2(self, p): + def p_license_list_version_error(self, p): self.creation_info["logger"].append( f"Error while parsing LicenseListVersion: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("doc_comment : DOC_COMMENT text_or_line") - def p_doc_comment_1(self, p): + def p_doc_comment(self, p): self.creation_info["document_comment"] = p[2] @grammar_rule("doc_comment : DOC_COMMENT error") - def p_doc_comment_2(self, p): + def p_doc_comment_error(self, p): self.creation_info["logger"].append( f"Error while parsing DocumentComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("doc_namespace : DOC_NAMESPACE LINE") - def p_doc_namespace_1(self, p): + def p_doc_namespace(self, p): self.creation_info["document_namespace"] = p[2] @grammar_rule("doc_namespace : DOC_NAMESPACE error") - def p_doc_namespace_2(self, p): + def p_doc_namespace_error(self, p): self.creation_info["logger"].append( f"Error while parsing DocumentNamespace: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("data_lics : DOC_LICENSE LINE") - def p_data_license_1(self, p): + def p_data_license(self, p): self.creation_info["data_license"] = p[2] @grammar_rule("data_lics : DOC_LICENSE error") - def p_data_license_2(self, p): + def p_data_license_error(self, p): self.creation_info["logger"].append( f"Error while parsing DataLicense: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("doc_name : DOC_NAME LINE") - def p_doc_name_1(self, p): + def p_doc_name(self, p): self.creation_info["name"] = p[2] @grammar_rule("doc_name : DOC_NAME error") - def p_doc_name_2(self, p): + def p_doc_name_error(self, p): self.creation_info["logger"].append( f"Error while parsing DocumentName: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("ext_doc_ref : EXT_DOC_REF DOC_REF_ID DOC_URI EXT_DOC_REF_CHECKSUM") - def p_ext_doc_refs_1(self, p): + def p_external_document_ref(self, p): document_ref_id = p[2] document_uri = p[3] - checksum = parse_checksum(self.current_element["logger"], p[4]) + checksum = parse_checksum(self.creation_info["logger"], p[4]) external_document_ref = ExternalDocumentRef(document_ref_id, document_uri, checksum) self.creation_info.setdefault("external_document_refs", []).append(external_document_ref) @grammar_rule("ext_doc_ref : EXT_DOC_REF error") - def p_ext_doc_refs_2(self, p): + def p_external_document_ref_error(self, p): self.creation_info["logger"].append( f"Error while parsing ExternalDocumentRef: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("spdx_version : DOC_VERSION LINE") - def p_spdx_version_1(self, p): + def p_spdx_version(self, p): self.creation_info["spdx_version"] = p[2] @grammar_rule("spdx_version : DOC_VERSION error") - def p_spdx_version_2(self, p): + def p_spdx_version_error(self, p): self.creation_info["logger"].append( f"Error while parsing SPDXVersion: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("creator_comment : CREATOR_COMMENT text_or_line") - def p_creator_comment_1(self, p): + def p_creator_comment(self, p): self.creation_info["creator_comment"] = p[2] @grammar_rule("creator_comment : CREATOR_COMMENT error") - def p_creator_comment_2(self, p): + def p_creator_comment_error(self, p): self.creation_info["logger"].append( f"Error while parsing CreatorComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") - def p_creator_1(self, p): + def p_creator(self, p): """creator : CREATOR PERSON_VALUE\n| CREATOR TOOL_VALUE\n| CREATOR ORG_VALUE""" self.creation_info.setdefault("creators", []).append(ActorParser.parse_actor(p[2])) @grammar_rule("creator : CREATOR error") - def p_creator_2(self, p): + def p_creator_error(self, p): self.creation_info["logger"].append( f"Error while parsing Creator: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("created : CREATED DATE") - def p_created_1(self, p): + def p_created(self, p): self.creation_info["created"] = datetime_from_str(p[2]) @grammar_rule("created : CREATED error") - def p_created_2(self, p): + def p_created_error(self, p): self.creation_info["logger"].append( f"Error while parsing Created: Token did not match specified grammar rule. Line: {p.lineno(1)}") # parsing methods for extracted licensing info @grammar_rule("extr_lic_id : LICS_ID LINE") - def p_extr_lic_id_1(self, p): + def p_extracted_license_id(self, p): self.initialize_new_current_element(ExtractedLicensingInfo) self.current_element["license_id"] = p[2] @grammar_rule("extr_lic_id : LICS_ID error") - def p_extr_lic_id_2(self, p): + def p_extracted_license_id_error(self, p): self.current_element["logger"].append( f"Error while parsing LicenseID: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("lic_xref : LICS_CRS_REF LINE") - def p_lic_xref_1(self, p): + def p_extracted_cross_reference(self, p): self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) self.current_element.setdefault("cross_references", []).append(p[2]) @grammar_rule("lic_xref : LICS_CRS_REF error") - def p_lic_xref_2(self, p): + def p_extracted_cross_reference_error(self, p): self.current_element["logger"].append( f"Error while parsing LicenseCrossReference: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @grammar_rule("lic_comment : LICS_COMMENT text_or_line") - def p_lic_comment_1(self, p): + def p_license_comment(self, p): self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) self.current_element["comment"] = p[2] @grammar_rule("lic_comment : LICS_COMMENT error") - def p_lic_comment_2(self, p): + def p_license_comment_error(self, p): self.current_element["logger"].append( f"Error while parsing LicenseComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("extr_lic_name : LICS_NAME line_or_no_assertion") - def p_extr_lic_name_1(self, p): + def p_extracted_license_name(self, p): self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) self.current_element["license_name"] = p[2] @grammar_rule("extr_lic_name : LICS_NAME error") - def p_extr_lic_name_2(self, p): + def p_extracted_license_name_error(self, p): self.current_element["logger"].append( f"Error while parsing LicenseName: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("extr_lic_text : LICS_TEXT text_or_line") - def p_extr_lic_text_1(self, p): + def p_extracted_license_text(self, p): self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) self.current_element["extracted_text"] = p[2] @grammar_rule("extr_lic_text : LICS_TEXT error") - def p_extr_lic_text_2(self, p): + def p_extracted_license_text_error(self, p): self.current_element["logger"].append( f"Error while parsing ExtractedText: Token did not match specified grammar rule. Line: {p.lineno(1)}") # parsing methods for file @grammar_rule("file_name : FILE_NAME LINE") - def p_file_name_1(self, p): + def p_file_name(self, p): self.initialize_new_current_element(File) self.current_element["name"] = p[2] @grammar_rule("file_name : FILE_NAME error") - def p_file_name_2(self, p): + def p_file_name_error(self, p): self.initialize_new_current_element(File) self.current_element["logger"].append( f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("file_contrib : FILE_CONTRIB LINE") - def p_file_contrib_1(self, p): + def p_file_contributor(self, p): self.check_that_current_element_matches_class_for_value(File) self.current_element.setdefault("contributors", []).append(p[2]) @grammar_rule("file_contrib : FILE_CONTRIB error") - def p_file_contrib_2(self, p): + def p_file_contributor_error(self, p): self.current_element["logger"].append( f"Error while parsing FileContributor: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("file_notice : FILE_NOTICE text_or_line") - def p_file_notice_1(self, p): + def p_file_notice(self, p): self.check_that_current_element_matches_class_for_value(File) self.current_element["notice"] = p[2] @grammar_rule("file_notice : FILE_NOTICE error") - def p_file_notice_2(self, p): + def p_file_notice_error(self, p): self.current_element["logger"].append( f"Error while parsing FileNotice: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("file_cr_text : FILE_CR_TEXT line_or_no_assertion_or_none") - def p_file_cr_text_1(self, p): + def p_file_copyright_text(self, p): self.check_that_current_element_matches_class_for_value(File) self.current_element["copyright_text"] = p[2] @grammar_rule("file_cr_text : FILE_CR_TEXT error") - def p_file_cr_text_2(self, p): + def p_file_copyright_text_error(self, p): self.current_element["logger"].append( f"Error while parsing FileCopyrightText: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("file_lics_comment : FILE_LICS_COMMENT text_or_line") - def p_file_lics_comment_1(self, p): + def p_file_license_comment(self, p): self.check_that_current_element_matches_class_for_value(File) self.current_element["license_comment"] = p[2] @grammar_rule("file_lics_comment : FILE_LICS_COMMENT error") - def p_file_lics_comment_2(self, p): + def p_file_license_comment_error(self, p): self.current_element["logger"].append( f"Error while parsing LicenseComments in file: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @grammar_rule("file_attribution_text : FILE_ATTRIBUTION_TEXT text_or_line") - def p_file_attribution_text_1(self, p): + def p_file_attribution_text(self, p): self.check_that_current_element_matches_class_for_value(File) self.current_element.setdefault("attribution_texts", []).append(p[2]) @grammar_rule("file_attribution_text : FILE_ATTRIBUTION_TEXT error") - def p_file_attribution_text_2(self, p): + def p_file_attribution_text_error(self, p): self.current_element["logger"].append( f"Error while parsing FileAttributionText: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("file_lics_info : FILE_LICS_INFO license_or_no_assertion_or_none") - def p_file_lics_info_1(self, p): + def p_file_license_info(self, p): self.check_that_current_element_matches_class_for_value(File) if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): self.current_element["license_info_in_file"] = p[2] @@ -376,27 +376,27 @@ def p_file_lics_info_1(self, p): self.current_element.setdefault("license_info_in_file", []).append(p[2]) @grammar_rule("file_lics_info : FILE_LICS_INFO error") - def p_file_lics_info_2(self, p): + def p_file_license_info_error(self, p): self.current_element["logger"].append( f"Error while parsing LicenseInfoInFile: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("file_comment : FILE_COMMENT text_or_line") - def p_file_comment_1(self, p): + def p_file_comment(self, p): self.check_that_current_element_matches_class_for_value(File) self.current_element["comment"] = p[2] @grammar_rule("file_comment : FILE_COMMENT error") - def p_file_comment_2(self, p): + def p_file_comment_error(self, p): self.current_element["logger"].append( f"Error while parsing FileComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("file_type : FILE_TYPE file_type_value") - def p_file_type_1(self, p): + def p_file_type(self, p): self.check_that_current_element_matches_class_for_value(File) self.current_element.setdefault("file_type", []).append(FileType[p[2]]) @grammar_rule("file_type : FILE_TYPE error") - def p_file_type_2(self, p): + def p_file_type_error(self, p): self.current_element["logger"].append( f"Error while parsing FileType: Token did not match any of the valid values. Line: {p.lineno(1)}") @@ -407,23 +407,23 @@ def p_file_type_value(self, p): p[0] = p[1] @grammar_rule("file_checksum : FILE_CHECKSUM CHECKSUM") - def p_file_checksum_1(self, p): + def p_file_checksum(self, p): self.check_that_current_element_matches_class_for_value(File) checksum = parse_checksum(self.current_element["logger"], p[2]) self.current_element.setdefault("checksums", []).append(checksum) @grammar_rule("file_checksum : FILE_CHECKSUM error") - def p_file_checksum_2(self, p): + def p_file_checksum_error(self, p): self.current_element["logger"].append( f"Error while parsing Checksum in file: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("file_conc : FILE_LICS_CONC license_or_no_assertion_or_none") - def p_file_conc_1(self, p): + def p_file_license_concluded(self, p): self.check_that_current_element_matches_class_for_value(File) self.current_element["license_concluded"] = p[2] @grammar_rule("file_conc : FILE_LICS_CONC error") - def p_file_conc_2(self, p): + def p_file_license_concluded_error(self, p): self.current_element["logger"].append( f"Error while parsing LicenseConcluded in file: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @@ -436,7 +436,7 @@ def p_package_name(self, p): self.current_element["name"] = p[2] @grammar_rule("package_name : PKG_NAME error") - def p_package_name_1(self, p): + def p_package_name_error(self, p): self.initialize_new_current_element(Package) self.construct_current_element() self.current_element["class"] = Package @@ -444,59 +444,59 @@ def p_package_name_1(self, p): f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("pkg_desc : PKG_DESC text_or_line") - def p_pkg_desc_1(self, p): + def p_pkg_description(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["description"] = p[2] @grammar_rule("pkg_desc : PKG_DESC error") - def p_pkg_desc_2(self, p): + def p_pkg_description_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageDescription: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("pkg_comment : PKG_COMMENT text_or_line") - def p_pkg_comment_1(self, p): + def p_pkg_comment(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["comment"] = p[2] @grammar_rule("pkg_comment : PKG_COMMENT error") - def p_pkg_comment_2(self, p): + def p_pkg_comment_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("pkg_attribution_text : PKG_ATTRIBUTION_TEXT text_or_line") - def p_pkg_attribution_text_1(self, p): + def p_pkg_attribution_text(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element.setdefault("attribution_texts", []).append(p[2]) @grammar_rule("pkg_attribution_text : PKG_ATTRIBUTION_TEXT error") - def p_pkg_attribution_text_2(self, p): + def p_pkg_attribution_text_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageAttributionText: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @grammar_rule("pkg_summary : PKG_SUM text_or_line") - def p_pkg_summary_1(self, p): + def p_pkg_summary(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["summary"] = p[2] @grammar_rule("pkg_summary : PKG_SUM error") - def p_pkg_summary_2(self, p): + def p_pkg_summary_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageSummary: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("pkg_cr_text : PKG_CPY_TEXT line_or_no_assertion_or_none") - def p_pkg_cr_text_1(self, p): + def p_pkg_copyright_text(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["copyright_text"] = p[2] @grammar_rule("pkg_cr_text : PKG_CPY_TEXT error") - def p_pkg_cr_text_2(self, p): + def p_pkg_copyright_text_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageCopyrightText: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @grammar_rule("pkg_ext_ref : PKG_EXT_REF LINE PKG_EXT_REF_COMMENT text_or_line\n | PKG_EXT_REF LINE") - def p_pkg_ext_refs_1(self, p): + def p_pkg_external_refs(self, p): self.check_that_current_element_matches_class_for_value(Package) category, reference_type, locator = p[2].split(" ") comment = None @@ -519,35 +519,35 @@ def p_pkg_ext_refs_1(self, p): self.current_element.setdefault("external_references", []).append(external_package_ref) @grammar_rule("pkg_ext_ref : PKG_EXT_REF error") - def p_pkg_ext_refs_2(self, p): + def p_pkg_external_refs_error(self, p): self.current_element["logger"].append( f"Error while parsing ExternalRef in package: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @grammar_rule("pkg_lic_comment : PKG_LICS_COMMENT text_or_line") - def p_pkg_lic_comment_1(self, p): + def p_pkg_license_comment(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["license_comment"] = p[2] @grammar_rule("pkg_lic_comment : PKG_LICS_COMMENT error") - def p_pkg_lic_comment_2(self, p): + def p_pkg_license_comment_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageLicenseComments: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @grammar_rule("pkg_lic_decl : PKG_LICS_DECL license_or_no_assertion_or_none") - def p_pkg_lic_decl_1(self, p): + def p_pkg_license_declared(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["license_declared"] = p[2] @grammar_rule("pkg_lic_decl : PKG_LICS_DECL error") - def p_pkg_lic_decl_2(self, p): + def p_pkg_license_declared_error(self, p): self.current_element["logger"].append( f"Error while parsing LicenseDeclared in package: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @grammar_rule("pkg_lic_ff : PKG_LICS_FFILE license_or_no_assertion_or_none") - def p_pkg_lic_ff_1(self, p): + def p_pkg_license_info_from_file(self, p): self.check_that_current_element_matches_class_for_value(Package) if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): self.current_element["license_info_from_files"] = p[2] @@ -555,45 +555,45 @@ def p_pkg_lic_ff_1(self, p): self.current_element.setdefault("license_info_from_files", []).append(p[2]) @grammar_rule("pkg_lic_ff : PKG_LICS_FFILE error") - def p_pkg_lic_ff_error(self, p): + def p_pkg_license_info_from_file_error(self, p): self.current_element["logger"].append( f"Error while parsing LicenseInfoFromFiles in package: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @grammar_rule("pkg_lic_conc : PKG_LICS_CONC license_or_no_assertion_or_none") - def p_pkg_lic_conc_1(self, p): + def p_pkg_license_concluded(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["license_concluded"] = p[2] @grammar_rule("pkg_lic_conc : PKG_LICS_CONC error") - def p_pkg_lic_conc_2(self, p): + def p_pkg_license_concluded_error(self, p): self.current_element["logger"].append( f"Error while parsing LicenseConcluded in package: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @grammar_rule("pkg_src_info : PKG_SRC_INFO text_or_line") - def p_pkg_src_info_1(self, p): + def p_pkg_source_info(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["source_info"] = p[2] @grammar_rule("pkg_src_info : PKG_SRC_INFO error") - def p_pkg_src_info_2(self, p): + def p_pkg_source_info_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageSourceInfo: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("pkg_checksum : PKG_CHECKSUM CHECKSUM") - def p_pkg_checksum_1(self, p): + def p_pkg_checksum(self, p): self.check_that_current_element_matches_class_for_value(Package) checksum = parse_checksum(self.current_element["logger"], p[2]) self.current_element.setdefault("checksums", []).append(checksum) @grammar_rule("pkg_checksum : PKG_CHECKSUM error") - def p_pkg_checksum_2(self, p): + def p_pkg_checksum_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageChecksum: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("pkg_verif : PKG_VERF_CODE LINE") - def p_pkg_verif_1(self, p): + def p_pkg_verification_code(self, p): self.check_that_current_element_matches_class_for_value(Package) verif_code_regex = re.compile(r"([0-9a-f]+)\s*(\(excludes:\s*(.+)\))?", re.UNICODE) verif_code_code_grp = 1 @@ -606,34 +606,34 @@ def p_pkg_verif_1(self, p): self.current_element["verification_code"] = PackageVerificationCode(value, excluded_files) @grammar_rule("pkg_verif : PKG_VERF_CODE error") - def p_pkg_verif_2(self, p): + def p_pkg_verification_code_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageVerificationCode: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @grammar_rule("pkg_home : PKG_HOME line_or_no_assertion_or_none") - def p_pkg_home_1(self, p): + def p_pkg_homepage(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["homepage"] = p[2] @grammar_rule("pkg_home : PKG_HOME error") - def p_pkg_home_2(self, p): + def p_pkg_homepage_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageHomePage: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("pkg_down_location : PKG_DOWN line_or_no_assertion_or_none") - def p_pkg_down_location_1(self, p): + def p_pkg_download_location(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["download_location"] = p[2] @grammar_rule("pkg_down_location : PKG_DOWN error") - def p_pkg_down_location_2(self, p): + def p_pkg_download_location_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageDownloadLocation: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @grammar_rule("pkg_files_analyzed : PKG_FILES_ANALYZED LINE") - def p_pkg_files_analyzed_1(self, p): + def p_pkg_files_analyzed(self, p): self.check_that_current_element_matches_class_for_value(Package) if p[2] in ['false', 'False']: self.current_element["files_analyzed"] = False @@ -641,28 +641,28 @@ def p_pkg_files_analyzed_1(self, p): self.current_element["files_analyzed"] = True @grammar_rule("pkg_files_analyzed : PKG_FILES_ANALYZED error") - def p_pkg_files_analyzed_2(self, p): + def p_pkg_files_analyzed_error(self, p): self.current_element["logger"].append( f"Error while parsing FilesAnalyzed in package: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @grammar_rule("pkg_orig : PKG_ORIG pkg_supplier_values") - def p_pkg_orig_1(self, p): + def p_pkg_originator(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["originator"] = p[2] @grammar_rule("pkg_orig : PKG_ORIG error") - def p_pkg_orig_2(self, p): + def p_pkg_originator_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageOriginator: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("pkg_supplier : PKG_SUPPL pkg_supplier_values") - def p_pkg_supplier_1(self, p): + def p_pkg_supplier(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["supplier"] = p[2] @grammar_rule("pkg_supplier : PKG_SUPPL error") - def p_pkg_supplier_2(self, p): + def p_pkg_supplier_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageSupplier: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -680,27 +680,27 @@ def p_pkg_file_name(self, p): self.current_element["file_name"] = p[2] @grammar_rule("pkg_file_name : PKG_FILE_NAME error") - def p_pkg_file_name_1(self, p): + def p_pkg_file_name_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageFileName: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("package_version : PKG_VERSION LINE") - def p_package_version_1(self, p): + def p_package_version(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["version"] = p[2] @grammar_rule("package_version : PKG_VERSION error") - def p_package_version_2(self, p): + def p_package_version_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageVersion: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("primary_package_purpose : PRIMARY_PACKAGE_PURPOSE primary_package_purpose_value") - def p_primary_package_purpose_1(self, p): + def p_primary_package_purpose(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["primary_package_purpose"] = PackagePurpose[p[2].replace("-", "_")] @grammar_rule("primary_package_purpose : PRIMARY_PACKAGE_PURPOSE error") - def p_primary_package_purpose_2(self, p): + def p_primary_package_purpose_error(self, p): self.current_element["logger"].append( f"Error while parsing PrimaryPackagePurpose: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @@ -711,43 +711,43 @@ def p_primary_package_purpose_value(self, p): p[0] = p[1] @grammar_rule("built_date : BUILT_DATE DATE") - def p_built_date_1(self, p): + def p_built_date(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["built_date"] = datetime_from_str(p[2]) @grammar_rule("built_date : BUILT_DATE error") - def p_built_date_2(self, p): + def p_built_date_error(self, p): self.current_element["logger"].append( f"Error while parsing BuiltDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("release_date : RELEASE_DATE DATE") - def p_release_date_1(self, p): + def p_release_date(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["release_date"] = datetime_from_str(p[2]) @grammar_rule("release_date : RELEASE_DATE error") - def p_release_date_2(self, p): + def p_release_date_error(self, p): self.current_element["logger"].append( f"Error while parsing ReleaseDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("valid_until_date : VALID_UNTIL_DATE DATE") - def p_valid_until_date_1(self, p): + def p_valid_until_date(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["valid_until_date"] = datetime_from_str(p[2]) @grammar_rule("valid_until_date : VALID_UNTIL_DATE error") - def p_valid_until_date_2(self, p): + def p_valid_until_date_error(self, p): self.current_element["logger"].append( f"Error while parsing ValidUntilDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") # parsing methods for snippet @grammar_rule("snip_spdx_id : SNIPPET_SPDX_ID LINE") - def p_snip_spdx_id(self, p): + def p_snippet_spdx_id(self, p): self.initialize_new_current_element(Snippet) self.current_element["spdx_id"] = p[2] @grammar_rule("snip_spdx_id : SNIPPET_SPDX_ID error") - def p_snip_spdx_id_1(self, p): + def p_snippet_spdx_id_error(self, p): self.initialize_new_current_element(Snippet) self.current_element["logger"].append( f"Error while parsing SnippetSPDXID: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -758,7 +758,7 @@ def p_snippet_name(self, p): self.current_element["name"] = p[2] @grammar_rule("snip_name : SNIPPET_NAME error") - def p_snippet_name_1(self, p): + def p_snippet_name_error(self, p): self.current_element["logger"].append( f"Error while parsing SnippetName: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -768,50 +768,50 @@ def p_snippet_comment(self, p): self.current_element["comment"] = p[2] @grammar_rule("snip_comment : SNIPPET_COMMENT error") - def p_snippet_comment_1(self, p): + def p_snippet_comment_error(self, p): self.current_element["logger"].append( f"Error while parsing SnippetComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT text_or_line") - def p_snippet_attribution_text_1(self, p): + def p_snippet_attribution_text(self, p): self.check_that_current_element_matches_class_for_value(Snippet) self.current_element.setdefault("attribution_texts", []).append(p[2]) @grammar_rule("snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT error") - def p_snippet_attribution_text_2(self, p): + def p_snippet_attribution_text_error(self, p): self.current_element["logger"].append( f"Error while parsing SnippetAttributionText: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @grammar_rule("snip_cr_text : SNIPPET_CR_TEXT line_or_no_assertion_or_none") - def p_snippet_cr_text(self, p): + def p_snippet_copyright_text(self, p): self.check_that_current_element_matches_class_for_value(Snippet) self.current_element["copyright_text"] = p[2] @grammar_rule("snip_cr_text : SNIPPET_CR_TEXT error") - def p_snippet_cr_text_1(self, p): + def p_snippet_copyright_text_error(self, p): self.current_element["logger"].append( f"Error while parsing SnippetCopyrightText: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @grammar_rule("snip_lic_comment : SNIPPET_LICS_COMMENT text_or_line") - def p_snippet_lic_comment(self, p): + def p_snippet_license_comment(self, p): self.check_that_current_element_matches_class_for_value(Snippet) self.current_element["license_comment"] = p[2] @grammar_rule("snip_lic_comment : SNIPPET_LICS_COMMENT error") - def p_snippet_lic_comment_1(self, p): + def p_snippet_license_comment_error(self, p): self.current_element["logger"].append( f"Error while parsing SnippetLicenseComments: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @grammar_rule("snip_file_spdx_id : SNIPPET_FILE_SPDXID LINE") - def p_snip_from_file_spdxid(self, p): + def p_snippet_from_file_spdxid(self, p): self.check_that_current_element_matches_class_for_value(Snippet) self.current_element["file_spdx_id"] = p[2] @grammar_rule("snip_file_spdx_id : SNIPPET_FILE_SPDXID error") - def p_snip_from_file_spdxid_1(self, p): + def p_snippet_from_file_spdxid_error(self, p): self.current_element["logger"].append( f"Error while parsing SnippetFromFileSPDXID: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @@ -822,13 +822,13 @@ def p_snippet_concluded_license(self, p): self.current_element["license_concluded"] = p[2] @grammar_rule("snip_lics_conc : SNIPPET_LICS_CONC error") - def p_snippet_concluded_license_1(self, p): + def p_snippet_concluded_license_error(self, p): self.current_element["logger"].append( f"Error while parsing SnippetLicenseConcluded: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @grammar_rule("snip_lics_info : SNIPPET_LICS_INFO license_or_no_assertion_or_none") - def p_snippet_lics_info(self, p): + def p_snippet_license_info(self, p): self.check_that_current_element_matches_class_for_value(Snippet) if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): self.current_element["license_info_in_snippet"] = p[2] @@ -836,7 +836,7 @@ def p_snippet_lics_info(self, p): self.current_element.setdefault("license_info_in_snippet", []).append(p[2]) @grammar_rule("snip_lics_info : SNIPPET_LICS_INFO error") - def p_snippet_lics_info_1(self, p): + def p_snippet_license_info_error(self, p): self.current_element["logger"].append( f"Error while parsing LicenseInfoInSnippet: Token did not match specified grammar rule. " @@ -854,8 +854,7 @@ def p_snippet_byte_range(self, p): self.current_element["byte_range"] = startpoint, endpoint @grammar_rule("snip_byte_range : SNIPPET_BYTE_RANGE error") - def p_snippet_byte_range_1(self, p): - + def p_snippet_byte_range_error(self, p): self.current_element["logger"].append( f"Error while parsing SnippetByteRange: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -871,12 +870,12 @@ def p_snippet_line_range(self, p): self.current_element["line_range"] = startpoint, endpoint @grammar_rule("snip_line_range : SNIPPET_LINE_RANGE error") - def p_snippet_line_range_1(self, p): + def p_snippet_line_range_error(self, p): self.current_element["logger"].append( f"Error while parsing SnippetLineRange: Token did not match specified grammar rule. Line: {p.lineno(1)}") # parsing methods for annotation - def p_annotator_1(self, p): + def p_annotator(self, p): """annotator : ANNOTATOR PERSON_VALUE\n| TOOL_VALUE\n| ORG_VALUE""" self.initialize_new_current_element(Annotation) try: @@ -885,38 +884,38 @@ def p_annotator_1(self, p): self.current_element["logger"].append(err.get_messages()) @grammar_rule("annotator : ANNOTATOR error") - def p_annotator_2(self, p): + def p_annotator_error(self, p): self.initialize_new_current_element(Annotation) self.current_element["logger"].append( f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("annotation_date : ANNOTATION_DATE DATE") - def p_annotation_date_1(self, p): + def p_annotation_date(self, p): self.check_that_current_element_matches_class_for_value(Annotation) self.current_element["annotation_date"] = datetime_from_str(p[2]) @grammar_rule("annotation_date : ANNOTATION_DATE error") - def p_annotation_date_2(self, p): + def p_annotation_date_error(self, p): self.current_element["logger"].append( f"Error while parsing AnnotationDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("annotation_comment : ANNOTATION_COMMENT text_or_line") - def p_annotation_comment_1(self, p): + def p_annotation_comment(self, p): self.check_that_current_element_matches_class_for_value(Annotation) self.current_element["annotation_comment"] = p[2] @grammar_rule("annotation_comment : ANNOTATION_COMMENT error") - def p_annotation_comment_2(self, p): + def p_annotation_comment_error(self, p): self.current_element["logger"].append( f"Error while parsing AnnotationComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("annotation_type : ANNOTATION_TYPE annotation_type_value") - def p_annotation_type_1(self, p): + def p_annotation_type(self, p): self.check_that_current_element_matches_class_for_value(Annotation) self.current_element["annotation_type"] = AnnotationType[p[2]] @grammar_rule("annotation_type : ANNOTATION_TYPE error") - def p_annotation_type_2(self, p): + def p_annotation_type_error(self, p): self.current_element["logger"].append( f"Error while parsing AnnotationType: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -925,11 +924,11 @@ def p_annotation_type_value(self, p): p[0] = p[1] @grammar_rule("annotation_spdx_id : ANNOTATION_SPDX_ID LINE") - def p_annotation_spdx_id_1(self, p): + def p_annotation_spdx_id(self, p): self.current_element["spdx_id"] = p[2] @grammar_rule("annotation_spdx_id : ANNOTATION_SPDX_ID error") - def p_annotation_spdx_id_2(self, p): + def p_annotation_spdx_id_error(self, p): self.current_element["logger"].append( f"Error while parsing SPDXREF in annotation: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @@ -937,7 +936,7 @@ def p_annotation_spdx_id_2(self, p): # parsing methods for relationship @grammar_rule("relationship : RELATIONSHIP relationship_value RELATIONSHIP_COMMENT text_or_line\n " "| RELATIONSHIP relationship_value") - def p_relationship_1(self, p): + def p_relationship(self, p): self.initialize_new_current_element(Relationship) try: spdx_element_id, relationship_type, related_spdx_element_id = p[2].split(" ") @@ -960,7 +959,7 @@ def p_relationship_1(self, p): self.current_element["comment"] = p[4] @grammar_rule("relationship : RELATIONSHIP error") - def p_relationship_2(self, p): + def p_relationship_error(self, p): self.initialize_new_current_element(Relationship) self.current_element["logger"].append( f"Error while parsing Relationship: Token did not match specified grammar rule. Line: {p.lineno(1)}") From d3d3200991e1530c10e97f4cc321f0f088e1aeb4 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Mar 2023 14:41:42 +0100 Subject: [PATCH 317/362] [refactor] merge parsing functions that return the same values Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 53 ++++++--------------- 1 file changed, 14 insertions(+), 39 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 6a905e6dd..2e7e32cd3 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -103,44 +103,29 @@ def p_unknown_tag(self, p): self.logger.append(f"Unknown tag provided in line {p.lineno(1)}") @grammar_rule("text_or_line : TEXT") - def p_text_or_line_value(self, p): + def p_text(self, p): p[0] = str_from_text(p[1]) - @grammar_rule("text_or_line : LINE") - def p_text_or_line_value_error(self, p): + @grammar_rule("text_or_line : LINE\n line_or_no_assertion : LINE\nline_or_no_assertion_or_none : text_or_line") + def p_line(self, p): p[0] = p[1] - @grammar_rule("license_or_no_assertion_or_none : NO_ASSERTION") - def p_license_or_no_assertion_or_none(self, p): + @grammar_rule("license_or_no_assertion_or_none : NO_ASSERTION\n actor_or_no_assertion : NO_ASSERTION\n" + "line_or_no_assertion : NO_ASSERTION\n line_or_no_assertion_or_none : NO_ASSERTION") + def p_no_assertion(self, p): p[0] = SpdxNoAssertion() - @grammar_rule("license_or_no_assertion_or_none : NONE") - def p_license_or_no_assertion_or_none_error(self, p): + @grammar_rule("license_or_no_assertion_or_none : NONE\n line_or_no_assertion_or_none : NONE") + def p_none(self, p): p[0] = SpdxNone() @grammar_rule("license_or_no_assertion_or_none : LINE") - def p_license_or_no_assertion_or_none_3(self, p): + def p_license(self, p): p[0] = get_spdx_licensing().parse(p[1]) - @grammar_rule("line_or_no_assertion : LINE") - def p_line_or_no_assertion(self, p): - p[0] = p[1] - - @grammar_rule("line_or_no_assertion : NO_ASSERTION") - def p_line_or_no_assertion_error(self, p): - p[0] = SpdxNoAssertion() - - @grammar_rule("line_or_no_assertion_or_none : text_or_line") - def p_line(self, p): - p[0] = p[1] - - @grammar_rule("line_or_no_assertion_or_none : NO_ASSERTION") - def p_no_assertion_error(self, p): - p[0] = SpdxNoAssertion() - - @grammar_rule("line_or_no_assertion_or_none : NONE") - def p_none_error(self, p): - p[0] = SpdxNoAssertion() + @grammar_rule("actor_or_no_assertion : PERSON_VALUE\n | ORG_VALUE") + def p_actor_values(self, p): + p[0] = ActorParser.parse_actor(p[1]) @grammar_rule("spdx_id : SPDX_ID LINE") def p_spdx_id(self, p): @@ -438,8 +423,6 @@ def p_package_name(self, p): @grammar_rule("package_name : PKG_NAME error") def p_package_name_error(self, p): self.initialize_new_current_element(Package) - self.construct_current_element() - self.current_element["class"] = Package self.current_element["logger"].append( f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -646,7 +629,7 @@ def p_pkg_files_analyzed_error(self, p): f"Error while parsing FilesAnalyzed in package: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") - @grammar_rule("pkg_orig : PKG_ORIG pkg_supplier_values") + @grammar_rule("pkg_orig : PKG_ORIG actor_or_no_assertion") def p_pkg_originator(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["originator"] = p[2] @@ -656,7 +639,7 @@ def p_pkg_originator_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageOriginator: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("pkg_supplier : PKG_SUPPL pkg_supplier_values") + @grammar_rule("pkg_supplier : PKG_SUPPL actor_or_no_assertion") def p_pkg_supplier(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element["supplier"] = p[2] @@ -666,14 +649,6 @@ def p_pkg_supplier_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageSupplier: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("pkg_supplier_values : NO_ASSERTION") - def p_pkg_supplier_values_1(self, p): - p[0] = SpdxNoAssertion() - - @grammar_rule("pkg_supplier_values : PERSON_VALUE\n | ORG_VALUE\n | TOOL_VALUE") - def p_pkg_supplier_values_2(self, p): - p[0] = ActorParser.parse_actor(p[1]) - @grammar_rule("pkg_file_name : PKG_FILE_NAME LINE") def p_pkg_file_name(self, p): self.check_that_current_element_matches_class_for_value(Package) From 055dce2e0d77822d4abedac75eb4280dd56a335f Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Mar 2023 15:34:13 +0100 Subject: [PATCH 318/362] [refactor] creation_info_parser Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 91 +++++++++++-------- .../tagvalue/test_creation_info_parser.py | 5 +- 2 files changed, 56 insertions(+), 40 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 2e7e32cd3..cd9b2eb98 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -11,7 +11,7 @@ # limitations under the License. import re -from typing import Any, List, Dict +from typing import Any, List, Dict, Optional, Callable from license_expression import get_spdx_licensing from ply import yacc @@ -72,28 +72,29 @@ def p_start_start_attrib(self, p): def p_start_attrib(self, p): pass - @grammar_rule("attrib : spdx_version\n| spdx_id\n| data_lics\n| doc_name\n| doc_comment\n| doc_namespace\n| " - "creator\n| created\n| creator_comment\n| lics_list_ver\n| ext_doc_ref\n" - # attributes for file - "| file_name\n| file_type\n| file_checksum\n| file_conc\n| file_lics_info\n| file_cr_text\n" - "| file_lics_comment\n| file_attribution_text\n| file_notice\n| file_comment\n| file_contrib\n" - # attributes for annotation - "| annotator\n| annotation_date\n| annotation_comment\n| annotation_type\n| annotation_spdx_id\n" - # attributes for relationship - "| relationship\n" - # attributes for snippet - "| snip_spdx_id\n| snip_name\n| snip_comment\n| snippet_attribution_text\n| snip_cr_text\n" - "| snip_lic_comment\n| snip_file_spdx_id\n| snip_lics_conc\n| snip_lics_info\n| snip_byte_range\n" - "| snip_line_range\n" - # attributes for package - "| package_name\n| package_version\n| pkg_down_location\n| pkg_files_analyzed\n| pkg_home\n" - "| pkg_summary\n| pkg_src_info\n| pkg_file_name\n| pkg_supplier\n| pkg_orig\n| pkg_checksum\n" - "| pkg_verif\n| pkg_desc\n| pkg_comment\n| pkg_attribution_text\n| pkg_lic_decl\n| pkg_lic_conc\n" - "| pkg_lic_ff\n| pkg_lic_comment\n| pkg_cr_text\n| pkg_ext_ref\n| primary_package_purpose\n" - "| built_date\n| release_date\n| valid_until_date\n" - # attributes for extracted licensing info - "| extr_lic_id\n| extr_lic_text\n| extr_lic_name\n| lic_xref\n| lic_comment\n" - "| unknown_tag ") + @grammar_rule( + "attrib : spdx_version\n| spdx_id\n| data_license\n| doc_name\n| document_comment\n| document_namespace\n| " + "creator\n| created\n| creator_comment\n| license_list_version\n| ext_doc_ref\n" + # attributes for file + "| file_name\n| file_type\n| file_checksum\n| file_conc\n| file_lics_info\n| file_cr_text\n" + "| file_lics_comment\n| file_attribution_text\n| file_notice\n| file_comment\n| file_contrib\n" + # attributes for annotation + "| annotator\n| annotation_date\n| annotation_comment\n| annotation_type\n| annotation_spdx_id\n" + # attributes for relationship + "| relationship\n" + # attributes for snippet + "| snip_spdx_id\n| snip_name\n| snip_comment\n| snippet_attribution_text\n| snip_cr_text\n" + "| snip_lic_comment\n| snip_file_spdx_id\n| snip_lics_conc\n| snip_lics_info\n| snip_byte_range\n" + "| snip_line_range\n" + # attributes for package + "| package_name\n| package_version\n| pkg_down_location\n| pkg_files_analyzed\n| pkg_home\n" + "| pkg_summary\n| pkg_src_info\n| pkg_file_name\n| pkg_supplier\n| pkg_orig\n| pkg_checksum\n" + "| pkg_verif\n| pkg_desc\n| pkg_comment\n| pkg_attribution_text\n| pkg_lic_decl\n| pkg_lic_conc\n" + "| pkg_lic_ff\n| pkg_lic_comment\n| pkg_cr_text\n| pkg_ext_ref\n| primary_package_purpose\n" + "| built_date\n| release_date\n| valid_until_date\n" + # attributes for extracted licensing info + "| extr_lic_id\n| extr_lic_text\n| extr_lic_name\n| lic_xref\n| lic_comment\n" + "| unknown_tag ") def p_attrib(self, p): pass @@ -137,49 +138,61 @@ def p_spdx_id(self, p): self.creation_info["spdx_id"] = p[2] # parsing methods for creation info / document level + def set_creation_info_value(self, parsed_value: Any, argument_name: Optional[str] = None, + method_to_apply: Callable = lambda x: x): + if not argument_name: + argument_name = str(parsed_value.slice[0]) + if argument_name in self.creation_info: + self.creation_info["logger"].append( + f"Multiple values for {parsed_value[1]} found. Line: {parsed_value.lineno(1)}") + return + self.creation_info[argument_name] = method_to_apply(parsed_value[2]) - @grammar_rule("lics_list_ver : LIC_LIST_VER LINE") + @grammar_rule("license_list_version : LIC_LIST_VER LINE") def p_license_list_version(self, p): try: - self.creation_info["license_list_version"] = Version.from_string(p[2]) + if str(p.slice[0]) in self.creation_info: + self.creation_info["logger"].append(f"Multiple values for {p[1]} found. Line: {p.lineno(1)}") + return + self.creation_info[str(p.slice[0])] = Version.from_string(p[2]) except ValueError as err: self.creation_info["logger"].append(err.args[0]) - @grammar_rule("lics_list_ver : LIC_LIST_VER error") + @grammar_rule("license_list_version : LIC_LIST_VER error") def p_license_list_version_error(self, p): self.creation_info["logger"].append( f"Error while parsing LicenseListVersion: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("doc_comment : DOC_COMMENT text_or_line") + @grammar_rule("document_comment : DOC_COMMENT text_or_line") def p_doc_comment(self, p): - self.creation_info["document_comment"] = p[2] + self.set_creation_info_value(p) - @grammar_rule("doc_comment : DOC_COMMENT error") + @grammar_rule("document_comment : DOC_COMMENT error") def p_doc_comment_error(self, p): self.creation_info["logger"].append( f"Error while parsing DocumentComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("doc_namespace : DOC_NAMESPACE LINE") + @grammar_rule("document_namespace : DOC_NAMESPACE LINE") def p_doc_namespace(self, p): - self.creation_info["document_namespace"] = p[2] + self.set_creation_info_value(p) - @grammar_rule("doc_namespace : DOC_NAMESPACE error") + @grammar_rule("document_namespace : DOC_NAMESPACE error") def p_doc_namespace_error(self, p): self.creation_info["logger"].append( f"Error while parsing DocumentNamespace: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("data_lics : DOC_LICENSE LINE") + @grammar_rule("data_license : DOC_LICENSE LINE") def p_data_license(self, p): - self.creation_info["data_license"] = p[2] + self.set_creation_info_value(p) - @grammar_rule("data_lics : DOC_LICENSE error") + @grammar_rule("data_license : DOC_LICENSE error") def p_data_license_error(self, p): self.creation_info["logger"].append( f"Error while parsing DataLicense: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("doc_name : DOC_NAME LINE") def p_doc_name(self, p): - self.creation_info["name"] = p[2] + self.set_creation_info_value(p, "name") @grammar_rule("doc_name : DOC_NAME error") def p_doc_name_error(self, p): @@ -201,7 +214,7 @@ def p_external_document_ref_error(self, p): @grammar_rule("spdx_version : DOC_VERSION LINE") def p_spdx_version(self, p): - self.creation_info["spdx_version"] = p[2] + self.set_creation_info_value(p) @grammar_rule("spdx_version : DOC_VERSION error") def p_spdx_version_error(self, p): @@ -210,7 +223,7 @@ def p_spdx_version_error(self, p): @grammar_rule("creator_comment : CREATOR_COMMENT text_or_line") def p_creator_comment(self, p): - self.creation_info["creator_comment"] = p[2] + self.set_creation_info_value(p) @grammar_rule("creator_comment : CREATOR_COMMENT error") def p_creator_comment_error(self, p): @@ -228,7 +241,7 @@ def p_creator_error(self, p): @grammar_rule("created : CREATED DATE") def p_created(self, p): - self.creation_info["created"] = datetime_from_str(p[2]) + self.set_creation_info_value(p, method_to_apply=datetime_from_str) @grammar_rule("created : CREATED error") def p_created_error(self, p): diff --git a/tests/spdx/parser/tagvalue/test_creation_info_parser.py b/tests/spdx/parser/tagvalue/test_creation_info_parser.py index 6d9e4d427..d36e822c6 100644 --- a/tests/spdx/parser/tagvalue/test_creation_info_parser.py +++ b/tests/spdx/parser/tagvalue/test_creation_info_parser.py @@ -81,7 +81,10 @@ def test_creation_info(): 'SPDXID: SPDXRef-DOCUMENT']), ['Error while constructing CreationInfo: CreationInfo.__init__() missing 3 ' "required positional arguments: 'document_namespace', 'creators', and " - "'created'"])])) + "'created'"]), + ('LicenseListVersion: 3.5\nLicenseListVersion: 3.7', + [["Error while parsing CreationInfo: ['Multiple values for LicenseListVersion " + "found. Line: 2']"]])])) def test_invalid_creation_info(document_str, expected_message): parser = Parser() with pytest.raises(SPDXParsingError) as err: From e019c2dc02a4f76dd354d5decb75333d41f7bd1f Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Mar 2023 16:15:04 +0100 Subject: [PATCH 319/362] [refactor] use helper method to set unique values Signed-off-by: Meret Behrens --- .../parser/tagvalue/parser/helper_methods.py | 19 +- src/spdx/parser/tagvalue/parser/tagvalue.py | 181 ++++++++---------- 2 files changed, 102 insertions(+), 98 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/helper_methods.py b/src/spdx/parser/tagvalue/parser/helper_methods.py index 63690a829..c47e5b5c0 100644 --- a/src/spdx/parser/tagvalue/parser/helper_methods.py +++ b/src/spdx/parser/tagvalue/parser/helper_methods.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import re -from typing import Optional +from typing import Optional, Callable, Any, Dict from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.parser.error import SPDXParsingError @@ -22,6 +22,7 @@ def grammar_rule(doc): def decorate(func): func.__doc__ = doc return func + return decorate @@ -51,3 +52,19 @@ def parse_checksum(logger: Logger, checksum_str: str) -> Optional[Checksum]: logger.append(err.get_messages()) checksum = None return checksum + + +def set_value(parsed_value: Any, dict_to_fill: Dict[str, Any], argument_name: Optional[str] = None, + method_to_apply: Callable = lambda x: x): + if not argument_name: + argument_name = str(parsed_value.slice[0]) + if argument_name in dict_to_fill: + dict_to_fill["logger"].append( + f"Multiple values for {parsed_value[1]} found. Line: {parsed_value.lineno(1)}") + return + try: + dict_to_fill[argument_name] = method_to_apply(parsed_value[2]) + except SPDXParsingError as err: + dict_to_fill["logger"].append(err.get_messages()) + except ValueError as err: + dict_to_fill["logger"].append(err.args[0]) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index cd9b2eb98..64237b46e 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -11,7 +11,7 @@ # limitations under the License. import re -from typing import Any, List, Dict, Optional, Callable +from typing import Any, List, Dict from license_expression import get_spdx_licensing from ply import yacc @@ -35,7 +35,7 @@ from spdx.parser.logger import Logger from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.tagvalue.lexer.tagvalue import SPDXLexer -from spdx.parser.tagvalue.parser.helper_methods import grammar_rule, str_from_text, parse_checksum +from spdx.parser.tagvalue.parser.helper_methods import grammar_rule, str_from_text, parse_checksum, set_value CLASS_MAPPING = dict(File="files", Annotation="annotations", Relationship="relationships", Snippet="snippets", Package="packages", ExtractedLicensingInfo="extracted_licensing_info") @@ -84,16 +84,16 @@ def p_start_attrib(self, p): "| relationship\n" # attributes for snippet "| snip_spdx_id\n| snip_name\n| snip_comment\n| snippet_attribution_text\n| snip_cr_text\n" - "| snip_lic_comment\n| snip_file_spdx_id\n| snip_lics_conc\n| snip_lics_info\n| snip_byte_range\n" + "| snip_lic_comment\n| file_spdx_id\n| snip_lics_conc\n| snip_lics_info\n| snip_byte_range\n" "| snip_line_range\n" # attributes for package - "| package_name\n| package_version\n| pkg_down_location\n| pkg_files_analyzed\n| pkg_home\n" - "| pkg_summary\n| pkg_src_info\n| pkg_file_name\n| pkg_supplier\n| pkg_orig\n| pkg_checksum\n" - "| pkg_verif\n| pkg_desc\n| pkg_comment\n| pkg_attribution_text\n| pkg_lic_decl\n| pkg_lic_conc\n" + "| package_name\n| package_version\n| download_location\n| pkg_files_analyzed\n| homepage\n" + "| summary\n| source_info\n| pkg_file_name\n| supplier\n| originator\n| pkg_checksum\n" + "| pkg_verif\n| description\n| pkg_comment\n| pkg_attribution_text\n| pkg_lic_decl\n| pkg_lic_conc\n" "| pkg_lic_ff\n| pkg_lic_comment\n| pkg_cr_text\n| pkg_ext_ref\n| primary_package_purpose\n" "| built_date\n| release_date\n| valid_until_date\n" # attributes for extracted licensing info - "| extr_lic_id\n| extr_lic_text\n| extr_lic_name\n| lic_xref\n| lic_comment\n" + "| license_id\n| extracted_text\n| license_name\n| lic_xref\n| lic_comment\n" "| unknown_tag ") def p_attrib(self, p): pass @@ -138,25 +138,10 @@ def p_spdx_id(self, p): self.creation_info["spdx_id"] = p[2] # parsing methods for creation info / document level - def set_creation_info_value(self, parsed_value: Any, argument_name: Optional[str] = None, - method_to_apply: Callable = lambda x: x): - if not argument_name: - argument_name = str(parsed_value.slice[0]) - if argument_name in self.creation_info: - self.creation_info["logger"].append( - f"Multiple values for {parsed_value[1]} found. Line: {parsed_value.lineno(1)}") - return - self.creation_info[argument_name] = method_to_apply(parsed_value[2]) @grammar_rule("license_list_version : LIC_LIST_VER LINE") def p_license_list_version(self, p): - try: - if str(p.slice[0]) in self.creation_info: - self.creation_info["logger"].append(f"Multiple values for {p[1]} found. Line: {p.lineno(1)}") - return - self.creation_info[str(p.slice[0])] = Version.from_string(p[2]) - except ValueError as err: - self.creation_info["logger"].append(err.args[0]) + set_value(p, self.creation_info, method_to_apply=Version.from_string) @grammar_rule("license_list_version : LIC_LIST_VER error") def p_license_list_version_error(self, p): @@ -165,7 +150,7 @@ def p_license_list_version_error(self, p): @grammar_rule("document_comment : DOC_COMMENT text_or_line") def p_doc_comment(self, p): - self.set_creation_info_value(p) + set_value(p, self.creation_info) @grammar_rule("document_comment : DOC_COMMENT error") def p_doc_comment_error(self, p): @@ -174,7 +159,7 @@ def p_doc_comment_error(self, p): @grammar_rule("document_namespace : DOC_NAMESPACE LINE") def p_doc_namespace(self, p): - self.set_creation_info_value(p) + set_value(p, self.creation_info) @grammar_rule("document_namespace : DOC_NAMESPACE error") def p_doc_namespace_error(self, p): @@ -183,7 +168,7 @@ def p_doc_namespace_error(self, p): @grammar_rule("data_license : DOC_LICENSE LINE") def p_data_license(self, p): - self.set_creation_info_value(p) + set_value(p, self.creation_info) @grammar_rule("data_license : DOC_LICENSE error") def p_data_license_error(self, p): @@ -192,7 +177,7 @@ def p_data_license_error(self, p): @grammar_rule("doc_name : DOC_NAME LINE") def p_doc_name(self, p): - self.set_creation_info_value(p, "name") + set_value(p, self.creation_info, "name") @grammar_rule("doc_name : DOC_NAME error") def p_doc_name_error(self, p): @@ -214,7 +199,7 @@ def p_external_document_ref_error(self, p): @grammar_rule("spdx_version : DOC_VERSION LINE") def p_spdx_version(self, p): - self.set_creation_info_value(p) + set_value(p, self.creation_info) @grammar_rule("spdx_version : DOC_VERSION error") def p_spdx_version_error(self, p): @@ -223,7 +208,7 @@ def p_spdx_version_error(self, p): @grammar_rule("creator_comment : CREATOR_COMMENT text_or_line") def p_creator_comment(self, p): - self.set_creation_info_value(p) + set_value(p, self.creation_info) @grammar_rule("creator_comment : CREATOR_COMMENT error") def p_creator_comment_error(self, p): @@ -241,7 +226,7 @@ def p_creator_error(self, p): @grammar_rule("created : CREATED DATE") def p_created(self, p): - self.set_creation_info_value(p, method_to_apply=datetime_from_str) + set_value(p, self.creation_info, method_to_apply=datetime_from_str) @grammar_rule("created : CREATED error") def p_created_error(self, p): @@ -250,12 +235,12 @@ def p_created_error(self, p): # parsing methods for extracted licensing info - @grammar_rule("extr_lic_id : LICS_ID LINE") + @grammar_rule("license_id : LICS_ID LINE") def p_extracted_license_id(self, p): self.initialize_new_current_element(ExtractedLicensingInfo) - self.current_element["license_id"] = p[2] + set_value(p, self.current_element) - @grammar_rule("extr_lic_id : LICS_ID error") + @grammar_rule("license_id : LICS_ID error") def p_extracted_license_id_error(self, p): self.current_element["logger"].append( f"Error while parsing LicenseID: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -274,29 +259,29 @@ def p_extracted_cross_reference_error(self, p): @grammar_rule("lic_comment : LICS_COMMENT text_or_line") def p_license_comment(self, p): self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) - self.current_element["comment"] = p[2] + set_value(p, self.current_element, argument_name="comment") @grammar_rule("lic_comment : LICS_COMMENT error") def p_license_comment_error(self, p): self.current_element["logger"].append( f"Error while parsing LicenseComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("extr_lic_name : LICS_NAME line_or_no_assertion") + @grammar_rule("license_name : LICS_NAME line_or_no_assertion") def p_extracted_license_name(self, p): self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) - self.current_element["license_name"] = p[2] + set_value(p, self.current_element) - @grammar_rule("extr_lic_name : LICS_NAME error") + @grammar_rule("license_name : LICS_NAME error") def p_extracted_license_name_error(self, p): self.current_element["logger"].append( f"Error while parsing LicenseName: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("extr_lic_text : LICS_TEXT text_or_line") + @grammar_rule("extracted_text : LICS_TEXT text_or_line") def p_extracted_license_text(self, p): self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) - self.current_element["extracted_text"] = p[2] + set_value(p, self.current_element) - @grammar_rule("extr_lic_text : LICS_TEXT error") + @grammar_rule("extracted_text : LICS_TEXT error") def p_extracted_license_text_error(self, p): self.current_element["logger"].append( f"Error while parsing ExtractedText: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -306,7 +291,7 @@ def p_extracted_license_text_error(self, p): @grammar_rule("file_name : FILE_NAME LINE") def p_file_name(self, p): self.initialize_new_current_element(File) - self.current_element["name"] = p[2] + set_value(p, self.current_element, argument_name="name") @grammar_rule("file_name : FILE_NAME error") def p_file_name_error(self, p): @@ -327,7 +312,7 @@ def p_file_contributor_error(self, p): @grammar_rule("file_notice : FILE_NOTICE text_or_line") def p_file_notice(self, p): self.check_that_current_element_matches_class_for_value(File) - self.current_element["notice"] = p[2] + set_value(p, self.current_element, argument_name="notice") @grammar_rule("file_notice : FILE_NOTICE error") def p_file_notice_error(self, p): @@ -337,7 +322,7 @@ def p_file_notice_error(self, p): @grammar_rule("file_cr_text : FILE_CR_TEXT line_or_no_assertion_or_none") def p_file_copyright_text(self, p): self.check_that_current_element_matches_class_for_value(File) - self.current_element["copyright_text"] = p[2] + set_value(p, self.current_element, argument_name="copyright_text") @grammar_rule("file_cr_text : FILE_CR_TEXT error") def p_file_copyright_text_error(self, p): @@ -347,7 +332,7 @@ def p_file_copyright_text_error(self, p): @grammar_rule("file_lics_comment : FILE_LICS_COMMENT text_or_line") def p_file_license_comment(self, p): self.check_that_current_element_matches_class_for_value(File) - self.current_element["license_comment"] = p[2] + set_value(p, self.current_element, argument_name="license_comment") @grammar_rule("file_lics_comment : FILE_LICS_COMMENT error") def p_file_license_comment_error(self, p): @@ -381,7 +366,7 @@ def p_file_license_info_error(self, p): @grammar_rule("file_comment : FILE_COMMENT text_or_line") def p_file_comment(self, p): self.check_that_current_element_matches_class_for_value(File) - self.current_element["comment"] = p[2] + set_value(p, self.current_element, argument_name="comment") @grammar_rule("file_comment : FILE_COMMENT error") def p_file_comment_error(self, p): @@ -418,7 +403,7 @@ def p_file_checksum_error(self, p): @grammar_rule("file_conc : FILE_LICS_CONC license_or_no_assertion_or_none") def p_file_license_concluded(self, p): self.check_that_current_element_matches_class_for_value(File) - self.current_element["license_concluded"] = p[2] + set_value(p, self.current_element, argument_name="license_concluded") @grammar_rule("file_conc : FILE_LICS_CONC error") def p_file_license_concluded_error(self, p): @@ -431,7 +416,7 @@ def p_file_license_concluded_error(self, p): @grammar_rule("package_name : PKG_NAME LINE") def p_package_name(self, p): self.initialize_new_current_element(Package) - self.current_element["name"] = p[2] + set_value(p, self.current_element, argument_name="name") @grammar_rule("package_name : PKG_NAME error") def p_package_name_error(self, p): @@ -439,12 +424,12 @@ def p_package_name_error(self, p): self.current_element["logger"].append( f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("pkg_desc : PKG_DESC text_or_line") + @grammar_rule("description : PKG_DESC text_or_line") def p_pkg_description(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["description"] = p[2] + set_value(p, self.current_element) - @grammar_rule("pkg_desc : PKG_DESC error") + @grammar_rule("description : PKG_DESC error") def p_pkg_description_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageDescription: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -452,7 +437,7 @@ def p_pkg_description_error(self, p): @grammar_rule("pkg_comment : PKG_COMMENT text_or_line") def p_pkg_comment(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["comment"] = p[2] + set_value(p, self.current_element, argument_name="comment") @grammar_rule("pkg_comment : PKG_COMMENT error") def p_pkg_comment_error(self, p): @@ -470,12 +455,12 @@ def p_pkg_attribution_text_error(self, p): f"Error while parsing PackageAttributionText: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") - @grammar_rule("pkg_summary : PKG_SUM text_or_line") + @grammar_rule("summary : PKG_SUM text_or_line") def p_pkg_summary(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["summary"] = p[2] + set_value(p, self.current_element) - @grammar_rule("pkg_summary : PKG_SUM error") + @grammar_rule("summary : PKG_SUM error") def p_pkg_summary_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageSummary: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -483,7 +468,7 @@ def p_pkg_summary_error(self, p): @grammar_rule("pkg_cr_text : PKG_CPY_TEXT line_or_no_assertion_or_none") def p_pkg_copyright_text(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["copyright_text"] = p[2] + set_value(p, self.current_element, argument_name="copyright_text") @grammar_rule("pkg_cr_text : PKG_CPY_TEXT error") def p_pkg_copyright_text_error(self, p): @@ -494,7 +479,12 @@ def p_pkg_copyright_text_error(self, p): @grammar_rule("pkg_ext_ref : PKG_EXT_REF LINE PKG_EXT_REF_COMMENT text_or_line\n | PKG_EXT_REF LINE") def p_pkg_external_refs(self, p): self.check_that_current_element_matches_class_for_value(Package) - category, reference_type, locator = p[2].split(" ") + try: + category, reference_type, locator = p[2].split(" ") + except ValueError: + self.current_element["logger"].append( + f"Couldn't split PackageExternalRef in category, reference_type and locator. Line: {p.lineno(1)}") + return comment = None if len(p) == 5: comment = p[4] @@ -523,7 +513,7 @@ def p_pkg_external_refs_error(self, p): @grammar_rule("pkg_lic_comment : PKG_LICS_COMMENT text_or_line") def p_pkg_license_comment(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["license_comment"] = p[2] + set_value(p, self.current_element, argument_name="license_comment") @grammar_rule("pkg_lic_comment : PKG_LICS_COMMENT error") def p_pkg_license_comment_error(self, p): @@ -534,7 +524,7 @@ def p_pkg_license_comment_error(self, p): @grammar_rule("pkg_lic_decl : PKG_LICS_DECL license_or_no_assertion_or_none") def p_pkg_license_declared(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["license_declared"] = p[2] + set_value(p, self.current_element, argument_name="license_declared") @grammar_rule("pkg_lic_decl : PKG_LICS_DECL error") def p_pkg_license_declared_error(self, p): @@ -559,7 +549,7 @@ def p_pkg_license_info_from_file_error(self, p): @grammar_rule("pkg_lic_conc : PKG_LICS_CONC license_or_no_assertion_or_none") def p_pkg_license_concluded(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["license_concluded"] = p[2] + set_value(p, self.current_element, argument_name="license_concluded") @grammar_rule("pkg_lic_conc : PKG_LICS_CONC error") def p_pkg_license_concluded_error(self, p): @@ -567,12 +557,12 @@ def p_pkg_license_concluded_error(self, p): f"Error while parsing LicenseConcluded in package: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") - @grammar_rule("pkg_src_info : PKG_SRC_INFO text_or_line") + @grammar_rule("source_info : PKG_SRC_INFO text_or_line") def p_pkg_source_info(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["source_info"] = p[2] + set_value(p, self.current_element) - @grammar_rule("pkg_src_info : PKG_SRC_INFO error") + @grammar_rule("source_info : PKG_SRC_INFO error") def p_pkg_source_info_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageSourceInfo: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -607,22 +597,22 @@ def p_pkg_verification_code_error(self, p): f"Error while parsing PackageVerificationCode: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") - @grammar_rule("pkg_home : PKG_HOME line_or_no_assertion_or_none") + @grammar_rule("homepage : PKG_HOME line_or_no_assertion_or_none") def p_pkg_homepage(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["homepage"] = p[2] + set_value(p, self.current_element) - @grammar_rule("pkg_home : PKG_HOME error") + @grammar_rule("homepage : PKG_HOME error") def p_pkg_homepage_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageHomePage: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("pkg_down_location : PKG_DOWN line_or_no_assertion_or_none") + @grammar_rule("download_location : PKG_DOWN line_or_no_assertion_or_none") def p_pkg_download_location(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["download_location"] = p[2] + set_value(p, self.current_element) - @grammar_rule("pkg_down_location : PKG_DOWN error") + @grammar_rule("download_location : PKG_DOWN error") def p_pkg_download_location_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageDownloadLocation: Token did not match specified grammar rule. " @@ -642,22 +632,22 @@ def p_pkg_files_analyzed_error(self, p): f"Error while parsing FilesAnalyzed in package: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") - @grammar_rule("pkg_orig : PKG_ORIG actor_or_no_assertion") + @grammar_rule("originator : PKG_ORIG actor_or_no_assertion") def p_pkg_originator(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["originator"] = p[2] + set_value(p, self.current_element) - @grammar_rule("pkg_orig : PKG_ORIG error") + @grammar_rule("originator : PKG_ORIG error") def p_pkg_originator_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageOriginator: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("pkg_supplier : PKG_SUPPL actor_or_no_assertion") + @grammar_rule("supplier : PKG_SUPPL actor_or_no_assertion") def p_pkg_supplier(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["supplier"] = p[2] + set_value(p, self.current_element) - @grammar_rule("pkg_supplier : PKG_SUPPL error") + @grammar_rule("supplier : PKG_SUPPL error") def p_pkg_supplier_error(self, p): self.current_element["logger"].append( f"Error while parsing PackageSupplier: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -665,7 +655,7 @@ def p_pkg_supplier_error(self, p): @grammar_rule("pkg_file_name : PKG_FILE_NAME LINE") def p_pkg_file_name(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["file_name"] = p[2] + set_value(p, self.current_element, argument_name="file_name") @grammar_rule("pkg_file_name : PKG_FILE_NAME error") def p_pkg_file_name_error(self, p): @@ -675,7 +665,7 @@ def p_pkg_file_name_error(self, p): @grammar_rule("package_version : PKG_VERSION LINE") def p_package_version(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["version"] = p[2] + set_value(p, self.current_element, argument_name="version") @grammar_rule("package_version : PKG_VERSION error") def p_package_version_error(self, p): @@ -685,7 +675,7 @@ def p_package_version_error(self, p): @grammar_rule("primary_package_purpose : PRIMARY_PACKAGE_PURPOSE primary_package_purpose_value") def p_primary_package_purpose(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["primary_package_purpose"] = PackagePurpose[p[2].replace("-", "_")] + set_value(p, self.current_element, method_to_apply=lambda x: PackagePurpose[x.replace("-", "_")]) @grammar_rule("primary_package_purpose : PRIMARY_PACKAGE_PURPOSE error") def p_primary_package_purpose_error(self, p): @@ -701,7 +691,7 @@ def p_primary_package_purpose_value(self, p): @grammar_rule("built_date : BUILT_DATE DATE") def p_built_date(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["built_date"] = datetime_from_str(p[2]) + set_value(p, self.current_element, method_to_apply=datetime_from_str) @grammar_rule("built_date : BUILT_DATE error") def p_built_date_error(self, p): @@ -711,7 +701,7 @@ def p_built_date_error(self, p): @grammar_rule("release_date : RELEASE_DATE DATE") def p_release_date(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["release_date"] = datetime_from_str(p[2]) + set_value(p, self.current_element, method_to_apply=datetime_from_str) @grammar_rule("release_date : RELEASE_DATE error") def p_release_date_error(self, p): @@ -721,7 +711,7 @@ def p_release_date_error(self, p): @grammar_rule("valid_until_date : VALID_UNTIL_DATE DATE") def p_valid_until_date(self, p): self.check_that_current_element_matches_class_for_value(Package) - self.current_element["valid_until_date"] = datetime_from_str(p[2]) + set_value(p, self.current_element, method_to_apply=datetime_from_str) @grammar_rule("valid_until_date : VALID_UNTIL_DATE error") def p_valid_until_date_error(self, p): @@ -732,7 +722,7 @@ def p_valid_until_date_error(self, p): @grammar_rule("snip_spdx_id : SNIPPET_SPDX_ID LINE") def p_snippet_spdx_id(self, p): self.initialize_new_current_element(Snippet) - self.current_element["spdx_id"] = p[2] + set_value(p, self.current_element, argument_name="spdx_id") @grammar_rule("snip_spdx_id : SNIPPET_SPDX_ID error") def p_snippet_spdx_id_error(self, p): @@ -743,7 +733,7 @@ def p_snippet_spdx_id_error(self, p): @grammar_rule("snip_name : SNIPPET_NAME LINE") def p_snippet_name(self, p): self.check_that_current_element_matches_class_for_value(Snippet) - self.current_element["name"] = p[2] + set_value(p, self.current_element, argument_name="name") @grammar_rule("snip_name : SNIPPET_NAME error") def p_snippet_name_error(self, p): @@ -753,7 +743,7 @@ def p_snippet_name_error(self, p): @grammar_rule("snip_comment : SNIPPET_COMMENT text_or_line") def p_snippet_comment(self, p): self.check_that_current_element_matches_class_for_value(Snippet) - self.current_element["comment"] = p[2] + set_value(p, self.current_element, argument_name="comment") @grammar_rule("snip_comment : SNIPPET_COMMENT error") def p_snippet_comment_error(self, p): @@ -774,7 +764,7 @@ def p_snippet_attribution_text_error(self, p): @grammar_rule("snip_cr_text : SNIPPET_CR_TEXT line_or_no_assertion_or_none") def p_snippet_copyright_text(self, p): self.check_that_current_element_matches_class_for_value(Snippet) - self.current_element["copyright_text"] = p[2] + set_value(p, self.current_element, argument_name="copyright_text") @grammar_rule("snip_cr_text : SNIPPET_CR_TEXT error") def p_snippet_copyright_text_error(self, p): @@ -785,7 +775,7 @@ def p_snippet_copyright_text_error(self, p): @grammar_rule("snip_lic_comment : SNIPPET_LICS_COMMENT text_or_line") def p_snippet_license_comment(self, p): self.check_that_current_element_matches_class_for_value(Snippet) - self.current_element["license_comment"] = p[2] + set_value(p, self.current_element, argument_name="license_comment") @grammar_rule("snip_lic_comment : SNIPPET_LICS_COMMENT error") def p_snippet_license_comment_error(self, p): @@ -793,12 +783,12 @@ def p_snippet_license_comment_error(self, p): f"Error while parsing SnippetLicenseComments: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") - @grammar_rule("snip_file_spdx_id : SNIPPET_FILE_SPDXID LINE") + @grammar_rule("file_spdx_id : SNIPPET_FILE_SPDXID LINE") def p_snippet_from_file_spdxid(self, p): self.check_that_current_element_matches_class_for_value(Snippet) - self.current_element["file_spdx_id"] = p[2] + set_value(p, self.current_element) - @grammar_rule("snip_file_spdx_id : SNIPPET_FILE_SPDXID error") + @grammar_rule("file_spdx_id : SNIPPET_FILE_SPDXID error") def p_snippet_from_file_spdxid_error(self, p): self.current_element["logger"].append( f"Error while parsing SnippetFromFileSPDXID: Token did not match specified grammar rule. " @@ -807,7 +797,7 @@ def p_snippet_from_file_spdxid_error(self, p): @grammar_rule("snip_lics_conc : SNIPPET_LICS_CONC license_or_no_assertion_or_none") def p_snippet_concluded_license(self, p): self.check_that_current_element_matches_class_for_value(Snippet) - self.current_element["license_concluded"] = p[2] + set_value(p, self.current_element, argument_name="license_concluded") @grammar_rule("snip_lics_conc : SNIPPET_LICS_CONC error") def p_snippet_concluded_license_error(self, p): @@ -866,10 +856,7 @@ def p_snippet_line_range_error(self, p): def p_annotator(self, p): """annotator : ANNOTATOR PERSON_VALUE\n| TOOL_VALUE\n| ORG_VALUE""" self.initialize_new_current_element(Annotation) - try: - self.current_element["annotator"] = ActorParser.parse_actor(p[2]) - except SPDXParsingError as err: - self.current_element["logger"].append(err.get_messages()) + set_value(p, self.current_element, method_to_apply=ActorParser.parse_actor) @grammar_rule("annotator : ANNOTATOR error") def p_annotator_error(self, p): @@ -880,7 +867,7 @@ def p_annotator_error(self, p): @grammar_rule("annotation_date : ANNOTATION_DATE DATE") def p_annotation_date(self, p): self.check_that_current_element_matches_class_for_value(Annotation) - self.current_element["annotation_date"] = datetime_from_str(p[2]) + set_value(p, self.current_element, method_to_apply=datetime_from_str) @grammar_rule("annotation_date : ANNOTATION_DATE error") def p_annotation_date_error(self, p): @@ -890,7 +877,7 @@ def p_annotation_date_error(self, p): @grammar_rule("annotation_comment : ANNOTATION_COMMENT text_or_line") def p_annotation_comment(self, p): self.check_that_current_element_matches_class_for_value(Annotation) - self.current_element["annotation_comment"] = p[2] + set_value(p, self.current_element) @grammar_rule("annotation_comment : ANNOTATION_COMMENT error") def p_annotation_comment_error(self, p): @@ -900,7 +887,7 @@ def p_annotation_comment_error(self, p): @grammar_rule("annotation_type : ANNOTATION_TYPE annotation_type_value") def p_annotation_type(self, p): self.check_that_current_element_matches_class_for_value(Annotation) - self.current_element["annotation_type"] = AnnotationType[p[2]] + set_value(p, self.current_element, method_to_apply=lambda x: AnnotationType[x]) @grammar_rule("annotation_type : ANNOTATION_TYPE error") def p_annotation_type_error(self, p): @@ -913,7 +900,7 @@ def p_annotation_type_value(self, p): @grammar_rule("annotation_spdx_id : ANNOTATION_SPDX_ID LINE") def p_annotation_spdx_id(self, p): - self.current_element["spdx_id"] = p[2] + set_value(p, self.current_element, argument_name="spdx_id") @grammar_rule("annotation_spdx_id : ANNOTATION_SPDX_ID error") def p_annotation_spdx_id_error(self, p): From d566d99a72bcb85fe51185e964570c0f9133254a Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 1 Mar 2023 16:25:23 +0100 Subject: [PATCH 320/362] [refactor] merge parsing methods for package dates Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 64237b46e..6eec4efa1 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -688,7 +688,8 @@ def p_primary_package_purpose_error(self, p): def p_primary_package_purpose_value(self, p): p[0] = p[1] - @grammar_rule("built_date : BUILT_DATE DATE") + @grammar_rule("built_date : BUILT_DATE DATE\n release_date : RELEASE_DATE DATE\n " + "valid_until_date : VALID_UNTIL_DATE DATE") def p_built_date(self, p): self.check_that_current_element_matches_class_for_value(Package) set_value(p, self.current_element, method_to_apply=datetime_from_str) @@ -698,21 +699,11 @@ def p_built_date_error(self, p): self.current_element["logger"].append( f"Error while parsing BuiltDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("release_date : RELEASE_DATE DATE") - def p_release_date(self, p): - self.check_that_current_element_matches_class_for_value(Package) - set_value(p, self.current_element, method_to_apply=datetime_from_str) - @grammar_rule("release_date : RELEASE_DATE error") def p_release_date_error(self, p): self.current_element["logger"].append( f"Error while parsing ReleaseDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("valid_until_date : VALID_UNTIL_DATE DATE") - def p_valid_until_date(self, p): - self.check_that_current_element_matches_class_for_value(Package) - set_value(p, self.current_element, method_to_apply=datetime_from_str) - @grammar_rule("valid_until_date : VALID_UNTIL_DATE error") def p_valid_until_date_error(self, p): self.current_element["logger"].append( From a746a5a609010b1e897b6521f302608e7bc5668e Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 2 Mar 2023 08:51:38 +0100 Subject: [PATCH 321/362] [issue-382] rename tests Signed-off-by: Meret Behrens --- tests/spdx/parser/tagvalue/test_creation_info_parser.py | 4 ++-- .../parser/tagvalue/test_extracted_licensing_info_parser.py | 4 ++-- tests/spdx/parser/tagvalue/test_file_parser.py | 4 ++-- tests/spdx/parser/tagvalue/test_package_parser.py | 2 +- tests/spdx/parser/tagvalue/test_relationship_parser.py | 4 ++-- tests/spdx/parser/tagvalue/test_snippet_parser.py | 2 +- tests/spdx/parser/tagvalue/test_tag_value_parser.py | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/spdx/parser/tagvalue/test_creation_info_parser.py b/tests/spdx/parser/tagvalue/test_creation_info_parser.py index d36e822c6..de5a794c3 100644 --- a/tests/spdx/parser/tagvalue/test_creation_info_parser.py +++ b/tests/spdx/parser/tagvalue/test_creation_info_parser.py @@ -36,7 +36,7 @@ ]) -def test_creation_info(): +def test_parse_creation_info(): parser = Parser() document = parser.parse(DOCUMENT_STR) assert document is not None @@ -85,7 +85,7 @@ def test_creation_info(): ('LicenseListVersion: 3.5\nLicenseListVersion: 3.7', [["Error while parsing CreationInfo: ['Multiple values for LicenseListVersion " "found. Line: 2']"]])])) -def test_invalid_creation_info(document_str, expected_message): +def test_parse_invalid_creation_info(document_str, expected_message): parser = Parser() with pytest.raises(SPDXParsingError) as err: parser.parse(document_str) diff --git a/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py b/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py index 753577cb2..ffffcafb5 100644 --- a/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py +++ b/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py @@ -17,7 +17,7 @@ from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR -def test_extracted_licensing_info(): +def test_parse_extracted_licensing_info(): parser = Parser() extracted_licensing_info_str = '\n'.join([ 'LicenseID: LicenseRef-Beerware-4.2', @@ -43,7 +43,7 @@ def test_extracted_licensing_info(): assert extracted_licensing_info.comment == "The beerware license has a couple of other standard variants." -def test_parse_invalid_licensing_info(): +def test_parse_invalid_extracted_licensing_info(): parser = Parser() extracted_licensing_info_str = '\n'.join([ 'ExtractedText: "THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' diff --git a/tests/spdx/parser/tagvalue/test_file_parser.py b/tests/spdx/parser/tagvalue/test_file_parser.py index ec5a9df5d..1b37b2b5a 100644 --- a/tests/spdx/parser/tagvalue/test_file_parser.py +++ b/tests/spdx/parser/tagvalue/test_file_parser.py @@ -17,7 +17,7 @@ from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR -def test_file(): +def test_parse_file(): parser = Parser() file_str = '\n'.join([ 'FileName: testfile.java', @@ -45,7 +45,7 @@ def test_file(): assert spdx_file.license_concluded == get_spdx_licensing().parse("Apache-2.0") -def test_invalid_file(): +def test_parse_invalid_file(): parser = Parser() file_str = '\n'.join([ 'FileName: testfile.java', diff --git a/tests/spdx/parser/tagvalue/test_package_parser.py b/tests/spdx/parser/tagvalue/test_package_parser.py index c17516610..eeb97af55 100644 --- a/tests/spdx/parser/tagvalue/test_package_parser.py +++ b/tests/spdx/parser/tagvalue/test_package_parser.py @@ -18,7 +18,7 @@ from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR -def test_package(): +def test_parse_package(): parser = Parser() package_str = '\n'.join([ 'PackageName: Test', diff --git a/tests/spdx/parser/tagvalue/test_relationship_parser.py b/tests/spdx/parser/tagvalue/test_relationship_parser.py index c60e7eb76..f6776299b 100644 --- a/tests/spdx/parser/tagvalue/test_relationship_parser.py +++ b/tests/spdx/parser/tagvalue/test_relationship_parser.py @@ -32,7 +32,7 @@ Relationship("DocumentRef-ExternalDocument:SPDXRef-Test", RelationshipType.DEPENDS_ON, "DocumentRef:AnotherRef")) ]) -def test_relationship(relationship_str, expected_relationship): +def test_parse_relationship(relationship_str, expected_relationship): parser = Parser() document = parser.parse("\n".join([DOCUMENT_STR, relationship_str])) assert document is not None @@ -50,7 +50,7 @@ def test_relationship(relationship_str, expected_relationship): [["Error while parsing Relationship: ['Error while parsing Relationship: Token " "did not match specified grammar rule. Line: 1']"]]) ]) -def test_falsy_relationship(relationship_str, expected_message): +def test_parse_invalid_relationship(relationship_str, expected_message): parser = Parser() with pytest.raises(SPDXParsingError) as err: parser.parse(relationship_str) diff --git a/tests/spdx/parser/tagvalue/test_snippet_parser.py b/tests/spdx/parser/tagvalue/test_snippet_parser.py index 80a10bd40..d8d95202d 100644 --- a/tests/spdx/parser/tagvalue/test_snippet_parser.py +++ b/tests/spdx/parser/tagvalue/test_snippet_parser.py @@ -14,7 +14,7 @@ from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR -def test_snippet(): +def test_parse_snippet(): parser = Parser() snippet_str = '\n'.join([ 'SnippetSPDXID: SPDXRef-Snippet', diff --git a/tests/spdx/parser/tagvalue/test_tag_value_parser.py b/tests/spdx/parser/tagvalue/test_tag_value_parser.py index af88aab53..e96fa9c80 100644 --- a/tests/spdx/parser/tagvalue/test_tag_value_parser.py +++ b/tests/spdx/parser/tagvalue/test_tag_value_parser.py @@ -18,7 +18,7 @@ from spdx.parser.tagvalue.parser.tagvalue import Parser -def test_unknown_str(): +def test_parse_unknown_tag(): parser = Parser() unknown_tag_str = 'UnknownTag: This is an example for an unknown tag.' @@ -26,7 +26,7 @@ def test_unknown_str(): parser.parse(unknown_tag_str) -def test_parse_file(): +def test_tag_value_parser(): parser = Parser() fn = os.path.join(os.path.dirname(__file__), "../../data/formats/SPDXTagExample-v2.3.spdx") From 09e1e8e88b79dc4f82b9646c723f6464de27965f Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 2 Mar 2023 09:08:01 +0100 Subject: [PATCH 322/362] [issue-382] add negative tests for snippet_parser Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 8 ++++- .../parser/tagvalue/test_snippet_parser.py | 30 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 6eec4efa1..9932e0a60 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -806,7 +806,6 @@ def p_snippet_license_info(self, p): @grammar_rule("snip_lics_info : SNIPPET_LICS_INFO error") def p_snippet_license_info_error(self, p): - self.current_element["logger"].append( f"Error while parsing LicenseInfoInSnippet: Token did not match specified grammar rule. " f"Line: {p.lineno(1)}") @@ -814,6 +813,9 @@ def p_snippet_license_info_error(self, p): @grammar_rule("snip_byte_range : SNIPPET_BYTE_RANGE LINE") def p_snippet_byte_range(self, p): self.check_that_current_element_matches_class_for_value(Snippet) + if "byte_range" in self.current_element: + self.current_element["logger"].append( + f"Multiple values for {p[1]} found. Line: {p.lineno(1)}") range_re = re.compile(r"^(\d+):(\d+)$", re.UNICODE) if not range_re.match(p[2].strip()): self.current_element["logger"].append("Value for SnippetByteRange doesn't match valid range pattern.") @@ -830,6 +832,10 @@ def p_snippet_byte_range_error(self, p): @grammar_rule("snip_line_range : SNIPPET_LINE_RANGE LINE") def p_snippet_line_range(self, p): self.check_that_current_element_matches_class_for_value(Snippet) + if "line_range" in self.current_element: + self.current_element["logger"].append( + f"Multiple values for {p[1]} found. Line: {p.lineno(1)}") + return range_re = re.compile(r"^(\d+):(\d+)$", re.UNICODE) if not range_re.match(p[2].strip()): self.current_element["logger"].append("Value for SnippetLineRange doesn't match valid range pattern.") diff --git a/tests/spdx/parser/tagvalue/test_snippet_parser.py b/tests/spdx/parser/tagvalue/test_snippet_parser.py index d8d95202d..a6f84d415 100644 --- a/tests/spdx/parser/tagvalue/test_snippet_parser.py +++ b/tests/spdx/parser/tagvalue/test_snippet_parser.py @@ -8,8 +8,12 @@ # 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 license_expression import get_spdx_licensing +from spdx.parser.error import SPDXParsingError from spdx.parser.tagvalue.parser.tagvalue import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR @@ -27,6 +31,8 @@ def test_parse_snippet(): 'LicenseInfoInSnippet: Apache-2.0', 'SnippetByteRange: 310:420', 'SnippetLineRange: 5:23', + 'SnippetAttributionText: This is a text\nthat spans multiple lines.', + 'SnippetAttributionText: This text spans one line but has trailing and leading whitespaces. ' ]) document = parser.parse("\n".join([DOCUMENT_STR, snippet_str])) @@ -45,3 +51,27 @@ def test_parse_snippet(): assert snippet.byte_range[1] == 420 assert snippet.line_range[0] == 5 assert snippet.line_range[1] == 23 + TestCase().assertCountEqual( + snippet.attribution_texts, ["This is a text\nthat spans multiple lines.", + "This text spans one line but has trailing and leading whitespaces."]) + + +@pytest.mark.parametrize("snippet_str, expected_message", [ + ('SnippetName: TestSnippet', ['Element Snippet is not the current element in scope, probably the expected ' + 'tag to start the element (SnippetSPDXID) is missing.']), + ('SnippetSPDXID: SPDXDRef-Snippet\nSnippetByteRange: 1,4', + [['Error while parsing Snippet: ["Value for SnippetByteRange doesn\'t match ' + 'valid range pattern."]']]), + ('SnippetSPDXID: SPDXDRef-Snippet\nSnippetByteRange: 1:4\nSnippetByteRange:10:23', + [["Error while parsing Snippet: ['Multiple values for SnippetByteRange found. " + "Line: 3']"]]), + ('SnippetSPDXID: SPDXRef-Snippet', [['Error while constructing Snippet: Snippet.__init__() missing 2 required ' + "positional arguments: 'file_spdx_id' and 'byte_range'"]]) +]) +def test_parse_invalid_snippet(snippet_str, expected_message): + parser = Parser() + + with pytest.raises(SPDXParsingError) as err: + parser.parse(snippet_str) + + assert err.value.get_messages() == expected_message From b9b776fbe40caeae13f065fb4ffaf7099a354d51 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 3 Mar 2023 08:47:03 +0100 Subject: [PATCH 323/362] [issue-382] add negative tests for package parser Signed-off-by: Meret Behrens --- .../parser/tagvalue/test_package_parser.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/spdx/parser/tagvalue/test_package_parser.py b/tests/spdx/parser/tagvalue/test_package_parser.py index eeb97af55..9f2de8997 100644 --- a/tests/spdx/parser/tagvalue/test_package_parser.py +++ b/tests/spdx/parser/tagvalue/test_package_parser.py @@ -11,9 +11,11 @@ from datetime import datetime from unittest import TestCase +import pytest from license_expression import get_spdx_licensing from spdx.model.package import ExternalPackageRef, ExternalPackageRefCategory, PackagePurpose +from spdx.parser.error import SPDXParsingError from spdx.parser.tagvalue.parser.tagvalue import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR @@ -70,3 +72,38 @@ def test_parse_package(): assert package.built_date == datetime(2020, 1, 1, 12, 0, 0) assert package.release_date == datetime(2021, 1, 1, 12, 0, 0) assert package.valid_until_date == datetime(2022, 1, 1, 12, 0, 0) + + +@pytest.mark.parametrize("package_str, expected_message", + [('PackageDownloadLocation: SPDXRef-Package', + ['Element Package is not the current element in scope, probably the expected ' + 'tag to start the element (PackageName) is missing.']), + ('PackageName: TestPackage', + [['Error while constructing Package: Package.__init__() missing 2 required ' + "positional arguments: 'spdx_id' and 'download_location'"]]), + ('PackageName: TestPackage\nPackageCopyrightText:This is a copyright\n' + 'PackageCopyrightText:MultipleCopyright', + [["Error while parsing Package: ['Multiple values for PackageCopyrightText " + "found. Line: 3']"]]), + ('PackageName: TestPackage\nExternalRef: reference locator', + [['Error while parsing Package: ["Couldn\'t split PackageExternalRef in ' + 'category, reference_type and locator. Line: 2"]']]), + ('PackageName: TestPackage\nExternalRef: category reference locator', + [["Error while parsing Package: ['Invalid ExternalPackageRefCategory: " + "category']"]]), + ('SPDXID:SPDXRef-DOCUMENT\nPackageName: TestPackage\nSPDXID:SPDXRef-Package\n' + 'PackageDownloadLocation: download.com\nPackageVerificationCode: category reference locator', + [["Error while parsing Package: ['Error while parsing PackageVerificationCode: " + "Value did not match expected format. Line: 5']"]]), + ('PackageName: TestPackage\nBuiltDate: 2012\nValidUntilDate:202-11-02T00:00', + [["Error while parsing Package: ['Error while parsing BuiltDate: Token did not " + "match specified grammar rule. Line: 2', 'Error while parsing " + "ValidUntilDate: Token did not match specified grammar rule. Line: 3']"]]) + ]) +def test_parse_invalid_package(package_str, expected_message): + parser = Parser() + + with pytest.raises(SPDXParsingError) as err: + parser.parse(package_str) + + assert err.value.get_messages() == expected_message From e724eb5b34752f80a97fa5ef435f3d44d7842743 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 3 Mar 2023 08:47:14 +0100 Subject: [PATCH 324/362] [issue-382] merge parsing methods Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 497 ++++-------------- .../spdx/parser/tagvalue/test_file_parser.py | 4 +- 2 files changed, 90 insertions(+), 411 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 9932e0a60..9f8f78043 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -87,9 +87,9 @@ def p_start_attrib(self, p): "| snip_lic_comment\n| file_spdx_id\n| snip_lics_conc\n| snip_lics_info\n| snip_byte_range\n" "| snip_line_range\n" # attributes for package - "| package_name\n| package_version\n| download_location\n| pkg_files_analyzed\n| homepage\n" + "| package_name\n| package_version\n| download_location\n| files_analyzed\n| homepage\n" "| summary\n| source_info\n| pkg_file_name\n| supplier\n| originator\n| pkg_checksum\n" - "| pkg_verif\n| description\n| pkg_comment\n| pkg_attribution_text\n| pkg_lic_decl\n| pkg_lic_conc\n" + "| verification_code\n| description\n| pkg_comment\n| pkg_attribution_text\n| pkg_lic_decl\n| pkg_lic_conc\n" "| pkg_lic_ff\n| pkg_lic_comment\n| pkg_cr_text\n| pkg_ext_ref\n| primary_package_purpose\n" "| built_date\n| release_date\n| valid_until_date\n" # attributes for extracted licensing info @@ -139,50 +139,27 @@ def p_spdx_id(self, p): # parsing methods for creation info / document level - @grammar_rule("license_list_version : LIC_LIST_VER LINE") - def p_license_list_version(self, p): - set_value(p, self.creation_info, method_to_apply=Version.from_string) - - @grammar_rule("license_list_version : LIC_LIST_VER error") - def p_license_list_version_error(self, p): - self.creation_info["logger"].append( - f"Error while parsing LicenseListVersion: Token did not match specified grammar rule. Line: {p.lineno(1)}") - - @grammar_rule("document_comment : DOC_COMMENT text_or_line") - def p_doc_comment(self, p): - set_value(p, self.creation_info) - - @grammar_rule("document_comment : DOC_COMMENT error") - def p_doc_comment_error(self, p): - self.creation_info["logger"].append( - f"Error while parsing DocumentComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") - - @grammar_rule("document_namespace : DOC_NAMESPACE LINE") - def p_doc_namespace(self, p): - set_value(p, self.creation_info) - - @grammar_rule("document_namespace : DOC_NAMESPACE error") - def p_doc_namespace_error(self, p): + @grammar_rule("license_list_version : LIC_LIST_VER error\n document_comment : DOC_COMMENT error\n " + "document_namespace : DOC_NAMESPACE error\n data_license : DOC_LICENSE error\n " + "doc_name : DOC_NAME error\n ext_doc_ref : EXT_DOC_REF error\n spdx_version : DOC_VERSION error\n " + "creator_comment : CREATOR_COMMENT error\n creator : CREATOR error\n created : CREATED error") + def p_creation_info_value_error(self, p): self.creation_info["logger"].append( - f"Error while parsing DocumentNamespace: Token did not match specified grammar rule. Line: {p.lineno(1)}") + f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("data_license : DOC_LICENSE LINE") - def p_data_license(self, p): + @grammar_rule("document_comment : DOC_COMMENT text_or_line\n document_namespace : DOC_NAMESPACE LINE\n " + "data_license : DOC_LICENSE LINE\n spdx_version : DOC_VERSION LINE\n " + "creator_comment : CREATOR_COMMENT text_or_line") + def p_generic_value_creation_info(self, p): set_value(p, self.creation_info) - @grammar_rule("data_license : DOC_LICENSE error") - def p_data_license_error(self, p): - self.creation_info["logger"].append( - f"Error while parsing DataLicense: Token did not match specified grammar rule. Line: {p.lineno(1)}") + @grammar_rule("license_list_version : LIC_LIST_VER LINE") + def p_license_list_version(self, p): + set_value(p, self.creation_info, method_to_apply=Version.from_string) @grammar_rule("doc_name : DOC_NAME LINE") def p_doc_name(self, p): - set_value(p, self.creation_info, "name") - - @grammar_rule("doc_name : DOC_NAME error") - def p_doc_name_error(self, p): - self.creation_info["logger"].append( - f"Error while parsing DocumentName: Token did not match specified grammar rule. Line: {p.lineno(1)}") + set_value(p, self.creation_info, argument_name="name") @grammar_rule("ext_doc_ref : EXT_DOC_REF DOC_REF_ID DOC_URI EXT_DOC_REF_CHECKSUM") def p_external_document_ref(self, p): @@ -192,101 +169,52 @@ def p_external_document_ref(self, p): external_document_ref = ExternalDocumentRef(document_ref_id, document_uri, checksum) self.creation_info.setdefault("external_document_refs", []).append(external_document_ref) - @grammar_rule("ext_doc_ref : EXT_DOC_REF error") - def p_external_document_ref_error(self, p): - self.creation_info["logger"].append( - f"Error while parsing ExternalDocumentRef: Token did not match specified grammar rule. Line: {p.lineno(1)}") - - @grammar_rule("spdx_version : DOC_VERSION LINE") - def p_spdx_version(self, p): - set_value(p, self.creation_info) - - @grammar_rule("spdx_version : DOC_VERSION error") - def p_spdx_version_error(self, p): - self.creation_info["logger"].append( - f"Error while parsing SPDXVersion: Token did not match specified grammar rule. Line: {p.lineno(1)}") - - @grammar_rule("creator_comment : CREATOR_COMMENT text_or_line") - def p_creator_comment(self, p): - set_value(p, self.creation_info) - - @grammar_rule("creator_comment : CREATOR_COMMENT error") - def p_creator_comment_error(self, p): - self.creation_info["logger"].append( - f"Error while parsing CreatorComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") - def p_creator(self, p): """creator : CREATOR PERSON_VALUE\n| CREATOR TOOL_VALUE\n| CREATOR ORG_VALUE""" self.creation_info.setdefault("creators", []).append(ActorParser.parse_actor(p[2])) - @grammar_rule("creator : CREATOR error") - def p_creator_error(self, p): - self.creation_info["logger"].append( - f"Error while parsing Creator: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("created : CREATED DATE") def p_created(self, p): set_value(p, self.creation_info, method_to_apply=datetime_from_str) - @grammar_rule("created : CREATED error") - def p_created_error(self, p): - self.creation_info["logger"].append( - f"Error while parsing Created: Token did not match specified grammar rule. Line: {p.lineno(1)}") - # parsing methods for extracted licensing info + @grammar_rule("license_id : LICS_ID error\n lic_xref : LICS_CRS_REF error\n lic_comment : LICS_COMMENT error\n " + "license_name : LICS_NAME error\n extracted_text : LICS_TEXT error") + def p_extracted_licensing_info_value_error(self, p): + self.current_element["logger"].append( + f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("license_name : LICS_NAME line_or_no_assertion\n extracted_text : LICS_TEXT text_or_line") + def p_generic_value_extracted_licensing_info(self, p): + self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) + set_value(p, self.current_element) + @grammar_rule("license_id : LICS_ID LINE") def p_extracted_license_id(self, p): self.initialize_new_current_element(ExtractedLicensingInfo) set_value(p, self.current_element) - @grammar_rule("license_id : LICS_ID error") - def p_extracted_license_id_error(self, p): - self.current_element["logger"].append( - f"Error while parsing LicenseID: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("lic_xref : LICS_CRS_REF LINE") def p_extracted_cross_reference(self, p): self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) self.current_element.setdefault("cross_references", []).append(p[2]) - @grammar_rule("lic_xref : LICS_CRS_REF error") - def p_extracted_cross_reference_error(self, p): - self.current_element["logger"].append( - f"Error while parsing LicenseCrossReference: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - @grammar_rule("lic_comment : LICS_COMMENT text_or_line") def p_license_comment(self, p): self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) set_value(p, self.current_element, argument_name="comment") - @grammar_rule("lic_comment : LICS_COMMENT error") - def p_license_comment_error(self, p): - self.current_element["logger"].append( - f"Error while parsing LicenseComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") - - @grammar_rule("license_name : LICS_NAME line_or_no_assertion") - def p_extracted_license_name(self, p): - self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) - set_value(p, self.current_element) - - @grammar_rule("license_name : LICS_NAME error") - def p_extracted_license_name_error(self, p): - self.current_element["logger"].append( - f"Error while parsing LicenseName: Token did not match specified grammar rule. Line: {p.lineno(1)}") - - @grammar_rule("extracted_text : LICS_TEXT text_or_line") - def p_extracted_license_text(self, p): - self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) - set_value(p, self.current_element) + # parsing methods for file - @grammar_rule("extracted_text : LICS_TEXT error") - def p_extracted_license_text_error(self, p): + @grammar_rule("file_contrib : FILE_CONTRIB error\n file_notice : FILE_NOTICE error\n " + "file_cr_text : FILE_CR_TEXT error\n file_lics_comment : FILE_LICS_COMMENT error\n " + "file_attribution_text : FILE_ATTRIBUTION_TEXT error\n file_lics_info : FILE_LICS_INFO error\n " + "file_comment : FILE_COMMENT error\n file_checksum : FILE_CHECKSUM error\n " + "file_conc : FILE_LICS_CONC error\n file_type : FILE_TYPE error") + def p_file_value_error(self, p): self.current_element["logger"].append( - f"Error while parsing ExtractedText: Token did not match specified grammar rule. Line: {p.lineno(1)}") - - # parsing methods for file + f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("file_name : FILE_NAME LINE") def p_file_name(self, p): @@ -304,52 +232,26 @@ def p_file_contributor(self, p): self.check_that_current_element_matches_class_for_value(File) self.current_element.setdefault("contributors", []).append(p[2]) - @grammar_rule("file_contrib : FILE_CONTRIB error") - def p_file_contributor_error(self, p): - self.current_element["logger"].append( - f"Error while parsing FileContributor: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("file_notice : FILE_NOTICE text_or_line") def p_file_notice(self, p): self.check_that_current_element_matches_class_for_value(File) set_value(p, self.current_element, argument_name="notice") - @grammar_rule("file_notice : FILE_NOTICE error") - def p_file_notice_error(self, p): - self.current_element["logger"].append( - f"Error while parsing FileNotice: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("file_cr_text : FILE_CR_TEXT line_or_no_assertion_or_none") def p_file_copyright_text(self, p): self.check_that_current_element_matches_class_for_value(File) set_value(p, self.current_element, argument_name="copyright_text") - @grammar_rule("file_cr_text : FILE_CR_TEXT error") - def p_file_copyright_text_error(self, p): - self.current_element["logger"].append( - f"Error while parsing FileCopyrightText: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("file_lics_comment : FILE_LICS_COMMENT text_or_line") def p_file_license_comment(self, p): self.check_that_current_element_matches_class_for_value(File) set_value(p, self.current_element, argument_name="license_comment") - @grammar_rule("file_lics_comment : FILE_LICS_COMMENT error") - def p_file_license_comment_error(self, p): - self.current_element["logger"].append( - f"Error while parsing LicenseComments in file: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - @grammar_rule("file_attribution_text : FILE_ATTRIBUTION_TEXT text_or_line") def p_file_attribution_text(self, p): self.check_that_current_element_matches_class_for_value(File) self.current_element.setdefault("attribution_texts", []).append(p[2]) - @grammar_rule("file_attribution_text : FILE_ATTRIBUTION_TEXT error") - def p_file_attribution_text_error(self, p): - self.current_element["logger"].append( - f"Error while parsing FileAttributionText: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("file_lics_info : FILE_LICS_INFO license_or_no_assertion_or_none") def p_file_license_info(self, p): self.check_that_current_element_matches_class_for_value(File) @@ -358,31 +260,16 @@ def p_file_license_info(self, p): return self.current_element.setdefault("license_info_in_file", []).append(p[2]) - @grammar_rule("file_lics_info : FILE_LICS_INFO error") - def p_file_license_info_error(self, p): - self.current_element["logger"].append( - f"Error while parsing LicenseInfoInFile: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("file_comment : FILE_COMMENT text_or_line") def p_file_comment(self, p): self.check_that_current_element_matches_class_for_value(File) set_value(p, self.current_element, argument_name="comment") - @grammar_rule("file_comment : FILE_COMMENT error") - def p_file_comment_error(self, p): - self.current_element["logger"].append( - f"Error while parsing FileComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("file_type : FILE_TYPE file_type_value") def p_file_type(self, p): self.check_that_current_element_matches_class_for_value(File) self.current_element.setdefault("file_type", []).append(FileType[p[2]]) - @grammar_rule("file_type : FILE_TYPE error") - def p_file_type_error(self, p): - self.current_element["logger"].append( - f"Error while parsing FileType: Token did not match any of the valid values. Line: {p.lineno(1)}") - @grammar_rule( "file_type_value : SOURCE\n| BINARY\n| ARCHIVE\n | APPLICATION\n | AUDIO\n | IMAGE\n | FILETYPE_TEXT\n| VIDEO\n" " | DOCUMENTATION\n| SPDX \n| OTHER ") @@ -395,23 +282,35 @@ def p_file_checksum(self, p): checksum = parse_checksum(self.current_element["logger"], p[2]) self.current_element.setdefault("checksums", []).append(checksum) - @grammar_rule("file_checksum : FILE_CHECKSUM error") - def p_file_checksum_error(self, p): - self.current_element["logger"].append( - f"Error while parsing Checksum in file: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("file_conc : FILE_LICS_CONC license_or_no_assertion_or_none") def p_file_license_concluded(self, p): self.check_that_current_element_matches_class_for_value(File) set_value(p, self.current_element, argument_name="license_concluded") - @grammar_rule("file_conc : FILE_LICS_CONC error") - def p_file_license_concluded_error(self, p): + # parsing methods for package + + @grammar_rule("pkg_attribution_text : PKG_ATTRIBUTION_TEXT error\n description : PKG_DESC error\n " + "pkg_comment : PKG_COMMENT error\n summary : PKG_SUM error\n pkg_cr_text : PKG_CPY_TEXT error\n " + "pkg_ext_ref : PKG_EXT_REF error\n pkg_lic_comment : PKG_LICS_COMMENT error\n " + "pkg_lic_decl : PKG_LICS_DECL error\n pkg_lic_ff : PKG_LICS_FFILE error \n " + "pkg_lic_conc : PKG_LICS_CONC error\n source_info : PKG_SRC_INFO error\n homepage : PKG_HOME error\n " + "pkg_checksum : PKG_CHECKSUM error\n verification_code : PKG_VERF_CODE error\n " + "download_location : PKG_DOWN error\n files_analyzed : PKG_FILES_ANALYZED error\n " + "originator : PKG_ORIG error\n supplier : PKG_SUPPL error\n pkg_file_name : PKG_FILE_NAME error\n " + "package_version : PKG_VERSION error\n primary_package_purpose : PRIMARY_PACKAGE_PURPOSE error\n " + "built_date : BUILT_DATE error\n release_date : RELEASE_DATE error\n " + "valid_until_date : VALID_UNTIL_DATE error") + def p_package_value_error(self, p): self.current_element["logger"].append( - f"Error while parsing LicenseConcluded in file: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") + f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") - # parsing methods for package + @grammar_rule("description : PKG_DESC text_or_line\n summary : PKG_SUM text_or_line\n " + "source_info : PKG_SRC_INFO text_or_line\n homepage : PKG_HOME line_or_no_assertion_or_none\n " + "download_location : PKG_DOWN line_or_no_assertion_or_none\n " + "originator : PKG_ORIG actor_or_no_assertion\n supplier : PKG_SUPPL actor_or_no_assertion") + def p_generic_package_value(self, p): + self.check_that_current_element_matches_class_for_value(Package) + set_value(p, self.current_element) @grammar_rule("package_name : PKG_NAME LINE") def p_package_name(self, p): @@ -424,58 +323,21 @@ def p_package_name_error(self, p): self.current_element["logger"].append( f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("description : PKG_DESC text_or_line") - def p_pkg_description(self, p): - self.check_that_current_element_matches_class_for_value(Package) - set_value(p, self.current_element) - - @grammar_rule("description : PKG_DESC error") - def p_pkg_description_error(self, p): - self.current_element["logger"].append( - f"Error while parsing PackageDescription: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("pkg_comment : PKG_COMMENT text_or_line") def p_pkg_comment(self, p): self.check_that_current_element_matches_class_for_value(Package) set_value(p, self.current_element, argument_name="comment") - @grammar_rule("pkg_comment : PKG_COMMENT error") - def p_pkg_comment_error(self, p): - self.current_element["logger"].append( - f"Error while parsing PackageComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("pkg_attribution_text : PKG_ATTRIBUTION_TEXT text_or_line") def p_pkg_attribution_text(self, p): self.check_that_current_element_matches_class_for_value(Package) self.current_element.setdefault("attribution_texts", []).append(p[2]) - @grammar_rule("pkg_attribution_text : PKG_ATTRIBUTION_TEXT error") - def p_pkg_attribution_text_error(self, p): - self.current_element["logger"].append( - f"Error while parsing PackageAttributionText: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - - @grammar_rule("summary : PKG_SUM text_or_line") - def p_pkg_summary(self, p): - self.check_that_current_element_matches_class_for_value(Package) - set_value(p, self.current_element) - - @grammar_rule("summary : PKG_SUM error") - def p_pkg_summary_error(self, p): - self.current_element["logger"].append( - f"Error while parsing PackageSummary: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("pkg_cr_text : PKG_CPY_TEXT line_or_no_assertion_or_none") def p_pkg_copyright_text(self, p): self.check_that_current_element_matches_class_for_value(Package) set_value(p, self.current_element, argument_name="copyright_text") - @grammar_rule("pkg_cr_text : PKG_CPY_TEXT error") - def p_pkg_copyright_text_error(self, p): - self.current_element["logger"].append( - f"Error while parsing PackageCopyrightText: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - @grammar_rule("pkg_ext_ref : PKG_EXT_REF LINE PKG_EXT_REF_COMMENT text_or_line\n | PKG_EXT_REF LINE") def p_pkg_external_refs(self, p): self.check_that_current_element_matches_class_for_value(Package) @@ -504,34 +366,16 @@ def p_pkg_external_refs(self, p): return self.current_element.setdefault("external_references", []).append(external_package_ref) - @grammar_rule("pkg_ext_ref : PKG_EXT_REF error") - def p_pkg_external_refs_error(self, p): - self.current_element["logger"].append( - f"Error while parsing ExternalRef in package: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - @grammar_rule("pkg_lic_comment : PKG_LICS_COMMENT text_or_line") def p_pkg_license_comment(self, p): self.check_that_current_element_matches_class_for_value(Package) set_value(p, self.current_element, argument_name="license_comment") - @grammar_rule("pkg_lic_comment : PKG_LICS_COMMENT error") - def p_pkg_license_comment_error(self, p): - self.current_element["logger"].append( - f"Error while parsing PackageLicenseComments: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - @grammar_rule("pkg_lic_decl : PKG_LICS_DECL license_or_no_assertion_or_none") def p_pkg_license_declared(self, p): self.check_that_current_element_matches_class_for_value(Package) set_value(p, self.current_element, argument_name="license_declared") - @grammar_rule("pkg_lic_decl : PKG_LICS_DECL error") - def p_pkg_license_declared_error(self, p): - self.current_element["logger"].append( - f"Error while parsing LicenseDeclared in package: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - @grammar_rule("pkg_lic_ff : PKG_LICS_FFILE license_or_no_assertion_or_none") def p_pkg_license_info_from_file(self, p): self.check_that_current_element_matches_class_for_value(Package) @@ -540,149 +384,60 @@ def p_pkg_license_info_from_file(self, p): else: self.current_element.setdefault("license_info_from_files", []).append(p[2]) - @grammar_rule("pkg_lic_ff : PKG_LICS_FFILE error") - def p_pkg_license_info_from_file_error(self, p): - self.current_element["logger"].append( - f"Error while parsing LicenseInfoFromFiles in package: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - @grammar_rule("pkg_lic_conc : PKG_LICS_CONC license_or_no_assertion_or_none") def p_pkg_license_concluded(self, p): self.check_that_current_element_matches_class_for_value(Package) set_value(p, self.current_element, argument_name="license_concluded") - @grammar_rule("pkg_lic_conc : PKG_LICS_CONC error") - def p_pkg_license_concluded_error(self, p): - self.current_element["logger"].append( - f"Error while parsing LicenseConcluded in package: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - - @grammar_rule("source_info : PKG_SRC_INFO text_or_line") - def p_pkg_source_info(self, p): - self.check_that_current_element_matches_class_for_value(Package) - set_value(p, self.current_element) - - @grammar_rule("source_info : PKG_SRC_INFO error") - def p_pkg_source_info_error(self, p): - self.current_element["logger"].append( - f"Error while parsing PackageSourceInfo: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("pkg_checksum : PKG_CHECKSUM CHECKSUM") def p_pkg_checksum(self, p): self.check_that_current_element_matches_class_for_value(Package) checksum = parse_checksum(self.current_element["logger"], p[2]) self.current_element.setdefault("checksums", []).append(checksum) - @grammar_rule("pkg_checksum : PKG_CHECKSUM error") - def p_pkg_checksum_error(self, p): - self.current_element["logger"].append( - f"Error while parsing PackageChecksum: Token did not match specified grammar rule. Line: {p.lineno(1)}") - - @grammar_rule("pkg_verif : PKG_VERF_CODE LINE") + @grammar_rule("verification_code : PKG_VERF_CODE LINE") def p_pkg_verification_code(self, p): self.check_that_current_element_matches_class_for_value(Package) - verif_code_regex = re.compile(r"([0-9a-f]+)\s*(\(excludes:\s*(.+)\))?", re.UNICODE) + if str(p.slice[0]) in self.current_element: + self.current_element["logger"].append(f"Multiple values for {p[1]} found. Line: {p.lineno(1)}") + return + verif_code_regex = re.compile(r"([0-9a-f]{40})\s*(\(excludes:\s*(.+)\))?", re.UNICODE) verif_code_code_grp = 1 verif_code_exc_files_grp = 3 match = verif_code_regex.match(p[2]) + if not match: + self.current_element["logger"].append( + f"Error while parsing {p[1]}: Value did not match expected format. Line: {p.lineno(1)}") + return value = match.group(verif_code_code_grp) excluded_files = None if match.group(verif_code_exc_files_grp): excluded_files = match.group(verif_code_exc_files_grp).split(",") - self.current_element["verification_code"] = PackageVerificationCode(value, excluded_files) - - @grammar_rule("pkg_verif : PKG_VERF_CODE error") - def p_pkg_verification_code_error(self, p): - self.current_element["logger"].append( - f"Error while parsing PackageVerificationCode: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - - @grammar_rule("homepage : PKG_HOME line_or_no_assertion_or_none") - def p_pkg_homepage(self, p): - self.check_that_current_element_matches_class_for_value(Package) - set_value(p, self.current_element) + self.current_element[str(p.slice[0])] = PackageVerificationCode(value, excluded_files) - @grammar_rule("homepage : PKG_HOME error") - def p_pkg_homepage_error(self, p): - self.current_element["logger"].append( - f"Error while parsing PackageHomePage: Token did not match specified grammar rule. Line: {p.lineno(1)}") - - @grammar_rule("download_location : PKG_DOWN line_or_no_assertion_or_none") - def p_pkg_download_location(self, p): - self.check_that_current_element_matches_class_for_value(Package) - set_value(p, self.current_element) - - @grammar_rule("download_location : PKG_DOWN error") - def p_pkg_download_location_error(self, p): - self.current_element["logger"].append( - f"Error while parsing PackageDownloadLocation: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - - @grammar_rule("pkg_files_analyzed : PKG_FILES_ANALYZED LINE") + @grammar_rule("files_analyzed : PKG_FILES_ANALYZED LINE") def p_pkg_files_analyzed(self, p): self.check_that_current_element_matches_class_for_value(Package) - if p[2] in ['false', 'False']: - self.current_element["files_analyzed"] = False - if p[2] in ['true', 'True']: - self.current_element["files_analyzed"] = True - - @grammar_rule("pkg_files_analyzed : PKG_FILES_ANALYZED error") - def p_pkg_files_analyzed_error(self, p): - self.current_element["logger"].append( - f"Error while parsing FilesAnalyzed in package: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - - @grammar_rule("originator : PKG_ORIG actor_or_no_assertion") - def p_pkg_originator(self, p): - self.check_that_current_element_matches_class_for_value(Package) - set_value(p, self.current_element) - - @grammar_rule("originator : PKG_ORIG error") - def p_pkg_originator_error(self, p): - self.current_element["logger"].append( - f"Error while parsing PackageOriginator: Token did not match specified grammar rule. Line: {p.lineno(1)}") - - @grammar_rule("supplier : PKG_SUPPL actor_or_no_assertion") - def p_pkg_supplier(self, p): - self.check_that_current_element_matches_class_for_value(Package) - set_value(p, self.current_element) - - @grammar_rule("supplier : PKG_SUPPL error") - def p_pkg_supplier_error(self, p): - self.current_element["logger"].append( - f"Error while parsing PackageSupplier: Token did not match specified grammar rule. Line: {p.lineno(1)}") + if str(p.slice[0]) in self.current_element: + self.current_element["logger"].append(f"Multiple values for {p[1]} found. Line: {p.lineno(1)}") + return + self.current_element[str(p.slice[0])] = p[2] in ['true', 'True'] @grammar_rule("pkg_file_name : PKG_FILE_NAME LINE") def p_pkg_file_name(self, p): self.check_that_current_element_matches_class_for_value(Package) set_value(p, self.current_element, argument_name="file_name") - @grammar_rule("pkg_file_name : PKG_FILE_NAME error") - def p_pkg_file_name_error(self, p): - self.current_element["logger"].append( - f"Error while parsing PackageFileName: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("package_version : PKG_VERSION LINE") def p_package_version(self, p): self.check_that_current_element_matches_class_for_value(Package) set_value(p, self.current_element, argument_name="version") - @grammar_rule("package_version : PKG_VERSION error") - def p_package_version_error(self, p): - self.current_element["logger"].append( - f"Error while parsing PackageVersion: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("primary_package_purpose : PRIMARY_PACKAGE_PURPOSE primary_package_purpose_value") def p_primary_package_purpose(self, p): self.check_that_current_element_matches_class_for_value(Package) set_value(p, self.current_element, method_to_apply=lambda x: PackagePurpose[x.replace("-", "_")]) - @grammar_rule("primary_package_purpose : PRIMARY_PACKAGE_PURPOSE error") - def p_primary_package_purpose_error(self, p): - self.current_element["logger"].append( - f"Error while parsing PrimaryPackagePurpose: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - @grammar_rule("primary_package_purpose_value : APPLICATION\n | FRAMEWORK\n | LIBRARY\n | CONTAINER\n " "| OPERATING_SYSTEM \n | DEVICE \n| FIRMWARE\n | SOURCE\n | ARCHIVE\n | FILE\n | INSTALL\n | OTHER") def p_primary_package_purpose_value(self, p): @@ -690,26 +445,21 @@ def p_primary_package_purpose_value(self, p): @grammar_rule("built_date : BUILT_DATE DATE\n release_date : RELEASE_DATE DATE\n " "valid_until_date : VALID_UNTIL_DATE DATE") - def p_built_date(self, p): + def p_package_dates(self, p): self.check_that_current_element_matches_class_for_value(Package) set_value(p, self.current_element, method_to_apply=datetime_from_str) - @grammar_rule("built_date : BUILT_DATE error") - def p_built_date_error(self, p): - self.current_element["logger"].append( - f"Error while parsing BuiltDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") - - @grammar_rule("release_date : RELEASE_DATE error") - def p_release_date_error(self, p): - self.current_element["logger"].append( - f"Error while parsing ReleaseDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") + # parsing methods for snippet - @grammar_rule("valid_until_date : VALID_UNTIL_DATE error") - def p_valid_until_date_error(self, p): + @grammar_rule("snip_name : SNIPPET_NAME error\n snip_comment : SNIPPET_COMMENT error\n " + "snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT error\n snip_cr_text : SNIPPET_CR_TEXT error\n " + "snip_lic_comment : SNIPPET_LICS_COMMENT error\n file_spdx_id : SNIPPET_FILE_SPDXID error\n " + "snip_lics_conc : SNIPPET_LICS_CONC error\n snip_lics_info : SNIPPET_LICS_INFO error\n " + "snip_byte_range : SNIPPET_BYTE_RANGE error\n snip_line_range : SNIPPET_LINE_RANGE error\n ") + def p_snippet_value_error(self, p): self.current_element["logger"].append( - f"Error while parsing ValidUntilDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") + f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") - # parsing methods for snippet @grammar_rule("snip_spdx_id : SNIPPET_SPDX_ID LINE") def p_snippet_spdx_id(self, p): self.initialize_new_current_element(Snippet) @@ -719,83 +469,43 @@ def p_snippet_spdx_id(self, p): def p_snippet_spdx_id_error(self, p): self.initialize_new_current_element(Snippet) self.current_element["logger"].append( - f"Error while parsing SnippetSPDXID: Token did not match specified grammar rule. Line: {p.lineno(1)}") + f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("snip_name : SNIPPET_NAME LINE") def p_snippet_name(self, p): self.check_that_current_element_matches_class_for_value(Snippet) set_value(p, self.current_element, argument_name="name") - @grammar_rule("snip_name : SNIPPET_NAME error") - def p_snippet_name_error(self, p): - self.current_element["logger"].append( - f"Error while parsing SnippetName: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("snip_comment : SNIPPET_COMMENT text_or_line") def p_snippet_comment(self, p): self.check_that_current_element_matches_class_for_value(Snippet) set_value(p, self.current_element, argument_name="comment") - @grammar_rule("snip_comment : SNIPPET_COMMENT error") - def p_snippet_comment_error(self, p): - self.current_element["logger"].append( - f"Error while parsing SnippetComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT text_or_line") def p_snippet_attribution_text(self, p): self.check_that_current_element_matches_class_for_value(Snippet) self.current_element.setdefault("attribution_texts", []).append(p[2]) - @grammar_rule("snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT error") - def p_snippet_attribution_text_error(self, p): - self.current_element["logger"].append( - f"Error while parsing SnippetAttributionText: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - @grammar_rule("snip_cr_text : SNIPPET_CR_TEXT line_or_no_assertion_or_none") def p_snippet_copyright_text(self, p): self.check_that_current_element_matches_class_for_value(Snippet) set_value(p, self.current_element, argument_name="copyright_text") - @grammar_rule("snip_cr_text : SNIPPET_CR_TEXT error") - def p_snippet_copyright_text_error(self, p): - self.current_element["logger"].append( - f"Error while parsing SnippetCopyrightText: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - @grammar_rule("snip_lic_comment : SNIPPET_LICS_COMMENT text_or_line") def p_snippet_license_comment(self, p): self.check_that_current_element_matches_class_for_value(Snippet) set_value(p, self.current_element, argument_name="license_comment") - @grammar_rule("snip_lic_comment : SNIPPET_LICS_COMMENT error") - def p_snippet_license_comment_error(self, p): - self.current_element["logger"].append( - f"Error while parsing SnippetLicenseComments: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - @grammar_rule("file_spdx_id : SNIPPET_FILE_SPDXID LINE") def p_snippet_from_file_spdxid(self, p): self.check_that_current_element_matches_class_for_value(Snippet) set_value(p, self.current_element) - @grammar_rule("file_spdx_id : SNIPPET_FILE_SPDXID error") - def p_snippet_from_file_spdxid_error(self, p): - self.current_element["logger"].append( - f"Error while parsing SnippetFromFileSPDXID: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - @grammar_rule("snip_lics_conc : SNIPPET_LICS_CONC license_or_no_assertion_or_none") def p_snippet_concluded_license(self, p): self.check_that_current_element_matches_class_for_value(Snippet) set_value(p, self.current_element, argument_name="license_concluded") - @grammar_rule("snip_lics_conc : SNIPPET_LICS_CONC error") - def p_snippet_concluded_license_error(self, p): - self.current_element["logger"].append( - f"Error while parsing SnippetLicenseConcluded: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - @grammar_rule("snip_lics_info : SNIPPET_LICS_INFO license_or_no_assertion_or_none") def p_snippet_license_info(self, p): self.check_that_current_element_matches_class_for_value(Snippet) @@ -804,12 +514,6 @@ def p_snippet_license_info(self, p): else: self.current_element.setdefault("license_info_in_snippet", []).append(p[2]) - @grammar_rule("snip_lics_info : SNIPPET_LICS_INFO error") - def p_snippet_license_info_error(self, p): - self.current_element["logger"].append( - f"Error while parsing LicenseInfoInSnippet: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - @grammar_rule("snip_byte_range : SNIPPET_BYTE_RANGE LINE") def p_snippet_byte_range(self, p): self.check_that_current_element_matches_class_for_value(Snippet) @@ -824,11 +528,6 @@ def p_snippet_byte_range(self, p): endpoint = int(p[2].split(":")[-1]) self.current_element["byte_range"] = startpoint, endpoint - @grammar_rule("snip_byte_range : SNIPPET_BYTE_RANGE error") - def p_snippet_byte_range_error(self, p): - self.current_element["logger"].append( - f"Error while parsing SnippetByteRange: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("snip_line_range : SNIPPET_LINE_RANGE LINE") def p_snippet_line_range(self, p): self.check_that_current_element_matches_class_for_value(Snippet) @@ -844,12 +543,13 @@ def p_snippet_line_range(self, p): endpoint = int(p[2].split(":")[1]) self.current_element["line_range"] = startpoint, endpoint - @grammar_rule("snip_line_range : SNIPPET_LINE_RANGE error") - def p_snippet_line_range_error(self, p): + # parsing methods for annotation + @grammar_rule("annotation_date : ANNOTATION_DATE error\n annotation_comment : ANNOTATION_COMMENT error\n " + "annotation_type : ANNOTATION_TYPE error\n annotation_spdx_id : ANNOTATION_SPDX_ID error") + def p_annotation_value_error(self, p): self.current_element["logger"].append( - f"Error while parsing SnippetLineRange: Token did not match specified grammar rule. Line: {p.lineno(1)}") + f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") - # parsing methods for annotation def p_annotator(self, p): """annotator : ANNOTATOR PERSON_VALUE\n| TOOL_VALUE\n| ORG_VALUE""" self.initialize_new_current_element(Annotation) @@ -866,31 +566,16 @@ def p_annotation_date(self, p): self.check_that_current_element_matches_class_for_value(Annotation) set_value(p, self.current_element, method_to_apply=datetime_from_str) - @grammar_rule("annotation_date : ANNOTATION_DATE error") - def p_annotation_date_error(self, p): - self.current_element["logger"].append( - f"Error while parsing AnnotationDate: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("annotation_comment : ANNOTATION_COMMENT text_or_line") def p_annotation_comment(self, p): self.check_that_current_element_matches_class_for_value(Annotation) set_value(p, self.current_element) - @grammar_rule("annotation_comment : ANNOTATION_COMMENT error") - def p_annotation_comment_error(self, p): - self.current_element["logger"].append( - f"Error while parsing AnnotationComment: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("annotation_type : ANNOTATION_TYPE annotation_type_value") def p_annotation_type(self, p): self.check_that_current_element_matches_class_for_value(Annotation) set_value(p, self.current_element, method_to_apply=lambda x: AnnotationType[x]) - @grammar_rule("annotation_type : ANNOTATION_TYPE error") - def p_annotation_type_error(self, p): - self.current_element["logger"].append( - f"Error while parsing AnnotationType: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("annotation_type_value : OTHER\n| REVIEW") def p_annotation_type_value(self, p): p[0] = p[1] @@ -899,12 +584,6 @@ def p_annotation_type_value(self, p): def p_annotation_spdx_id(self, p): set_value(p, self.current_element, argument_name="spdx_id") - @grammar_rule("annotation_spdx_id : ANNOTATION_SPDX_ID error") - def p_annotation_spdx_id_error(self, p): - self.current_element["logger"].append( - f"Error while parsing SPDXREF in annotation: Token did not match specified grammar rule. " - f"Line: {p.lineno(1)}") - # parsing methods for relationship @grammar_rule("relationship : RELATIONSHIP relationship_value RELATIONSHIP_COMMENT text_or_line\n " "| RELATIONSHIP relationship_value") @@ -934,7 +613,7 @@ def p_relationship(self, p): def p_relationship_error(self, p): self.initialize_new_current_element(Relationship) self.current_element["logger"].append( - f"Error while parsing Relationship: Token did not match specified grammar rule. Line: {p.lineno(1)}") + f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") @grammar_rule("relationship_value : DOC_REF_ID LINE") def p_relationship_value_with_doc_ref(self, p): diff --git a/tests/spdx/parser/tagvalue/test_file_parser.py b/tests/spdx/parser/tagvalue/test_file_parser.py index 1b37b2b5a..6ca32efc7 100644 --- a/tests/spdx/parser/tagvalue/test_file_parser.py +++ b/tests/spdx/parser/tagvalue/test_file_parser.py @@ -64,5 +64,5 @@ def test_parse_invalid_file(): parser.parse(file_str) assert err.value.get_messages() == [["Error while parsing File: ['Error while parsing FileType: Token did not " - "match any of the valid values. Line: 3', 'Error while parsing Checksum in " - "file: Token did not match specified grammar rule. Line: 5']"]] + "match specified grammar rule. Line: 3', 'Error while parsing FileChecksum: " + "Token did not match specified grammar rule. Line: 5']"]] From 925492c0557edb36561f9bd6a36455645e2158db Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 2 Mar 2023 14:41:35 +0100 Subject: [PATCH 325/362] [issue-382] add tests for contains relationship Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 9 +++++-- .../parser/tagvalue/test_tag_value_parser.py | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 9f8f78043..b17e99045 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -99,7 +99,8 @@ def p_attrib(self, p): pass # general parsing methods - @grammar_rule("unknown_tag : UNKNOWN_TAG text_or_line\n | UNKNOWN_TAG DATE\n | UNKNOWN_TAG PERSON_VALUE") + @grammar_rule("unknown_tag : UNKNOWN_TAG text_or_line\n | UNKNOWN_TAG DATE\n | UNKNOWN_TAG PERSON_VALUE \n" + "| UNKNOWN_TAG") def p_unknown_tag(self, p): self.logger.append(f"Unknown tag provided in line {p.lineno(1)}") @@ -681,7 +682,11 @@ def check_for_preceding_package_and_build_contains_relationship(self): file_spdx_id = self.current_element["spdx_id"] if "packages" not in self.elements_build: return + # We assume that all files that are not contained in a package precede any package information. Any file + # information that follows any package information is assigned to the last parsed package by creating a + # corresponding contains relationship. + # (see https://spdx.github.io/spdx-spec/v2.3/composition-of-an-SPDX-document/#5.2.2) package_spdx_id = self.elements_build["packages"][-1].spdx_id relationship = Relationship(package_spdx_id, RelationshipType.CONTAINS, file_spdx_id) - if relationship not in self.elements_build["relationships"]: + if relationship not in self.elements_build.setdefault("relationships",[]): self.elements_build.setdefault("relationships", []).append(relationship) diff --git a/tests/spdx/parser/tagvalue/test_tag_value_parser.py b/tests/spdx/parser/tagvalue/test_tag_value_parser.py index e96fa9c80..9dde9dacf 100644 --- a/tests/spdx/parser/tagvalue/test_tag_value_parser.py +++ b/tests/spdx/parser/tagvalue/test_tag_value_parser.py @@ -14,8 +14,10 @@ import pytest from spdx.model.document import Document +from spdx.model.relationship import RelationshipType, Relationship from spdx.parser.error import SPDXParsingError from spdx.parser.tagvalue.parser.tagvalue import Parser +from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR def test_parse_unknown_tag(): @@ -40,3 +42,27 @@ def test_tag_value_parser(): assert len(doc.snippets) == 1 assert len(doc.relationships) == 13 assert len(doc.extracted_licensing_info) == 5 + + +def test_building_contains_relationship(): + parser = Parser() + document_str = "\n".join( + [DOCUMENT_STR, "SPDXID: SPDXRef-DOCUMENT", "FileName: File without package", "SPDXID: SPDXRef-File", + "FileChecksum: SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759", + "PackageName: Package with two files", "SPDXID: SPDXRef-Package-with-two-files", + "PackageDownloadLocation: https://download.com", + "FileName: File in package", "SPDXID: SPDXRef-File-in-Package", + "FileChecksum: SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759", + "FileName: Second file in package", "SPDXID: SPDXRef-Second-File-in-Package", + "FileChecksum: SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759", + "PackageName: Second package with file", "SPDXID: SPDXRef-Package-with-one-file", + "PackageDownloadLocation: https://download.com", + "FileName: File in package", "SPDXID: SPDXRef-File-in-different-Package", + "FileChecksum: SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759", + ]) + document = parser.parse(document_str) + + assert document.relationships == [ + Relationship("SPDXRef-Package-with-two-files", RelationshipType.CONTAINS, "SPDXRef-File-in-Package"), + Relationship("SPDXRef-Package-with-two-files", RelationshipType.CONTAINS, "SPDXRef-Second-File-in-Package"), + Relationship("SPDXRef-Package-with-one-file", RelationshipType.CONTAINS, "SPDXRef-File-in-different-Package")] From bd15f71b4f2964581018526ccb68b57527658ff4 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 2 Mar 2023 14:41:50 +0100 Subject: [PATCH 326/362] [issue-382] reformat Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 34 +++++++++++---------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index b17e99045..f639cb6d7 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -545,6 +545,7 @@ def p_snippet_line_range(self, p): self.current_element["line_range"] = startpoint, endpoint # parsing methods for annotation + @grammar_rule("annotation_date : ANNOTATION_DATE error\n annotation_comment : ANNOTATION_COMMENT error\n " "annotation_type : ANNOTATION_TYPE error\n annotation_spdx_id : ANNOTATION_SPDX_ID error") def p_annotation_value_error(self, p): @@ -586,6 +587,7 @@ def p_annotation_spdx_id(self, p): set_value(p, self.current_element, argument_name="spdx_id") # parsing methods for relationship + @grammar_rule("relationship : RELATIONSHIP relationship_value RELATIONSHIP_COMMENT text_or_line\n " "| RELATIONSHIP relationship_value") def p_relationship(self, p): @@ -642,6 +644,22 @@ def parse(self, text): document = construct_or_raise_parsing_error(Document, self.elements_build) return document + def initialize_new_current_element(self, class_name: Any): + if "class" in self.current_element and "spdx_id" in self.current_element: + self.element_stack.append({self.current_element["class"]: self.current_element["spdx_id"]}) + self.construct_current_element() + self.current_element["class"] = class_name + + def check_that_current_element_matches_class_for_value(self, expected_class): + if "class" not in self.current_element: + self.logger.append( + f"Element {expected_class.__name__} is not the current element in scope, probably the expected tag to " + f"start the element ({ELEMENT_EXPECTED_START_TAG[expected_class.__name__]}) is missing.") + elif expected_class != self.current_element["class"]: + self.logger.append( + f"Element {expected_class.__name__} is not the current element in scope, probably the expected tag to " + f"start the element ({ELEMENT_EXPECTED_START_TAG[expected_class.__name__]}) is missing.") + def construct_current_element(self): if "class" not in self.current_element: self.current_element = {"logger": Logger()} @@ -662,22 +680,6 @@ def construct_current_element(self): self.logger.append(err.get_messages()) self.current_element = {"logger": Logger()} - def check_that_current_element_matches_class_for_value(self, expected_class): - if "class" not in self.current_element: - self.logger.append( - f"Element {expected_class.__name__} is not the current element in scope, probably the expected tag to " - f"start the element ({ELEMENT_EXPECTED_START_TAG[expected_class.__name__]}) is missing.") - elif expected_class != self.current_element["class"]: - self.logger.append( - f"Element {expected_class.__name__} is not the current element in scope, probably the expected tag to " - f"start the element ({ELEMENT_EXPECTED_START_TAG[expected_class.__name__]}) is missing.") - - def initialize_new_current_element(self, class_name: Any): - if "class" in self.current_element and "spdx_id" in self.current_element: - self.element_stack.append({self.current_element["class"]: self.current_element["spdx_id"]}) - self.construct_current_element() - self.current_element["class"] = class_name - def check_for_preceding_package_and_build_contains_relationship(self): file_spdx_id = self.current_element["spdx_id"] if "packages" not in self.elements_build: From c9b718d7e5f377cdd5e985b6f442092433e31d79 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 2 Mar 2023 16:13:24 +0100 Subject: [PATCH 327/362] [issue-382] use tag-value parser to test tag-value writer Signed-off-by: Meret Behrens --- tests/spdx/writer/tagvalue/test_tagvalue_writer.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/spdx/writer/tagvalue/test_tagvalue_writer.py b/tests/spdx/writer/tagvalue/test_tagvalue_writer.py index 556b724ac..f4af5a505 100644 --- a/tests/spdx/writer/tagvalue/test_tagvalue_writer.py +++ b/tests/spdx/writer/tagvalue/test_tagvalue_writer.py @@ -13,6 +13,7 @@ import pytest +from spdx.parser.tagvalue.parser import tagvalue_parser from tests.spdx.fixtures import document_fixture from spdx.writer.tagvalue.tagvalue_writer import write_document_to_file @@ -29,5 +30,6 @@ def test_write_tag_value(temporary_file_path: str): write_document_to_file(document, temporary_file_path) - # without a tag-value parser we can only test that no errors occur while writing - # as soon as the tag-value parser is implemented (https://github.com/spdx/tools-python/issues/382) we can test for equality between the temporary file and the expected file in ./expected_results + parsed_document = tagvalue_parser.parse_from_file(temporary_file_path) + + assert parsed_document == document From be1b3e5b2a521e58a8fb64882edd730b14d3d2b4 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 2 Mar 2023 16:38:32 +0100 Subject: [PATCH 328/362] [issue-382] fix logging Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 6 ++--- .../parser/tagvalue/test_annotation_parser.py | 20 +++++++------- .../tagvalue/test_creation_info_parser.py | 16 ++++++------ .../spdx/parser/tagvalue/test_file_parser.py | 6 ++--- .../parser/tagvalue/test_package_parser.py | 26 +++++++++---------- .../tagvalue/test_relationship_parser.py | 10 +++---- .../parser/tagvalue/test_snippet_parser.py | 12 ++++----- 7 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index f639cb6d7..c2f655f0f 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -637,7 +637,7 @@ def parse(self, text): try: raise_parsing_error_if_logger_has_messages(self.creation_info.pop("logger"), "CreationInfo") except SPDXParsingError as err: - self.logger.append(err.get_messages()) + self.logger.extend(err.get_messages()) raise_parsing_error_if_logger_has_messages(self.logger) creation_info = construct_or_raise_parsing_error(CreationInfo, self.creation_info) self.elements_build["creation_info"] = creation_info @@ -668,7 +668,7 @@ def construct_current_element(self): try: raise_parsing_error_if_logger_has_messages(self.current_element.pop("logger"), class_name.__name__) except SPDXParsingError as err: - self.logger.append(err.get_messages()) + self.logger.extend(err.get_messages()) self.current_element = {"logger": Logger()} return try: @@ -677,7 +677,7 @@ def construct_current_element(self): if class_name == File: self.check_for_preceding_package_and_build_contains_relationship() except SPDXParsingError as err: - self.logger.append(err.get_messages()) + self.logger.extend(err.get_messages()) self.current_element = {"logger": Logger()} def check_for_preceding_package_and_build_contains_relationship(self): diff --git a/tests/spdx/parser/tagvalue/test_annotation_parser.py b/tests/spdx/parser/tagvalue/test_annotation_parser.py index e0b7b515d..3cf7fa3a0 100644 --- a/tests/spdx/parser/tagvalue/test_annotation_parser.py +++ b/tests/spdx/parser/tagvalue/test_annotation_parser.py @@ -39,19 +39,19 @@ def test_parse_annotation(): @pytest.mark.parametrize("annotation_str, expected_message", [ - ('Annotator: Person: Jane Doe()', [['Error while constructing Annotation: Annotation.__init__() missing 4 ' - "required positional arguments: 'spdx_id', 'annotation_type', " - "'annotation_date', and 'annotation_comment'"]]), + ('Annotator: Person: Jane Doe()', ['Error while constructing Annotation: Annotation.__init__() missing 4 ' + "required positional arguments: 'spdx_id', 'annotation_type', " + "'annotation_date', and 'annotation_comment'"]), ('Annotator: Person: Jane Doe()\nAnnotationType: SOURCE\nAnnotationDate: 201001-2912:23', - [["Error while parsing Annotation: ['Error while parsing AnnotationType: Token " - "did not match specified grammar rule. Line: 2', 'Error while parsing " - "AnnotationDate: Token did not match specified grammar rule. Line: 3']"]]), + ["Error while parsing Annotation: ['Error while parsing AnnotationType: Token " + "did not match specified grammar rule. Line: 2', 'Error while parsing " + "AnnotationDate: Token did not match specified grammar rule. Line: 3']"]), ('Annotator: Jane Doe()\nAnnotationDate: 201001-29T18:30:22Z\n' 'AnnotationComment: Document level annotation\nAnnotationType: OTHER\nSPDXREF: SPDXRef-DOCUMENT', - [["Error while parsing Annotation: ['Error while parsing Annotator: Token did " - "not match specified grammar rule. Line: 1', 'Error while parsing " - "AnnotationDate: Token did not match specified grammar rule. Line: 2']"]]), - ('Annotator: Person: ()', [["Error while parsing Annotation: [['No name for Person provided: Person: ().']]"]]), + ["Error while parsing Annotation: ['Error while parsing Annotator: Token did " + "not match specified grammar rule. Line: 1', 'Error while parsing " + "AnnotationDate: Token did not match specified grammar rule. Line: 2']"]), + ('Annotator: Person: ()', ["Error while parsing Annotation: [['No name for Person provided: Person: ().']]"]), ('AnnotationType: REVIEW', ['Element Annotation is not the current element in scope, probably the ' 'expected tag to start the element (Annotator) is missing.'])]) def test_parse_invalid_annotation(annotation_str, expected_message): diff --git a/tests/spdx/parser/tagvalue/test_creation_info_parser.py b/tests/spdx/parser/tagvalue/test_creation_info_parser.py index de5a794c3..f871e304c 100644 --- a/tests/spdx/parser/tagvalue/test_creation_info_parser.py +++ b/tests/spdx/parser/tagvalue/test_creation_info_parser.py @@ -70,12 +70,12 @@ def test_parse_creation_info(): 'Creator: Person Bob (bob@example.com)', 'Creator: Organization: Acme [email]', 'Created: 2010-02-03T00:00:0Z', 'CreatorComment: Sample Comment', 'LicenseListVersion: 7']), - [["Error while parsing CreationInfo: " - "['Error while parsing DocumentNamespace: Token did not match specified grammar rule. " - "Line: 6', 'Error while parsing ExternalDocumentRef: " - "Token did not match specified grammar rule. Line: 7', 'Error while parsing Creator: " - "Token did not match specified grammar rule. Line: 8', 'Error while parsing Created: " - "Token did not match specified grammar rule. Line: 10', '7 is not a valid version string']"]]), + ["Error while parsing CreationInfo: " + "['Error while parsing DocumentNamespace: Token did not match specified grammar rule. " + "Line: 6', 'Error while parsing ExternalDocumentRef: " + "Token did not match specified grammar rule. Line: 7', 'Error while parsing Creator: " + "Token did not match specified grammar rule. Line: 8', 'Error while parsing Created: " + "Token did not match specified grammar rule. Line: 10', '7 is not a valid version string']"]), ('\n'.join( ['SPDXVersion: SPDX-2.3', 'DataLicense: CC0-1.0', 'DocumentName: Sample_Document-V2.3', 'SPDXID: SPDXRef-DOCUMENT']), @@ -83,8 +83,8 @@ def test_parse_creation_info(): "required positional arguments: 'document_namespace', 'creators', and " "'created'"]), ('LicenseListVersion: 3.5\nLicenseListVersion: 3.7', - [["Error while parsing CreationInfo: ['Multiple values for LicenseListVersion " - "found. Line: 2']"]])])) + ["Error while parsing CreationInfo: ['Multiple values for LicenseListVersion " + "found. Line: 2']"])])) def test_parse_invalid_creation_info(document_str, expected_message): parser = Parser() with pytest.raises(SPDXParsingError) as err: diff --git a/tests/spdx/parser/tagvalue/test_file_parser.py b/tests/spdx/parser/tagvalue/test_file_parser.py index 6ca32efc7..7ca5c4118 100644 --- a/tests/spdx/parser/tagvalue/test_file_parser.py +++ b/tests/spdx/parser/tagvalue/test_file_parser.py @@ -63,6 +63,6 @@ def test_parse_invalid_file(): with pytest.raises(SPDXParsingError) as err: parser.parse(file_str) - assert err.value.get_messages() == [["Error while parsing File: ['Error while parsing FileType: Token did not " - "match specified grammar rule. Line: 3', 'Error while parsing FileChecksum: " - "Token did not match specified grammar rule. Line: 5']"]] + assert err.value.get_messages() == ["Error while parsing File: ['Error while parsing FileType: Token did not " + "match specified grammar rule. Line: 3', 'Error while parsing FileChecksum: " + "Token did not match specified grammar rule. Line: 5']"] diff --git a/tests/spdx/parser/tagvalue/test_package_parser.py b/tests/spdx/parser/tagvalue/test_package_parser.py index 9f2de8997..a65650358 100644 --- a/tests/spdx/parser/tagvalue/test_package_parser.py +++ b/tests/spdx/parser/tagvalue/test_package_parser.py @@ -79,26 +79,26 @@ def test_parse_package(): ['Element Package is not the current element in scope, probably the expected ' 'tag to start the element (PackageName) is missing.']), ('PackageName: TestPackage', - [['Error while constructing Package: Package.__init__() missing 2 required ' - "positional arguments: 'spdx_id' and 'download_location'"]]), + ['Error while constructing Package: Package.__init__() missing 2 required ' + "positional arguments: 'spdx_id' and 'download_location'"]), ('PackageName: TestPackage\nPackageCopyrightText:This is a copyright\n' 'PackageCopyrightText:MultipleCopyright', - [["Error while parsing Package: ['Multiple values for PackageCopyrightText " - "found. Line: 3']"]]), + ["Error while parsing Package: ['Multiple values for PackageCopyrightText " + "found. Line: 3']"]), ('PackageName: TestPackage\nExternalRef: reference locator', - [['Error while parsing Package: ["Couldn\'t split PackageExternalRef in ' - 'category, reference_type and locator. Line: 2"]']]), + ['Error while parsing Package: ["Couldn\'t split PackageExternalRef in ' + 'category, reference_type and locator. Line: 2"]']), ('PackageName: TestPackage\nExternalRef: category reference locator', - [["Error while parsing Package: ['Invalid ExternalPackageRefCategory: " - "category']"]]), + ["Error while parsing Package: ['Invalid ExternalPackageRefCategory: " + "category']"]), ('SPDXID:SPDXRef-DOCUMENT\nPackageName: TestPackage\nSPDXID:SPDXRef-Package\n' 'PackageDownloadLocation: download.com\nPackageVerificationCode: category reference locator', - [["Error while parsing Package: ['Error while parsing PackageVerificationCode: " - "Value did not match expected format. Line: 5']"]]), + ["Error while parsing Package: ['Error while parsing PackageVerificationCode: " + "Value did not match expected format. Line: 5']"]), ('PackageName: TestPackage\nBuiltDate: 2012\nValidUntilDate:202-11-02T00:00', - [["Error while parsing Package: ['Error while parsing BuiltDate: Token did not " - "match specified grammar rule. Line: 2', 'Error while parsing " - "ValidUntilDate: Token did not match specified grammar rule. Line: 3']"]]) + ["Error while parsing Package: ['Error while parsing BuiltDate: Token did not " + "match specified grammar rule. Line: 2', 'Error while parsing " + "ValidUntilDate: Token did not match specified grammar rule. Line: 3']"]) ]) def test_parse_invalid_package(package_str, expected_message): parser = Parser() diff --git a/tests/spdx/parser/tagvalue/test_relationship_parser.py b/tests/spdx/parser/tagvalue/test_relationship_parser.py index f6776299b..dc93c1dc4 100644 --- a/tests/spdx/parser/tagvalue/test_relationship_parser.py +++ b/tests/spdx/parser/tagvalue/test_relationship_parser.py @@ -42,13 +42,13 @@ def test_parse_relationship(relationship_str, expected_relationship): @pytest.mark.parametrize("relationship_str, expected_message", [("Relationship: spdx_id DESCRIBES", - [['Error while parsing Relationship: ["Relationship couldn\'t be split in spdx_element_id, ' - 'relationship_type and related_spdx_element. Line: 1"]']]), + ['Error while parsing Relationship: ["Relationship couldn\'t be split in spdx_element_id, ' + 'relationship_type and related_spdx_element. Line: 1"]']), ("Relationship: spdx_id IS spdx_id", - [["Error while parsing Relationship: ['Invalid RelationshipType IS. Line: 1']"]]), + ["Error while parsing Relationship: ['Invalid RelationshipType IS. Line: 1']"]), ("Relationship: spdx_id IS spdx_id\nRelationshipComment: SOURCE", - [["Error while parsing Relationship: ['Error while parsing Relationship: Token " - "did not match specified grammar rule. Line: 1']"]]) + ["Error while parsing Relationship: ['Error while parsing Relationship: Token " + "did not match specified grammar rule. Line: 1']"]) ]) def test_parse_invalid_relationship(relationship_str, expected_message): parser = Parser() diff --git a/tests/spdx/parser/tagvalue/test_snippet_parser.py b/tests/spdx/parser/tagvalue/test_snippet_parser.py index a6f84d415..f4c62c68f 100644 --- a/tests/spdx/parser/tagvalue/test_snippet_parser.py +++ b/tests/spdx/parser/tagvalue/test_snippet_parser.py @@ -60,13 +60,13 @@ def test_parse_snippet(): ('SnippetName: TestSnippet', ['Element Snippet is not the current element in scope, probably the expected ' 'tag to start the element (SnippetSPDXID) is missing.']), ('SnippetSPDXID: SPDXDRef-Snippet\nSnippetByteRange: 1,4', - [['Error while parsing Snippet: ["Value for SnippetByteRange doesn\'t match ' - 'valid range pattern."]']]), + ['Error while parsing Snippet: ["Value for SnippetByteRange doesn\'t match ' + 'valid range pattern."]']), ('SnippetSPDXID: SPDXDRef-Snippet\nSnippetByteRange: 1:4\nSnippetByteRange:10:23', - [["Error while parsing Snippet: ['Multiple values for SnippetByteRange found. " - "Line: 3']"]]), - ('SnippetSPDXID: SPDXRef-Snippet', [['Error while constructing Snippet: Snippet.__init__() missing 2 required ' - "positional arguments: 'file_spdx_id' and 'byte_range'"]]) + ["Error while parsing Snippet: ['Multiple values for SnippetByteRange found. " + "Line: 3']"]), + ('SnippetSPDXID: SPDXRef-Snippet', ['Error while constructing Snippet: Snippet.__init__() missing 2 required ' + "positional arguments: 'file_spdx_id' and 'byte_range'"]) ]) def test_parse_invalid_snippet(snippet_str, expected_message): parser = Parser() From 739b23d3c6ceb609259328c292ddd69f56398c4d Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 3 Mar 2023 09:14:48 +0100 Subject: [PATCH 329/362] [issue-382] delete element_stack as it is not needed Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index c2f655f0f..c5b5204b1 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -46,7 +46,6 @@ class Parser(object): tokens: List[str] logger: Logger - element_stack: List[Dict[str, str]] current_element: Dict[str, Any] creation_info: Dict[str, Any] elements_build: Dict[str, Any] @@ -56,7 +55,6 @@ class Parser(object): def __init__(self, **kwargs): self.tokens = SPDXLexer.tokens self.logger = Logger() - self.element_stack = [] self.current_element = {"logger": Logger()} self.creation_info = {"logger": Logger()} self.elements_build = dict() @@ -645,8 +643,6 @@ def parse(self, text): return document def initialize_new_current_element(self, class_name: Any): - if "class" in self.current_element and "spdx_id" in self.current_element: - self.element_stack.append({self.current_element["class"]: self.current_element["spdx_id"]}) self.construct_current_element() self.current_element["class"] = class_name From 5c431a82ef83305c190e45b7c548c8639aac531e Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 3 Mar 2023 09:33:04 +0100 Subject: [PATCH 330/362] [issue-382] add line number to error messages Signed-off-by: Meret Behrens --- .../parser/tagvalue/parser/helper_methods.py | 4 +- src/spdx/parser/tagvalue/parser/tagvalue.py | 106 +++++++++--------- .../parser/tagvalue/test_annotation_parser.py | 2 +- .../test_extracted_licensing_info_parser.py | 10 +- .../parser/tagvalue/test_package_parser.py | 2 +- .../parser/tagvalue/test_snippet_parser.py | 4 +- 6 files changed, 66 insertions(+), 62 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/helper_methods.py b/src/spdx/parser/tagvalue/parser/helper_methods.py index c47e5b5c0..869aa21e5 100644 --- a/src/spdx/parser/tagvalue/parser/helper_methods.py +++ b/src/spdx/parser/tagvalue/parser/helper_methods.py @@ -37,12 +37,12 @@ def str_from_text(text: Optional[str]) -> Optional[str]: return None -def parse_checksum(logger: Logger, checksum_str: str) -> Optional[Checksum]: +def parse_checksum(logger: Logger, checksum_str: str, line_number: int) -> Optional[Checksum]: try: algorithm, value = checksum_str.split(":") except ValueError: logger.append( - f"Couldn't split value for checksum in algorithm and value.") + f"Couldn't split value for checksum in algorithm and value. Line: {line_number}") return None algorithm = ChecksumAlgorithm[algorithm.upper().replace("-", "_")] value = value.strip() diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index c5b5204b1..1c9cb4512 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -164,7 +164,7 @@ def p_doc_name(self, p): def p_external_document_ref(self, p): document_ref_id = p[2] document_uri = p[3] - checksum = parse_checksum(self.creation_info["logger"], p[4]) + checksum = parse_checksum(self.creation_info["logger"], p[4], p.lineno(1)) external_document_ref = ExternalDocumentRef(document_ref_id, document_uri, checksum) self.creation_info.setdefault("external_document_refs", []).append(external_document_ref) @@ -186,7 +186,7 @@ def p_extracted_licensing_info_value_error(self, p): @grammar_rule("license_name : LICS_NAME line_or_no_assertion\n extracted_text : LICS_TEXT text_or_line") def p_generic_value_extracted_licensing_info(self, p): - self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) + self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo, p.lineno(1)) set_value(p, self.current_element) @grammar_rule("license_id : LICS_ID LINE") @@ -196,12 +196,12 @@ def p_extracted_license_id(self, p): @grammar_rule("lic_xref : LICS_CRS_REF LINE") def p_extracted_cross_reference(self, p): - self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) + self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo, p.lineno(1)) self.current_element.setdefault("cross_references", []).append(p[2]) @grammar_rule("lic_comment : LICS_COMMENT text_or_line") def p_license_comment(self, p): - self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo) + self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo, p.lineno(1)) set_value(p, self.current_element, argument_name="comment") # parsing methods for file @@ -228,32 +228,32 @@ def p_file_name_error(self, p): @grammar_rule("file_contrib : FILE_CONTRIB LINE") def p_file_contributor(self, p): - self.check_that_current_element_matches_class_for_value(File) + self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) self.current_element.setdefault("contributors", []).append(p[2]) @grammar_rule("file_notice : FILE_NOTICE text_or_line") def p_file_notice(self, p): - self.check_that_current_element_matches_class_for_value(File) + self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) set_value(p, self.current_element, argument_name="notice") @grammar_rule("file_cr_text : FILE_CR_TEXT line_or_no_assertion_or_none") def p_file_copyright_text(self, p): - self.check_that_current_element_matches_class_for_value(File) + self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) set_value(p, self.current_element, argument_name="copyright_text") @grammar_rule("file_lics_comment : FILE_LICS_COMMENT text_or_line") def p_file_license_comment(self, p): - self.check_that_current_element_matches_class_for_value(File) + self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) set_value(p, self.current_element, argument_name="license_comment") @grammar_rule("file_attribution_text : FILE_ATTRIBUTION_TEXT text_or_line") def p_file_attribution_text(self, p): - self.check_that_current_element_matches_class_for_value(File) + self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) self.current_element.setdefault("attribution_texts", []).append(p[2]) @grammar_rule("file_lics_info : FILE_LICS_INFO license_or_no_assertion_or_none") def p_file_license_info(self, p): - self.check_that_current_element_matches_class_for_value(File) + self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): self.current_element["license_info_in_file"] = p[2] return @@ -261,12 +261,12 @@ def p_file_license_info(self, p): @grammar_rule("file_comment : FILE_COMMENT text_or_line") def p_file_comment(self, p): - self.check_that_current_element_matches_class_for_value(File) + self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) set_value(p, self.current_element, argument_name="comment") @grammar_rule("file_type : FILE_TYPE file_type_value") def p_file_type(self, p): - self.check_that_current_element_matches_class_for_value(File) + self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) self.current_element.setdefault("file_type", []).append(FileType[p[2]]) @grammar_rule( @@ -277,13 +277,13 @@ def p_file_type_value(self, p): @grammar_rule("file_checksum : FILE_CHECKSUM CHECKSUM") def p_file_checksum(self, p): - self.check_that_current_element_matches_class_for_value(File) - checksum = parse_checksum(self.current_element["logger"], p[2]) + self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) + checksum = parse_checksum(self.current_element["logger"], p[2], p.lineno(1)) self.current_element.setdefault("checksums", []).append(checksum) @grammar_rule("file_conc : FILE_LICS_CONC license_or_no_assertion_or_none") def p_file_license_concluded(self, p): - self.check_that_current_element_matches_class_for_value(File) + self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) set_value(p, self.current_element, argument_name="license_concluded") # parsing methods for package @@ -308,7 +308,7 @@ def p_package_value_error(self, p): "download_location : PKG_DOWN line_or_no_assertion_or_none\n " "originator : PKG_ORIG actor_or_no_assertion\n supplier : PKG_SUPPL actor_or_no_assertion") def p_generic_package_value(self, p): - self.check_that_current_element_matches_class_for_value(Package) + self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) set_value(p, self.current_element) @grammar_rule("package_name : PKG_NAME LINE") @@ -324,22 +324,22 @@ def p_package_name_error(self, p): @grammar_rule("pkg_comment : PKG_COMMENT text_or_line") def p_pkg_comment(self, p): - self.check_that_current_element_matches_class_for_value(Package) + self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) set_value(p, self.current_element, argument_name="comment") @grammar_rule("pkg_attribution_text : PKG_ATTRIBUTION_TEXT text_or_line") def p_pkg_attribution_text(self, p): - self.check_that_current_element_matches_class_for_value(Package) + self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) self.current_element.setdefault("attribution_texts", []).append(p[2]) @grammar_rule("pkg_cr_text : PKG_CPY_TEXT line_or_no_assertion_or_none") def p_pkg_copyright_text(self, p): - self.check_that_current_element_matches_class_for_value(Package) + self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) set_value(p, self.current_element, argument_name="copyright_text") @grammar_rule("pkg_ext_ref : PKG_EXT_REF LINE PKG_EXT_REF_COMMENT text_or_line\n | PKG_EXT_REF LINE") def p_pkg_external_refs(self, p): - self.check_that_current_element_matches_class_for_value(Package) + self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) try: category, reference_type, locator = p[2].split(" ") except ValueError: @@ -367,17 +367,17 @@ def p_pkg_external_refs(self, p): @grammar_rule("pkg_lic_comment : PKG_LICS_COMMENT text_or_line") def p_pkg_license_comment(self, p): - self.check_that_current_element_matches_class_for_value(Package) + self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) set_value(p, self.current_element, argument_name="license_comment") @grammar_rule("pkg_lic_decl : PKG_LICS_DECL license_or_no_assertion_or_none") def p_pkg_license_declared(self, p): - self.check_that_current_element_matches_class_for_value(Package) + self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) set_value(p, self.current_element, argument_name="license_declared") @grammar_rule("pkg_lic_ff : PKG_LICS_FFILE license_or_no_assertion_or_none") def p_pkg_license_info_from_file(self, p): - self.check_that_current_element_matches_class_for_value(Package) + self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): self.current_element["license_info_from_files"] = p[2] else: @@ -385,18 +385,18 @@ def p_pkg_license_info_from_file(self, p): @grammar_rule("pkg_lic_conc : PKG_LICS_CONC license_or_no_assertion_or_none") def p_pkg_license_concluded(self, p): - self.check_that_current_element_matches_class_for_value(Package) + self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) set_value(p, self.current_element, argument_name="license_concluded") @grammar_rule("pkg_checksum : PKG_CHECKSUM CHECKSUM") def p_pkg_checksum(self, p): - self.check_that_current_element_matches_class_for_value(Package) - checksum = parse_checksum(self.current_element["logger"], p[2]) + self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) + checksum = parse_checksum(self.current_element["logger"], p[2], p.lineno(1)) self.current_element.setdefault("checksums", []).append(checksum) @grammar_rule("verification_code : PKG_VERF_CODE LINE") def p_pkg_verification_code(self, p): - self.check_that_current_element_matches_class_for_value(Package) + self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) if str(p.slice[0]) in self.current_element: self.current_element["logger"].append(f"Multiple values for {p[1]} found. Line: {p.lineno(1)}") return @@ -416,7 +416,7 @@ def p_pkg_verification_code(self, p): @grammar_rule("files_analyzed : PKG_FILES_ANALYZED LINE") def p_pkg_files_analyzed(self, p): - self.check_that_current_element_matches_class_for_value(Package) + self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) if str(p.slice[0]) in self.current_element: self.current_element["logger"].append(f"Multiple values for {p[1]} found. Line: {p.lineno(1)}") return @@ -424,17 +424,17 @@ def p_pkg_files_analyzed(self, p): @grammar_rule("pkg_file_name : PKG_FILE_NAME LINE") def p_pkg_file_name(self, p): - self.check_that_current_element_matches_class_for_value(Package) + self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) set_value(p, self.current_element, argument_name="file_name") @grammar_rule("package_version : PKG_VERSION LINE") def p_package_version(self, p): - self.check_that_current_element_matches_class_for_value(Package) + self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) set_value(p, self.current_element, argument_name="version") @grammar_rule("primary_package_purpose : PRIMARY_PACKAGE_PURPOSE primary_package_purpose_value") def p_primary_package_purpose(self, p): - self.check_that_current_element_matches_class_for_value(Package) + self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) set_value(p, self.current_element, method_to_apply=lambda x: PackagePurpose[x.replace("-", "_")]) @grammar_rule("primary_package_purpose_value : APPLICATION\n | FRAMEWORK\n | LIBRARY\n | CONTAINER\n " @@ -445,7 +445,7 @@ def p_primary_package_purpose_value(self, p): @grammar_rule("built_date : BUILT_DATE DATE\n release_date : RELEASE_DATE DATE\n " "valid_until_date : VALID_UNTIL_DATE DATE") def p_package_dates(self, p): - self.check_that_current_element_matches_class_for_value(Package) + self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) set_value(p, self.current_element, method_to_apply=datetime_from_str) # parsing methods for snippet @@ -472,42 +472,42 @@ def p_snippet_spdx_id_error(self, p): @grammar_rule("snip_name : SNIPPET_NAME LINE") def p_snippet_name(self, p): - self.check_that_current_element_matches_class_for_value(Snippet) + self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) set_value(p, self.current_element, argument_name="name") @grammar_rule("snip_comment : SNIPPET_COMMENT text_or_line") def p_snippet_comment(self, p): - self.check_that_current_element_matches_class_for_value(Snippet) + self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) set_value(p, self.current_element, argument_name="comment") @grammar_rule("snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT text_or_line") def p_snippet_attribution_text(self, p): - self.check_that_current_element_matches_class_for_value(Snippet) + self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) self.current_element.setdefault("attribution_texts", []).append(p[2]) @grammar_rule("snip_cr_text : SNIPPET_CR_TEXT line_or_no_assertion_or_none") def p_snippet_copyright_text(self, p): - self.check_that_current_element_matches_class_for_value(Snippet) + self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) set_value(p, self.current_element, argument_name="copyright_text") @grammar_rule("snip_lic_comment : SNIPPET_LICS_COMMENT text_or_line") def p_snippet_license_comment(self, p): - self.check_that_current_element_matches_class_for_value(Snippet) + self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) set_value(p, self.current_element, argument_name="license_comment") @grammar_rule("file_spdx_id : SNIPPET_FILE_SPDXID LINE") def p_snippet_from_file_spdxid(self, p): - self.check_that_current_element_matches_class_for_value(Snippet) + self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) set_value(p, self.current_element) @grammar_rule("snip_lics_conc : SNIPPET_LICS_CONC license_or_no_assertion_or_none") def p_snippet_concluded_license(self, p): - self.check_that_current_element_matches_class_for_value(Snippet) + self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) set_value(p, self.current_element, argument_name="license_concluded") @grammar_rule("snip_lics_info : SNIPPET_LICS_INFO license_or_no_assertion_or_none") def p_snippet_license_info(self, p): - self.check_that_current_element_matches_class_for_value(Snippet) + self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): self.current_element["license_info_in_snippet"] = p[2] else: @@ -515,13 +515,14 @@ def p_snippet_license_info(self, p): @grammar_rule("snip_byte_range : SNIPPET_BYTE_RANGE LINE") def p_snippet_byte_range(self, p): - self.check_that_current_element_matches_class_for_value(Snippet) + self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) if "byte_range" in self.current_element: self.current_element["logger"].append( f"Multiple values for {p[1]} found. Line: {p.lineno(1)}") range_re = re.compile(r"^(\d+):(\d+)$", re.UNICODE) if not range_re.match(p[2].strip()): - self.current_element["logger"].append("Value for SnippetByteRange doesn't match valid range pattern.") + self.current_element["logger"].append(f"Value for SnippetByteRange doesn't match valid range pattern. " + f"Line: {p.lineno(1)}") return startpoint = int(p[2].split(":")[0]) endpoint = int(p[2].split(":")[-1]) @@ -529,14 +530,15 @@ def p_snippet_byte_range(self, p): @grammar_rule("snip_line_range : SNIPPET_LINE_RANGE LINE") def p_snippet_line_range(self, p): - self.check_that_current_element_matches_class_for_value(Snippet) + self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) if "line_range" in self.current_element: self.current_element["logger"].append( f"Multiple values for {p[1]} found. Line: {p.lineno(1)}") return range_re = re.compile(r"^(\d+):(\d+)$", re.UNICODE) if not range_re.match(p[2].strip()): - self.current_element["logger"].append("Value for SnippetLineRange doesn't match valid range pattern.") + self.current_element["logger"].append(f"Value for SnippetLineRange doesn't match valid range pattern. " + f"Line: {p.lineno(1)}") return startpoint = int(p[2].split(":")[0]) endpoint = int(p[2].split(":")[1]) @@ -563,17 +565,17 @@ def p_annotator_error(self, p): @grammar_rule("annotation_date : ANNOTATION_DATE DATE") def p_annotation_date(self, p): - self.check_that_current_element_matches_class_for_value(Annotation) + self.check_that_current_element_matches_class_for_value(Annotation, p.lineno(1)) set_value(p, self.current_element, method_to_apply=datetime_from_str) @grammar_rule("annotation_comment : ANNOTATION_COMMENT text_or_line") def p_annotation_comment(self, p): - self.check_that_current_element_matches_class_for_value(Annotation) + self.check_that_current_element_matches_class_for_value(Annotation, p.lineno(1)) set_value(p, self.current_element) @grammar_rule("annotation_type : ANNOTATION_TYPE annotation_type_value") def p_annotation_type(self, p): - self.check_that_current_element_matches_class_for_value(Annotation) + self.check_that_current_element_matches_class_for_value(Annotation, p.lineno(1)) set_value(p, self.current_element, method_to_apply=lambda x: AnnotationType[x]) @grammar_rule("annotation_type_value : OTHER\n| REVIEW") @@ -646,15 +648,17 @@ def initialize_new_current_element(self, class_name: Any): self.construct_current_element() self.current_element["class"] = class_name - def check_that_current_element_matches_class_for_value(self, expected_class): + def check_that_current_element_matches_class_for_value(self, expected_class, line_number): if "class" not in self.current_element: self.logger.append( f"Element {expected_class.__name__} is not the current element in scope, probably the expected tag to " - f"start the element ({ELEMENT_EXPECTED_START_TAG[expected_class.__name__]}) is missing.") + f"start the element ({ELEMENT_EXPECTED_START_TAG[expected_class.__name__]}) is missing. " + f"Line: {line_number}") elif expected_class != self.current_element["class"]: self.logger.append( f"Element {expected_class.__name__} is not the current element in scope, probably the expected tag to " - f"start the element ({ELEMENT_EXPECTED_START_TAG[expected_class.__name__]}) is missing.") + f"start the element ({ELEMENT_EXPECTED_START_TAG[expected_class.__name__]}) is missing. " + f"Line: {line_number}") def construct_current_element(self): if "class" not in self.current_element: @@ -686,5 +690,5 @@ def check_for_preceding_package_and_build_contains_relationship(self): # (see https://spdx.github.io/spdx-spec/v2.3/composition-of-an-SPDX-document/#5.2.2) package_spdx_id = self.elements_build["packages"][-1].spdx_id relationship = Relationship(package_spdx_id, RelationshipType.CONTAINS, file_spdx_id) - if relationship not in self.elements_build.setdefault("relationships",[]): + if relationship not in self.elements_build.setdefault("relationships", []): self.elements_build.setdefault("relationships", []).append(relationship) diff --git a/tests/spdx/parser/tagvalue/test_annotation_parser.py b/tests/spdx/parser/tagvalue/test_annotation_parser.py index 3cf7fa3a0..7e99bffcd 100644 --- a/tests/spdx/parser/tagvalue/test_annotation_parser.py +++ b/tests/spdx/parser/tagvalue/test_annotation_parser.py @@ -53,7 +53,7 @@ def test_parse_annotation(): "AnnotationDate: Token did not match specified grammar rule. Line: 2']"]), ('Annotator: Person: ()', ["Error while parsing Annotation: [['No name for Person provided: Person: ().']]"]), ('AnnotationType: REVIEW', ['Element Annotation is not the current element in scope, probably the ' - 'expected tag to start the element (Annotator) is missing.'])]) + 'expected tag to start the element (Annotator) is missing. Line: 1'])]) def test_parse_invalid_annotation(annotation_str, expected_message): parser = Parser() with pytest.raises(SPDXParsingError) as err: diff --git a/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py b/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py index ffffcafb5..a8c1c2f66 100644 --- a/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py +++ b/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py @@ -46,7 +46,7 @@ def test_parse_extracted_licensing_info(): def test_parse_invalid_extracted_licensing_info(): parser = Parser() extracted_licensing_info_str = '\n'.join([ - 'ExtractedText: "THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' + 'ExtractedText: "THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp', 'LicenseName: Beer-Ware License (Version 42)', 'LicenseCrossReference: http://people.freebsd.org/~phk/', 'LicenseComment: The beerware license has a couple of other standard variants.']) @@ -55,10 +55,10 @@ def test_parse_invalid_extracted_licensing_info(): parser.parse(extracted_licensing_info_str) assert err.value.get_messages() == ['Element ExtractedLicensingInfo is not the current element in scope, probably ' - 'the expected tag to start the element (LicenseID) is missing.', + 'the expected tag to start the element (LicenseID) is missing. Line: 1', 'Element ExtractedLicensingInfo is not the current element in scope, probably ' - 'the expected tag to start the element (LicenseID) is missing.', + 'the expected tag to start the element (LicenseID) is missing. Line: 2', 'Element ExtractedLicensingInfo is not the current element in scope, probably ' - 'the expected tag to start the element (LicenseID) is missing.', + 'the expected tag to start the element (LicenseID) is missing. Line: 3', 'Element ExtractedLicensingInfo is not the current element in scope, probably ' - 'the expected tag to start the element (LicenseID) is missing.'] + 'the expected tag to start the element (LicenseID) is missing. Line: 4'] diff --git a/tests/spdx/parser/tagvalue/test_package_parser.py b/tests/spdx/parser/tagvalue/test_package_parser.py index a65650358..0ecf9a5e5 100644 --- a/tests/spdx/parser/tagvalue/test_package_parser.py +++ b/tests/spdx/parser/tagvalue/test_package_parser.py @@ -77,7 +77,7 @@ def test_parse_package(): @pytest.mark.parametrize("package_str, expected_message", [('PackageDownloadLocation: SPDXRef-Package', ['Element Package is not the current element in scope, probably the expected ' - 'tag to start the element (PackageName) is missing.']), + 'tag to start the element (PackageName) is missing. Line: 1']), ('PackageName: TestPackage', ['Error while constructing Package: Package.__init__() missing 2 required ' "positional arguments: 'spdx_id' and 'download_location'"]), diff --git a/tests/spdx/parser/tagvalue/test_snippet_parser.py b/tests/spdx/parser/tagvalue/test_snippet_parser.py index f4c62c68f..0dd981fe3 100644 --- a/tests/spdx/parser/tagvalue/test_snippet_parser.py +++ b/tests/spdx/parser/tagvalue/test_snippet_parser.py @@ -58,10 +58,10 @@ def test_parse_snippet(): @pytest.mark.parametrize("snippet_str, expected_message", [ ('SnippetName: TestSnippet', ['Element Snippet is not the current element in scope, probably the expected ' - 'tag to start the element (SnippetSPDXID) is missing.']), + 'tag to start the element (SnippetSPDXID) is missing. Line: 1']), ('SnippetSPDXID: SPDXDRef-Snippet\nSnippetByteRange: 1,4', ['Error while parsing Snippet: ["Value for SnippetByteRange doesn\'t match ' - 'valid range pattern."]']), + 'valid range pattern. Line: 2"]']), ('SnippetSPDXID: SPDXDRef-Snippet\nSnippetByteRange: 1:4\nSnippetByteRange:10:23', ["Error while parsing Snippet: ['Multiple values for SnippetByteRange found. " "Line: 3']"]), From f34af921674015de202657482f3bc5d1a7a723f8 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 3 Mar 2023 10:55:10 +0100 Subject: [PATCH 331/362] squashed review commits concerning test improvement [review] assert that only one relationship exists [review] improve tests for package parser [review] don't use assertCountEqual if list contains only one element [review] add newline to field [review] delete default values in datetime [fix] soften comparison of error messages to also support older python versions Signed-off-by: Meret Behrens --- .../parser/tagvalue/test_annotation_parser.py | 28 ++++++------ .../tagvalue/test_creation_info_parser.py | 38 ++++++++-------- .../parser/tagvalue/test_package_parser.py | 43 ++++++++++--------- .../tagvalue/test_relationship_parser.py | 1 + .../parser/tagvalue/test_snippet_parser.py | 18 ++++---- 5 files changed, 65 insertions(+), 63 deletions(-) diff --git a/tests/spdx/parser/tagvalue/test_annotation_parser.py b/tests/spdx/parser/tagvalue/test_annotation_parser.py index 7e99bffcd..7df26534b 100644 --- a/tests/spdx/parser/tagvalue/test_annotation_parser.py +++ b/tests/spdx/parser/tagvalue/test_annotation_parser.py @@ -8,7 +8,9 @@ # 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 re from datetime import datetime +from unittest import TestCase import pytest @@ -39,24 +41,24 @@ def test_parse_annotation(): @pytest.mark.parametrize("annotation_str, expected_message", [ - ('Annotator: Person: Jane Doe()', ['Error while constructing Annotation: Annotation.__init__() missing 4 ' - "required positional arguments: 'spdx_id', 'annotation_type', " - "'annotation_date', and 'annotation_comment'"]), + ('Annotator: Person: Jane Doe()', r"__init__() missing 4 " + "required positional arguments: 'spdx_id', 'annotation_type', " + "'annotation_date', and 'annotation_comment'"), ('Annotator: Person: Jane Doe()\nAnnotationType: SOURCE\nAnnotationDate: 201001-2912:23', - ["Error while parsing Annotation: ['Error while parsing AnnotationType: Token " - "did not match specified grammar rule. Line: 2', 'Error while parsing " - "AnnotationDate: Token did not match specified grammar rule. Line: 3']"]), + "Error while parsing Annotation: ['Error while parsing AnnotationType: Token " + "did not match specified grammar rule. Line: 2', 'Error while parsing " + "AnnotationDate: Token did not match specified grammar rule. Line: 3']"), ('Annotator: Jane Doe()\nAnnotationDate: 201001-29T18:30:22Z\n' 'AnnotationComment: Document level annotation\nAnnotationType: OTHER\nSPDXREF: SPDXRef-DOCUMENT', - ["Error while parsing Annotation: ['Error while parsing Annotator: Token did " - "not match specified grammar rule. Line: 1', 'Error while parsing " - "AnnotationDate: Token did not match specified grammar rule. Line: 2']"]), - ('Annotator: Person: ()', ["Error while parsing Annotation: [['No name for Person provided: Person: ().']]"]), - ('AnnotationType: REVIEW', ['Element Annotation is not the current element in scope, probably the ' - 'expected tag to start the element (Annotator) is missing. Line: 1'])]) + "Error while parsing Annotation: ['Error while parsing Annotator: Token did " + "not match specified grammar rule. Line: 1', 'Error while parsing " + "AnnotationDate: Token did not match specified grammar rule. Line: 2']"), + ('Annotator: Person: ()', "Error while parsing Annotation: [['No name for Person provided: Person: ().']]"), + ('AnnotationType: REVIEW', 'Element Annotation is not the current element in scope, probably the ' + 'expected tag to start the element (Annotator) is missing. Line: 1')]) def test_parse_invalid_annotation(annotation_str, expected_message): parser = Parser() with pytest.raises(SPDXParsingError) as err: parser.parse(annotation_str) - assert err.value.get_messages() == expected_message + assert expected_message in err.value.get_messages()[0] diff --git a/tests/spdx/parser/tagvalue/test_creation_info_parser.py b/tests/spdx/parser/tagvalue/test_creation_info_parser.py index f871e304c..f98f997dd 100644 --- a/tests/spdx/parser/tagvalue/test_creation_info_parser.py +++ b/tests/spdx/parser/tagvalue/test_creation_info_parser.py @@ -31,7 +31,7 @@ 'Creator: Person: Bob (bob@example.com)', 'Creator: Organization: Acme.', 'Created: 2010-02-03T00:00:00Z', - 'CreatorComment: Sample Comment', + 'CreatorComment: Sample Comment \nwith multiple \nlines.', 'LicenseListVersion: 3.17' ]) @@ -51,14 +51,13 @@ def test_parse_creation_info(): TestCase().assertCountEqual(creation_info.creators, [Actor(ActorType.PERSON, "Bob", "bob@example.com"), Actor(ActorType.ORGANIZATION, "Acme.")]) - assert creation_info.creator_comment == 'Sample Comment' - assert creation_info.created == datetime(2010, 2, 3, 0, 0) + assert creation_info.creator_comment == 'Sample Comment \nwith multiple \nlines.' + assert creation_info.created == datetime(2010, 2, 3) assert creation_info.license_list_version == Version(3, 17) - TestCase().assertCountEqual(creation_info.external_document_refs, - [ExternalDocumentRef("DocumentRef-spdx-tool-1.2", - "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301", - Checksum(ChecksumAlgorithm.SHA1, - "d6a770ba38583ed4bb4525bd96e50461655d2759"))]) + assert creation_info.external_document_refs == [ExternalDocumentRef("DocumentRef-spdx-tool-1.2", + "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301", + Checksum(ChecksumAlgorithm.SHA1, + "d6a770ba38583ed4bb4525bd96e50461655d2759"))] @pytest.mark.parametrize("document_str, expected_message", @@ -70,23 +69,22 @@ def test_parse_creation_info(): 'Creator: Person Bob (bob@example.com)', 'Creator: Organization: Acme [email]', 'Created: 2010-02-03T00:00:0Z', 'CreatorComment: Sample Comment', 'LicenseListVersion: 7']), - ["Error while parsing CreationInfo: " - "['Error while parsing DocumentNamespace: Token did not match specified grammar rule. " - "Line: 6', 'Error while parsing ExternalDocumentRef: " - "Token did not match specified grammar rule. Line: 7', 'Error while parsing Creator: " - "Token did not match specified grammar rule. Line: 8', 'Error while parsing Created: " - "Token did not match specified grammar rule. Line: 10', '7 is not a valid version string']"]), + "Error while parsing CreationInfo: " + "['Error while parsing DocumentNamespace: Token did not match specified grammar rule. " + "Line: 6', 'Error while parsing ExternalDocumentRef: " + "Token did not match specified grammar rule. Line: 7', 'Error while parsing Creator: " + "Token did not match specified grammar rule. Line: 8', 'Error while parsing Created: " + "Token did not match specified grammar rule. Line: 10', '7 is not a valid version string']"), ('\n'.join( ['SPDXVersion: SPDX-2.3', 'DataLicense: CC0-1.0', 'DocumentName: Sample_Document-V2.3', 'SPDXID: SPDXRef-DOCUMENT']), - ['Error while constructing CreationInfo: CreationInfo.__init__() missing 3 ' - "required positional arguments: 'document_namespace', 'creators', and " - "'created'"]), + r"__init__() missing 3 required positional arguments: " + r"'document_namespace', 'creators', and 'created'"), ('LicenseListVersion: 3.5\nLicenseListVersion: 3.7', - ["Error while parsing CreationInfo: ['Multiple values for LicenseListVersion " - "found. Line: 2']"])])) + "Error while parsing CreationInfo: ['Multiple values for LicenseListVersion " + "found. Line: 2']")])) def test_parse_invalid_creation_info(document_str, expected_message): parser = Parser() with pytest.raises(SPDXParsingError) as err: parser.parse(document_str) - assert err.value.get_messages() == expected_message + assert expected_message in err.value.get_messages()[0] diff --git a/tests/spdx/parser/tagvalue/test_package_parser.py b/tests/spdx/parser/tagvalue/test_package_parser.py index 0ecf9a5e5..2e83f75ad 100644 --- a/tests/spdx/parser/tagvalue/test_package_parser.py +++ b/tests/spdx/parser/tagvalue/test_package_parser.py @@ -25,7 +25,7 @@ def test_parse_package(): package_str = '\n'.join([ 'PackageName: Test', 'SPDXID: SPDXRef-Package', - 'PackageVersion: Version 0.9.2', + 'PackageVersion: 1:22.36.1-8+deb11u1', 'PackageDownloadLocation: http://example.com/test', 'FilesAnalyzed: True', 'PackageSummary: Test package', @@ -56,8 +56,10 @@ def test_parse_package(): package = document.packages[0] assert package.name == 'Test' assert package.spdx_id == 'SPDXRef-Package' - assert package.version == 'Version 0.9.2' + assert package.version == '1:22.36.1-8+deb11u1' assert len(package.license_info_from_files) == 2 + TestCase().assertCountEqual(package.license_info_from_files, [get_spdx_licensing().parse("Apache-1.0"), + get_spdx_licensing().parse("Apache-2.0")]) assert package.license_concluded == get_spdx_licensing().parse('LicenseRef-2.0 AND Apache-2.0') assert package.files_analyzed is True assert package.comment == 'Comment on the package.' @@ -69,36 +71,35 @@ def test_parse_package(): ExternalPackageRef(ExternalPackageRefCategory.OTHER, "LocationRef-acmeforge", "acmecorp/acmenator/4.1.3-alpha")]) assert package.primary_package_purpose == PackagePurpose.OPERATING_SYSTEM - assert package.built_date == datetime(2020, 1, 1, 12, 0, 0) - assert package.release_date == datetime(2021, 1, 1, 12, 0, 0) - assert package.valid_until_date == datetime(2022, 1, 1, 12, 0, 0) + assert package.built_date == datetime(2020, 1, 1, 12) + assert package.release_date == datetime(2021, 1, 1, 12) + assert package.valid_until_date == datetime(2022, 1, 1, 12) @pytest.mark.parametrize("package_str, expected_message", [('PackageDownloadLocation: SPDXRef-Package', - ['Element Package is not the current element in scope, probably the expected ' - 'tag to start the element (PackageName) is missing. Line: 1']), + 'Element Package is not the current element in scope, probably the expected ' + 'tag to start the element (PackageName) is missing. Line: 1'), ('PackageName: TestPackage', - ['Error while constructing Package: Package.__init__() missing 2 required ' - "positional arguments: 'spdx_id' and 'download_location'"]), + r"__init__() missing 2 required positional arguments: 'spdx_id' and 'download_location'"), ('PackageName: TestPackage\nPackageCopyrightText:This is a copyright\n' 'PackageCopyrightText:MultipleCopyright', - ["Error while parsing Package: ['Multiple values for PackageCopyrightText " - "found. Line: 3']"]), + "Error while parsing Package: ['Multiple values for PackageCopyrightText " + "found. Line: 3']"), ('PackageName: TestPackage\nExternalRef: reference locator', - ['Error while parsing Package: ["Couldn\'t split PackageExternalRef in ' - 'category, reference_type and locator. Line: 2"]']), + 'Error while parsing Package: ["Couldn\'t split PackageExternalRef in ' + 'category, reference_type and locator. Line: 2"]'), ('PackageName: TestPackage\nExternalRef: category reference locator', - ["Error while parsing Package: ['Invalid ExternalPackageRefCategory: " - "category']"]), + "Error while parsing Package: ['Invalid ExternalPackageRefCategory: " + "category']"), ('SPDXID:SPDXRef-DOCUMENT\nPackageName: TestPackage\nSPDXID:SPDXRef-Package\n' 'PackageDownloadLocation: download.com\nPackageVerificationCode: category reference locator', - ["Error while parsing Package: ['Error while parsing PackageVerificationCode: " - "Value did not match expected format. Line: 5']"]), + "Error while parsing Package: ['Error while parsing PackageVerificationCode: " + "Value did not match expected format. Line: 5']"), ('PackageName: TestPackage\nBuiltDate: 2012\nValidUntilDate:202-11-02T00:00', - ["Error while parsing Package: ['Error while parsing BuiltDate: Token did not " - "match specified grammar rule. Line: 2', 'Error while parsing " - "ValidUntilDate: Token did not match specified grammar rule. Line: 3']"]) + "Error while parsing Package: ['Error while parsing BuiltDate: Token did not " + "match specified grammar rule. Line: 2', 'Error while parsing " + "ValidUntilDate: Token did not match specified grammar rule. Line: 3']") ]) def test_parse_invalid_package(package_str, expected_message): parser = Parser() @@ -106,4 +107,4 @@ def test_parse_invalid_package(package_str, expected_message): with pytest.raises(SPDXParsingError) as err: parser.parse(package_str) - assert err.value.get_messages() == expected_message + assert expected_message in err.value.get_messages()[0] diff --git a/tests/spdx/parser/tagvalue/test_relationship_parser.py b/tests/spdx/parser/tagvalue/test_relationship_parser.py index dc93c1dc4..bf8b821fa 100644 --- a/tests/spdx/parser/tagvalue/test_relationship_parser.py +++ b/tests/spdx/parser/tagvalue/test_relationship_parser.py @@ -36,6 +36,7 @@ def test_parse_relationship(relationship_str, expected_relationship): parser = Parser() document = parser.parse("\n".join([DOCUMENT_STR, relationship_str])) assert document is not None + assert len(document.relationships) == 1 relationship = document.relationships[0] assert relationship == expected_relationship diff --git a/tests/spdx/parser/tagvalue/test_snippet_parser.py b/tests/spdx/parser/tagvalue/test_snippet_parser.py index 0dd981fe3..7e76bd815 100644 --- a/tests/spdx/parser/tagvalue/test_snippet_parser.py +++ b/tests/spdx/parser/tagvalue/test_snippet_parser.py @@ -57,16 +57,16 @@ def test_parse_snippet(): @pytest.mark.parametrize("snippet_str, expected_message", [ - ('SnippetName: TestSnippet', ['Element Snippet is not the current element in scope, probably the expected ' - 'tag to start the element (SnippetSPDXID) is missing. Line: 1']), + ('SnippetName: TestSnippet', 'Element Snippet is not the current element in scope, probably the expected ' + 'tag to start the element (SnippetSPDXID) is missing. Line: 1'), ('SnippetSPDXID: SPDXDRef-Snippet\nSnippetByteRange: 1,4', - ['Error while parsing Snippet: ["Value for SnippetByteRange doesn\'t match ' - 'valid range pattern. Line: 2"]']), + 'Error while parsing Snippet: ["Value for SnippetByteRange doesn\'t match ' + 'valid range pattern. Line: 2"]'), ('SnippetSPDXID: SPDXDRef-Snippet\nSnippetByteRange: 1:4\nSnippetByteRange:10:23', - ["Error while parsing Snippet: ['Multiple values for SnippetByteRange found. " - "Line: 3']"]), - ('SnippetSPDXID: SPDXRef-Snippet', ['Error while constructing Snippet: Snippet.__init__() missing 2 required ' - "positional arguments: 'file_spdx_id' and 'byte_range'"]) + "Error while parsing Snippet: ['Multiple values for SnippetByteRange found. " + "Line: 3']"), + ('SnippetSPDXID: SPDXRef-Snippet', r"__init__() missing 2 required " + r"positional arguments: 'file_spdx_id' and 'byte_range'") ]) def test_parse_invalid_snippet(snippet_str, expected_message): parser = Parser() @@ -74,4 +74,4 @@ def test_parse_invalid_snippet(snippet_str, expected_message): with pytest.raises(SPDXParsingError) as err: parser.parse(snippet_str) - assert err.value.get_messages() == expected_message + assert expected_message in err.value.get_messages()[0] From 4e279f698a38e9285d4fb0226e6a88205d5173eb Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 8 Mar 2023 08:55:07 +0100 Subject: [PATCH 332/362] [review] fix type hint, parse_checksum and add test for the latter Signed-off-by: Meret Behrens --- .../parser/tagvalue/parser/helper_methods.py | 27 +++---- src/spdx/parser/tagvalue/parser/tagvalue.py | 6 +- .../parser/tagvalue/test_helper_methods.py | 73 +++++++++++++++++++ 3 files changed, 88 insertions(+), 18 deletions(-) create mode 100644 tests/spdx/parser/tagvalue/test_helper_methods.py diff --git a/src/spdx/parser/tagvalue/parser/helper_methods.py b/src/spdx/parser/tagvalue/parser/helper_methods.py index 869aa21e5..857667ef7 100644 --- a/src/spdx/parser/tagvalue/parser/helper_methods.py +++ b/src/spdx/parser/tagvalue/parser/helper_methods.py @@ -11,10 +11,10 @@ import re from typing import Optional, Callable, Any, Dict +from ply.yacc import YaccProduction + from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.parser.error import SPDXParsingError -from spdx.parser.logger import Logger -from spdx.parser.parsing_functions import construct_or_raise_parsing_error def grammar_rule(doc): @@ -37,25 +37,22 @@ def str_from_text(text: Optional[str]) -> Optional[str]: return None -def parse_checksum(logger: Logger, checksum_str: str, line_number: int) -> Optional[Checksum]: - try: - algorithm, value = checksum_str.split(":") - except ValueError: - logger.append( - f"Couldn't split value for checksum in algorithm and value. Line: {line_number}") - return None +def parse_checksum(checksum_str: str) -> Checksum: + # The lexer and the corresponding regex for the token CHECKSUM and EXT_DOC_REF_CHECKSUM ensure that the passed + # checksum_str is formatted in the way that the following lines of code can't cause an error. + algorithm, value = checksum_str.split(":") algorithm = ChecksumAlgorithm[algorithm.upper().replace("-", "_")] value = value.strip() - try: - checksum = construct_or_raise_parsing_error(Checksum, {"algorithm": algorithm, "value": value}) - except SPDXParsingError as err: - logger.append(err.get_messages()) - checksum = None + checksum = Checksum(algorithm, value) return checksum -def set_value(parsed_value: Any, dict_to_fill: Dict[str, Any], argument_name: Optional[str] = None, +def set_value(parsed_value: YaccProduction, dict_to_fill: Dict[str, Any], argument_name: Optional[str] = None, method_to_apply: Callable = lambda x: x): + # Parsed_value.slice returns a List of the objects in the corresponding grammar_rule for the parsed value, + # e.g. for @grammar_rule("created : CREATED DATE") the return value is something like + # p.slice = ["created", LexToken(CREATED,..), LexToken(DATE,..)]. + # So the first value is the name of the grammar_rule that we have named according to the field in the data model. if not argument_name: argument_name = str(parsed_value.slice[0]) if argument_name in dict_to_fill: diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 1c9cb4512..2cd4c2d5f 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -164,7 +164,7 @@ def p_doc_name(self, p): def p_external_document_ref(self, p): document_ref_id = p[2] document_uri = p[3] - checksum = parse_checksum(self.creation_info["logger"], p[4], p.lineno(1)) + checksum = parse_checksum(p[4]) external_document_ref = ExternalDocumentRef(document_ref_id, document_uri, checksum) self.creation_info.setdefault("external_document_refs", []).append(external_document_ref) @@ -278,7 +278,7 @@ def p_file_type_value(self, p): @grammar_rule("file_checksum : FILE_CHECKSUM CHECKSUM") def p_file_checksum(self, p): self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) - checksum = parse_checksum(self.current_element["logger"], p[2], p.lineno(1)) + checksum = parse_checksum(p[2]) self.current_element.setdefault("checksums", []).append(checksum) @grammar_rule("file_conc : FILE_LICS_CONC license_or_no_assertion_or_none") @@ -391,7 +391,7 @@ def p_pkg_license_concluded(self, p): @grammar_rule("pkg_checksum : PKG_CHECKSUM CHECKSUM") def p_pkg_checksum(self, p): self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) - checksum = parse_checksum(self.current_element["logger"], p[2], p.lineno(1)) + checksum = parse_checksum(p[2]) self.current_element.setdefault("checksums", []).append(checksum) @grammar_rule("verification_code : PKG_VERF_CODE LINE") diff --git a/tests/spdx/parser/tagvalue/test_helper_methods.py b/tests/spdx/parser/tagvalue/test_helper_methods.py new file mode 100644 index 000000000..d38952502 --- /dev/null +++ b/tests/spdx/parser/tagvalue/test_helper_methods.py @@ -0,0 +1,73 @@ +# Copyright (c) 2023 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. +import pytest + +from spdx.model.checksum import ChecksumAlgorithm +from spdx.parser.tagvalue.parser.helper_methods import parse_checksum + + +@pytest.mark.parametrize("checksum_str, algorithm, value", + [("SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759", + ChecksumAlgorithm.SHA1, "d6a770ba38583ed4bb4525bd96e50461655d2759"), + ("SHA224: 9c9f4e27d957a123cc32d86afe33ae53b1184192cccb23b0f257f588", + ChecksumAlgorithm.SHA224, + "9c9f4e27d957a123cc32d86afe33ae53b1184192cccb23b0f257f588"), + ("SHA256: fbea580d286bbbbb41314430d58ba887716a74d7134119c5307cdc9f0c7a4299", + ChecksumAlgorithm.SHA256, + "fbea580d286bbbbb41314430d58ba887716a74d7134119c5307cdc9f0c7a4299"), + ( + "SHA384: 73b4ad9a34e5f76cb2525ea6bb8b1dcf9ba79426b3295bd18bc6d148cba4fcc2ca3cf2630fd481b47caaac9127103933", + ChecksumAlgorithm.SHA384, + "73b4ad9a34e5f76cb2525ea6bb8b1dcf9ba79426b3295bd18bc6d148cba4fcc2ca3cf2630fd481b47caaac9127103933"), + ( + "SHA512: c2aa8a5d297f5e888ce9a30d3745ccc5a628533449a9f98524de3d23695a268f394a67faf8ef370727c2946f1dbbec34aeb7ac10f15af43e7cb5547f1a464053", + ChecksumAlgorithm.SHA512, + "c2aa8a5d297f5e888ce9a30d3745ccc5a628533449a9f98524de3d23695a268f394a67faf8ef370727c2946f1dbbec34aeb7ac10f15af43e7cb5547f1a464053"), + ("SHA3-256: 1e772489c042f49aeaae32b00fc5ef170a25afa741cffaafadde597d4d1727ce", + ChecksumAlgorithm.SHA3_256, + "1e772489c042f49aeaae32b00fc5ef170a25afa741cffaafadde597d4d1727ce"), ( + "SHA3-384: dd9e30747551865b483bd76bd967384dce0e5670d1b1c3f701cffac7f49b1c46791253493835136b3aa5f679e364c166", + ChecksumAlgorithm.SHA3_384, + "dd9e30747551865b483bd76bd967384dce0e5670d1b1c3f701cffac7f49b1c46791253493835136b3aa5f679e364c166"), + ( + "SHA3-512: 906bca5580be8c95ae44f775363fb69968ad568898dfb03e0ff96cd9445a0b75f817b68e5c1e80ad624031f851cfddd3a101e1d111310266a5d46e2bc1ffbb36", + ChecksumAlgorithm.SHA3_512, + "906bca5580be8c95ae44f775363fb69968ad568898dfb03e0ff96cd9445a0b75f817b68e5c1e80ad624031f851cfddd3a101e1d111310266a5d46e2bc1ffbb36"), + ("BLAKE2b-256: a0eb3ddfa5807780a562b9c313b2537f1e8dc621e9a524f8c1ffcf07a79e35c7", + ChecksumAlgorithm.BLAKE2B_256, + "a0eb3ddfa5807780a562b9c313b2537f1e8dc621e9a524f8c1ffcf07a79e35c7"), ( + "BLAKE2B-384: 902511afc8939c0193d87857f45a19eddfd7e0413b0f8701a3baaf1b025f882b45a8fbf623fa0ad79b64850ac7a4d0b2", + ChecksumAlgorithm.BLAKE2B_384, + "902511afc8939c0193d87857f45a19eddfd7e0413b0f8701a3baaf1b025f882b45a8fbf623fa0ad79b64850ac7a4d0b2"), + ( + "BLAKE2B-512: 72c23b0160e1af3cb159f0cc96210c5e9aecc5a65d4618566776fa6117bf84929dcef56c7f8b087691c23000c945470842d90b5e8c4af74dce531ca8ebd8824c", + ChecksumAlgorithm.BLAKE2B_512, + "72c23b0160e1af3cb159f0cc96210c5e9aecc5a65d4618566776fa6117bf84929dcef56c7f8b087691c23000c945470842d90b5e8c4af74dce531ca8ebd8824c"), + ( + "BLAKE3: a872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafed", + ChecksumAlgorithm.BLAKE3, + "a872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafeda872cac2efd29ed2ad8b5faa79b63f983341bea41183582b8863d952f6ac3e1cdfe0189967a13006857d3b9985174bf67239874dcec4cbbc9839496179feafed"), + ("MD2: af1eec2a1b18886c3f3cc244349d91d8", ChecksumAlgorithm.MD2, + "af1eec2a1b18886c3f3cc244349d91d8"), + ("MD4: d4c41ce30a517d6ce9d79c8c17bb4b66", ChecksumAlgorithm.MD4, + "d4c41ce30a517d6ce9d79c8c17bb4b66"), + ("MD5: 0d7f61beb7018b3924c6b8f96549fa39", ChecksumAlgorithm.MD5, + "0d7f61beb7018b3924c6b8f96549fa39"), + ( + "MD6: af1eec2a1b18886c3f3cc244349d91d8d4c41ce30a517d6ce9d79c8c17bb4b660d7f61beb7018b3924c6b8f96549fa39", + ChecksumAlgorithm.MD6, + "af1eec2a1b18886c3f3cc244349d91d8d4c41ce30a517d6ce9d79c8c17bb4b660d7f61beb7018b3924c6b8f96549fa39"), + ("ADLER32: 02ec0130", ChecksumAlgorithm.ADLER32, "02ec0130")]) +def test_parse_checksum(checksum_str, algorithm, value): + checksum = parse_checksum(checksum_str) + + assert checksum.algorithm == algorithm + assert checksum.value == value From bafc163ae46086523feed7ad96239347b1140d58 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 8 Mar 2023 08:59:25 +0100 Subject: [PATCH 333/362] squashed review commits concerning the tag value parser [review] add comments to parser to improve code readability [review] merge parsing methods for byte_range and line_range [review] delete superfluous except block [review] delete superfluous call to setdefault [review] delete superfluous case distinction [review] rename parameter [review] get rid of docstrings [review] rename Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/parser/tagvalue.py | 99 ++++++++++----------- 1 file changed, 45 insertions(+), 54 deletions(-) diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 2cd4c2d5f..9376d76cc 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -48,7 +48,7 @@ class Parser(object): logger: Logger current_element: Dict[str, Any] creation_info: Dict[str, Any] - elements_build: Dict[str, Any] + elements_built: Dict[str, Any] lex: SPDXLexer yacc: LRParser @@ -57,7 +57,7 @@ def __init__(self, **kwargs): self.logger = Logger() self.current_element = {"logger": Logger()} self.creation_info = {"logger": Logger()} - self.elements_build = dict() + self.elements_built = dict() self.lex = SPDXLexer() self.lex.build(reflags=re.UNICODE) self.yacc = yacc.yacc(module=self, **kwargs) @@ -168,8 +168,8 @@ def p_external_document_ref(self, p): external_document_ref = ExternalDocumentRef(document_ref_id, document_uri, checksum) self.creation_info.setdefault("external_document_refs", []).append(external_document_ref) + @grammar_rule("creator : CREATOR PERSON_VALUE\n| CREATOR TOOL_VALUE\n| CREATOR ORG_VALUE") def p_creator(self, p): - """creator : CREATOR PERSON_VALUE\n| CREATOR TOOL_VALUE\n| CREATOR ORG_VALUE""" self.creation_info.setdefault("creators", []).append(ActorParser.parse_actor(p[2])) @grammar_rule("created : CREATED DATE") @@ -513,36 +513,26 @@ def p_snippet_license_info(self, p): else: self.current_element.setdefault("license_info_in_snippet", []).append(p[2]) - @grammar_rule("snip_byte_range : SNIPPET_BYTE_RANGE LINE") - def p_snippet_byte_range(self, p): - self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) - if "byte_range" in self.current_element: - self.current_element["logger"].append( - f"Multiple values for {p[1]} found. Line: {p.lineno(1)}") - range_re = re.compile(r"^(\d+):(\d+)$", re.UNICODE) - if not range_re.match(p[2].strip()): - self.current_element["logger"].append(f"Value for SnippetByteRange doesn't match valid range pattern. " - f"Line: {p.lineno(1)}") + @grammar_rule("snip_byte_range : SNIPPET_BYTE_RANGE LINE\n snip_line_range : SNIPPET_LINE_RANGE LINE") + def p_snippet_range(self, p): + if p[1] == "SnippetByteRange": + argument_name = "byte_range" + elif p[1] == "SnippetLineRange": + argument_name = "line_range" + else: return - startpoint = int(p[2].split(":")[0]) - endpoint = int(p[2].split(":")[-1]) - self.current_element["byte_range"] = startpoint, endpoint - - @grammar_rule("snip_line_range : SNIPPET_LINE_RANGE LINE") - def p_snippet_line_range(self, p): self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) - if "line_range" in self.current_element: + if argument_name in self.current_element: self.current_element["logger"].append( f"Multiple values for {p[1]} found. Line: {p.lineno(1)}") - return range_re = re.compile(r"^(\d+):(\d+)$", re.UNICODE) if not range_re.match(p[2].strip()): - self.current_element["logger"].append(f"Value for SnippetLineRange doesn't match valid range pattern. " + self.current_element["logger"].append(f"Value for {p[1]} doesn't match valid range pattern. " f"Line: {p.lineno(1)}") return startpoint = int(p[2].split(":")[0]) - endpoint = int(p[2].split(":")[1]) - self.current_element["line_range"] = startpoint, endpoint + endpoint = int(p[2].split(":")[-1]) + self.current_element[argument_name] = startpoint, endpoint # parsing methods for annotation @@ -552,8 +542,8 @@ def p_annotation_value_error(self, p): self.current_element["logger"].append( f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") + @grammar_rule("annotator : ANNOTATOR PERSON_VALUE\n| TOOL_VALUE\n| ORG_VALUE") def p_annotator(self, p): - """annotator : ANNOTATOR PERSON_VALUE\n| TOOL_VALUE\n| ORG_VALUE""" self.initialize_new_current_element(Annotation) set_value(p, self.current_element, method_to_apply=ActorParser.parse_actor) @@ -632,29 +622,32 @@ def p_error(self, p): pass def parse(self, text): + # entry point for the tag-value parser self.yacc.parse(text, lexer=self.lex) + # this constructs the last remaining element; all other elements are constructed at the start of + # their subsequent element self.construct_current_element() - try: - raise_parsing_error_if_logger_has_messages(self.creation_info.pop("logger"), "CreationInfo") - except SPDXParsingError as err: - self.logger.extend(err.get_messages()) + + # To be able to parse creation info values if they appear in between other elements, e.g. packages, we use + # two different dictionaries to collect the creation info and all other elements. Therefore, we have a separate + # logger for the creation info whose messages we need to add to the main logger to than raise all collected + # messages at once. + creation_info_logger = self.creation_info.pop("logger") + if creation_info_logger.has_messages(): + self.logger.extend([f"Error while parsing CreationInfo: {creation_info_logger.get_messages()}"]) + raise_parsing_error_if_logger_has_messages(self.logger) creation_info = construct_or_raise_parsing_error(CreationInfo, self.creation_info) - self.elements_build["creation_info"] = creation_info - document = construct_or_raise_parsing_error(Document, self.elements_build) + self.elements_built["creation_info"] = creation_info + document = construct_or_raise_parsing_error(Document, self.elements_built) return document - def initialize_new_current_element(self, class_name: Any): + def initialize_new_current_element(self, clazz: Any): self.construct_current_element() - self.current_element["class"] = class_name + self.current_element["class"] = clazz def check_that_current_element_matches_class_for_value(self, expected_class, line_number): - if "class" not in self.current_element: - self.logger.append( - f"Element {expected_class.__name__} is not the current element in scope, probably the expected tag to " - f"start the element ({ELEMENT_EXPECTED_START_TAG[expected_class.__name__]}) is missing. " - f"Line: {line_number}") - elif expected_class != self.current_element["class"]: + if "class" not in self.current_element or expected_class != self.current_element["class"]: self.logger.append( f"Element {expected_class.__name__} is not the current element in scope, probably the expected tag to " f"start the element ({ELEMENT_EXPECTED_START_TAG[expected_class.__name__]}) is missing. " @@ -662,19 +655,17 @@ def check_that_current_element_matches_class_for_value(self, expected_class, lin def construct_current_element(self): if "class" not in self.current_element: - self.current_element = {"logger": Logger()} - return - class_name = self.current_element.pop("class") - try: - raise_parsing_error_if_logger_has_messages(self.current_element.pop("logger"), class_name.__name__) - except SPDXParsingError as err: - self.logger.extend(err.get_messages()) - self.current_element = {"logger": Logger()} + # When the first element of the document is instantiated we don't have a current element in scope + # and the key "class" doesn't exist. Additionally, if the first element doesn't have the expected start + # value the key "class" wouldn't exist. To prevent a KeyError we use early return. return + + clazz = self.current_element.pop("class") try: - self.elements_build.setdefault(CLASS_MAPPING[class_name.__name__], []).append( - construct_or_raise_parsing_error(class_name, self.current_element)) - if class_name == File: + raise_parsing_error_if_logger_has_messages(self.current_element.pop("logger"), clazz.__name__) + self.elements_built.setdefault(CLASS_MAPPING[clazz.__name__], []).append( + construct_or_raise_parsing_error(clazz, self.current_element)) + if clazz == File: self.check_for_preceding_package_and_build_contains_relationship() except SPDXParsingError as err: self.logger.extend(err.get_messages()) @@ -682,13 +673,13 @@ def construct_current_element(self): def check_for_preceding_package_and_build_contains_relationship(self): file_spdx_id = self.current_element["spdx_id"] - if "packages" not in self.elements_build: + if "packages" not in self.elements_built: return # We assume that all files that are not contained in a package precede any package information. Any file # information that follows any package information is assigned to the last parsed package by creating a # corresponding contains relationship. # (see https://spdx.github.io/spdx-spec/v2.3/composition-of-an-SPDX-document/#5.2.2) - package_spdx_id = self.elements_build["packages"][-1].spdx_id + package_spdx_id = self.elements_built["packages"][-1].spdx_id relationship = Relationship(package_spdx_id, RelationshipType.CONTAINS, file_spdx_id) - if relationship not in self.elements_build.setdefault("relationships", []): - self.elements_build.setdefault("relationships", []).append(relationship) + if relationship not in self.elements_built.setdefault("relationships", []): + self.elements_built["relationships"].append(relationship) From 8039b09894701ed54304436a97aaae562d24a793 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 8 Mar 2023 12:11:22 +0100 Subject: [PATCH 334/362] squashed review commits concerning structure of the tag value parser [review] use strings instead of p.slice [review] merge generic parsing methods [review] parse value only if the current_element matches [review] merge parsing methods [review] merge error methods for current elements [review] delete tokens for enum values and let the parser take care of correct values Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/lexer/tagvalue.py | 22 +- .../parser/tagvalue/parser/helper_methods.py | 54 ++- src/spdx/parser/tagvalue/parser/tagvalue.py | 398 ++++++------------ .../parser/tagvalue/test_annotation_parser.py | 6 +- .../spdx/parser/tagvalue/test_file_parser.py | 5 +- .../parser/tagvalue/test_package_parser.py | 2 +- .../tagvalue/test_relationship_parser.py | 6 +- .../parser/tagvalue/test_tag_value_lexer.py | 8 +- .../parser/tagvalue/test_tag_value_parser.py | 16 +- 9 files changed, 200 insertions(+), 317 deletions(-) diff --git a/src/spdx/parser/tagvalue/lexer/tagvalue.py b/src/spdx/parser/tagvalue/lexer/tagvalue.py index e6737db47..3e4bb3569 100644 --- a/src/spdx/parser/tagvalue/lexer/tagvalue.py +++ b/src/spdx/parser/tagvalue/lexer/tagvalue.py @@ -97,27 +97,7 @@ class SPDXLexer(object): "SnippetLineRange": "SNIPPET_LINE_RANGE", # Common fields "NOASSERTION": "NO_ASSERTION", - "NONE": "NONE", - "SOURCE": "SOURCE", - "BINARY": "BINARY", - "ARCHIVE": "ARCHIVE", - "APPLICATION": "APPLICATION", - "AUDIO": "AUDIO", - "IMAGE": "IMAGE", - "TEXT": "FILETYPE_TEXT", - "VIDEO": "VIDEO", - "DOCUMENTATION": "DOCUMENTATION", - "SPDX": "SPDX", - "OTHER": "OTHER", - "REVIEW": "REVIEW", - "FRAMEWORK": "FRAMEWORK", - "LIBRARY": "LIBRARY", - "CONTAINER": "CONTAINER", - "OPERATING-SYSTEM": "OPERATING_SYSTEM", - "DEVICE": "DEVICE", - "FIRMWARE": "FIRMWARE", - "FILE": "FILE", - "INSTALL": "INSTALL" + "NONE": "NONE" } states = (("text", "exclusive"),) diff --git a/src/spdx/parser/tagvalue/parser/helper_methods.py b/src/spdx/parser/tagvalue/parser/helper_methods.py index 857667ef7..f13204b78 100644 --- a/src/spdx/parser/tagvalue/parser/helper_methods.py +++ b/src/spdx/parser/tagvalue/parser/helper_methods.py @@ -13,7 +13,14 @@ from ply.yacc import YaccProduction +from spdx.casing_tools import camel_case_to_snake_case +from spdx.model.annotation import Annotation from spdx.model.checksum import Checksum, ChecksumAlgorithm +from spdx.model.document import CreationInfo +from spdx.model.extracted_licensing_info import ExtractedLicensingInfo +from spdx.model.file import File +from spdx.model.package import Package +from spdx.model.snippet import Snippet from spdx.parser.error import SPDXParsingError @@ -49,12 +56,8 @@ def parse_checksum(checksum_str: str) -> Checksum: def set_value(parsed_value: YaccProduction, dict_to_fill: Dict[str, Any], argument_name: Optional[str] = None, method_to_apply: Callable = lambda x: x): - # Parsed_value.slice returns a List of the objects in the corresponding grammar_rule for the parsed value, - # e.g. for @grammar_rule("created : CREATED DATE") the return value is something like - # p.slice = ["created", LexToken(CREATED,..), LexToken(DATE,..)]. - # So the first value is the name of the grammar_rule that we have named according to the field in the data model. if not argument_name: - argument_name = str(parsed_value.slice[0]) + argument_name = get_property(parsed_value[1]) if argument_name in dict_to_fill: dict_to_fill["logger"].append( f"Multiple values for {parsed_value[1]} found. Line: {parsed_value.lineno(1)}") @@ -65,3 +68,44 @@ def set_value(parsed_value: YaccProduction, dict_to_fill: Dict[str, Any], argume dict_to_fill["logger"].append(err.get_messages()) except ValueError as err: dict_to_fill["logger"].append(err.args[0]) + except KeyError: + dict_to_fill["logger"].append(f"Invalid {parsed_value[1]}: {parsed_value[2]}. Line: {parsed_value.lineno(1)}") + + +def get_property(tag: str): + if tag not in TAG_DATA_MODEL_FIELD.keys(): + return camel_case_to_snake_case(tag) + return TAG_DATA_MODEL_FIELD[tag][1] + + +# This dictionary serves as a mapping from a tag to the corresponding class and field in the internal data model. +# This mapping is not complete as we only list the values which can be parsed by a generic method and don't need any +# individual logic. +TAG_DATA_MODEL_FIELD = { + "SPDXVersion": (CreationInfo, "spdx_version"), "DataLicense": (CreationInfo, "data_license"), + "DocumentName": (CreationInfo, "name"), "DocumentComment": (CreationInfo, "document_comment"), + "DocumentNamespace": (CreationInfo, "document_namespace"), "Creator": (CreationInfo, "creator"), + "Created": (CreationInfo, "created"), "CreatorComment": (CreationInfo, "creator_comment"), + "LicenseListVersion": (CreationInfo, "license_list_version"), + "ExternalDocumentRef": (CreationInfo, "external_document_refs"), + "FileName": (File, "name"), "FileType": (File, "file_type"), "FileChecksum": (File, "checksums"), + "FileNotice": (File, "notice"), "FileCopyrightText": (File, "copyright_text"), + "LicenseComments": (File, "license_comment"), "FileComment": (File, "comment"), + "LicenseConcluded": (File, "license_concluded"), "LicenseDeclared": (File, "license_declared"), + "PackageName": (Package, "name"), "PackageComment": (Package, "comment"), + "PackageCopyrightText": (Package, "copyright_text"), "PackageLicenseComments": (Package, "license_comment"), + "PackageLicenseDeclared": (Package, "license_declared"), "PackageLicenseConcluded": (Package, "license_concluded"), + "PackageFileName": (Package, "file_name"), "PackageVersion": (Package, "version"), + "PackageDownloadLocation": (Package, "download_location"), "PackageSummary": (Package, "summary"), + "PackageSourceInfo": (Package, "source_info"), "PackageSupplier": (Package, "supplier"), + "PackageOriginator": (Package, "originator"), "PackageDescription": (Package, "description"), + "PackageHomePage": (Package, "homepage"), + "SnippetSPDXID": (Snippet, "spdx_id"), "SnippetFromFileSPDXID": (Snippet, "file_spdx_id"), + "SnippetName": (Snippet, "name"), + "SnippetComment": (Snippet, "comment"), "SnippetCopyrightText": (Snippet, "copyright_text"), + "SnippetLicenseComments": (Snippet, "license_comment"), "SnippetLicenseConcluded": (Snippet, "license_concluded"), + "SnippetByteRange": (Snippet, "byte_range"), "SnippetLineRange": (Snippet, "line_range"), + "SPDXREF": (Annotation, "spdx_id"), "AnnotationComment": (Annotation, "annotation_comment"), + "LicenseID": (ExtractedLicensingInfo, "license_id"), "ExtractedText": (ExtractedLicensingInfo, "extracted_text"), + "LicenseComment": (ExtractedLicensingInfo, "comment"), "LicenseName": (ExtractedLicensingInfo, "license_name") +} diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser/tagvalue.py index 9376d76cc..9ae442f3e 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser/tagvalue.py @@ -35,12 +35,16 @@ from spdx.parser.logger import Logger from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.tagvalue.lexer.tagvalue import SPDXLexer -from spdx.parser.tagvalue.parser.helper_methods import grammar_rule, str_from_text, parse_checksum, set_value +from spdx.parser.tagvalue.parser.helper_methods import grammar_rule, str_from_text, parse_checksum, set_value, \ + TAG_DATA_MODEL_FIELD CLASS_MAPPING = dict(File="files", Annotation="annotations", Relationship="relationships", Snippet="snippets", Package="packages", ExtractedLicensingInfo="extracted_licensing_info") ELEMENT_EXPECTED_START_TAG = dict(File="FileName", Annotation="Annotator", Relationship="Relationship", Snippet="SnippetSPDXID", Package="PackageName", ExtractedLicensingInfo="LicenseID") +EXPECTED_START_TAG_ELEMENT = {"FileName": File, "PackageName": Package, "Annotator": Annotation, + "Relationship": Relationship, "SnippetSPDXID": Snippet, + "LicenseID": ExtractedLicensingInfo} class Parser(object): @@ -97,6 +101,68 @@ def p_attrib(self, p): pass # general parsing methods + @grammar_rule("license_id : LICS_ID error\n lic_xref : LICS_CRS_REF error\n lic_comment : LICS_COMMENT error\n " + "license_name : LICS_NAME error\n extracted_text : LICS_TEXT error\n " + "file_name : FILE_NAME error\n file_contrib : FILE_CONTRIB error\n file_notice : FILE_NOTICE error\n " + "file_cr_text : FILE_CR_TEXT error\n file_lics_comment : FILE_LICS_COMMENT error\n " + "file_attribution_text : FILE_ATTRIBUTION_TEXT error\n file_lics_info : FILE_LICS_INFO error\n " + "file_comment : FILE_COMMENT error\n file_checksum : FILE_CHECKSUM error\n " + "file_conc : FILE_LICS_CONC error\n file_type : FILE_TYPE error\n " + "package_name : PKG_NAME error\n pkg_attribution_text : PKG_ATTRIBUTION_TEXT error\n " + "description : PKG_DESC error\n pkg_comment : PKG_COMMENT error\n summary : PKG_SUM error\n " + "pkg_cr_text : PKG_CPY_TEXT error\n pkg_ext_ref : PKG_EXT_REF error\n " + "pkg_lic_comment : PKG_LICS_COMMENT error\n pkg_lic_decl : PKG_LICS_DECL error\n " + "pkg_lic_ff : PKG_LICS_FFILE error \n pkg_lic_conc : PKG_LICS_CONC error\n " + "source_info : PKG_SRC_INFO error\n homepage : PKG_HOME error\n pkg_checksum : PKG_CHECKSUM error\n " + "verification_code : PKG_VERF_CODE error\n download_location : PKG_DOWN error\n " + "files_analyzed : PKG_FILES_ANALYZED error\n originator : PKG_ORIG error\n " + "supplier : PKG_SUPPL error\n pkg_file_name : PKG_FILE_NAME error\n " + "package_version : PKG_VERSION error\n primary_package_purpose : PRIMARY_PACKAGE_PURPOSE error\n " + "built_date : BUILT_DATE error\n release_date : RELEASE_DATE error\n " + "valid_until_date : VALID_UNTIL_DATE error\n snip_spdx_id : SNIPPET_SPDX_ID error\n " + "snip_name : SNIPPET_NAME error\n snip_comment : SNIPPET_COMMENT error\n " + "snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT error\n snip_cr_text : SNIPPET_CR_TEXT error\n " + "snip_lic_comment : SNIPPET_LICS_COMMENT error\n file_spdx_id : SNIPPET_FILE_SPDXID error\n " + "snip_lics_conc : SNIPPET_LICS_CONC error\n snip_lics_info : SNIPPET_LICS_INFO error\n " + "snip_byte_range : SNIPPET_BYTE_RANGE error\n snip_line_range : SNIPPET_LINE_RANGE error\n " + "annotator : ANNOTATOR error\n annotation_date : ANNOTATION_DATE error\n " + "annotation_comment : ANNOTATION_COMMENT error\n annotation_type : ANNOTATION_TYPE error\n " + "annotation_spdx_id : ANNOTATION_SPDX_ID error\n relationship : RELATIONSHIP error") + def p_current_element_error(self, p): + if p[1] in EXPECTED_START_TAG_ELEMENT.keys(): + self.initialize_new_current_element(EXPECTED_START_TAG_ELEMENT[p[1]]) + self.current_element["logger"].append( + f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") + + @grammar_rule("license_name : LICS_NAME line_or_no_assertion\n extracted_text : LICS_TEXT text_or_line\n " + "lic_comment : LICS_COMMENT text_or_line\n license_id : LICS_ID LINE\n " + "file_name : FILE_NAME LINE \n file_notice : FILE_NOTICE text_or_line\n " + "file_cr_text : FILE_CR_TEXT line_or_no_assertion_or_none\n " + "file_lics_comment : FILE_LICS_COMMENT text_or_line\n file_comment : FILE_COMMENT text_or_line\n " + "file_conc : FILE_LICS_CONC license_or_no_assertion_or_none\n " + "package_name : PKG_NAME LINE\n description : PKG_DESC text_or_line\n summary : PKG_SUM text_or_line\n " + "source_info : PKG_SRC_INFO text_or_line\n homepage : PKG_HOME line_or_no_assertion_or_none\n " + "download_location : PKG_DOWN line_or_no_assertion_or_none\n originator : PKG_ORIG actor_or_no_assertion\n " + "supplier : PKG_SUPPL actor_or_no_assertion\n pkg_comment : PKG_COMMENT text_or_line\n " + "pkg_cr_text : PKG_CPY_TEXT line_or_no_assertion_or_none\n " + "pkg_lic_decl : PKG_LICS_DECL license_or_no_assertion_or_none\n pkg_file_name : PKG_FILE_NAME LINE\n " + "pkg_lic_conc : PKG_LICS_CONC license_or_no_assertion_or_none\n package_version : PKG_VERSION LINE\n " + "pkg_lic_comment : PKG_LICS_COMMENT text_or_line\n " + "snip_spdx_id : SNIPPET_SPDX_ID LINE\n snip_name : SNIPPET_NAME LINE\n " + "snip_comment : SNIPPET_COMMENT text_or_line\n " + "snip_cr_text : SNIPPET_CR_TEXT line_or_no_assertion_or_none\n " + "snip_lic_comment : SNIPPET_LICS_COMMENT text_or_line\n file_spdx_id : SNIPPET_FILE_SPDXID LINE\n " + "snip_lics_conc : SNIPPET_LICS_CONC license_or_no_assertion_or_none\n " + "annotation_spdx_id : ANNOTATION_SPDX_ID LINE\n " + "annotation_comment : ANNOTATION_COMMENT text_or_line\n " + + ) + def p_generic_value(self, p): + if p[1] in EXPECTED_START_TAG_ELEMENT.keys(): + self.initialize_new_current_element(EXPECTED_START_TAG_ELEMENT[p[1]]) + if self.check_that_current_element_matches_class_for_value(TAG_DATA_MODEL_FIELD[p[1]][0], p.lineno(1)): + set_value(p, self.current_element) + @grammar_rule("unknown_tag : UNKNOWN_TAG text_or_line\n | UNKNOWN_TAG DATE\n | UNKNOWN_TAG PERSON_VALUE \n" "| UNKNOWN_TAG") def p_unknown_tag(self, p): @@ -148,7 +214,7 @@ def p_creation_info_value_error(self, p): @grammar_rule("document_comment : DOC_COMMENT text_or_line\n document_namespace : DOC_NAMESPACE LINE\n " "data_license : DOC_LICENSE LINE\n spdx_version : DOC_VERSION LINE\n " - "creator_comment : CREATOR_COMMENT text_or_line") + "creator_comment : CREATOR_COMMENT text_or_line\n doc_name : DOC_NAME LINE") def p_generic_value_creation_info(self, p): set_value(p, self.creation_info) @@ -156,10 +222,6 @@ def p_generic_value_creation_info(self, p): def p_license_list_version(self, p): set_value(p, self.creation_info, method_to_apply=Version.from_string) - @grammar_rule("doc_name : DOC_NAME LINE") - def p_doc_name(self, p): - set_value(p, self.creation_info, argument_name="name") - @grammar_rule("ext_doc_ref : EXT_DOC_REF DOC_REF_ID DOC_URI EXT_DOC_REF_CHECKSUM") def p_external_document_ref(self, p): document_ref_id = p[2] @@ -178,168 +240,61 @@ def p_created(self, p): # parsing methods for extracted licensing info - @grammar_rule("license_id : LICS_ID error\n lic_xref : LICS_CRS_REF error\n lic_comment : LICS_COMMENT error\n " - "license_name : LICS_NAME error\n extracted_text : LICS_TEXT error") - def p_extracted_licensing_info_value_error(self, p): - self.current_element["logger"].append( - f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") - - @grammar_rule("license_name : LICS_NAME line_or_no_assertion\n extracted_text : LICS_TEXT text_or_line") - def p_generic_value_extracted_licensing_info(self, p): - self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo, p.lineno(1)) - set_value(p, self.current_element) - - @grammar_rule("license_id : LICS_ID LINE") - def p_extracted_license_id(self, p): - self.initialize_new_current_element(ExtractedLicensingInfo) - set_value(p, self.current_element) - @grammar_rule("lic_xref : LICS_CRS_REF LINE") def p_extracted_cross_reference(self, p): - self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo, p.lineno(1)) - self.current_element.setdefault("cross_references", []).append(p[2]) - - @grammar_rule("lic_comment : LICS_COMMENT text_or_line") - def p_license_comment(self, p): - self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo, p.lineno(1)) - set_value(p, self.current_element, argument_name="comment") + if self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo, p.lineno(1)): + self.current_element.setdefault("cross_references", []).append(p[2]) # parsing methods for file - @grammar_rule("file_contrib : FILE_CONTRIB error\n file_notice : FILE_NOTICE error\n " - "file_cr_text : FILE_CR_TEXT error\n file_lics_comment : FILE_LICS_COMMENT error\n " - "file_attribution_text : FILE_ATTRIBUTION_TEXT error\n file_lics_info : FILE_LICS_INFO error\n " - "file_comment : FILE_COMMENT error\n file_checksum : FILE_CHECKSUM error\n " - "file_conc : FILE_LICS_CONC error\n file_type : FILE_TYPE error") - def p_file_value_error(self, p): - self.current_element["logger"].append( - f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") - - @grammar_rule("file_name : FILE_NAME LINE") - def p_file_name(self, p): - self.initialize_new_current_element(File) - set_value(p, self.current_element, argument_name="name") - - @grammar_rule("file_name : FILE_NAME error") - def p_file_name_error(self, p): - self.initialize_new_current_element(File) - self.current_element["logger"].append( - f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("file_contrib : FILE_CONTRIB LINE") def p_file_contributor(self, p): - self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) - self.current_element.setdefault("contributors", []).append(p[2]) - - @grammar_rule("file_notice : FILE_NOTICE text_or_line") - def p_file_notice(self, p): - self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) - set_value(p, self.current_element, argument_name="notice") - - @grammar_rule("file_cr_text : FILE_CR_TEXT line_or_no_assertion_or_none") - def p_file_copyright_text(self, p): - self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) - set_value(p, self.current_element, argument_name="copyright_text") - - @grammar_rule("file_lics_comment : FILE_LICS_COMMENT text_or_line") - def p_file_license_comment(self, p): - self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) - set_value(p, self.current_element, argument_name="license_comment") + if self.check_that_current_element_matches_class_for_value(File, p.lineno(1)): + self.current_element.setdefault("contributors", []).append(p[2]) @grammar_rule("file_attribution_text : FILE_ATTRIBUTION_TEXT text_or_line") def p_file_attribution_text(self, p): - self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) - self.current_element.setdefault("attribution_texts", []).append(p[2]) + if self.check_that_current_element_matches_class_for_value(File, p.lineno(1)): + self.current_element.setdefault("attribution_texts", []).append(p[2]) @grammar_rule("file_lics_info : FILE_LICS_INFO license_or_no_assertion_or_none") def p_file_license_info(self, p): - self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) + if not self.check_that_current_element_matches_class_for_value(File, p.lineno(1)): + return if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): self.current_element["license_info_in_file"] = p[2] return self.current_element.setdefault("license_info_in_file", []).append(p[2]) - @grammar_rule("file_comment : FILE_COMMENT text_or_line") - def p_file_comment(self, p): - self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) - set_value(p, self.current_element, argument_name="comment") - - @grammar_rule("file_type : FILE_TYPE file_type_value") + @grammar_rule("file_type : FILE_TYPE LINE") def p_file_type(self, p): - self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) - self.current_element.setdefault("file_type", []).append(FileType[p[2]]) - - @grammar_rule( - "file_type_value : SOURCE\n| BINARY\n| ARCHIVE\n | APPLICATION\n | AUDIO\n | IMAGE\n | FILETYPE_TEXT\n| VIDEO\n" - " | DOCUMENTATION\n| SPDX \n| OTHER ") - def p_file_type_value(self, p): - p[0] = p[1] + if not self.check_that_current_element_matches_class_for_value(File, p.lineno(1)): + return + try: + file_type = FileType[p[2].strip()] + except KeyError: + self.current_element["logger"].append(f"Invalid FileType: {p[2]}. Line {p.lineno(1)}") + return + self.current_element.setdefault("file_type", []).append(file_type) @grammar_rule("file_checksum : FILE_CHECKSUM CHECKSUM") def p_file_checksum(self, p): - self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) + if not self.check_that_current_element_matches_class_for_value(File, p.lineno(1)): + return checksum = parse_checksum(p[2]) self.current_element.setdefault("checksums", []).append(checksum) - @grammar_rule("file_conc : FILE_LICS_CONC license_or_no_assertion_or_none") - def p_file_license_concluded(self, p): - self.check_that_current_element_matches_class_for_value(File, p.lineno(1)) - set_value(p, self.current_element, argument_name="license_concluded") - # parsing methods for package - @grammar_rule("pkg_attribution_text : PKG_ATTRIBUTION_TEXT error\n description : PKG_DESC error\n " - "pkg_comment : PKG_COMMENT error\n summary : PKG_SUM error\n pkg_cr_text : PKG_CPY_TEXT error\n " - "pkg_ext_ref : PKG_EXT_REF error\n pkg_lic_comment : PKG_LICS_COMMENT error\n " - "pkg_lic_decl : PKG_LICS_DECL error\n pkg_lic_ff : PKG_LICS_FFILE error \n " - "pkg_lic_conc : PKG_LICS_CONC error\n source_info : PKG_SRC_INFO error\n homepage : PKG_HOME error\n " - "pkg_checksum : PKG_CHECKSUM error\n verification_code : PKG_VERF_CODE error\n " - "download_location : PKG_DOWN error\n files_analyzed : PKG_FILES_ANALYZED error\n " - "originator : PKG_ORIG error\n supplier : PKG_SUPPL error\n pkg_file_name : PKG_FILE_NAME error\n " - "package_version : PKG_VERSION error\n primary_package_purpose : PRIMARY_PACKAGE_PURPOSE error\n " - "built_date : BUILT_DATE error\n release_date : RELEASE_DATE error\n " - "valid_until_date : VALID_UNTIL_DATE error") - def p_package_value_error(self, p): - self.current_element["logger"].append( - f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") - - @grammar_rule("description : PKG_DESC text_or_line\n summary : PKG_SUM text_or_line\n " - "source_info : PKG_SRC_INFO text_or_line\n homepage : PKG_HOME line_or_no_assertion_or_none\n " - "download_location : PKG_DOWN line_or_no_assertion_or_none\n " - "originator : PKG_ORIG actor_or_no_assertion\n supplier : PKG_SUPPL actor_or_no_assertion") - def p_generic_package_value(self, p): - self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) - set_value(p, self.current_element) - - @grammar_rule("package_name : PKG_NAME LINE") - def p_package_name(self, p): - self.initialize_new_current_element(Package) - set_value(p, self.current_element, argument_name="name") - - @grammar_rule("package_name : PKG_NAME error") - def p_package_name_error(self, p): - self.initialize_new_current_element(Package) - self.current_element["logger"].append( - f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") - - @grammar_rule("pkg_comment : PKG_COMMENT text_or_line") - def p_pkg_comment(self, p): - self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) - set_value(p, self.current_element, argument_name="comment") - @grammar_rule("pkg_attribution_text : PKG_ATTRIBUTION_TEXT text_or_line") def p_pkg_attribution_text(self, p): self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) self.current_element.setdefault("attribution_texts", []).append(p[2]) - @grammar_rule("pkg_cr_text : PKG_CPY_TEXT line_or_no_assertion_or_none") - def p_pkg_copyright_text(self, p): - self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) - set_value(p, self.current_element, argument_name="copyright_text") - @grammar_rule("pkg_ext_ref : PKG_EXT_REF LINE PKG_EXT_REF_COMMENT text_or_line\n | PKG_EXT_REF LINE") def p_pkg_external_refs(self, p): - self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) + if not self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)): + return try: category, reference_type, locator = p[2].split(" ") except ValueError: @@ -352,7 +307,8 @@ def p_pkg_external_refs(self, p): try: category = ExternalPackageRefCategory[category.replace("-", "_")] except KeyError: - self.current_element["logger"].append(f"Invalid ExternalPackageRefCategory: {category}") + self.current_element["logger"].append( + f"Invalid ExternalPackageRefCategory: {category}. Line: {p.lineno(1)}") return try: external_package_ref = construct_or_raise_parsing_error(ExternalPackageRef, @@ -365,39 +321,28 @@ def p_pkg_external_refs(self, p): return self.current_element.setdefault("external_references", []).append(external_package_ref) - @grammar_rule("pkg_lic_comment : PKG_LICS_COMMENT text_or_line") - def p_pkg_license_comment(self, p): - self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) - set_value(p, self.current_element, argument_name="license_comment") - - @grammar_rule("pkg_lic_decl : PKG_LICS_DECL license_or_no_assertion_or_none") - def p_pkg_license_declared(self, p): - self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) - set_value(p, self.current_element, argument_name="license_declared") - @grammar_rule("pkg_lic_ff : PKG_LICS_FFILE license_or_no_assertion_or_none") def p_pkg_license_info_from_file(self, p): - self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) + if not self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)): + return if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): self.current_element["license_info_from_files"] = p[2] else: self.current_element.setdefault("license_info_from_files", []).append(p[2]) - @grammar_rule("pkg_lic_conc : PKG_LICS_CONC license_or_no_assertion_or_none") - def p_pkg_license_concluded(self, p): - self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) - set_value(p, self.current_element, argument_name="license_concluded") - @grammar_rule("pkg_checksum : PKG_CHECKSUM CHECKSUM") def p_pkg_checksum(self, p): - self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) + if not self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)): + return checksum = parse_checksum(p[2]) self.current_element.setdefault("checksums", []).append(checksum) @grammar_rule("verification_code : PKG_VERF_CODE LINE") def p_pkg_verification_code(self, p): - self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) - if str(p.slice[0]) in self.current_element: + if not self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)): + return + + if "verification_code" in self.current_element: self.current_element["logger"].append(f"Multiple values for {p[1]} found. Line: {p.lineno(1)}") return verif_code_regex = re.compile(r"([0-9a-f]{40})\s*(\(excludes:\s*(.+)\))?", re.UNICODE) @@ -412,102 +357,39 @@ def p_pkg_verification_code(self, p): excluded_files = None if match.group(verif_code_exc_files_grp): excluded_files = match.group(verif_code_exc_files_grp).split(",") - self.current_element[str(p.slice[0])] = PackageVerificationCode(value, excluded_files) + self.current_element["verification_code"] = PackageVerificationCode(value, excluded_files) @grammar_rule("files_analyzed : PKG_FILES_ANALYZED LINE") def p_pkg_files_analyzed(self, p): - self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) - if str(p.slice[0]) in self.current_element: + if not self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)): + return + if "files_analyzed" in self.current_element: self.current_element["logger"].append(f"Multiple values for {p[1]} found. Line: {p.lineno(1)}") return - self.current_element[str(p.slice[0])] = p[2] in ['true', 'True'] - - @grammar_rule("pkg_file_name : PKG_FILE_NAME LINE") - def p_pkg_file_name(self, p): - self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) - set_value(p, self.current_element, argument_name="file_name") - - @grammar_rule("package_version : PKG_VERSION LINE") - def p_package_version(self, p): - self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) - set_value(p, self.current_element, argument_name="version") + self.current_element["files_analyzed"] = p[2] in ['true', 'True'] - @grammar_rule("primary_package_purpose : PRIMARY_PACKAGE_PURPOSE primary_package_purpose_value") + @grammar_rule("primary_package_purpose : PRIMARY_PACKAGE_PURPOSE LINE") def p_primary_package_purpose(self, p): - self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) - set_value(p, self.current_element, method_to_apply=lambda x: PackagePurpose[x.replace("-", "_")]) - - @grammar_rule("primary_package_purpose_value : APPLICATION\n | FRAMEWORK\n | LIBRARY\n | CONTAINER\n " - "| OPERATING_SYSTEM \n | DEVICE \n| FIRMWARE\n | SOURCE\n | ARCHIVE\n | FILE\n | INSTALL\n | OTHER") - def p_primary_package_purpose_value(self, p): - p[0] = p[1] + if self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)): + set_value(p, self.current_element, method_to_apply=lambda x: PackagePurpose[x.replace("-", "_")]) @grammar_rule("built_date : BUILT_DATE DATE\n release_date : RELEASE_DATE DATE\n " "valid_until_date : VALID_UNTIL_DATE DATE") def p_package_dates(self, p): - self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) - set_value(p, self.current_element, method_to_apply=datetime_from_str) + if self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)): + set_value(p, self.current_element, method_to_apply=datetime_from_str) # parsing methods for snippet - @grammar_rule("snip_name : SNIPPET_NAME error\n snip_comment : SNIPPET_COMMENT error\n " - "snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT error\n snip_cr_text : SNIPPET_CR_TEXT error\n " - "snip_lic_comment : SNIPPET_LICS_COMMENT error\n file_spdx_id : SNIPPET_FILE_SPDXID error\n " - "snip_lics_conc : SNIPPET_LICS_CONC error\n snip_lics_info : SNIPPET_LICS_INFO error\n " - "snip_byte_range : SNIPPET_BYTE_RANGE error\n snip_line_range : SNIPPET_LINE_RANGE error\n ") - def p_snippet_value_error(self, p): - self.current_element["logger"].append( - f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") - - @grammar_rule("snip_spdx_id : SNIPPET_SPDX_ID LINE") - def p_snippet_spdx_id(self, p): - self.initialize_new_current_element(Snippet) - set_value(p, self.current_element, argument_name="spdx_id") - - @grammar_rule("snip_spdx_id : SNIPPET_SPDX_ID error") - def p_snippet_spdx_id_error(self, p): - self.initialize_new_current_element(Snippet) - self.current_element["logger"].append( - f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") - - @grammar_rule("snip_name : SNIPPET_NAME LINE") - def p_snippet_name(self, p): - self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) - set_value(p, self.current_element, argument_name="name") - - @grammar_rule("snip_comment : SNIPPET_COMMENT text_or_line") - def p_snippet_comment(self, p): - self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) - set_value(p, self.current_element, argument_name="comment") - @grammar_rule("snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT text_or_line") def p_snippet_attribution_text(self, p): - self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) - self.current_element.setdefault("attribution_texts", []).append(p[2]) - - @grammar_rule("snip_cr_text : SNIPPET_CR_TEXT line_or_no_assertion_or_none") - def p_snippet_copyright_text(self, p): - self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) - set_value(p, self.current_element, argument_name="copyright_text") - - @grammar_rule("snip_lic_comment : SNIPPET_LICS_COMMENT text_or_line") - def p_snippet_license_comment(self, p): - self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) - set_value(p, self.current_element, argument_name="license_comment") - - @grammar_rule("file_spdx_id : SNIPPET_FILE_SPDXID LINE") - def p_snippet_from_file_spdxid(self, p): - self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) - set_value(p, self.current_element) - - @grammar_rule("snip_lics_conc : SNIPPET_LICS_CONC license_or_no_assertion_or_none") - def p_snippet_concluded_license(self, p): - self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) - set_value(p, self.current_element, argument_name="license_concluded") + if self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)): + self.current_element.setdefault("attribution_texts", []).append(p[2]) @grammar_rule("snip_lics_info : SNIPPET_LICS_INFO license_or_no_assertion_or_none") def p_snippet_license_info(self, p): - self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) + if not self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)): + return if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): self.current_element["license_info_in_snippet"] = p[2] else: @@ -515,13 +397,10 @@ def p_snippet_license_info(self, p): @grammar_rule("snip_byte_range : SNIPPET_BYTE_RANGE LINE\n snip_line_range : SNIPPET_LINE_RANGE LINE") def p_snippet_range(self, p): - if p[1] == "SnippetByteRange": - argument_name = "byte_range" - elif p[1] == "SnippetLineRange": - argument_name = "line_range" - else: + if not self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)): return - self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)) + + argument_name = TAG_DATA_MODEL_FIELD[p[1]][1] if argument_name in self.current_element: self.current_element["logger"].append( f"Multiple values for {p[1]} found. Line: {p.lineno(1)}") @@ -536,45 +415,20 @@ def p_snippet_range(self, p): # parsing methods for annotation - @grammar_rule("annotation_date : ANNOTATION_DATE error\n annotation_comment : ANNOTATION_COMMENT error\n " - "annotation_type : ANNOTATION_TYPE error\n annotation_spdx_id : ANNOTATION_SPDX_ID error") - def p_annotation_value_error(self, p): - self.current_element["logger"].append( - f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("annotator : ANNOTATOR PERSON_VALUE\n| TOOL_VALUE\n| ORG_VALUE") def p_annotator(self, p): self.initialize_new_current_element(Annotation) set_value(p, self.current_element, method_to_apply=ActorParser.parse_actor) - @grammar_rule("annotator : ANNOTATOR error") - def p_annotator_error(self, p): - self.initialize_new_current_element(Annotation) - self.current_element["logger"].append( - f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("annotation_date : ANNOTATION_DATE DATE") def p_annotation_date(self, p): - self.check_that_current_element_matches_class_for_value(Annotation, p.lineno(1)) - set_value(p, self.current_element, method_to_apply=datetime_from_str) - - @grammar_rule("annotation_comment : ANNOTATION_COMMENT text_or_line") - def p_annotation_comment(self, p): - self.check_that_current_element_matches_class_for_value(Annotation, p.lineno(1)) - set_value(p, self.current_element) + if self.check_that_current_element_matches_class_for_value(Annotation, p.lineno(1)): + set_value(p, self.current_element, method_to_apply=datetime_from_str) - @grammar_rule("annotation_type : ANNOTATION_TYPE annotation_type_value") + @grammar_rule("annotation_type : ANNOTATION_TYPE LINE") def p_annotation_type(self, p): - self.check_that_current_element_matches_class_for_value(Annotation, p.lineno(1)) - set_value(p, self.current_element, method_to_apply=lambda x: AnnotationType[x]) - - @grammar_rule("annotation_type_value : OTHER\n| REVIEW") - def p_annotation_type_value(self, p): - p[0] = p[1] - - @grammar_rule("annotation_spdx_id : ANNOTATION_SPDX_ID LINE") - def p_annotation_spdx_id(self, p): - set_value(p, self.current_element, argument_name="spdx_id") + if self.check_that_current_element_matches_class_for_value(Annotation, p.lineno(1)): + set_value(p, self.current_element, method_to_apply=lambda x: AnnotationType[x]) # parsing methods for relationship @@ -602,12 +456,6 @@ def p_relationship(self, p): if len(p) == 5: self.current_element["comment"] = p[4] - @grammar_rule("relationship : RELATIONSHIP error") - def p_relationship_error(self, p): - self.initialize_new_current_element(Relationship) - self.current_element["logger"].append( - f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("relationship_value : DOC_REF_ID LINE") def p_relationship_value_with_doc_ref(self, p): @@ -646,12 +494,14 @@ def initialize_new_current_element(self, clazz: Any): self.construct_current_element() self.current_element["class"] = clazz - def check_that_current_element_matches_class_for_value(self, expected_class, line_number): + def check_that_current_element_matches_class_for_value(self, expected_class, line_number) -> bool: if "class" not in self.current_element or expected_class != self.current_element["class"]: self.logger.append( f"Element {expected_class.__name__} is not the current element in scope, probably the expected tag to " f"start the element ({ELEMENT_EXPECTED_START_TAG[expected_class.__name__]}) is missing. " f"Line: {line_number}") + return False + return True def construct_current_element(self): if "class" not in self.current_element: diff --git a/tests/spdx/parser/tagvalue/test_annotation_parser.py b/tests/spdx/parser/tagvalue/test_annotation_parser.py index 7df26534b..cf9ee7614 100644 --- a/tests/spdx/parser/tagvalue/test_annotation_parser.py +++ b/tests/spdx/parser/tagvalue/test_annotation_parser.py @@ -45,9 +45,9 @@ def test_parse_annotation(): "required positional arguments: 'spdx_id', 'annotation_type', " "'annotation_date', and 'annotation_comment'"), ('Annotator: Person: Jane Doe()\nAnnotationType: SOURCE\nAnnotationDate: 201001-2912:23', - "Error while parsing Annotation: ['Error while parsing AnnotationType: Token " - "did not match specified grammar rule. Line: 2', 'Error while parsing " - "AnnotationDate: Token did not match specified grammar rule. Line: 3']"), + "Error while parsing Annotation: ['Invalid AnnotationType: SOURCE. Line: 2', " + "'Error while parsing AnnotationDate: Token did not match specified grammar " + "rule. Line: 3']"), ('Annotator: Jane Doe()\nAnnotationDate: 201001-29T18:30:22Z\n' 'AnnotationComment: Document level annotation\nAnnotationType: OTHER\nSPDXREF: SPDXRef-DOCUMENT', "Error while parsing Annotation: ['Error while parsing Annotator: Token did " diff --git a/tests/spdx/parser/tagvalue/test_file_parser.py b/tests/spdx/parser/tagvalue/test_file_parser.py index 7ca5c4118..c6190850a 100644 --- a/tests/spdx/parser/tagvalue/test_file_parser.py +++ b/tests/spdx/parser/tagvalue/test_file_parser.py @@ -63,6 +63,5 @@ def test_parse_invalid_file(): with pytest.raises(SPDXParsingError) as err: parser.parse(file_str) - assert err.value.get_messages() == ["Error while parsing File: ['Error while parsing FileType: Token did not " - "match specified grammar rule. Line: 3', 'Error while parsing FileChecksum: " - "Token did not match specified grammar rule. Line: 5']"] + assert err.value.get_messages() == ["Error while parsing File: ['Invalid FileType: SOUCE. Line 3', 'Error while " + "parsing FileChecksum: Token did not match specified grammar rule. Line: 5']"] diff --git a/tests/spdx/parser/tagvalue/test_package_parser.py b/tests/spdx/parser/tagvalue/test_package_parser.py index 2e83f75ad..02e9dea2a 100644 --- a/tests/spdx/parser/tagvalue/test_package_parser.py +++ b/tests/spdx/parser/tagvalue/test_package_parser.py @@ -91,7 +91,7 @@ def test_parse_package(): 'category, reference_type and locator. Line: 2"]'), ('PackageName: TestPackage\nExternalRef: category reference locator', "Error while parsing Package: ['Invalid ExternalPackageRefCategory: " - "category']"), + "category. Line: 2']"), ('SPDXID:SPDXRef-DOCUMENT\nPackageName: TestPackage\nSPDXID:SPDXRef-Package\n' 'PackageDownloadLocation: download.com\nPackageVerificationCode: category reference locator', "Error while parsing Package: ['Error while parsing PackageVerificationCode: " diff --git a/tests/spdx/parser/tagvalue/test_relationship_parser.py b/tests/spdx/parser/tagvalue/test_relationship_parser.py index bf8b821fa..adc9a2ecb 100644 --- a/tests/spdx/parser/tagvalue/test_relationship_parser.py +++ b/tests/spdx/parser/tagvalue/test_relationship_parser.py @@ -46,11 +46,7 @@ def test_parse_relationship(relationship_str, expected_relationship): ['Error while parsing Relationship: ["Relationship couldn\'t be split in spdx_element_id, ' 'relationship_type and related_spdx_element. Line: 1"]']), ("Relationship: spdx_id IS spdx_id", - ["Error while parsing Relationship: ['Invalid RelationshipType IS. Line: 1']"]), - ("Relationship: spdx_id IS spdx_id\nRelationshipComment: SOURCE", - ["Error while parsing Relationship: ['Error while parsing Relationship: Token " - "did not match specified grammar rule. Line: 1']"]) - ]) + ["Error while parsing Relationship: ['Invalid RelationshipType IS. Line: 1']"])]) def test_parse_invalid_relationship(relationship_str, expected_message): parser = Parser() with pytest.raises(SPDXParsingError) as err: diff --git a/tests/spdx/parser/tagvalue/test_tag_value_lexer.py b/tests/spdx/parser/tagvalue/test_tag_value_lexer.py index ce6b9a159..bd82fab3b 100644 --- a/tests/spdx/parser/tagvalue/test_tag_value_lexer.py +++ b/tests/spdx/parser/tagvalue/test_tag_value_lexer.py @@ -86,9 +86,9 @@ def test_tokenization_of_file(lexer): token_assert_helper(lexer.token(), 'SPDX_ID', 'SPDXID', 2) token_assert_helper(lexer.token(), 'LINE', 'SPDXRef-File', 2) token_assert_helper(lexer.token(), 'FILE_TYPE', 'FileType', 3) - token_assert_helper(lexer.token(), 'SOURCE', 'SOURCE', 3) + token_assert_helper(lexer.token(), 'LINE', 'SOURCE', 3) token_assert_helper(lexer.token(), 'FILE_TYPE', 'FileType', 4) - token_assert_helper(lexer.token(), 'FILETYPE_TEXT', 'TEXT', 4) + token_assert_helper(lexer.token(), 'LINE', 'TEXT', 4) token_assert_helper(lexer.token(), 'FILE_CHECKSUM', 'FileChecksum', 5) token_assert_helper(lexer.token(), 'CHECKSUM', 'SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', 5) token_assert_helper(lexer.token(), 'FILE_LICS_CONC', 'LicenseConcluded', 6) @@ -202,7 +202,7 @@ def test_tokenization_of_package(lexer): token_assert_helper(lexer.token(), 'PKG_EXT_REF_COMMENT', 'ExternalRefComment', 22) token_assert_helper(lexer.token(), 'TEXT', 'Some comment about the package.', 22) token_assert_helper(lexer.token(), 'PRIMARY_PACKAGE_PURPOSE', 'PrimaryPackagePurpose', 23) - token_assert_helper(lexer.token(), 'OPERATING_SYSTEM', 'OPERATING-SYSTEM', 23) + token_assert_helper(lexer.token(), 'LINE', 'OPERATING-SYSTEM', 23) token_assert_helper(lexer.token(), 'BUILT_DATE', 'BuiltDate', 24) token_assert_helper(lexer.token(), 'DATE', '2020-01-01T12:00:00Z', 24) token_assert_helper(lexer.token(), 'RELEASE_DATE', 'ReleaseDate', 25) @@ -272,7 +272,7 @@ def test_tokenization_of_annotation(lexer): token_assert_helper(lexer.token(), 'ANNOTATION_COMMENT', 'AnnotationComment', 3) token_assert_helper(lexer.token(), 'TEXT', 'Document level annotation', 3) token_assert_helper(lexer.token(), 'ANNOTATION_TYPE', 'AnnotationType', 4) - token_assert_helper(lexer.token(), 'OTHER', 'OTHER', 4) + token_assert_helper(lexer.token(), 'LINE', 'OTHER', 4) token_assert_helper(lexer.token(), 'ANNOTATION_SPDX_ID', 'SPDXREF', 5) token_assert_helper(lexer.token(), 'LINE', 'SPDXRef-DOCUMENT', 5) diff --git a/tests/spdx/parser/tagvalue/test_tag_value_parser.py b/tests/spdx/parser/tagvalue/test_tag_value_parser.py index 9dde9dacf..38755a24a 100644 --- a/tests/spdx/parser/tagvalue/test_tag_value_parser.py +++ b/tests/spdx/parser/tagvalue/test_tag_value_parser.py @@ -47,7 +47,7 @@ def test_tag_value_parser(): def test_building_contains_relationship(): parser = Parser() document_str = "\n".join( - [DOCUMENT_STR, "SPDXID: SPDXRef-DOCUMENT", "FileName: File without package", "SPDXID: SPDXRef-File", + [DOCUMENT_STR, "FileName: File without package", "SPDXID: SPDXRef-File", "FileChecksum: SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759", "PackageName: Package with two files", "SPDXID: SPDXRef-Package-with-two-files", "PackageDownloadLocation: https://download.com", @@ -66,3 +66,17 @@ def test_building_contains_relationship(): Relationship("SPDXRef-Package-with-two-files", RelationshipType.CONTAINS, "SPDXRef-File-in-Package"), Relationship("SPDXRef-Package-with-two-files", RelationshipType.CONTAINS, "SPDXRef-Second-File-in-Package"), Relationship("SPDXRef-Package-with-one-file", RelationshipType.CONTAINS, "SPDXRef-File-in-different-Package")] + + +def test_document_with_mixed_values(): + parser = Parser() + document_str = "\n".join( + ["SPDXID:SPDXRef-DOCUMENT", "FileName: File without package", "SPDXID: SPDXRef-File", + "PackageDownloadLocation: https://download.com", + "FileChecksum: SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759"]) + + with pytest.raises(SPDXParsingError) as err: + parser.parse(document_str) + + assert err.value.get_messages() == ["Element Package is not the current element in scope, probably the expected " + "tag to start the element (PackageName) is missing. Line: 4"] From 08ad4df500267854effc36b4d301fa956c171936 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 8 Mar 2023 15:26:29 +0100 Subject: [PATCH 335/362] squashed review commits concerning structure, double quotes and naming [review] use double quotes [review] change file structure of tag value parser [review] rename Signed-off-by: Meret Behrens --- .gitignore | 2 +- src/spdx/parser/parse_anything.py | 2 +- .../parser/tagvalue/{lexer => }/__init__.py | 0 .../tagvalue/{parser => }/helper_methods.py | 0 .../tagvalue/{lexer/tagvalue.py => lexer.py} | 72 +-- .../{parser/tagvalue.py => parser.py} | 141 +++--- src/spdx/parser/tagvalue/parser/__init__.py | 0 .../tagvalue/{parser => }/tagvalue_parser.py | 2 +- .../parser/tagvalue/test_annotation_parser.py | 36 +- .../tagvalue/test_creation_info_parser.py | 84 ++-- .../test_extracted_licensing_info_parser.py | 54 +-- .../spdx/parser/tagvalue/test_file_parser.py | 54 +-- .../parser/tagvalue/test_helper_methods.py | 2 +- .../parser/tagvalue/test_package_parser.py | 94 ++-- .../tagvalue/test_relationship_parser.py | 16 +- .../parser/tagvalue/test_snippet_parser.py | 54 +-- .../parser/tagvalue/test_tag_value_lexer.py | 424 +++++++++--------- .../parser/tagvalue/test_tag_value_parser.py | 4 +- .../writer/tagvalue/test_tagvalue_writer.py | 2 +- 19 files changed, 526 insertions(+), 517 deletions(-) rename src/spdx/parser/tagvalue/{lexer => }/__init__.py (100%) rename src/spdx/parser/tagvalue/{parser => }/helper_methods.py (100%) rename src/spdx/parser/tagvalue/{lexer/tagvalue.py => lexer.py} (76%) rename src/spdx/parser/tagvalue/{parser/tagvalue.py => parser.py} (78%) delete mode 100644 src/spdx/parser/tagvalue/parser/__init__.py rename src/spdx/parser/tagvalue/{parser => }/tagvalue_parser.py (93%) diff --git a/.gitignore b/.gitignore index 5ef28e630..201c079bc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ __pycache__/ /build/ /dist/ /tmp/ -src/spdx/parser/tagvalue/parser/parsetab.py +src/spdx/parser/tagvalue/parsetab.py /.cache/ .tox diff --git a/src/spdx/parser/parse_anything.py b/src/spdx/parser/parse_anything.py index b2d1dfd87..8b156cf34 100644 --- a/src/spdx/parser/parse_anything.py +++ b/src/spdx/parser/parse_anything.py @@ -11,7 +11,7 @@ from spdx.formats import file_name_to_format, FileFormat from spdx.parser.json import json_parser from spdx.parser.rdf import rdf_parser -from spdx.parser.tagvalue.parser import tagvalue_parser +from spdx.parser.tagvalue import tagvalue_parser from spdx.parser.xml import xml_parser from spdx.parser.yaml import yaml_parser diff --git a/src/spdx/parser/tagvalue/lexer/__init__.py b/src/spdx/parser/tagvalue/__init__.py similarity index 100% rename from src/spdx/parser/tagvalue/lexer/__init__.py rename to src/spdx/parser/tagvalue/__init__.py diff --git a/src/spdx/parser/tagvalue/parser/helper_methods.py b/src/spdx/parser/tagvalue/helper_methods.py similarity index 100% rename from src/spdx/parser/tagvalue/parser/helper_methods.py rename to src/spdx/parser/tagvalue/helper_methods.py diff --git a/src/spdx/parser/tagvalue/lexer/tagvalue.py b/src/spdx/parser/tagvalue/lexer.py similarity index 76% rename from src/spdx/parser/tagvalue/lexer/tagvalue.py rename to src/spdx/parser/tagvalue/lexer.py index 3e4bb3569..906e26067 100644 --- a/src/spdx/parser/tagvalue/lexer/tagvalue.py +++ b/src/spdx/parser/tagvalue/lexer.py @@ -28,7 +28,7 @@ class SPDXLexer(object): "Creator": "CREATOR", "Created": "CREATED", "CreatorComment": "CREATOR_COMMENT", - "LicenseListVersion": "LIC_LIST_VER", + "LicenseListVersion": "LICENSE_LIST_VERSION", # Annotation fields "Annotator": "ANNOTATOR", "AnnotationDate": "ANNOTATION_DATE", @@ -41,25 +41,25 @@ class SPDXLexer(object): # Package fields "PackageName": "PKG_NAME", "PackageVersion": "PKG_VERSION", - "PackageDownloadLocation": "PKG_DOWN", + "PackageDownloadLocation": "PKG_DOWWNLOAD_LOCATION", "FilesAnalyzed": "PKG_FILES_ANALYZED", - "PackageSummary": "PKG_SUM", - "PackageSourceInfo": "PKG_SRC_INFO", + "PackageSummary": "PKG_SUMMARY", + "PackageSourceInfo": "PKG_SOURCE_INFO", "PackageFileName": "PKG_FILE_NAME", - "PackageSupplier": "PKG_SUPPL", - "PackageOriginator": "PKG_ORIG", + "PackageSupplier": "PKG_SUPPLIER", + "PackageOriginator": "PKG_ORIGINATOR", "PackageChecksum": "PKG_CHECKSUM", - "PackageVerificationCode": "PKG_VERF_CODE", - "PackageDescription": "PKG_DESC", + "PackageVerificationCode": "PKG_VERIFICATION_CODE", + "PackageDescription": "PKG_DESCRIPTION", "PackageComment": "PKG_COMMENT", - "PackageLicenseDeclared": "PKG_LICS_DECL", - "PackageLicenseConcluded": "PKG_LICS_CONC", - "PackageLicenseInfoFromFiles": "PKG_LICS_FFILE", - "PackageLicenseComments": "PKG_LICS_COMMENT", - "PackageCopyrightText": "PKG_CPY_TEXT", - "PackageHomePage": "PKG_HOME", - "ExternalRef": "PKG_EXT_REF", - "ExternalRefComment": "PKG_EXT_REF_COMMENT", + "PackageLicenseDeclared": "PKG_LICENSE_DECLARED", + "PackageLicenseConcluded": "PKG_LICENSE_CONCLUDED", + "PackageLicenseInfoFromFiles": "PKG_LICENSE_INFO", + "PackageLicenseComments": "PKG_LICENSE_COMMENT", + "PackageCopyrightText": "PKG_COPYRIGHT_TEXT", + "PackageHomePage": "PKG_HOMEPAGE", + "ExternalRef": "PKG_EXTERNAL_REF", + "ExternalRefComment": "PKG_EXTERNAL_REF_COMMENT", "PackageAttributionText": "PKG_ATTRIBUTION_TEXT", "PrimaryPackagePurpose": "PRIMARY_PACKAGE_PURPOSE", "BuiltDate": "BUILT_DATE", @@ -69,29 +69,29 @@ class SPDXLexer(object): "FileName": "FILE_NAME", "FileType": "FILE_TYPE", "FileChecksum": "FILE_CHECKSUM", - "LicenseConcluded": "FILE_LICS_CONC", - "LicenseInfoInFile": "FILE_LICS_INFO", - "FileCopyrightText": "FILE_CR_TEXT", - "LicenseComments": "FILE_LICS_COMMENT", + "LicenseConcluded": "FILE_LICENSE_CONCLUDED", + "LicenseInfoInFile": "FILE_LICENSE_INFO", + "FileCopyrightText": "FILE_COPYRIGHT_TEXT", + "LicenseComments": "FILE_LICENSE_COMMENT", "FileComment": "FILE_COMMENT", "FileNotice": "FILE_NOTICE", - "FileContributor": "FILE_CONTRIB", + "FileContributor": "FILE_CONTRIBUTOR", "FileAttributionText": "FILE_ATTRIBUTION_TEXT", # ExtractedLicensingInfo fields - "LicenseID": "LICS_ID", - "ExtractedText": "LICS_TEXT", - "LicenseName": "LICS_NAME", - "LicenseCrossReference": "LICS_CRS_REF", - "LicenseComment": "LICS_COMMENT", + "LicenseID": "LICENSE_ID", + "ExtractedText": "LICENSE_TEXT", + "LicenseName": "LICENSE_NAME", + "LicenseCrossReference": "LICENSE_CROSS_REF", + "LicenseComment": "LICENSE_COMMENT", # Snippet fields "SnippetSPDXID": "SNIPPET_SPDX_ID", "SnippetName": "SNIPPET_NAME", "SnippetComment": "SNIPPET_COMMENT", - "SnippetCopyrightText": "SNIPPET_CR_TEXT", - "SnippetLicenseComments": "SNIPPET_LICS_COMMENT", + "SnippetCopyrightText": "SNIPPET_COPYRIGHT_TEXT", + "SnippetLicenseComments": "SNIPPET_LICENSE_COMMENT", "SnippetFromFileSPDXID": "SNIPPET_FILE_SPDXID", - "SnippetLicenseConcluded": "SNIPPET_LICS_CONC", - "LicenseInfoInSnippet": "SNIPPET_LICS_INFO", + "SnippetLicenseConcluded": "SNIPPET_LICENSE_CONCLUDED", + "LicenseInfoInSnippet": "SNIPPET_LICENSE_INFO", "SnippetAttributionText": "SNIPPET_ATTRIBUTION_TEXT", "SnippetByteRange": "SNIPPET_BYTE_RANGE", "SnippetLineRange": "SNIPPET_LINE_RANGE", @@ -105,13 +105,13 @@ class SPDXLexer(object): "TEXT", "TOOL_VALUE", "UNKNOWN_TAG", - "ORG_VALUE", + "ORGANIZATION_VALUE", "PERSON_VALUE", "DATE", "LINE", "CHECKSUM", - "DOC_REF_ID", - "DOC_URI", + "EXT_DOC_REF_ID", + "EXT_DOC_URI", "EXT_DOC_REF_CHECKSUM", ] + list(reserved.values()) @@ -146,12 +146,12 @@ def t_CHECKSUM(self, t): return t @TOKEN(r":\s*DocumentRef-([A-Za-z0-9\+\.\-]+)") - def t_DOC_REF_ID(self, t): + def t_EXT_DOC_REF_ID(self, t): t.value = t.value[1:].strip() return t @TOKEN(r"\s*((ht|f)tps?:\/\/\S*)") - def t_DOC_URI(self, t): + def t_EXT_DOC_URI(self, t): t.value = t.value.strip() return t @@ -166,7 +166,7 @@ def t_TOOL_VALUE(self, t): return t @TOKEN(r":\s*Organization:.+") - def t_ORG_VALUE(self, t): + def t_ORGANIZATION_VALUE(self, t): t.value = t.value[1:].strip() return t diff --git a/src/spdx/parser/tagvalue/parser/tagvalue.py b/src/spdx/parser/tagvalue/parser.py similarity index 78% rename from src/spdx/parser/tagvalue/parser/tagvalue.py rename to src/spdx/parser/tagvalue/parser.py index 9ae442f3e..8fbc3d019 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue.py +++ b/src/spdx/parser/tagvalue/parser.py @@ -34,8 +34,8 @@ from spdx.parser.error import SPDXParsingError from spdx.parser.logger import Logger from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages -from spdx.parser.tagvalue.lexer.tagvalue import SPDXLexer -from spdx.parser.tagvalue.parser.helper_methods import grammar_rule, str_from_text, parse_checksum, set_value, \ +from spdx.parser.tagvalue.lexer import SPDXLexer +from spdx.parser.tagvalue.helper_methods import grammar_rule, str_from_text, parse_checksum, set_value, \ TAG_DATA_MODEL_FIELD CLASS_MAPPING = dict(File="files", Annotation="annotations", Relationship="relationships", Snippet="snippets", @@ -78,53 +78,59 @@ def p_start_attrib(self, p): "attrib : spdx_version\n| spdx_id\n| data_license\n| doc_name\n| document_comment\n| document_namespace\n| " "creator\n| created\n| creator_comment\n| license_list_version\n| ext_doc_ref\n" # attributes for file - "| file_name\n| file_type\n| file_checksum\n| file_conc\n| file_lics_info\n| file_cr_text\n" - "| file_lics_comment\n| file_attribution_text\n| file_notice\n| file_comment\n| file_contrib\n" + "| file_name\n| file_type\n| file_checksum\n| file_license_concluded\n| file_license_info\n" + "| file_copyright_text\n| file_license_comment\n| file_attribution_text\n| file_notice\n| file_comment\n" + "| file_contributor\n" # attributes for annotation "| annotator\n| annotation_date\n| annotation_comment\n| annotation_type\n| annotation_spdx_id\n" # attributes for relationship "| relationship\n" # attributes for snippet - "| snip_spdx_id\n| snip_name\n| snip_comment\n| snippet_attribution_text\n| snip_cr_text\n" - "| snip_lic_comment\n| file_spdx_id\n| snip_lics_conc\n| snip_lics_info\n| snip_byte_range\n" - "| snip_line_range\n" + "| snippet_spdx_id\n| snippet_name\n| snippet_comment\n| snippet_attribution_text\n| snippet_copyright_text\n" + "| snippet_license_comment\n| file_spdx_id\n| snippet_license_concluded\n| snippet_license_info\n" + "| snippet_byte_range\n| snippet_line_range\n" # attributes for package "| package_name\n| package_version\n| download_location\n| files_analyzed\n| homepage\n" "| summary\n| source_info\n| pkg_file_name\n| supplier\n| originator\n| pkg_checksum\n" - "| verification_code\n| description\n| pkg_comment\n| pkg_attribution_text\n| pkg_lic_decl\n| pkg_lic_conc\n" - "| pkg_lic_ff\n| pkg_lic_comment\n| pkg_cr_text\n| pkg_ext_ref\n| primary_package_purpose\n" - "| built_date\n| release_date\n| valid_until_date\n" + "| verification_code\n| description\n| pkg_comment\n| pkg_attribution_text\n| pkg_license_declared\n" + "| pkg_license_concluded\n| pkg_license_info\n| pkg_license_comment\n| pkg_copyright_text\n" + "| pkg_external_ref\n| primary_package_purpose\n| built_date\n| release_date\n| valid_until_date\n" # attributes for extracted licensing info - "| license_id\n| extracted_text\n| license_name\n| lic_xref\n| lic_comment\n" + "| license_id\n| extracted_text\n| license_name\n| license_cross_ref\n| lic_comment\n" "| unknown_tag ") def p_attrib(self, p): pass # general parsing methods - @grammar_rule("license_id : LICS_ID error\n lic_xref : LICS_CRS_REF error\n lic_comment : LICS_COMMENT error\n " - "license_name : LICS_NAME error\n extracted_text : LICS_TEXT error\n " - "file_name : FILE_NAME error\n file_contrib : FILE_CONTRIB error\n file_notice : FILE_NOTICE error\n " - "file_cr_text : FILE_CR_TEXT error\n file_lics_comment : FILE_LICS_COMMENT error\n " - "file_attribution_text : FILE_ATTRIBUTION_TEXT error\n file_lics_info : FILE_LICS_INFO error\n " - "file_comment : FILE_COMMENT error\n file_checksum : FILE_CHECKSUM error\n " - "file_conc : FILE_LICS_CONC error\n file_type : FILE_TYPE error\n " + @grammar_rule("license_id : LICENSE_ID error\n license_cross_ref : LICENSE_CROSS_REF error\n " + "lic_comment : LICENSE_COMMENT error\n license_name : LICENSE_NAME error\n " + "extracted_text : LICENSE_TEXT error\n " + "file_name : FILE_NAME error\n file_contributor : FILE_CONTRIBUTOR error\n " + "file_notice : FILE_NOTICE error\n file_copyright_text : FILE_COPYRIGHT_TEXT error\n " + "file_license_comment : FILE_LICENSE_COMMENT error\n " + "file_license_info : FILE_LICENSE_INFO error\n file_comment : FILE_COMMENT error\n " + "file_checksum : FILE_CHECKSUM error\n file_license_concluded : FILE_LICENSE_CONCLUDED error\n " + "file_type : FILE_TYPE error\n file_attribution_text : FILE_ATTRIBUTION_TEXT error\n " "package_name : PKG_NAME error\n pkg_attribution_text : PKG_ATTRIBUTION_TEXT error\n " - "description : PKG_DESC error\n pkg_comment : PKG_COMMENT error\n summary : PKG_SUM error\n " - "pkg_cr_text : PKG_CPY_TEXT error\n pkg_ext_ref : PKG_EXT_REF error\n " - "pkg_lic_comment : PKG_LICS_COMMENT error\n pkg_lic_decl : PKG_LICS_DECL error\n " - "pkg_lic_ff : PKG_LICS_FFILE error \n pkg_lic_conc : PKG_LICS_CONC error\n " - "source_info : PKG_SRC_INFO error\n homepage : PKG_HOME error\n pkg_checksum : PKG_CHECKSUM error\n " - "verification_code : PKG_VERF_CODE error\n download_location : PKG_DOWN error\n " - "files_analyzed : PKG_FILES_ANALYZED error\n originator : PKG_ORIG error\n " - "supplier : PKG_SUPPL error\n pkg_file_name : PKG_FILE_NAME error\n " + "description : PKG_DESCRIPTION error\n pkg_comment : PKG_COMMENT error\n " + "summary : PKG_SUMMARY error\n pkg_copyright_text : PKG_COPYRIGHT_TEXT error\n " + "pkg_external_ref : PKG_EXTERNAL_REF error\n pkg_license_comment : PKG_LICENSE_COMMENT error\n " + "pkg_license_declared : PKG_LICENSE_DECLARED error\n pkg_license_info : PKG_LICENSE_INFO error \n " + "pkg_license_concluded : PKG_LICENSE_CONCLUDED error\n source_info : PKG_SOURCE_INFO error\n " + "homepage : PKG_HOMEPAGE error\n pkg_checksum : PKG_CHECKSUM error\n " + "verification_code : PKG_VERIFICATION_CODE error\n originator : PKG_ORIGINATOR error\n " + "download_location : PKG_DOWWNLOAD_LOCATION error\n files_analyzed : PKG_FILES_ANALYZED error\n " + "supplier : PKG_SUPPLIER error\n pkg_file_name : PKG_FILE_NAME error\n " "package_version : PKG_VERSION error\n primary_package_purpose : PRIMARY_PACKAGE_PURPOSE error\n " "built_date : BUILT_DATE error\n release_date : RELEASE_DATE error\n " - "valid_until_date : VALID_UNTIL_DATE error\n snip_spdx_id : SNIPPET_SPDX_ID error\n " - "snip_name : SNIPPET_NAME error\n snip_comment : SNIPPET_COMMENT error\n " - "snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT error\n snip_cr_text : SNIPPET_CR_TEXT error\n " - "snip_lic_comment : SNIPPET_LICS_COMMENT error\n file_spdx_id : SNIPPET_FILE_SPDXID error\n " - "snip_lics_conc : SNIPPET_LICS_CONC error\n snip_lics_info : SNIPPET_LICS_INFO error\n " - "snip_byte_range : SNIPPET_BYTE_RANGE error\n snip_line_range : SNIPPET_LINE_RANGE error\n " + "valid_until_date : VALID_UNTIL_DATE error\n snippet_spdx_id : SNIPPET_SPDX_ID error\n " + "snippet_name : SNIPPET_NAME error\n snippet_comment : SNIPPET_COMMENT error\n " + "snippet_attribution_text : SNIPPET_ATTRIBUTION_TEXT error\n " + "snippet_copyright_text : SNIPPET_COPYRIGHT_TEXT error\n " + "snippet_license_comment : SNIPPET_LICENSE_COMMENT error\n file_spdx_id : SNIPPET_FILE_SPDXID error\n " + "snippet_license_concluded : SNIPPET_LICENSE_CONCLUDED error\n " + "snippet_license_info : SNIPPET_LICENSE_INFO error\n " + "snippet_byte_range : SNIPPET_BYTE_RANGE error\n snippet_line_range : SNIPPET_LINE_RANGE error\n " "annotator : ANNOTATOR error\n annotation_date : ANNOTATION_DATE error\n " "annotation_comment : ANNOTATION_COMMENT error\n annotation_type : ANNOTATION_TYPE error\n " "annotation_spdx_id : ANNOTATION_SPDX_ID error\n relationship : RELATIONSHIP error") @@ -134,25 +140,30 @@ def p_current_element_error(self, p): self.current_element["logger"].append( f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") - @grammar_rule("license_name : LICS_NAME line_or_no_assertion\n extracted_text : LICS_TEXT text_or_line\n " - "lic_comment : LICS_COMMENT text_or_line\n license_id : LICS_ID LINE\n " + @grammar_rule("license_name : LICENSE_NAME line_or_no_assertion\n extracted_text : LICENSE_TEXT text_or_line\n " + "lic_comment : LICENSE_COMMENT text_or_line\n license_id : LICENSE_ID LINE\n " "file_name : FILE_NAME LINE \n file_notice : FILE_NOTICE text_or_line\n " - "file_cr_text : FILE_CR_TEXT line_or_no_assertion_or_none\n " - "file_lics_comment : FILE_LICS_COMMENT text_or_line\n file_comment : FILE_COMMENT text_or_line\n " - "file_conc : FILE_LICS_CONC license_or_no_assertion_or_none\n " - "package_name : PKG_NAME LINE\n description : PKG_DESC text_or_line\n summary : PKG_SUM text_or_line\n " - "source_info : PKG_SRC_INFO text_or_line\n homepage : PKG_HOME line_or_no_assertion_or_none\n " - "download_location : PKG_DOWN line_or_no_assertion_or_none\n originator : PKG_ORIG actor_or_no_assertion\n " - "supplier : PKG_SUPPL actor_or_no_assertion\n pkg_comment : PKG_COMMENT text_or_line\n " - "pkg_cr_text : PKG_CPY_TEXT line_or_no_assertion_or_none\n " - "pkg_lic_decl : PKG_LICS_DECL license_or_no_assertion_or_none\n pkg_file_name : PKG_FILE_NAME LINE\n " - "pkg_lic_conc : PKG_LICS_CONC license_or_no_assertion_or_none\n package_version : PKG_VERSION LINE\n " - "pkg_lic_comment : PKG_LICS_COMMENT text_or_line\n " - "snip_spdx_id : SNIPPET_SPDX_ID LINE\n snip_name : SNIPPET_NAME LINE\n " - "snip_comment : SNIPPET_COMMENT text_or_line\n " - "snip_cr_text : SNIPPET_CR_TEXT line_or_no_assertion_or_none\n " - "snip_lic_comment : SNIPPET_LICS_COMMENT text_or_line\n file_spdx_id : SNIPPET_FILE_SPDXID LINE\n " - "snip_lics_conc : SNIPPET_LICS_CONC license_or_no_assertion_or_none\n " + "file_copyright_text : FILE_COPYRIGHT_TEXT line_or_no_assertion_or_none\n " + "file_license_comment : FILE_LICENSE_COMMENT text_or_line\n " + "file_comment : FILE_COMMENT text_or_line\n " + "file_license_concluded : FILE_LICENSE_CONCLUDED license_or_no_assertion_or_none\n " + "package_name : PKG_NAME LINE\n description : PKG_DESCRIPTION text_or_line\n " + "summary : PKG_SUMMARY text_or_line\n source_info : PKG_SOURCE_INFO text_or_line\n " + "homepage : PKG_HOMEPAGE line_or_no_assertion_or_none\n " + "download_location : PKG_DOWWNLOAD_LOCATION line_or_no_assertion_or_none\n " + "originator : PKG_ORIGINATOR actor_or_no_assertion\n supplier : PKG_SUPPLIER actor_or_no_assertion\n " + "pkg_comment : PKG_COMMENT text_or_line\n " + "pkg_copyright_text : PKG_COPYRIGHT_TEXT line_or_no_assertion_or_none\n " + "pkg_license_declared : PKG_LICENSE_DECLARED license_or_no_assertion_or_none\n " + "pkg_file_name : PKG_FILE_NAME LINE\n " + "pkg_license_concluded : PKG_LICENSE_CONCLUDED license_or_no_assertion_or_none\n " + "package_version : PKG_VERSION LINE\n pkg_license_comment : PKG_LICENSE_COMMENT text_or_line\n " + "snippet_spdx_id : SNIPPET_SPDX_ID LINE\n snippet_name : SNIPPET_NAME LINE\n " + "snippet_comment : SNIPPET_COMMENT text_or_line\n " + "snippet_copyright_text : SNIPPET_COPYRIGHT_TEXT line_or_no_assertion_or_none\n " + "snippet_license_comment : SNIPPET_LICENSE_COMMENT text_or_line\n " + "file_spdx_id : SNIPPET_FILE_SPDXID LINE\n " + "snippet_license_concluded : SNIPPET_LICENSE_CONCLUDED license_or_no_assertion_or_none\n " "annotation_spdx_id : ANNOTATION_SPDX_ID LINE\n " "annotation_comment : ANNOTATION_COMMENT text_or_line\n " @@ -189,7 +200,7 @@ def p_none(self, p): def p_license(self, p): p[0] = get_spdx_licensing().parse(p[1]) - @grammar_rule("actor_or_no_assertion : PERSON_VALUE\n | ORG_VALUE") + @grammar_rule("actor_or_no_assertion : PERSON_VALUE\n | ORGANIZATION_VALUE") def p_actor_values(self, p): p[0] = ActorParser.parse_actor(p[1]) @@ -204,7 +215,7 @@ def p_spdx_id(self, p): # parsing methods for creation info / document level - @grammar_rule("license_list_version : LIC_LIST_VER error\n document_comment : DOC_COMMENT error\n " + @grammar_rule("license_list_version : LICENSE_LIST_VERSION error\n document_comment : DOC_COMMENT error\n " "document_namespace : DOC_NAMESPACE error\n data_license : DOC_LICENSE error\n " "doc_name : DOC_NAME error\n ext_doc_ref : EXT_DOC_REF error\n spdx_version : DOC_VERSION error\n " "creator_comment : CREATOR_COMMENT error\n creator : CREATOR error\n created : CREATED error") @@ -218,11 +229,11 @@ def p_creation_info_value_error(self, p): def p_generic_value_creation_info(self, p): set_value(p, self.creation_info) - @grammar_rule("license_list_version : LIC_LIST_VER LINE") + @grammar_rule("license_list_version : LICENSE_LIST_VERSION LINE") def p_license_list_version(self, p): set_value(p, self.creation_info, method_to_apply=Version.from_string) - @grammar_rule("ext_doc_ref : EXT_DOC_REF DOC_REF_ID DOC_URI EXT_DOC_REF_CHECKSUM") + @grammar_rule("ext_doc_ref : EXT_DOC_REF EXT_DOC_REF_ID EXT_DOC_URI EXT_DOC_REF_CHECKSUM") def p_external_document_ref(self, p): document_ref_id = p[2] document_uri = p[3] @@ -230,7 +241,7 @@ def p_external_document_ref(self, p): external_document_ref = ExternalDocumentRef(document_ref_id, document_uri, checksum) self.creation_info.setdefault("external_document_refs", []).append(external_document_ref) - @grammar_rule("creator : CREATOR PERSON_VALUE\n| CREATOR TOOL_VALUE\n| CREATOR ORG_VALUE") + @grammar_rule("creator : CREATOR PERSON_VALUE\n| CREATOR TOOL_VALUE\n| CREATOR ORGANIZATION_VALUE") def p_creator(self, p): self.creation_info.setdefault("creators", []).append(ActorParser.parse_actor(p[2])) @@ -240,14 +251,14 @@ def p_created(self, p): # parsing methods for extracted licensing info - @grammar_rule("lic_xref : LICS_CRS_REF LINE") + @grammar_rule("license_cross_ref : LICENSE_CROSS_REF LINE") def p_extracted_cross_reference(self, p): if self.check_that_current_element_matches_class_for_value(ExtractedLicensingInfo, p.lineno(1)): self.current_element.setdefault("cross_references", []).append(p[2]) # parsing methods for file - @grammar_rule("file_contrib : FILE_CONTRIB LINE") + @grammar_rule("file_contributor : FILE_CONTRIBUTOR LINE") def p_file_contributor(self, p): if self.check_that_current_element_matches_class_for_value(File, p.lineno(1)): self.current_element.setdefault("contributors", []).append(p[2]) @@ -257,7 +268,7 @@ def p_file_attribution_text(self, p): if self.check_that_current_element_matches_class_for_value(File, p.lineno(1)): self.current_element.setdefault("attribution_texts", []).append(p[2]) - @grammar_rule("file_lics_info : FILE_LICS_INFO license_or_no_assertion_or_none") + @grammar_rule("file_license_info : FILE_LICENSE_INFO license_or_no_assertion_or_none") def p_file_license_info(self, p): if not self.check_that_current_element_matches_class_for_value(File, p.lineno(1)): return @@ -291,7 +302,7 @@ def p_pkg_attribution_text(self, p): self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) self.current_element.setdefault("attribution_texts", []).append(p[2]) - @grammar_rule("pkg_ext_ref : PKG_EXT_REF LINE PKG_EXT_REF_COMMENT text_or_line\n | PKG_EXT_REF LINE") + @grammar_rule("pkg_external_ref : PKG_EXTERNAL_REF LINE PKG_EXTERNAL_REF_COMMENT text_or_line\n | PKG_EXTERNAL_REF LINE") def p_pkg_external_refs(self, p): if not self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)): return @@ -321,7 +332,7 @@ def p_pkg_external_refs(self, p): return self.current_element.setdefault("external_references", []).append(external_package_ref) - @grammar_rule("pkg_lic_ff : PKG_LICS_FFILE license_or_no_assertion_or_none") + @grammar_rule("pkg_license_info : PKG_LICENSE_INFO license_or_no_assertion_or_none") def p_pkg_license_info_from_file(self, p): if not self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)): return @@ -337,7 +348,7 @@ def p_pkg_checksum(self, p): checksum = parse_checksum(p[2]) self.current_element.setdefault("checksums", []).append(checksum) - @grammar_rule("verification_code : PKG_VERF_CODE LINE") + @grammar_rule("verification_code : PKG_VERIFICATION_CODE LINE") def p_pkg_verification_code(self, p): if not self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)): return @@ -386,7 +397,7 @@ def p_snippet_attribution_text(self, p): if self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)): self.current_element.setdefault("attribution_texts", []).append(p[2]) - @grammar_rule("snip_lics_info : SNIPPET_LICS_INFO license_or_no_assertion_or_none") + @grammar_rule("snippet_license_info : SNIPPET_LICENSE_INFO license_or_no_assertion_or_none") def p_snippet_license_info(self, p): if not self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)): return @@ -395,7 +406,7 @@ def p_snippet_license_info(self, p): else: self.current_element.setdefault("license_info_in_snippet", []).append(p[2]) - @grammar_rule("snip_byte_range : SNIPPET_BYTE_RANGE LINE\n snip_line_range : SNIPPET_LINE_RANGE LINE") + @grammar_rule("snippet_byte_range : SNIPPET_BYTE_RANGE LINE\n snippet_line_range : SNIPPET_LINE_RANGE LINE") def p_snippet_range(self, p): if not self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)): return @@ -415,7 +426,7 @@ def p_snippet_range(self, p): # parsing methods for annotation - @grammar_rule("annotator : ANNOTATOR PERSON_VALUE\n| TOOL_VALUE\n| ORG_VALUE") + @grammar_rule("annotator : ANNOTATOR PERSON_VALUE\n| TOOL_VALUE\n| ORGANIZATION_VALUE") def p_annotator(self, p): self.initialize_new_current_element(Annotation) set_value(p, self.current_element, method_to_apply=ActorParser.parse_actor) @@ -456,7 +467,7 @@ def p_relationship(self, p): if len(p) == 5: self.current_element["comment"] = p[4] - @grammar_rule("relationship_value : DOC_REF_ID LINE") + @grammar_rule("relationship_value : EXT_DOC_REF_ID LINE") def p_relationship_value_with_doc_ref(self, p): p[0] = p[1] + ":" + p[2] diff --git a/src/spdx/parser/tagvalue/parser/__init__.py b/src/spdx/parser/tagvalue/parser/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/spdx/parser/tagvalue/parser/tagvalue_parser.py b/src/spdx/parser/tagvalue/tagvalue_parser.py similarity index 93% rename from src/spdx/parser/tagvalue/parser/tagvalue_parser.py rename to src/spdx/parser/tagvalue/tagvalue_parser.py index ba4a53ead..d71c3c047 100644 --- a/src/spdx/parser/tagvalue/parser/tagvalue_parser.py +++ b/src/spdx/parser/tagvalue/tagvalue_parser.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from spdx.model.document import Document -from spdx.parser.tagvalue.parser.tagvalue import Parser +from spdx.parser.tagvalue.parser import Parser def parse_from_file(file_name: str) -> Document: diff --git a/tests/spdx/parser/tagvalue/test_annotation_parser.py b/tests/spdx/parser/tagvalue/test_annotation_parser.py index cf9ee7614..65e9fa1d1 100644 --- a/tests/spdx/parser/tagvalue/test_annotation_parser.py +++ b/tests/spdx/parser/tagvalue/test_annotation_parser.py @@ -8,54 +8,52 @@ # 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 re from datetime import datetime -from unittest import TestCase import pytest from spdx.model.annotation import AnnotationType from spdx.parser.error import SPDXParsingError -from spdx.parser.tagvalue.parser.tagvalue import Parser +from spdx.parser.tagvalue.parser import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR def test_parse_annotation(): parser = Parser() - annotation_str = '\n'.join([ - 'Annotator: Person: Jane Doe()', - 'AnnotationDate: 2010-01-29T18:30:22Z', - 'AnnotationComment: Document level annotation', - 'AnnotationType: OTHER', - 'SPDXREF: SPDXRef-DOCUMENT' + annotation_str = "\n".join([ + "Annotator: Person: Jane Doe()", + "AnnotationDate: 2010-01-29T18:30:22Z", + "AnnotationComment: Document level annotation", + "AnnotationType: OTHER", + "SPDXREF: SPDXRef-DOCUMENT" ]) document = parser.parse("\n".join([DOCUMENT_STR, annotation_str])) assert document is not None assert len(document.annotations) == 1 annotation = document.annotations[0] - assert annotation.annotator.name == 'Jane Doe' + assert annotation.annotator.name == "Jane Doe" assert annotation.annotation_date == datetime(2010, 1, 29, 18, 30, 22) - assert annotation.annotation_comment == 'Document level annotation' + assert annotation.annotation_comment == "Document level annotation" assert annotation.annotation_type == AnnotationType.OTHER - assert annotation.spdx_id == 'SPDXRef-DOCUMENT' + assert annotation.spdx_id == "SPDXRef-DOCUMENT" @pytest.mark.parametrize("annotation_str, expected_message", [ - ('Annotator: Person: Jane Doe()', r"__init__() missing 4 " + ("Annotator: Person: Jane Doe()", r"__init__() missing 4 " "required positional arguments: 'spdx_id', 'annotation_type', " "'annotation_date', and 'annotation_comment'"), - ('Annotator: Person: Jane Doe()\nAnnotationType: SOURCE\nAnnotationDate: 201001-2912:23', + ("Annotator: Person: Jane Doe()\nAnnotationType: SOURCE\nAnnotationDate: 201001-2912:23", "Error while parsing Annotation: ['Invalid AnnotationType: SOURCE. Line: 2', " "'Error while parsing AnnotationDate: Token did not match specified grammar " "rule. Line: 3']"), - ('Annotator: Jane Doe()\nAnnotationDate: 201001-29T18:30:22Z\n' - 'AnnotationComment: Document level annotation\nAnnotationType: OTHER\nSPDXREF: SPDXRef-DOCUMENT', + ("Annotator: Jane Doe()\nAnnotationDate: 201001-29T18:30:22Z\n" + "AnnotationComment: Document level annotation\nAnnotationType: OTHER\nSPDXREF: SPDXRef-DOCUMENT", "Error while parsing Annotation: ['Error while parsing Annotator: Token did " "not match specified grammar rule. Line: 1', 'Error while parsing " "AnnotationDate: Token did not match specified grammar rule. Line: 2']"), - ('Annotator: Person: ()', "Error while parsing Annotation: [['No name for Person provided: Person: ().']]"), - ('AnnotationType: REVIEW', 'Element Annotation is not the current element in scope, probably the ' - 'expected tag to start the element (Annotator) is missing. Line: 1')]) + ("Annotator: Person: ()", "Error while parsing Annotation: [['No name for Person provided: Person: ().']]"), + ("AnnotationType: REVIEW", "Element Annotation is not the current element in scope, probably the " + "expected tag to start the element (Annotator) is missing. Line: 1")]) def test_parse_invalid_annotation(annotation_str, expected_message): parser = Parser() with pytest.raises(SPDXParsingError) as err: diff --git a/tests/spdx/parser/tagvalue/test_creation_info_parser.py b/tests/spdx/parser/tagvalue/test_creation_info_parser.py index f98f997dd..2d789229b 100644 --- a/tests/spdx/parser/tagvalue/test_creation_info_parser.py +++ b/tests/spdx/parser/tagvalue/test_creation_info_parser.py @@ -18,21 +18,21 @@ from spdx.model.external_document_ref import ExternalDocumentRef from spdx.model.version import Version from spdx.parser.error import SPDXParsingError -from spdx.parser.tagvalue.parser.tagvalue import Parser +from spdx.parser.tagvalue.parser import Parser -DOCUMENT_STR = '\n'.join([ - 'SPDXVersion: SPDX-2.3', - 'DataLicense: CC0-1.0', - 'DocumentName: Sample_Document-V2.3', - 'SPDXID: SPDXRef-DOCUMENT', - 'DocumentComment: Sample Comment', - 'DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301', - 'ExternalDocumentRef: DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759', - 'Creator: Person: Bob (bob@example.com)', - 'Creator: Organization: Acme.', - 'Created: 2010-02-03T00:00:00Z', - 'CreatorComment: Sample Comment \nwith multiple \nlines.', - 'LicenseListVersion: 3.17' +DOCUMENT_STR = "\n".join([ + "SPDXVersion: SPDX-2.3", + "DataLicense: CC0-1.0", + "DocumentName: Sample_Document-V2.3", + "SPDXID: SPDXRef-DOCUMENT", + "DocumentComment: Sample Comment", + "DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301", + "ExternalDocumentRef: DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759", + "Creator: Person: Bob (bob@example.com)", + "Creator: Organization: Acme.", + "Created: 2010-02-03T00:00:00Z", + "CreatorComment: Sample Comment \nwith multiple \nlines.", + "LicenseListVersion: 3.17" ]) @@ -43,15 +43,15 @@ def test_parse_creation_info(): creation_info = document.creation_info assert creation_info is not None assert creation_info.spdx_version == "SPDX-2.3" - assert creation_info.data_license == 'CC0-1.0' - assert creation_info.name == 'Sample_Document-V2.3' - assert creation_info.spdx_id == 'SPDXRef-DOCUMENT' - assert creation_info.document_comment == 'Sample Comment' - assert creation_info.document_namespace == 'https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301' + assert creation_info.data_license == "CC0-1.0" + assert creation_info.name == "Sample_Document-V2.3" + assert creation_info.spdx_id == "SPDXRef-DOCUMENT" + assert creation_info.document_comment == "Sample Comment" + assert creation_info.document_namespace == "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301" TestCase().assertCountEqual(creation_info.creators, [Actor(ActorType.PERSON, "Bob", "bob@example.com"), Actor(ActorType.ORGANIZATION, "Acme.")]) - assert creation_info.creator_comment == 'Sample Comment \nwith multiple \nlines.' + assert creation_info.creator_comment == "Sample Comment \nwith multiple \nlines." assert creation_info.created == datetime(2010, 2, 3) assert creation_info.license_list_version == Version(3, 17) assert creation_info.external_document_refs == [ExternalDocumentRef("DocumentRef-spdx-tool-1.2", @@ -61,28 +61,28 @@ def test_parse_creation_info(): @pytest.mark.parametrize("document_str, expected_message", - ([('\n'.join( - ['SPDXVersion: SPDX-2.3', 'DataLicense: CC0-1.0', 'DocumentName: Sample_Document-V2.3', - 'SPDXID: SPDXRef-DOCUMENT', 'DocumentComment: Sample Comment', - 'DocumentNamespace: Sample Comment', - 'ExternalDocumentRef: DocumentRef-spdx-tool-1.2:htp://spdx.org:SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759', - 'Creator: Person Bob (bob@example.com)', 'Creator: Organization: Acme [email]', - 'Created: 2010-02-03T00:00:0Z', 'CreatorComment: Sample Comment', - 'LicenseListVersion: 7']), - "Error while parsing CreationInfo: " - "['Error while parsing DocumentNamespace: Token did not match specified grammar rule. " - "Line: 6', 'Error while parsing ExternalDocumentRef: " - "Token did not match specified grammar rule. Line: 7', 'Error while parsing Creator: " - "Token did not match specified grammar rule. Line: 8', 'Error while parsing Created: " - "Token did not match specified grammar rule. Line: 10', '7 is not a valid version string']"), - ('\n'.join( - ['SPDXVersion: SPDX-2.3', 'DataLicense: CC0-1.0', 'DocumentName: Sample_Document-V2.3', - 'SPDXID: SPDXRef-DOCUMENT']), - r"__init__() missing 3 required positional arguments: " - r"'document_namespace', 'creators', and 'created'"), - ('LicenseListVersion: 3.5\nLicenseListVersion: 3.7', - "Error while parsing CreationInfo: ['Multiple values for LicenseListVersion " - "found. Line: 2']")])) + ([("\n".join( + ["SPDXVersion: SPDX-2.3", "DataLicense: CC0-1.0", "DocumentName: Sample_Document-V2.3", + "SPDXID: SPDXRef-DOCUMENT", "DocumentComment: Sample Comment", + "DocumentNamespace: Sample Comment", + "ExternalDocumentRef: DocumentRef-spdx-tool-1.2:htp://spdx.org:SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759", + "Creator: Person Bob (bob@example.com)", "Creator: Organization: Acme [email]", + "Created: 2010-02-03T00:00:0Z", "CreatorComment: Sample Comment", + "LicenseListVersion: 7"]), + "Error while parsing CreationInfo: ['Error while parsing DocumentNamespace: " + "Token did not match specified grammar rule. Line: 6', 'Error while parsing " + "ExternalDocumentRef: Token did not match specified grammar rule. Line: 7', " + "'Error while parsing Creator: Token did not match specified grammar rule. Line: 8', " + "'Error while parsing Created: Token did not match specified grammar rule. Line: 10', " + "'7 is not a valid version string']"), + ("\n".join( + ["SPDXVersion: SPDX-2.3", "DataLicense: CC0-1.0", "DocumentName: Sample_Document-V2.3", + "SPDXID: SPDXRef-DOCUMENT"]), + r"__init__() missing 3 required positional arguments: 'document_namespace', " + r"'creators', and 'created'"), + ("LicenseListVersion: 3.5\nLicenseListVersion: 3.7", + "Error while parsing CreationInfo: ['Multiple values for LicenseListVersion found. " + "Line: 2']")])) def test_parse_invalid_creation_info(document_str, expected_message): parser = Parser() with pytest.raises(SPDXParsingError) as err: diff --git a/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py b/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py index a8c1c2f66..f8e27ace1 100644 --- a/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py +++ b/tests/spdx/parser/tagvalue/test_extracted_licensing_info_parser.py @@ -13,30 +13,30 @@ import pytest from spdx.parser.error import SPDXParsingError -from spdx.parser.tagvalue.parser.tagvalue import Parser +from spdx.parser.tagvalue.parser import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR def test_parse_extracted_licensing_info(): parser = Parser() - extracted_licensing_info_str = '\n'.join([ - 'LicenseID: LicenseRef-Beerware-4.2', - 'ExtractedText: "THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you ' - 'retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this ' - 'stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' - 'LicenseName: Beer-Ware License (Version 42)', - 'LicenseCrossReference: http://people.freebsd.org/~phk/', - 'LicenseCrossReference: http://another.cross.reference/', - 'LicenseComment: The beerware license has a couple of other standard variants.' + extracted_licensing_info_str = "\n".join([ + "LicenseID: LicenseRef-Beerware-4.2", + "ExtractedText: \"THE BEER-WARE LICENSE\" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you " + "retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this " + "stuff is worth it, you can buy me a beer in return Poul-Henning Kamp" + "LicenseName: Beer-Ware License (Version 42)", + "LicenseCrossReference: http://people.freebsd.org/~phk/", + "LicenseCrossReference: http://another.cross.reference/", + "LicenseComment: The beerware license has a couple of other standard variants." ]) document = parser.parse("\n".join([DOCUMENT_STR, extracted_licensing_info_str])) assert document is not None assert len(document.extracted_licensing_info) == 1 extracted_licensing_info = document.extracted_licensing_info[0] assert extracted_licensing_info.license_id == "LicenseRef-Beerware-4.2" - assert extracted_licensing_info.extracted_text == '"THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. ' \ - 'As long as you retain this notice you can do whatever you want with this stuff. ' \ - 'If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp' + assert extracted_licensing_info.extracted_text == "\"THE BEER-WARE LICENSE\" (Revision 42): phk@FreeBSD.ORG wrote this file. " \ + "As long as you retain this notice you can do whatever you want with this stuff. " \ + "If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp" assert extracted_licensing_info.license_name == "Beer-Ware License (Version 42)" TestCase().assertCountEqual(extracted_licensing_info.cross_references, ["http://people.freebsd.org/~phk/", "http://another.cross.reference/"]) @@ -45,20 +45,22 @@ def test_parse_extracted_licensing_info(): def test_parse_invalid_extracted_licensing_info(): parser = Parser() - extracted_licensing_info_str = '\n'.join([ - 'ExtractedText: "THE BEER-WARE LICENSE" (Revision 42): phk@FreeBSD.ORG wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp', - 'LicenseName: Beer-Ware License (Version 42)', - 'LicenseCrossReference: http://people.freebsd.org/~phk/', - 'LicenseComment: The beerware license has a couple of other standard variants.']) + extracted_licensing_info_str = "\n".join([ + "ExtractedText: \"THE BEER-WARE LICENSE\" (Revision 42): phk@FreeBSD.ORG wrote this file. " + "As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you " + "think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp", + "LicenseName: Beer-Ware License (Version 42)", + "LicenseCrossReference: http://people.freebsd.org/~phk/", + "LicenseComment: The beerware license has a couple of other standard variants."]) with pytest.raises(SPDXParsingError) as err: parser.parse(extracted_licensing_info_str) - assert err.value.get_messages() == ['Element ExtractedLicensingInfo is not the current element in scope, probably ' - 'the expected tag to start the element (LicenseID) is missing. Line: 1', - 'Element ExtractedLicensingInfo is not the current element in scope, probably ' - 'the expected tag to start the element (LicenseID) is missing. Line: 2', - 'Element ExtractedLicensingInfo is not the current element in scope, probably ' - 'the expected tag to start the element (LicenseID) is missing. Line: 3', - 'Element ExtractedLicensingInfo is not the current element in scope, probably ' - 'the expected tag to start the element (LicenseID) is missing. Line: 4'] + assert err.value.get_messages() == ["Element ExtractedLicensingInfo is not the current element in scope, probably " + "the expected tag to start the element (LicenseID) is missing. Line: 1", + "Element ExtractedLicensingInfo is not the current element in scope, probably " + "the expected tag to start the element (LicenseID) is missing. Line: 2", + "Element ExtractedLicensingInfo is not the current element in scope, probably " + "the expected tag to start the element (LicenseID) is missing. Line: 3", + "Element ExtractedLicensingInfo is not the current element in scope, probably " + "the expected tag to start the element (LicenseID) is missing. Line: 4"] diff --git a/tests/spdx/parser/tagvalue/test_file_parser.py b/tests/spdx/parser/tagvalue/test_file_parser.py index c6190850a..fecc8d77b 100644 --- a/tests/spdx/parser/tagvalue/test_file_parser.py +++ b/tests/spdx/parser/tagvalue/test_file_parser.py @@ -13,51 +13,51 @@ from spdx.model.file import FileType from spdx.parser.error import SPDXParsingError -from spdx.parser.tagvalue.parser.tagvalue import Parser +from spdx.parser.tagvalue.parser import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR def test_parse_file(): parser = Parser() - file_str = '\n'.join([ - 'FileName: testfile.java', - 'SPDXID: SPDXRef-File', - 'FileType: SOURCE', - 'FileType: TEXT', - 'FileChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', - 'LicenseConcluded: Apache-2.0', - 'LicenseInfoInFile: Apache-2.0', - 'FileCopyrightText: Copyright 2014 Acme Inc.', - 'FileComment: Very long file', - 'FileAttributionText: Acknowledgements that might be required to be communicated in some contexts.' + file_str = "\n".join([ + "FileName: testfile.java", + "SPDXID: SPDXRef-File", + "FileType: SOURCE", + "FileType: TEXT", + "FileChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", + "LicenseConcluded: Apache-2.0", + "LicenseInfoInFile: Apache-2.0", + "FileCopyrightText: Copyright 2014 Acme Inc.", + "FileComment: Very long file", + "FileAttributionText: Acknowledgements that might be required to be communicated in some contexts." ]) document = parser.parse("\n".join([DOCUMENT_STR, file_str])) assert document is not None assert len(document.files) == 1 spdx_file = document.files[0] - assert spdx_file.name == 'testfile.java' - assert spdx_file.spdx_id == 'SPDXRef-File' + assert spdx_file.name == "testfile.java" + assert spdx_file.spdx_id == "SPDXRef-File" assert spdx_file.file_type == [FileType.SOURCE, FileType.TEXT] - assert spdx_file.comment == 'Very long file' + assert spdx_file.comment == "Very long file" assert spdx_file.attribution_texts == [ - 'Acknowledgements that might be required to be communicated in some contexts.'] + "Acknowledgements that might be required to be communicated in some contexts."] assert spdx_file.license_info_in_file == [get_spdx_licensing().parse("Apache-2.0")] assert spdx_file.license_concluded == get_spdx_licensing().parse("Apache-2.0") def test_parse_invalid_file(): parser = Parser() - file_str = '\n'.join([ - 'FileName: testfile.java', - 'SPDXID: SPDXRef-File', - 'FileType: SOUCE', - 'FileType: TEXT', - 'FileChecksum: SHA3: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', - 'LicenseConcluded: Apache-2.0', - 'LicenseInfoInFile: Apache-2.0', - 'FileCopyrightText: Copyright 2014 Acme Inc.', - 'FileComment: Very long file', - 'FileAttributionText: Acknowledgements that might be required to be communicated in some contexts.' + file_str = "\n".join([ + "FileName: testfile.java", + "SPDXID: SPDXRef-File", + "FileType: SOUCE", + "FileType: TEXT", + "FileChecksum: SHA3: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", + "LicenseConcluded: Apache-2.0", + "LicenseInfoInFile: Apache-2.0", + "FileCopyrightText: Copyright 2014 Acme Inc.", + "FileComment: Very long file", + "FileAttributionText: Acknowledgements that might be required to be communicated in some contexts." ]) with pytest.raises(SPDXParsingError) as err: diff --git a/tests/spdx/parser/tagvalue/test_helper_methods.py b/tests/spdx/parser/tagvalue/test_helper_methods.py index d38952502..75e7f0742 100644 --- a/tests/spdx/parser/tagvalue/test_helper_methods.py +++ b/tests/spdx/parser/tagvalue/test_helper_methods.py @@ -11,7 +11,7 @@ import pytest from spdx.model.checksum import ChecksumAlgorithm -from spdx.parser.tagvalue.parser.helper_methods import parse_checksum +from spdx.parser.tagvalue.helper_methods import parse_checksum @pytest.mark.parametrize("checksum_str, algorithm, value", diff --git a/tests/spdx/parser/tagvalue/test_package_parser.py b/tests/spdx/parser/tagvalue/test_package_parser.py index 02e9dea2a..84b61f0d0 100644 --- a/tests/spdx/parser/tagvalue/test_package_parser.py +++ b/tests/spdx/parser/tagvalue/test_package_parser.py @@ -16,53 +16,53 @@ from spdx.model.package import ExternalPackageRef, ExternalPackageRefCategory, PackagePurpose from spdx.parser.error import SPDXParsingError -from spdx.parser.tagvalue.parser.tagvalue import Parser +from spdx.parser.tagvalue.parser import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR def test_parse_package(): parser = Parser() - package_str = '\n'.join([ - 'PackageName: Test', - 'SPDXID: SPDXRef-Package', - 'PackageVersion: 1:22.36.1-8+deb11u1', - 'PackageDownloadLocation: http://example.com/test', - 'FilesAnalyzed: True', - 'PackageSummary: Test package', - 'PackageSourceInfo: Version 1.0 of test', - 'PackageFileName: test-1.0.zip', - 'PackageSupplier: Organization:ACME', - 'PackageOriginator: Organization:ACME', - 'PackageChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', - 'PackageVerificationCode: 4e3211c67a2d28fced849ee1bb76e7391b93feba (something.rdf, something.txt)', - 'PackageDescription: A package.', - 'PackageComment: Comment on the package.', - 'PackageCopyrightText: Copyright 2014 Acme Inc.', - 'PackageLicenseDeclared: Apache-2.0', - 'PackageLicenseConcluded: (LicenseRef-2.0 and Apache-2.0)', - 'PackageLicenseInfoFromFiles: Apache-1.0', - 'PackageLicenseInfoFromFiles: Apache-2.0', - 'PackageLicenseComments: License Comments', - 'ExternalRef: SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:', - 'ExternalRefComment: Some comment about the package.', - 'ExternalRef: OTHER LocationRef-acmeforge acmecorp/acmenator/4.1.3-alpha', - 'PrimaryPackagePurpose: OPERATING-SYSTEM', - 'BuiltDate: 2020-01-01T12:00:00Z', - 'ReleaseDate: 2021-01-01T12:00:00Z', - 'ValidUntilDate: 2022-01-01T12:00:00Z' + package_str = "\n".join([ + "PackageName: Test", + "SPDXID: SPDXRef-Package", + "PackageVersion: 1:22.36.1-8+deb11u1", + "PackageDownloadLocation: http://example.com/test", + "FilesAnalyzed: True", + "PackageSummary: Test package", + "PackageSourceInfo: Version 1.0 of test", + "PackageFileName: test-1.0.zip", + "PackageSupplier: Organization:ACME", + "PackageOriginator: Organization:ACME", + "PackageChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", + "PackageVerificationCode: 4e3211c67a2d28fced849ee1bb76e7391b93feba (something.rdf, something.txt)", + "PackageDescription: A package.", + "PackageComment: Comment on the package.", + "PackageCopyrightText: Copyright 2014 Acme Inc.", + "PackageLicenseDeclared: Apache-2.0", + "PackageLicenseConcluded: (LicenseRef-2.0 and Apache-2.0)", + "PackageLicenseInfoFromFiles: Apache-1.0", + "PackageLicenseInfoFromFiles: Apache-2.0", + "PackageLicenseComments: License Comments", + "ExternalRef: SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:", + "ExternalRefComment: Some comment about the package.", + "ExternalRef: OTHER LocationRef-acmeforge acmecorp/acmenator/4.1.3-alpha", + "PrimaryPackagePurpose: OPERATING-SYSTEM", + "BuiltDate: 2020-01-01T12:00:00Z", + "ReleaseDate: 2021-01-01T12:00:00Z", + "ValidUntilDate: 2022-01-01T12:00:00Z" ]) document = parser.parse("\n".join([DOCUMENT_STR, package_str])) assert document is not None package = document.packages[0] - assert package.name == 'Test' - assert package.spdx_id == 'SPDXRef-Package' - assert package.version == '1:22.36.1-8+deb11u1' + assert package.name == "Test" + assert package.spdx_id == "SPDXRef-Package" + assert package.version == "1:22.36.1-8+deb11u1" assert len(package.license_info_from_files) == 2 TestCase().assertCountEqual(package.license_info_from_files, [get_spdx_licensing().parse("Apache-1.0"), get_spdx_licensing().parse("Apache-2.0")]) - assert package.license_concluded == get_spdx_licensing().parse('LicenseRef-2.0 AND Apache-2.0') + assert package.license_concluded == get_spdx_licensing().parse("LicenseRef-2.0 AND Apache-2.0") assert package.files_analyzed is True - assert package.comment == 'Comment on the package.' + assert package.comment == "Comment on the package." assert len(package.external_references) == 2 TestCase().assertCountEqual(package.external_references, [ExternalPackageRef(ExternalPackageRefCategory.SECURITY, "cpe23Type", @@ -77,26 +77,26 @@ def test_parse_package(): @pytest.mark.parametrize("package_str, expected_message", - [('PackageDownloadLocation: SPDXRef-Package', - 'Element Package is not the current element in scope, probably the expected ' - 'tag to start the element (PackageName) is missing. Line: 1'), - ('PackageName: TestPackage', + [("PackageDownloadLocation: SPDXRef-Package", + "Element Package is not the current element in scope, probably the expected " + "tag to start the element (PackageName) is missing. Line: 1"), + ("PackageName: TestPackage", r"__init__() missing 2 required positional arguments: 'spdx_id' and 'download_location'"), - ('PackageName: TestPackage\nPackageCopyrightText:This is a copyright\n' - 'PackageCopyrightText:MultipleCopyright', + ("PackageName: TestPackage\nPackageCopyrightText:This is a copyright\n" + "PackageCopyrightText:MultipleCopyright", "Error while parsing Package: ['Multiple values for PackageCopyrightText " "found. Line: 3']"), - ('PackageName: TestPackage\nExternalRef: reference locator', - 'Error while parsing Package: ["Couldn\'t split PackageExternalRef in ' - 'category, reference_type and locator. Line: 2"]'), - ('PackageName: TestPackage\nExternalRef: category reference locator', + ("PackageName: TestPackage\nExternalRef: reference locator", + ('Error while parsing Package: ["Couldn\'t split PackageExternalRef in category, ' + 'reference_type and locator. Line: 2"]')), + ("PackageName: TestPackage\nExternalRef: category reference locator", "Error while parsing Package: ['Invalid ExternalPackageRefCategory: " "category. Line: 2']"), - ('SPDXID:SPDXRef-DOCUMENT\nPackageName: TestPackage\nSPDXID:SPDXRef-Package\n' - 'PackageDownloadLocation: download.com\nPackageVerificationCode: category reference locator', + ("SPDXID:SPDXRef-DOCUMENT\nPackageName: TestPackage\nSPDXID:SPDXRef-Package\n" + "PackageDownloadLocation: download.com\nPackageVerificationCode: category reference locator", "Error while parsing Package: ['Error while parsing PackageVerificationCode: " "Value did not match expected format. Line: 5']"), - ('PackageName: TestPackage\nBuiltDate: 2012\nValidUntilDate:202-11-02T00:00', + ("PackageName: TestPackage\nBuiltDate: 2012\nValidUntilDate:202-11-02T00:00", "Error while parsing Package: ['Error while parsing BuiltDate: Token did not " "match specified grammar rule. Line: 2', 'Error while parsing " "ValidUntilDate: Token did not match specified grammar rule. Line: 3']") diff --git a/tests/spdx/parser/tagvalue/test_relationship_parser.py b/tests/spdx/parser/tagvalue/test_relationship_parser.py index adc9a2ecb..18a6ee3b8 100644 --- a/tests/spdx/parser/tagvalue/test_relationship_parser.py +++ b/tests/spdx/parser/tagvalue/test_relationship_parser.py @@ -14,21 +14,21 @@ from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone from spdx.parser.error import SPDXParsingError -from spdx.parser.tagvalue.parser.tagvalue import Parser +from spdx.parser.tagvalue.parser import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR @pytest.mark.parametrize("relationship_str, expected_relationship", - [('\n'.join(['Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File', - 'RelationshipComment: This is a comment.']), + [("\n".join(["Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File", + "RelationshipComment: This is a comment."]), Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-File", "This is a comment.")), - ('Relationship: SPDXRef-DOCUMENT PATCH_FOR NOASSERTION', + ("Relationship: SPDXRef-DOCUMENT PATCH_FOR NOASSERTION", Relationship("SPDXRef-DOCUMENT", RelationshipType.PATCH_FOR, SpdxNoAssertion())), - ('Relationship: SPDXRef-CarolCompression DEPENDS_ON NONE', + ("Relationship: SPDXRef-CarolCompression DEPENDS_ON NONE", Relationship("SPDXRef-CarolCompression", RelationshipType.DEPENDS_ON, SpdxNone())), - ('Relationship: DocumentRef-ExternalDocument: SPDXRef-Test DEPENDS_ON DocumentRef:AnotherRef', + ("Relationship: DocumentRef-ExternalDocument: SPDXRef-Test DEPENDS_ON DocumentRef:AnotherRef", Relationship("DocumentRef-ExternalDocument:SPDXRef-Test", RelationshipType.DEPENDS_ON, "DocumentRef:AnotherRef")) ]) @@ -43,8 +43,8 @@ def test_parse_relationship(relationship_str, expected_relationship): @pytest.mark.parametrize("relationship_str, expected_message", [("Relationship: spdx_id DESCRIBES", - ['Error while parsing Relationship: ["Relationship couldn\'t be split in spdx_element_id, ' - 'relationship_type and related_spdx_element. Line: 1"]']), + ['Error while parsing Relationship: ["Relationship couldn\'t be split in ' + 'spdx_element_id, relationship_type and related_spdx_element. Line: 1"]']), ("Relationship: spdx_id IS spdx_id", ["Error while parsing Relationship: ['Invalid RelationshipType IS. Line: 1']"])]) def test_parse_invalid_relationship(relationship_str, expected_message): diff --git a/tests/spdx/parser/tagvalue/test_snippet_parser.py b/tests/spdx/parser/tagvalue/test_snippet_parser.py index 7e76bd815..5ce2b0f74 100644 --- a/tests/spdx/parser/tagvalue/test_snippet_parser.py +++ b/tests/spdx/parser/tagvalue/test_snippet_parser.py @@ -14,39 +14,39 @@ from license_expression import get_spdx_licensing from spdx.parser.error import SPDXParsingError -from spdx.parser.tagvalue.parser.tagvalue import Parser +from spdx.parser.tagvalue.parser import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR def test_parse_snippet(): parser = Parser() - snippet_str = '\n'.join([ - 'SnippetSPDXID: SPDXRef-Snippet', - 'SnippetLicenseComments: Some lic comment.', - 'SnippetCopyrightText: Copyright 2008-2010 John Smith ', - 'SnippetComment: Some snippet comment.', - 'SnippetName: from linux kernel', - 'SnippetFromFileSPDXID: SPDXRef-DoapSource', - 'SnippetLicenseConcluded: Apache-2.0', - 'LicenseInfoInSnippet: Apache-2.0', - 'SnippetByteRange: 310:420', - 'SnippetLineRange: 5:23', - 'SnippetAttributionText: This is a text\nthat spans multiple lines.', - 'SnippetAttributionText: This text spans one line but has trailing and leading whitespaces. ' + snippet_str = "\n".join([ + "SnippetSPDXID: SPDXRef-Snippet", + "SnippetLicenseComments: Some lic comment.", + "SnippetCopyrightText: Copyright 2008-2010 John Smith ", + "SnippetComment: Some snippet comment.", + "SnippetName: from linux kernel", + "SnippetFromFileSPDXID: SPDXRef-DoapSource", + "SnippetLicenseConcluded: Apache-2.0", + "LicenseInfoInSnippet: Apache-2.0", + "SnippetByteRange: 310:420", + "SnippetLineRange: 5:23", + "SnippetAttributionText: This is a text\nthat spans multiple lines.", + "SnippetAttributionText: This text spans one line but has trailing and leading whitespaces. " ]) document = parser.parse("\n".join([DOCUMENT_STR, snippet_str])) assert document is not None assert len(document.snippets) == 1 snippet = document.snippets[0] - assert snippet.spdx_id == 'SPDXRef-Snippet' - assert snippet.name == 'from linux kernel' - assert snippet.comment == 'Some snippet comment.' - assert snippet.copyright_text == ' Copyright 2008-2010 John Smith ' - assert snippet.license_comment == 'Some lic comment.' - assert snippet.file_spdx_id == 'SPDXRef-DoapSource' - assert snippet.license_concluded == get_spdx_licensing().parse('Apache-2.0') - assert snippet.license_info_in_snippet == [get_spdx_licensing().parse('Apache-2.0')] + assert snippet.spdx_id == "SPDXRef-Snippet" + assert snippet.name == "from linux kernel" + assert snippet.comment == "Some snippet comment." + assert snippet.copyright_text == " Copyright 2008-2010 John Smith " + assert snippet.license_comment == "Some lic comment." + assert snippet.file_spdx_id == "SPDXRef-DoapSource" + assert snippet.license_concluded == get_spdx_licensing().parse("Apache-2.0") + assert snippet.license_info_in_snippet == [get_spdx_licensing().parse("Apache-2.0")] assert snippet.byte_range[0] == 310 assert snippet.byte_range[1] == 420 assert snippet.line_range[0] == 5 @@ -57,15 +57,15 @@ def test_parse_snippet(): @pytest.mark.parametrize("snippet_str, expected_message", [ - ('SnippetName: TestSnippet', 'Element Snippet is not the current element in scope, probably the expected ' - 'tag to start the element (SnippetSPDXID) is missing. Line: 1'), - ('SnippetSPDXID: SPDXDRef-Snippet\nSnippetByteRange: 1,4', + ("SnippetName: TestSnippet", "Element Snippet is not the current element in scope, probably the expected " + "tag to start the element (SnippetSPDXID) is missing. Line: 1"), + ("SnippetSPDXID: SPDXDRef-Snippet\nSnippetByteRange: 1,4", 'Error while parsing Snippet: ["Value for SnippetByteRange doesn\'t match ' 'valid range pattern. Line: 2"]'), - ('SnippetSPDXID: SPDXDRef-Snippet\nSnippetByteRange: 1:4\nSnippetByteRange:10:23', + ("SnippetSPDXID: SPDXDRef-Snippet\nSnippetByteRange: 1:4\nSnippetByteRange:10:23", "Error while parsing Snippet: ['Multiple values for SnippetByteRange found. " "Line: 3']"), - ('SnippetSPDXID: SPDXRef-Snippet', r"__init__() missing 2 required " + ("SnippetSPDXID: SPDXRef-Snippet", r"__init__() missing 2 required " r"positional arguments: 'file_spdx_id' and 'byte_range'") ]) def test_parse_invalid_snippet(snippet_str, expected_message): diff --git a/tests/spdx/parser/tagvalue/test_tag_value_lexer.py b/tests/spdx/parser/tagvalue/test_tag_value_lexer.py index bd82fab3b..4d4b2cdb9 100644 --- a/tests/spdx/parser/tagvalue/test_tag_value_lexer.py +++ b/tests/spdx/parser/tagvalue/test_tag_value_lexer.py @@ -9,11 +9,9 @@ # 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 spdx.parser.tagvalue.lexer.tagvalue import SPDXLexer +from spdx.parser.tagvalue.lexer import SPDXLexer @pytest.fixture @@ -30,259 +28,259 @@ def token_assert_helper(token, token_type, value, line_number): def test_tokenization_of_document(lexer): - document_str = '\n'.join([ - 'SPDXVersion: SPDX-2.1', - 'DataLicense: CC0-1.0', - 'DocumentName: Sample_Document-V2.1', - 'SPDXID: SPDXRef-DOCUMENT', - 'DocumentComment: Sample Comment', - 'DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301' + document_str = "\n".join([ + "SPDXVersion: SPDX-2.1", + "DataLicense: CC0-1.0", + "DocumentName: Sample_Document-V2.1", + "SPDXID: SPDXRef-DOCUMENT", + "DocumentComment: Sample Comment", + "DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301" ]) lexer.input(document_str) - token_assert_helper(lexer.token(), 'DOC_VERSION', 'SPDXVersion', 1) - token_assert_helper(lexer.token(), 'LINE', 'SPDX-2.1', 1) - token_assert_helper(lexer.token(), 'DOC_LICENSE', 'DataLicense', 2) - token_assert_helper(lexer.token(), 'LINE', 'CC0-1.0', 2) - token_assert_helper(lexer.token(), 'DOC_NAME', 'DocumentName', 3) - token_assert_helper(lexer.token(), 'LINE', 'Sample_Document-V2.1', 3) - token_assert_helper(lexer.token(), 'SPDX_ID', 'SPDXID', 4) - token_assert_helper(lexer.token(), 'LINE', 'SPDXRef-DOCUMENT', 4) - token_assert_helper(lexer.token(), 'DOC_COMMENT', 'DocumentComment', 5) - token_assert_helper(lexer.token(), 'TEXT', 'Sample Comment', 5) - token_assert_helper(lexer.token(), 'DOC_NAMESPACE', 'DocumentNamespace', 6) - token_assert_helper(lexer.token(), 'LINE', - 'https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301', 6) + token_assert_helper(lexer.token(), "DOC_VERSION", "SPDXVersion", 1) + token_assert_helper(lexer.token(), "LINE", "SPDX-2.1", 1) + token_assert_helper(lexer.token(), "DOC_LICENSE", "DataLicense", 2) + token_assert_helper(lexer.token(), "LINE", "CC0-1.0", 2) + token_assert_helper(lexer.token(), "DOC_NAME", "DocumentName", 3) + token_assert_helper(lexer.token(), "LINE", "Sample_Document-V2.1", 3) + token_assert_helper(lexer.token(), "SPDX_ID", "SPDXID", 4) + token_assert_helper(lexer.token(), "LINE", "SPDXRef-DOCUMENT", 4) + token_assert_helper(lexer.token(), "DOC_COMMENT", "DocumentComment", 5) + token_assert_helper(lexer.token(), "TEXT", "Sample Comment", 5) + token_assert_helper(lexer.token(), "DOC_NAMESPACE", "DocumentNamespace", 6) + token_assert_helper(lexer.token(), "LINE", + "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301", 6) def test_tokenization_of_external_document_references(lexer): - data = ''' + data = """ ExternalDocumentRef:DocumentRef-spdx-tool-2.1 http://spdx.org/spdxdocs/spdx-tools-v2.1-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759 - ''' + """ lexer.input(data) - token_assert_helper(lexer.token(), 'EXT_DOC_REF', 'ExternalDocumentRef', 2) - token_assert_helper(lexer.token(), 'DOC_REF_ID', 'DocumentRef-spdx-tool-2.1', 2) - token_assert_helper(lexer.token(), 'DOC_URI', 'http://spdx.org/spdxdocs/spdx-tools-v2.1-3F25' - '04E0-4F89-41D3-9A0C-0305E82C3301', 2) - token_assert_helper(lexer.token(), 'EXT_DOC_REF_CHECKSUM', 'SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759', 2) + token_assert_helper(lexer.token(), "EXT_DOC_REF", "ExternalDocumentRef", 2) + token_assert_helper(lexer.token(), "EXT_DOC_REF_ID", "DocumentRef-spdx-tool-2.1", 2) + token_assert_helper(lexer.token(), "EXT_DOC_URI", "http://spdx.org/spdxdocs/spdx-tools-v2.1-3F25" + "04E0-4F89-41D3-9A0C-0305E82C3301", 2) + token_assert_helper(lexer.token(), "EXT_DOC_REF_CHECKSUM", "SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759", 2) def test_tokenization_of_file(lexer): - file_str = '\n'.join([ - 'FileName: testfile.java', - 'SPDXID: SPDXRef-File', - 'FileType: SOURCE', - 'FileType: TEXT', - 'FileChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', - 'LicenseConcluded: Apache-2.0', - 'LicenseInfoInFile: Apache-2.0', - 'FileCopyrightText: Copyright 2014 Acme Inc.', - 'FileComment: Very long file', - 'FileAttributionText: Acknowledgements that might be required to be communicated in some contexts.' + file_str = "\n".join([ + "FileName: testfile.java", + "SPDXID: SPDXRef-File", + "FileType: SOURCE", + "FileType: TEXT", + "FileChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", + "LicenseConcluded: Apache-2.0", + "LicenseInfoInFile: Apache-2.0", + "FileCopyrightText: Copyright 2014 Acme Inc.", + "FileComment: Very long file", + "FileAttributionText: Acknowledgements that might be required to be communicated in some contexts." ]) lexer.input(file_str) - token_assert_helper(lexer.token(), 'FILE_NAME', 'FileName', 1) - token_assert_helper(lexer.token(), 'LINE', 'testfile.java', 1) - token_assert_helper(lexer.token(), 'SPDX_ID', 'SPDXID', 2) - token_assert_helper(lexer.token(), 'LINE', 'SPDXRef-File', 2) - token_assert_helper(lexer.token(), 'FILE_TYPE', 'FileType', 3) - token_assert_helper(lexer.token(), 'LINE', 'SOURCE', 3) - token_assert_helper(lexer.token(), 'FILE_TYPE', 'FileType', 4) - token_assert_helper(lexer.token(), 'LINE', 'TEXT', 4) - token_assert_helper(lexer.token(), 'FILE_CHECKSUM', 'FileChecksum', 5) - token_assert_helper(lexer.token(), 'CHECKSUM', 'SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', 5) - token_assert_helper(lexer.token(), 'FILE_LICS_CONC', 'LicenseConcluded', 6) - token_assert_helper(lexer.token(), 'LINE', 'Apache-2.0', 6) - token_assert_helper(lexer.token(), 'FILE_LICS_INFO', 'LicenseInfoInFile', 7) - token_assert_helper(lexer.token(), 'LINE', 'Apache-2.0', 7) - token_assert_helper(lexer.token(), 'FILE_CR_TEXT', 'FileCopyrightText', 8) - token_assert_helper(lexer.token(), 'TEXT', 'Copyright 2014 Acme Inc.', 8) - token_assert_helper(lexer.token(), 'FILE_COMMENT', 'FileComment', 9) - token_assert_helper(lexer.token(), 'TEXT', 'Very long file', 9) - token_assert_helper(lexer.token(), 'FILE_ATTRIBUTION_TEXT', 'FileAttributionText', 10) - token_assert_helper(lexer.token(), 'TEXT', - 'Acknowledgements that might be required to be communicated in some contexts.', + token_assert_helper(lexer.token(), "FILE_NAME", "FileName", 1) + token_assert_helper(lexer.token(), "LINE", "testfile.java", 1) + token_assert_helper(lexer.token(), "SPDX_ID", "SPDXID", 2) + token_assert_helper(lexer.token(), "LINE", "SPDXRef-File", 2) + token_assert_helper(lexer.token(), "FILE_TYPE", "FileType", 3) + token_assert_helper(lexer.token(), "LINE", "SOURCE", 3) + token_assert_helper(lexer.token(), "FILE_TYPE", "FileType", 4) + token_assert_helper(lexer.token(), "LINE", "TEXT", 4) + token_assert_helper(lexer.token(), "FILE_CHECKSUM", "FileChecksum", 5) + token_assert_helper(lexer.token(), "CHECKSUM", "SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", 5) + token_assert_helper(lexer.token(), "FILE_LICENSE_CONCLUDED", "LicenseConcluded", 6) + token_assert_helper(lexer.token(), "LINE", "Apache-2.0", 6) + token_assert_helper(lexer.token(), "FILE_LICENSE_INFO", "LicenseInfoInFile", 7) + token_assert_helper(lexer.token(), "LINE", "Apache-2.0", 7) + token_assert_helper(lexer.token(), "FILE_COPYRIGHT_TEXT", "FileCopyrightText", 8) + token_assert_helper(lexer.token(), "TEXT", "Copyright 2014 Acme Inc.", 8) + token_assert_helper(lexer.token(), "FILE_COMMENT", "FileComment", 9) + token_assert_helper(lexer.token(), "TEXT", "Very long file", 9) + token_assert_helper(lexer.token(), "FILE_ATTRIBUTION_TEXT", "FileAttributionText", 10) + token_assert_helper(lexer.token(), "TEXT", + "Acknowledgements that might be required to be communicated in some contexts.", 10) def test_tokenization_of_creation_info(lexer): - creation_str = '\n'.join([ - 'Creator: Person: Bob (bob@example.com)', - 'Creator: Organization: Acme.', - 'Created: 2010-02-03T00:00:00Z', - 'CreatorComment: Sample Comment' + creation_str = "\n".join([ + "Creator: Person: Bob (bob@example.com)", + "Creator: Organization: Acme.", + "Created: 2010-02-03T00:00:00Z", + "CreatorComment: Sample Comment" ]) lexer.input(creation_str) - token_assert_helper(lexer.token(), 'CREATOR', 'Creator', 1) - token_assert_helper(lexer.token(), 'PERSON_VALUE', "Person: Bob (bob@example.com)", 1) - token_assert_helper(lexer.token(), 'CREATOR', 'Creator', 2) - token_assert_helper(lexer.token(), 'ORG_VALUE', 'Organization: Acme.', 2) - token_assert_helper(lexer.token(), 'CREATED', 'Created', 3) - token_assert_helper(lexer.token(), 'DATE', '2010-02-03T00:00:00Z', 3) - token_assert_helper(lexer.token(), 'CREATOR_COMMENT', 'CreatorComment', 4) - token_assert_helper(lexer.token(), 'TEXT', 'Sample Comment', 4) + token_assert_helper(lexer.token(), "CREATOR", "Creator", 1) + token_assert_helper(lexer.token(), "PERSON_VALUE", "Person: Bob (bob@example.com)", 1) + token_assert_helper(lexer.token(), "CREATOR", "Creator", 2) + token_assert_helper(lexer.token(), "ORGANIZATION_VALUE", "Organization: Acme.", 2) + token_assert_helper(lexer.token(), "CREATED", "Created", 3) + token_assert_helper(lexer.token(), "DATE", "2010-02-03T00:00:00Z", 3) + token_assert_helper(lexer.token(), "CREATOR_COMMENT", "CreatorComment", 4) + token_assert_helper(lexer.token(), "TEXT", "Sample Comment", 4) def test_tokenization_of_package(lexer): - package_str = '\n'.join([ - 'PackageName: Test', - 'SPDXID: SPDXRef-Package', - 'PackageVersion: Version 0.9.2', - 'PackageDownloadLocation: http://example.com/test', - 'FilesAnalyzed: True', - 'PackageSummary: Test package', - 'PackageSourceInfo: Version 1.0 of test', - 'PackageFileName: test-1.0.zip', - 'PackageSupplier: Organization:ACME', - 'PackageOriginator: Organization:ACME', - 'PackageChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', - 'PackageVerificationCode: 4e3211c67a2d28fced849ee1bb76e7391b93feba (something.rdf, something.txt)', - 'PackageDescription: A package.', - 'PackageComment: Comment on the package.', - 'PackageCopyrightText: Copyright 2014 Acme Inc.', - 'PackageLicenseDeclared: Apache-2.0', - 'PackageLicenseConcluded: (LicenseRef-2.0 and Apache-2.0)', - 'PackageLicenseInfoFromFiles: Apache-1.0', - 'PackageLicenseInfoFromFiles: Apache-2.0', - 'PackageLicenseComments: License Comments', - 'ExternalRef: SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:', - 'ExternalRefComment: Some comment about the package.', - 'PrimaryPackagePurpose: OPERATING-SYSTEM', - 'BuiltDate: 2020-01-01T12:00:00Z', - 'ReleaseDate: 2021-01-01T12:00:00Z', - 'ValidUntilDate: 2022-01-01T12:00:00Z' + package_str = "\n".join([ + "PackageName: Test", + "SPDXID: SPDXRef-Package", + "PackageVersion: Version 0.9.2", + "PackageDownloadLocation: http://example.com/test", + "FilesAnalyzed: True", + "PackageSummary: Test package", + "PackageSourceInfo: Version 1.0 of test", + "PackageFileName: test-1.0.zip", + "PackageSupplier: Organization:ACME", + "PackageOriginator: Organization:ACME", + "PackageChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", + "PackageVerificationCode: 4e3211c67a2d28fced849ee1bb76e7391b93feba (something.rdf, something.txt)", + "PackageDescription: A package.", + "PackageComment: Comment on the package.", + "PackageCopyrightText: Copyright 2014 Acme Inc.", + "PackageLicenseDeclared: Apache-2.0", + "PackageLicenseConcluded: (LicenseRef-2.0 and Apache-2.0)", + "PackageLicenseInfoFromFiles: Apache-1.0", + "PackageLicenseInfoFromFiles: Apache-2.0", + "PackageLicenseComments: License Comments", + "ExternalRef: SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:", + "ExternalRefComment: Some comment about the package.", + "PrimaryPackagePurpose: OPERATING-SYSTEM", + "BuiltDate: 2020-01-01T12:00:00Z", + "ReleaseDate: 2021-01-01T12:00:00Z", + "ValidUntilDate: 2022-01-01T12:00:00Z" ]) lexer.input(package_str) - token_assert_helper(lexer.token(), 'PKG_NAME', 'PackageName', 1) - token_assert_helper(lexer.token(), 'LINE', 'Test', 1) - token_assert_helper(lexer.token(), 'SPDX_ID', 'SPDXID', 2) - token_assert_helper(lexer.token(), 'LINE', 'SPDXRef-Package', 2) - token_assert_helper(lexer.token(), 'PKG_VERSION', 'PackageVersion', 3) - token_assert_helper(lexer.token(), 'LINE', 'Version 0.9.2', 3) - token_assert_helper(lexer.token(), 'PKG_DOWN', 'PackageDownloadLocation', 4) - token_assert_helper(lexer.token(), 'LINE', 'http://example.com/test', 4) - token_assert_helper(lexer.token(), 'PKG_FILES_ANALYZED', 'FilesAnalyzed', 5) - token_assert_helper(lexer.token(), 'LINE', 'True', 5) - token_assert_helper(lexer.token(), 'PKG_SUM', 'PackageSummary', 6) - token_assert_helper(lexer.token(), 'TEXT', 'Test package', 6) - token_assert_helper(lexer.token(), 'PKG_SRC_INFO', 'PackageSourceInfo', 7) - token_assert_helper(lexer.token(), 'TEXT', 'Version 1.0 of test', 7) - token_assert_helper(lexer.token(), 'PKG_FILE_NAME', 'PackageFileName', 8) - token_assert_helper(lexer.token(), 'LINE', 'test-1.0.zip', 8) - token_assert_helper(lexer.token(), 'PKG_SUPPL', 'PackageSupplier', 9) - token_assert_helper(lexer.token(), 'ORG_VALUE', 'Organization:ACME', 9) - token_assert_helper(lexer.token(), 'PKG_ORIG', 'PackageOriginator', 10) - token_assert_helper(lexer.token(), 'ORG_VALUE', 'Organization:ACME', 10) - token_assert_helper(lexer.token(), 'PKG_CHECKSUM', 'PackageChecksum', 11) - token_assert_helper(lexer.token(), 'CHECKSUM', 'SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', 11) - token_assert_helper(lexer.token(), 'PKG_VERF_CODE', 'PackageVerificationCode', 12) - token_assert_helper(lexer.token(), 'LINE', - '4e3211c67a2d28fced849ee1bb76e7391b93feba (something.rdf, something.txt)', 12) - token_assert_helper(lexer.token(), 'PKG_DESC', 'PackageDescription', 13) - token_assert_helper(lexer.token(), 'TEXT', 'A package.', 13) - token_assert_helper(lexer.token(), 'PKG_COMMENT', 'PackageComment', 14) - token_assert_helper(lexer.token(), 'TEXT', 'Comment on the package.', 14) - token_assert_helper(lexer.token(), 'PKG_CPY_TEXT', 'PackageCopyrightText', 15) - token_assert_helper(lexer.token(), 'TEXT', ' Copyright 2014 Acme Inc.', 15) - token_assert_helper(lexer.token(), 'PKG_LICS_DECL', 'PackageLicenseDeclared', 16) - token_assert_helper(lexer.token(), 'LINE', 'Apache-2.0', 16) - token_assert_helper(lexer.token(), 'PKG_LICS_CONC', 'PackageLicenseConcluded', 17) - token_assert_helper(lexer.token(), 'LINE', '(LicenseRef-2.0 and Apache-2.0)', 17) - token_assert_helper(lexer.token(), 'PKG_LICS_FFILE', 'PackageLicenseInfoFromFiles', 18) - token_assert_helper(lexer.token(), 'LINE', 'Apache-1.0', 18) - token_assert_helper(lexer.token(), 'PKG_LICS_FFILE', 'PackageLicenseInfoFromFiles', 19) - token_assert_helper(lexer.token(), 'LINE', 'Apache-2.0', 19) - token_assert_helper(lexer.token(), 'PKG_LICS_COMMENT', 'PackageLicenseComments', 20) - token_assert_helper(lexer.token(), 'TEXT', 'License Comments', 20) - token_assert_helper(lexer.token(), 'PKG_EXT_REF', 'ExternalRef', 21) - token_assert_helper(lexer.token(), 'LINE', - 'SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:', 21) - token_assert_helper(lexer.token(), 'PKG_EXT_REF_COMMENT', 'ExternalRefComment', 22) - token_assert_helper(lexer.token(), 'TEXT', 'Some comment about the package.', 22) - token_assert_helper(lexer.token(), 'PRIMARY_PACKAGE_PURPOSE', 'PrimaryPackagePurpose', 23) - token_assert_helper(lexer.token(), 'LINE', 'OPERATING-SYSTEM', 23) - token_assert_helper(lexer.token(), 'BUILT_DATE', 'BuiltDate', 24) - token_assert_helper(lexer.token(), 'DATE', '2020-01-01T12:00:00Z', 24) - token_assert_helper(lexer.token(), 'RELEASE_DATE', 'ReleaseDate', 25) - token_assert_helper(lexer.token(), 'DATE', '2021-01-01T12:00:00Z', 25) - token_assert_helper(lexer.token(), 'VALID_UNTIL_DATE', 'ValidUntilDate', 26) - token_assert_helper(lexer.token(), 'DATE', '2022-01-01T12:00:00Z', 26) + token_assert_helper(lexer.token(), "PKG_NAME", "PackageName", 1) + token_assert_helper(lexer.token(), "LINE", "Test", 1) + token_assert_helper(lexer.token(), "SPDX_ID", "SPDXID", 2) + token_assert_helper(lexer.token(), "LINE", "SPDXRef-Package", 2) + token_assert_helper(lexer.token(), "PKG_VERSION", "PackageVersion", 3) + token_assert_helper(lexer.token(), "LINE", "Version 0.9.2", 3) + token_assert_helper(lexer.token(), "PKG_DOWWNLOAD_LOCATION", "PackageDownloadLocation", 4) + token_assert_helper(lexer.token(), "LINE", "http://example.com/test", 4) + token_assert_helper(lexer.token(), "PKG_FILES_ANALYZED", "FilesAnalyzed", 5) + token_assert_helper(lexer.token(), "LINE", "True", 5) + token_assert_helper(lexer.token(), "PKG_SUMMARY", "PackageSummary", 6) + token_assert_helper(lexer.token(), "TEXT", "Test package", 6) + token_assert_helper(lexer.token(), "PKG_SOURCE_INFO", "PackageSourceInfo", 7) + token_assert_helper(lexer.token(), "TEXT", "Version 1.0 of test", 7) + token_assert_helper(lexer.token(), "PKG_FILE_NAME", "PackageFileName", 8) + token_assert_helper(lexer.token(), "LINE", "test-1.0.zip", 8) + token_assert_helper(lexer.token(), "PKG_SUPPLIER", "PackageSupplier", 9) + token_assert_helper(lexer.token(), "ORGANIZATION_VALUE", "Organization:ACME", 9) + token_assert_helper(lexer.token(), "PKG_ORIGINATOR", "PackageOriginator", 10) + token_assert_helper(lexer.token(), "ORGANIZATION_VALUE", "Organization:ACME", 10) + token_assert_helper(lexer.token(), "PKG_CHECKSUM", "PackageChecksum", 11) + token_assert_helper(lexer.token(), "CHECKSUM", "SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", 11) + token_assert_helper(lexer.token(), "PKG_VERIFICATION_CODE", "PackageVerificationCode", 12) + token_assert_helper(lexer.token(), "LINE", + "4e3211c67a2d28fced849ee1bb76e7391b93feba (something.rdf, something.txt)", 12) + token_assert_helper(lexer.token(), "PKG_DESCRIPTION", "PackageDescription", 13) + token_assert_helper(lexer.token(), "TEXT", "A package.", 13) + token_assert_helper(lexer.token(), "PKG_COMMENT", "PackageComment", 14) + token_assert_helper(lexer.token(), "TEXT", "Comment on the package.", 14) + token_assert_helper(lexer.token(), "PKG_COPYRIGHT_TEXT", "PackageCopyrightText", 15) + token_assert_helper(lexer.token(), "TEXT", " Copyright 2014 Acme Inc.", 15) + token_assert_helper(lexer.token(), "PKG_LICENSE_DECLARED", "PackageLicenseDeclared", 16) + token_assert_helper(lexer.token(), "LINE", "Apache-2.0", 16) + token_assert_helper(lexer.token(), "PKG_LICENSE_CONCLUDED", "PackageLicenseConcluded", 17) + token_assert_helper(lexer.token(), "LINE", "(LicenseRef-2.0 and Apache-2.0)", 17) + token_assert_helper(lexer.token(), "PKG_LICENSE_INFO", "PackageLicenseInfoFromFiles", 18) + token_assert_helper(lexer.token(), "LINE", "Apache-1.0", 18) + token_assert_helper(lexer.token(), "PKG_LICENSE_INFO", "PackageLicenseInfoFromFiles", 19) + token_assert_helper(lexer.token(), "LINE", "Apache-2.0", 19) + token_assert_helper(lexer.token(), "PKG_LICENSE_COMMENT", "PackageLicenseComments", 20) + token_assert_helper(lexer.token(), "TEXT", "License Comments", 20) + token_assert_helper(lexer.token(), "PKG_EXTERNAL_REF", "ExternalRef", 21) + token_assert_helper(lexer.token(), "LINE", + "SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:", 21) + token_assert_helper(lexer.token(), "PKG_EXTERNAL_REF_COMMENT", "ExternalRefComment", 22) + token_assert_helper(lexer.token(), "TEXT", "Some comment about the package.", 22) + token_assert_helper(lexer.token(), "PRIMARY_PACKAGE_PURPOSE", "PrimaryPackagePurpose", 23) + token_assert_helper(lexer.token(), "LINE", "OPERATING-SYSTEM", 23) + token_assert_helper(lexer.token(), "BUILT_DATE", "BuiltDate", 24) + token_assert_helper(lexer.token(), "DATE", "2020-01-01T12:00:00Z", 24) + token_assert_helper(lexer.token(), "RELEASE_DATE", "ReleaseDate", 25) + token_assert_helper(lexer.token(), "DATE", "2021-01-01T12:00:00Z", 25) + token_assert_helper(lexer.token(), "VALID_UNTIL_DATE", "ValidUntilDate", 26) + token_assert_helper(lexer.token(), "DATE", "2022-01-01T12:00:00Z", 26) def test_tokenization_of_unknown_tag(lexer): - unknown_tag_str = 'SomeUnknownTag: SomeUnknownValue' + unknown_tag_str = "SomeUnknownTag: SomeUnknownValue" lexer.input(unknown_tag_str) - token_assert_helper(lexer.token(), 'UNKNOWN_TAG', 'SomeUnknownTag', 1) - token_assert_helper(lexer.token(), 'LINE', 'SomeUnknownValue', 1) + token_assert_helper(lexer.token(), "UNKNOWN_TAG", "SomeUnknownTag", 1) + token_assert_helper(lexer.token(), "LINE", "SomeUnknownValue", 1) def test_tokenization_of_snippet(lexer): - snippet_str = '\n'.join([ - 'SnippetSPDXID: SPDXRef-Snippet', - 'SnippetLicenseComments: Some lic comment.', - 'SnippetCopyrightText: Copyright 2008-2010 John Smith ', - 'SnippetComment: Some snippet comment.', - 'SnippetName: from linux kernel', - 'SnippetFromFileSPDXID: SPDXRef-DoapSource', - 'SnippetLicenseConcluded: Apache-2.0', - 'LicenseInfoInSnippet: Apache-2.0', - 'SnippetByteRange: 310:420', - 'SnippetLineRange: 5:23', + snippet_str = "\n".join([ + "SnippetSPDXID: SPDXRef-Snippet", + "SnippetLicenseComments: Some lic comment.", + "SnippetCopyrightText: Copyright 2008-2010 John Smith ", + "SnippetComment: Some snippet comment.", + "SnippetName: from linux kernel", + "SnippetFromFileSPDXID: SPDXRef-DoapSource", + "SnippetLicenseConcluded: Apache-2.0", + "LicenseInfoInSnippet: Apache-2.0", + "SnippetByteRange: 310:420", + "SnippetLineRange: 5:23", ]) lexer.input(snippet_str) - token_assert_helper(lexer.token(), 'SNIPPET_SPDX_ID', 'SnippetSPDXID', 1) - token_assert_helper(lexer.token(), 'LINE', 'SPDXRef-Snippet', 1) - token_assert_helper(lexer.token(), 'SNIPPET_LICS_COMMENT', 'SnippetLicenseComments', 2) - token_assert_helper(lexer.token(), 'TEXT', 'Some lic comment.', 2) - token_assert_helper(lexer.token(), 'SNIPPET_CR_TEXT', 'SnippetCopyrightText', 3) - token_assert_helper(lexer.token(), 'TEXT', ' Copyright 2008-2010 John Smith ', 3) - token_assert_helper(lexer.token(), 'SNIPPET_COMMENT', 'SnippetComment', 4) - token_assert_helper(lexer.token(), 'TEXT', 'Some snippet comment.', 4) - token_assert_helper(lexer.token(), 'SNIPPET_NAME', 'SnippetName', 5) - token_assert_helper(lexer.token(), 'LINE', 'from linux kernel', 5) - token_assert_helper(lexer.token(), 'SNIPPET_FILE_SPDXID', 'SnippetFromFileSPDXID', 6) - token_assert_helper(lexer.token(), 'LINE', 'SPDXRef-DoapSource', 6) - token_assert_helper(lexer.token(), 'SNIPPET_LICS_CONC', - 'SnippetLicenseConcluded', 7) - token_assert_helper(lexer.token(), 'LINE', 'Apache-2.0', 7) - token_assert_helper(lexer.token(), 'SNIPPET_LICS_INFO', 'LicenseInfoInSnippet', 8) - token_assert_helper(lexer.token(), 'LINE', 'Apache-2.0', 8) - token_assert_helper(lexer.token(), 'SNIPPET_BYTE_RANGE', 'SnippetByteRange', 9) - token_assert_helper(lexer.token(), 'LINE', '310:420', 9) - token_assert_helper(lexer.token(), 'SNIPPET_LINE_RANGE', 'SnippetLineRange', 10) - token_assert_helper(lexer.token(), 'LINE', '5:23', 10) + token_assert_helper(lexer.token(), "SNIPPET_SPDX_ID", "SnippetSPDXID", 1) + token_assert_helper(lexer.token(), "LINE", "SPDXRef-Snippet", 1) + token_assert_helper(lexer.token(), "SNIPPET_LICENSE_COMMENT", "SnippetLicenseComments", 2) + token_assert_helper(lexer.token(), "TEXT", "Some lic comment.", 2) + token_assert_helper(lexer.token(), "SNIPPET_COPYRIGHT_TEXT", "SnippetCopyrightText", 3) + token_assert_helper(lexer.token(), "TEXT", " Copyright 2008-2010 John Smith ", 3) + token_assert_helper(lexer.token(), "SNIPPET_COMMENT", "SnippetComment", 4) + token_assert_helper(lexer.token(), "TEXT", "Some snippet comment.", 4) + token_assert_helper(lexer.token(), "SNIPPET_NAME", "SnippetName", 5) + token_assert_helper(lexer.token(), "LINE", "from linux kernel", 5) + token_assert_helper(lexer.token(), "SNIPPET_FILE_SPDXID", "SnippetFromFileSPDXID", 6) + token_assert_helper(lexer.token(), "LINE", "SPDXRef-DoapSource", 6) + token_assert_helper(lexer.token(), "SNIPPET_LICENSE_CONCLUDED", + "SnippetLicenseConcluded", 7) + token_assert_helper(lexer.token(), "LINE", "Apache-2.0", 7) + token_assert_helper(lexer.token(), "SNIPPET_LICENSE_INFO", "LicenseInfoInSnippet", 8) + token_assert_helper(lexer.token(), "LINE", "Apache-2.0", 8) + token_assert_helper(lexer.token(), "SNIPPET_BYTE_RANGE", "SnippetByteRange", 9) + token_assert_helper(lexer.token(), "LINE", "310:420", 9) + token_assert_helper(lexer.token(), "SNIPPET_LINE_RANGE", "SnippetLineRange", 10) + token_assert_helper(lexer.token(), "LINE", "5:23", 10) def test_tokenization_of_annotation(lexer): - annotation_str = '\n'.join([ - 'Annotator: Person: Jane Doe()', - 'AnnotationDate: 2010-01-29T18:30:22Z', - 'AnnotationComment: Document level annotation', - 'AnnotationType: OTHER', - 'SPDXREF: SPDXRef-DOCUMENT' + annotation_str = "\n".join([ + "Annotator: Person: Jane Doe()", + "AnnotationDate: 2010-01-29T18:30:22Z", + "AnnotationComment: Document level annotation", + "AnnotationType: OTHER", + "SPDXREF: SPDXRef-DOCUMENT" ]) lexer.input(annotation_str) - token_assert_helper(lexer.token(), 'ANNOTATOR', 'Annotator', 1) - token_assert_helper(lexer.token(), 'PERSON_VALUE', 'Person: Jane Doe()', 1) - token_assert_helper(lexer.token(), 'ANNOTATION_DATE', 'AnnotationDate', 2) - token_assert_helper(lexer.token(), 'DATE', '2010-01-29T18:30:22Z', 2) - token_assert_helper(lexer.token(), 'ANNOTATION_COMMENT', 'AnnotationComment', 3) - token_assert_helper(lexer.token(), 'TEXT', 'Document level annotation', 3) - token_assert_helper(lexer.token(), 'ANNOTATION_TYPE', 'AnnotationType', 4) - token_assert_helper(lexer.token(), 'LINE', 'OTHER', 4) - token_assert_helper(lexer.token(), 'ANNOTATION_SPDX_ID', 'SPDXREF', 5) - token_assert_helper(lexer.token(), 'LINE', 'SPDXRef-DOCUMENT', 5) + token_assert_helper(lexer.token(), "ANNOTATOR", "Annotator", 1) + token_assert_helper(lexer.token(), "PERSON_VALUE", "Person: Jane Doe()", 1) + token_assert_helper(lexer.token(), "ANNOTATION_DATE", "AnnotationDate", 2) + token_assert_helper(lexer.token(), "DATE", "2010-01-29T18:30:22Z", 2) + token_assert_helper(lexer.token(), "ANNOTATION_COMMENT", "AnnotationComment", 3) + token_assert_helper(lexer.token(), "TEXT", "Document level annotation", 3) + token_assert_helper(lexer.token(), "ANNOTATION_TYPE", "AnnotationType", 4) + token_assert_helper(lexer.token(), "LINE", "OTHER", 4) + token_assert_helper(lexer.token(), "ANNOTATION_SPDX_ID", "SPDXREF", 5) + token_assert_helper(lexer.token(), "LINE", "SPDXRef-DOCUMENT", 5) def test_tokenization_of_relationship(lexer): - relationship_str = '\n'.join(['Relationship: SPDXRef-DOCUMENT DESCRIBES NONE', - 'RelationshipComment: This is a comment.']) + relationship_str = "\n".join(["Relationship: SPDXRef-DOCUMENT DESCRIBES NONE", + "RelationshipComment: This is a comment."]) lexer.input(relationship_str) - token_assert_helper(lexer.token(), 'RELATIONSHIP', 'Relationship', 1) - token_assert_helper(lexer.token(), 'LINE', 'SPDXRef-DOCUMENT DESCRIBES NONE', 1) - token_assert_helper(lexer.token(), 'RELATIONSHIP_COMMENT', 'RelationshipComment', 2) - token_assert_helper(lexer.token(), 'LINE', 'This is a comment.', 2) + token_assert_helper(lexer.token(), "RELATIONSHIP", "Relationship", 1) + token_assert_helper(lexer.token(), "LINE", "SPDXRef-DOCUMENT DESCRIBES NONE", 1) + token_assert_helper(lexer.token(), "RELATIONSHIP_COMMENT", "RelationshipComment", 2) + token_assert_helper(lexer.token(), "LINE", "This is a comment.", 2) diff --git a/tests/spdx/parser/tagvalue/test_tag_value_parser.py b/tests/spdx/parser/tagvalue/test_tag_value_parser.py index 38755a24a..f194af2a7 100644 --- a/tests/spdx/parser/tagvalue/test_tag_value_parser.py +++ b/tests/spdx/parser/tagvalue/test_tag_value_parser.py @@ -16,13 +16,13 @@ from spdx.model.document import Document from spdx.model.relationship import RelationshipType, Relationship from spdx.parser.error import SPDXParsingError -from spdx.parser.tagvalue.parser.tagvalue import Parser +from spdx.parser.tagvalue.parser import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR def test_parse_unknown_tag(): parser = Parser() - unknown_tag_str = 'UnknownTag: This is an example for an unknown tag.' + unknown_tag_str = "UnknownTag: This is an example for an unknown tag." with pytest.raises(SPDXParsingError, match="Unknown tag"): parser.parse(unknown_tag_str) diff --git a/tests/spdx/writer/tagvalue/test_tagvalue_writer.py b/tests/spdx/writer/tagvalue/test_tagvalue_writer.py index f4af5a505..f289be6de 100644 --- a/tests/spdx/writer/tagvalue/test_tagvalue_writer.py +++ b/tests/spdx/writer/tagvalue/test_tagvalue_writer.py @@ -13,7 +13,7 @@ import pytest -from spdx.parser.tagvalue.parser import tagvalue_parser +from spdx.parser.tagvalue import tagvalue_parser from tests.spdx.fixtures import document_fixture from spdx.writer.tagvalue.tagvalue_writer import write_document_to_file From af58dfa644a0edeb747dfe566f02486f6a42daa1 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Mar 2023 08:23:41 +0100 Subject: [PATCH 336/362] squashed review commits with name fixes and comment improvement Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/helper_methods.py | 4 ++-- src/spdx/parser/tagvalue/lexer.py | 2 +- src/spdx/parser/tagvalue/parser.py | 21 +++++++++---------- .../parser/tagvalue/test_tag_value_lexer.py | 2 +- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/spdx/parser/tagvalue/helper_methods.py b/src/spdx/parser/tagvalue/helper_methods.py index f13204b78..26efc1407 100644 --- a/src/spdx/parser/tagvalue/helper_methods.py +++ b/src/spdx/parser/tagvalue/helper_methods.py @@ -57,7 +57,7 @@ def parse_checksum(checksum_str: str) -> Checksum: def set_value(parsed_value: YaccProduction, dict_to_fill: Dict[str, Any], argument_name: Optional[str] = None, method_to_apply: Callable = lambda x: x): if not argument_name: - argument_name = get_property(parsed_value[1]) + argument_name = get_property_name(parsed_value[1]) if argument_name in dict_to_fill: dict_to_fill["logger"].append( f"Multiple values for {parsed_value[1]} found. Line: {parsed_value.lineno(1)}") @@ -72,7 +72,7 @@ def set_value(parsed_value: YaccProduction, dict_to_fill: Dict[str, Any], argume dict_to_fill["logger"].append(f"Invalid {parsed_value[1]}: {parsed_value[2]}. Line: {parsed_value.lineno(1)}") -def get_property(tag: str): +def get_property_name(tag: str): if tag not in TAG_DATA_MODEL_FIELD.keys(): return camel_case_to_snake_case(tag) return TAG_DATA_MODEL_FIELD[tag][1] diff --git a/src/spdx/parser/tagvalue/lexer.py b/src/spdx/parser/tagvalue/lexer.py index 906e26067..b6dfca3bf 100644 --- a/src/spdx/parser/tagvalue/lexer.py +++ b/src/spdx/parser/tagvalue/lexer.py @@ -41,7 +41,7 @@ class SPDXLexer(object): # Package fields "PackageName": "PKG_NAME", "PackageVersion": "PKG_VERSION", - "PackageDownloadLocation": "PKG_DOWWNLOAD_LOCATION", + "PackageDownloadLocation": "PKG_DOWNLOAD_LOCATION", "FilesAnalyzed": "PKG_FILES_ANALYZED", "PackageSummary": "PKG_SUMMARY", "PackageSourceInfo": "PKG_SOURCE_INFO", diff --git a/src/spdx/parser/tagvalue/parser.py b/src/spdx/parser/tagvalue/parser.py index 8fbc3d019..573eede02 100644 --- a/src/spdx/parser/tagvalue/parser.py +++ b/src/spdx/parser/tagvalue/parser.py @@ -119,7 +119,7 @@ def p_attrib(self, p): "pkg_license_concluded : PKG_LICENSE_CONCLUDED error\n source_info : PKG_SOURCE_INFO error\n " "homepage : PKG_HOMEPAGE error\n pkg_checksum : PKG_CHECKSUM error\n " "verification_code : PKG_VERIFICATION_CODE error\n originator : PKG_ORIGINATOR error\n " - "download_location : PKG_DOWWNLOAD_LOCATION error\n files_analyzed : PKG_FILES_ANALYZED error\n " + "download_location : PKG_DOWNLOAD_LOCATION error\n files_analyzed : PKG_FILES_ANALYZED error\n " "supplier : PKG_SUPPLIER error\n pkg_file_name : PKG_FILE_NAME error\n " "package_version : PKG_VERSION error\n primary_package_purpose : PRIMARY_PACKAGE_PURPOSE error\n " "built_date : BUILT_DATE error\n release_date : RELEASE_DATE error\n " @@ -150,7 +150,7 @@ def p_current_element_error(self, p): "package_name : PKG_NAME LINE\n description : PKG_DESCRIPTION text_or_line\n " "summary : PKG_SUMMARY text_or_line\n source_info : PKG_SOURCE_INFO text_or_line\n " "homepage : PKG_HOMEPAGE line_or_no_assertion_or_none\n " - "download_location : PKG_DOWWNLOAD_LOCATION line_or_no_assertion_or_none\n " + "download_location : PKG_DOWNLOAD_LOCATION line_or_no_assertion_or_none\n " "originator : PKG_ORIGINATOR actor_or_no_assertion\n supplier : PKG_SUPPLIER actor_or_no_assertion\n " "pkg_comment : PKG_COMMENT text_or_line\n " "pkg_copyright_text : PKG_COPYRIGHT_TEXT line_or_no_assertion_or_none\n " @@ -165,9 +165,7 @@ def p_current_element_error(self, p): "file_spdx_id : SNIPPET_FILE_SPDXID LINE\n " "snippet_license_concluded : SNIPPET_LICENSE_CONCLUDED license_or_no_assertion_or_none\n " "annotation_spdx_id : ANNOTATION_SPDX_ID LINE\n " - "annotation_comment : ANNOTATION_COMMENT text_or_line\n " - - ) + "annotation_comment : ANNOTATION_COMMENT text_or_line") def p_generic_value(self, p): if p[1] in EXPECTED_START_TAG_ELEMENT.keys(): self.initialize_new_current_element(EXPECTED_START_TAG_ELEMENT[p[1]]) @@ -206,8 +204,9 @@ def p_actor_values(self, p): @grammar_rule("spdx_id : SPDX_ID LINE") def p_spdx_id(self, p): - # We assume that the documents spdx_id is defined first in the SPDXDocument, before any package or file - # information. If this is not the case the parser will behave unexpectedly as the spdx_ids are assigned falsy. + # As all SPDX Ids share the same tag, there is no knowing which spdx_id belongs to the document. + # We assume that to be the first spdx_id we encounter. As the specification does not explicitly require this, + # our approach might lead to unwanted behavior when the document's SPDX Id is defined later in the document. if "spdx_id" in self.creation_info: self.current_element["spdx_id"] = p[2] else: @@ -302,7 +301,8 @@ def p_pkg_attribution_text(self, p): self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)) self.current_element.setdefault("attribution_texts", []).append(p[2]) - @grammar_rule("pkg_external_ref : PKG_EXTERNAL_REF LINE PKG_EXTERNAL_REF_COMMENT text_or_line\n | PKG_EXTERNAL_REF LINE") + @grammar_rule( + "pkg_external_ref : PKG_EXTERNAL_REF LINE PKG_EXTERNAL_REF_COMMENT text_or_line\n | PKG_EXTERNAL_REF LINE") def p_pkg_external_refs(self, p): if not self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)): return @@ -516,9 +516,8 @@ def check_that_current_element_matches_class_for_value(self, expected_class, lin def construct_current_element(self): if "class" not in self.current_element: - # When the first element of the document is instantiated we don't have a current element in scope - # and the key "class" doesn't exist. Additionally, if the first element doesn't have the expected start - # value the key "class" wouldn't exist. To prevent a KeyError we use early return. + # This happens when the first element is initialized via initialize_new_current_element() or if the first + # element is missing its expected starting tag. In both cases we are unable to construct an element. return clazz = self.current_element.pop("class") diff --git a/tests/spdx/parser/tagvalue/test_tag_value_lexer.py b/tests/spdx/parser/tagvalue/test_tag_value_lexer.py index 4d4b2cdb9..afef98f1b 100644 --- a/tests/spdx/parser/tagvalue/test_tag_value_lexer.py +++ b/tests/spdx/parser/tagvalue/test_tag_value_lexer.py @@ -159,7 +159,7 @@ def test_tokenization_of_package(lexer): token_assert_helper(lexer.token(), "LINE", "SPDXRef-Package", 2) token_assert_helper(lexer.token(), "PKG_VERSION", "PackageVersion", 3) token_assert_helper(lexer.token(), "LINE", "Version 0.9.2", 3) - token_assert_helper(lexer.token(), "PKG_DOWWNLOAD_LOCATION", "PackageDownloadLocation", 4) + token_assert_helper(lexer.token(), "PKG_DOWNLOAD_LOCATION", "PackageDownloadLocation", 4) token_assert_helper(lexer.token(), "LINE", "http://example.com/test", 4) token_assert_helper(lexer.token(), "PKG_FILES_ANALYZED", "FilesAnalyzed", 5) token_assert_helper(lexer.token(), "LINE", "True", 5) From e5af2eb70140d6908046df0ad26ea8ed37bcfa62 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Mar 2023 08:39:21 +0100 Subject: [PATCH 337/362] squashed review commits [review] fix parsing of external document ref [review] use only one dictionary [review] return if multiple values for snippet range found Signed-off-by: Meret Behrens --- src/spdx/parser/tagvalue/helper_methods.py | 1 + src/spdx/parser/tagvalue/lexer.py | 20 +-------- src/spdx/parser/tagvalue/parser.py | 45 +++++++++---------- .../tagvalue/test_creation_info_parser.py | 30 +++++++------ .../tagvalue/test_relationship_parser.py | 2 +- .../parser/tagvalue/test_tag_value_lexer.py | 22 +++++---- 6 files changed, 55 insertions(+), 65 deletions(-) diff --git a/src/spdx/parser/tagvalue/helper_methods.py b/src/spdx/parser/tagvalue/helper_methods.py index 26efc1407..3f7ad0012 100644 --- a/src/spdx/parser/tagvalue/helper_methods.py +++ b/src/spdx/parser/tagvalue/helper_methods.py @@ -105,6 +105,7 @@ def get_property_name(tag: str): "SnippetComment": (Snippet, "comment"), "SnippetCopyrightText": (Snippet, "copyright_text"), "SnippetLicenseComments": (Snippet, "license_comment"), "SnippetLicenseConcluded": (Snippet, "license_concluded"), "SnippetByteRange": (Snippet, "byte_range"), "SnippetLineRange": (Snippet, "line_range"), + "Annotator": (Annotation, "annotator"), "SPDXREF": (Annotation, "spdx_id"), "AnnotationComment": (Annotation, "annotation_comment"), "LicenseID": (ExtractedLicensingInfo, "license_id"), "ExtractedText": (ExtractedLicensingInfo, "extracted_text"), "LicenseComment": (ExtractedLicensingInfo, "comment"), "LicenseName": (ExtractedLicensingInfo, "license_name") diff --git a/src/spdx/parser/tagvalue/lexer.py b/src/spdx/parser/tagvalue/lexer.py index b6dfca3bf..9229ad64e 100644 --- a/src/spdx/parser/tagvalue/lexer.py +++ b/src/spdx/parser/tagvalue/lexer.py @@ -109,10 +109,7 @@ class SPDXLexer(object): "PERSON_VALUE", "DATE", "LINE", - "CHECKSUM", - "EXT_DOC_REF_ID", - "EXT_DOC_URI", - "EXT_DOC_REF_CHECKSUM", + "CHECKSUM" ] + list(reserved.values()) def __init__(self): @@ -145,21 +142,6 @@ def t_CHECKSUM(self, t): t.value = t.value[1:].strip() return t - @TOKEN(r":\s*DocumentRef-([A-Za-z0-9\+\.\-]+)") - def t_EXT_DOC_REF_ID(self, t): - t.value = t.value[1:].strip() - return t - - @TOKEN(r"\s*((ht|f)tps?:\/\/\S*)") - def t_EXT_DOC_URI(self, t): - t.value = t.value.strip() - return t - - @TOKEN(r"\s*SHA1:\s*[a-f0-9]{40}") - def t_EXT_DOC_REF_CHECKSUM(self, t): - t.value = t.value[1:].strip() - return t - @TOKEN(r":\s*Tool:.+") def t_TOOL_VALUE(self, t): t.value = t.value[1:].strip() diff --git a/src/spdx/parser/tagvalue/parser.py b/src/spdx/parser/tagvalue/parser.py index 573eede02..c1f68dbfc 100644 --- a/src/spdx/parser/tagvalue/parser.py +++ b/src/spdx/parser/tagvalue/parser.py @@ -42,9 +42,6 @@ Package="packages", ExtractedLicensingInfo="extracted_licensing_info") ELEMENT_EXPECTED_START_TAG = dict(File="FileName", Annotation="Annotator", Relationship="Relationship", Snippet="SnippetSPDXID", Package="PackageName", ExtractedLicensingInfo="LicenseID") -EXPECTED_START_TAG_ELEMENT = {"FileName": File, "PackageName": Package, "Annotator": Annotation, - "Relationship": Relationship, "SnippetSPDXID": Snippet, - "LicenseID": ExtractedLicensingInfo} class Parser(object): @@ -135,8 +132,8 @@ def p_attrib(self, p): "annotation_comment : ANNOTATION_COMMENT error\n annotation_type : ANNOTATION_TYPE error\n " "annotation_spdx_id : ANNOTATION_SPDX_ID error\n relationship : RELATIONSHIP error") def p_current_element_error(self, p): - if p[1] in EXPECTED_START_TAG_ELEMENT.keys(): - self.initialize_new_current_element(EXPECTED_START_TAG_ELEMENT[p[1]]) + if p[1] in ELEMENT_EXPECTED_START_TAG.values(): + self.initialize_new_current_element(TAG_DATA_MODEL_FIELD[p[1]][0]) self.current_element["logger"].append( f"Error while parsing {p[1]}: Token did not match specified grammar rule. Line: {p.lineno(1)}") @@ -167,8 +164,8 @@ def p_current_element_error(self, p): "annotation_spdx_id : ANNOTATION_SPDX_ID LINE\n " "annotation_comment : ANNOTATION_COMMENT text_or_line") def p_generic_value(self, p): - if p[1] in EXPECTED_START_TAG_ELEMENT.keys(): - self.initialize_new_current_element(EXPECTED_START_TAG_ELEMENT[p[1]]) + if p[1] in ELEMENT_EXPECTED_START_TAG.values(): + self.initialize_new_current_element(TAG_DATA_MODEL_FIELD[p[1]][0]) if self.check_that_current_element_matches_class_for_value(TAG_DATA_MODEL_FIELD[p[1]][0], p.lineno(1)): set_value(p, self.current_element) @@ -232,11 +229,22 @@ def p_generic_value_creation_info(self, p): def p_license_list_version(self, p): set_value(p, self.creation_info, method_to_apply=Version.from_string) - @grammar_rule("ext_doc_ref : EXT_DOC_REF EXT_DOC_REF_ID EXT_DOC_URI EXT_DOC_REF_CHECKSUM") + @grammar_rule("ext_doc_ref : EXT_DOC_REF LINE") def p_external_document_ref(self, p): - document_ref_id = p[2] - document_uri = p[3] - checksum = parse_checksum(p[4]) + external_doc_ref_regex = re.compile(r"(.*)(\s*SHA1:\s*[a-f0-9]{40})") + external_doc_ref_match = external_doc_ref_regex.match(p[2]) + if not external_doc_ref_match: + self.creation_info["logger"].append( + f"Error while parsing ExternalDocumentRef: Couldn\'t match Checksum. Line: {p.lineno(1)}") + return + try: + document_ref_id, document_uri = external_doc_ref_match.group(1).strip().split(" ") + except ValueError: + self.creation_info["logger"].append( + f"Error while parsing ExternalDocumentRef: Couldn't split the first part of the value into " + f"document_ref_id and document_uri. Line: {p.lineno(1)}") + return + checksum = parse_checksum(external_doc_ref_match.group(2).strip()) external_document_ref = ExternalDocumentRef(document_ref_id, document_uri, checksum) self.creation_info.setdefault("external_document_refs", []).append(external_document_ref) @@ -415,6 +423,7 @@ def p_snippet_range(self, p): if argument_name in self.current_element: self.current_element["logger"].append( f"Multiple values for {p[1]} found. Line: {p.lineno(1)}") + return range_re = re.compile(r"^(\d+):(\d+)$", re.UNICODE) if not range_re.match(p[2].strip()): self.current_element["logger"].append(f"Value for {p[1]} doesn't match valid range pattern. " @@ -443,8 +452,8 @@ def p_annotation_type(self, p): # parsing methods for relationship - @grammar_rule("relationship : RELATIONSHIP relationship_value RELATIONSHIP_COMMENT text_or_line\n " - "| RELATIONSHIP relationship_value") + @grammar_rule("relationship : RELATIONSHIP LINE RELATIONSHIP_COMMENT text_or_line\n " + "| RELATIONSHIP LINE") def p_relationship(self, p): self.initialize_new_current_element(Relationship) try: @@ -467,16 +476,6 @@ def p_relationship(self, p): if len(p) == 5: self.current_element["comment"] = p[4] - @grammar_rule("relationship_value : EXT_DOC_REF_ID LINE") - def p_relationship_value_with_doc_ref(self, p): - - p[0] = p[1] + ":" + p[2] - - @grammar_rule("relationship_value : LINE") - def p_relationship_value_without_doc_ref(self, p): - - p[0] = p[1] - def p_error(self, p): pass diff --git a/tests/spdx/parser/tagvalue/test_creation_info_parser.py b/tests/spdx/parser/tagvalue/test_creation_info_parser.py index 2d789229b..98971e24c 100644 --- a/tests/spdx/parser/tagvalue/test_creation_info_parser.py +++ b/tests/spdx/parser/tagvalue/test_creation_info_parser.py @@ -69,20 +69,22 @@ def test_parse_creation_info(): "Creator: Person Bob (bob@example.com)", "Creator: Organization: Acme [email]", "Created: 2010-02-03T00:00:0Z", "CreatorComment: Sample Comment", "LicenseListVersion: 7"]), - "Error while parsing CreationInfo: ['Error while parsing DocumentNamespace: " - "Token did not match specified grammar rule. Line: 6', 'Error while parsing " - "ExternalDocumentRef: Token did not match specified grammar rule. Line: 7', " - "'Error while parsing Creator: Token did not match specified grammar rule. Line: 8', " - "'Error while parsing Created: Token did not match specified grammar rule. Line: 10', " - "'7 is not a valid version string']"), - ("\n".join( - ["SPDXVersion: SPDX-2.3", "DataLicense: CC0-1.0", "DocumentName: Sample_Document-V2.3", - "SPDXID: SPDXRef-DOCUMENT"]), - r"__init__() missing 3 required positional arguments: 'document_namespace', " - r"'creators', and 'created'"), - ("LicenseListVersion: 3.5\nLicenseListVersion: 3.7", - "Error while parsing CreationInfo: ['Multiple values for LicenseListVersion found. " - "Line: 2']")])) + ("Error while parsing CreationInfo: ['Error while parsing DocumentNamespace: " + 'Token did not match specified grammar rule. Line: 6\', "Error while parsing ' + "ExternalDocumentRef: Couldn't split the first part of the value into " + 'document_ref_id and document_uri. Line: 7", \'Error while parsing Creator: ' + "Token did not match specified grammar rule. Line: 8', 'Error while parsing " + "Created: Token did not match specified grammar rule. Line: 10', '7 is not a " + "valid version string']")), + ("\n".join( + ["SPDXVersion: SPDX-2.3", "DataLicense: CC0-1.0", "DocumentName: Sample_Document-V2.3", + "SPDXID: SPDXRef-DOCUMENT"]), + r"__init__() missing 3 required positional arguments: 'document_namespace', 'creators', and 'created'"), + ("LicenseListVersion: 3.5\nLicenseListVersion: 3.7", + "Error while parsing CreationInfo: ['Multiple values for LicenseListVersion found. Line: 2']"), + ("ExternalDocumentRef: Document_ref document_uri SHA1: afded", + 'Error while parsing CreationInfo: ["Error while parsing ExternalDocumentRef: Couldn\'t match Checksum. Line: 1"]' + )])) def test_parse_invalid_creation_info(document_str, expected_message): parser = Parser() with pytest.raises(SPDXParsingError) as err: diff --git a/tests/spdx/parser/tagvalue/test_relationship_parser.py b/tests/spdx/parser/tagvalue/test_relationship_parser.py index 18a6ee3b8..90ef9da4e 100644 --- a/tests/spdx/parser/tagvalue/test_relationship_parser.py +++ b/tests/spdx/parser/tagvalue/test_relationship_parser.py @@ -28,7 +28,7 @@ SpdxNoAssertion())), ("Relationship: SPDXRef-CarolCompression DEPENDS_ON NONE", Relationship("SPDXRef-CarolCompression", RelationshipType.DEPENDS_ON, SpdxNone())), - ("Relationship: DocumentRef-ExternalDocument: SPDXRef-Test DEPENDS_ON DocumentRef:AnotherRef", + ("Relationship: DocumentRef-ExternalDocument:SPDXRef-Test DEPENDS_ON DocumentRef:AnotherRef", Relationship("DocumentRef-ExternalDocument:SPDXRef-Test", RelationshipType.DEPENDS_ON, "DocumentRef:AnotherRef")) ]) diff --git a/tests/spdx/parser/tagvalue/test_tag_value_lexer.py b/tests/spdx/parser/tagvalue/test_tag_value_lexer.py index afef98f1b..0aaf0d864 100644 --- a/tests/spdx/parser/tagvalue/test_tag_value_lexer.py +++ b/tests/spdx/parser/tagvalue/test_tag_value_lexer.py @@ -53,15 +53,18 @@ def test_tokenization_of_document(lexer): def test_tokenization_of_external_document_references(lexer): - data = """ - ExternalDocumentRef:DocumentRef-spdx-tool-2.1 http://spdx.org/spdxdocs/spdx-tools-v2.1-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759 - """ + data = "\n".join([ + "ExternalDocumentRef:DocumentRef-spdx-tool-2.1 http://spdx.org/spdxdocs/spdx-tools-v2.1-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759", + "ExternalDocumentRef:DocumentRef-spdx-tool-2.1 ldap://[2001:db8::7]/c=GB?objectClass?one SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759"]) lexer.input(data) + token_assert_helper(lexer.token(), "EXT_DOC_REF", "ExternalDocumentRef", 1) + token_assert_helper(lexer.token(), "LINE", + "DocumentRef-spdx-tool-2.1 http://spdx.org/spdxdocs/spdx-tools-v2.1-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759", + 1) token_assert_helper(lexer.token(), "EXT_DOC_REF", "ExternalDocumentRef", 2) - token_assert_helper(lexer.token(), "EXT_DOC_REF_ID", "DocumentRef-spdx-tool-2.1", 2) - token_assert_helper(lexer.token(), "EXT_DOC_URI", "http://spdx.org/spdxdocs/spdx-tools-v2.1-3F25" - "04E0-4F89-41D3-9A0C-0305E82C3301", 2) - token_assert_helper(lexer.token(), "EXT_DOC_REF_CHECKSUM", "SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759", 2) + token_assert_helper(lexer.token(), "LINE", + "DocumentRef-spdx-tool-2.1 ldap://[2001:db8::7]/c=GB?objectClass?one SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759", + 2) def test_tokenization_of_file(lexer): @@ -277,10 +280,13 @@ def test_tokenization_of_annotation(lexer): def test_tokenization_of_relationship(lexer): relationship_str = "\n".join(["Relationship: SPDXRef-DOCUMENT DESCRIBES NONE", - "RelationshipComment: This is a comment."]) + "RelationshipComment: This is a comment.", + "Relationship: DocumentRef-extern:SPDXRef-Package DESCRIBES NONE"]) lexer.input(relationship_str) token_assert_helper(lexer.token(), "RELATIONSHIP", "Relationship", 1) token_assert_helper(lexer.token(), "LINE", "SPDXRef-DOCUMENT DESCRIBES NONE", 1) token_assert_helper(lexer.token(), "RELATIONSHIP_COMMENT", "RelationshipComment", 2) token_assert_helper(lexer.token(), "LINE", "This is a comment.", 2) + token_assert_helper(lexer.token(), "RELATIONSHIP", "Relationship", 3) + token_assert_helper(lexer.token(), "LINE", "DocumentRef-extern:SPDXRef-Package DESCRIBES NONE", 3) From 752e964076cf932956aa3478af8540a8c8515f7d Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 10 Mar 2023 10:47:16 +0100 Subject: [PATCH 338/362] [issue-228] make type hints in code readable for downstream packages Signed-off-by: Meret Behrens --- pyproject.toml | 3 +++ src/py.typed | 0 2 files changed, 3 insertions(+) create mode 100644 src/py.typed diff --git a/pyproject.toml b/pyproject.toml index 933f1d264..2dd71b8e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,9 @@ include-package-data = true [tool.setuptools.packages.find] where = ["src"] +[tool.setuptools.package-data] +"*" = ["py.typed"] + # the default git describe resolves to the tag `python3.6` because the current release tag is not on main # by adding "v" the command resolves to the alpha release of 0.7.0 which leads to the desired name spdx-tools-0.7.0 [tool.setuptools_scm] diff --git a/src/py.typed b/src/py.typed new file mode 100644 index 000000000..e69de29bb From 0aff1b5fbbf594726fdaf23dd8494739897b71b9 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 10 Mar 2023 11:04:58 +0100 Subject: [PATCH 339/362] [fix] move "py.typed" to package root directory Signed-off-by: Meret Behrens --- src/{ => spdx}/py.typed | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{ => spdx}/py.typed (100%) diff --git a/src/py.typed b/src/spdx/py.typed similarity index 100% rename from src/py.typed rename to src/spdx/py.typed From 5d3cab0512eceee821fedf4a3a970b51c8a921b3 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 10 Mar 2023 11:37:42 +0100 Subject: [PATCH 340/362] [fix] write LicenseListVersion only when provided Signed-off-by: Meret Behrens --- src/spdx/writer/tagvalue/creation_info_writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spdx/writer/tagvalue/creation_info_writer.py b/src/spdx/writer/tagvalue/creation_info_writer.py index e03c222f2..219af57a5 100644 --- a/src/spdx/writer/tagvalue/creation_info_writer.py +++ b/src/spdx/writer/tagvalue/creation_info_writer.py @@ -32,7 +32,7 @@ def write_creation_info(creation_info: CreationInfo, text_output: TextIO): write_separator(text_output) text_output.write("## Creation Information\n") - write_value("LicenseListVersion", str(creation_info.license_list_version), text_output) + write_value("LicenseListVersion", creation_info.license_list_version, text_output) for creator in creation_info.creators: write_value("Creator", creator.to_serialized_string(), text_output) write_value("Created", datetime_to_iso_string(creation_info.created), text_output) From 26246900705a96461afa7430ce2c7d39014e081f Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 10 Mar 2023 11:57:03 +0100 Subject: [PATCH 341/362] [issue-394] add tests for creation info writer Signed-off-by: Meret Behrens --- .../tagvalue/test_creation_info_writer.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 tests/spdx/writer/tagvalue/test_creation_info_writer.py diff --git a/tests/spdx/writer/tagvalue/test_creation_info_writer.py b/tests/spdx/writer/tagvalue/test_creation_info_writer.py new file mode 100644 index 000000000..b4be91b5c --- /dev/null +++ b/tests/spdx/writer/tagvalue/test_creation_info_writer.py @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: 2023 SPDX Contributors +# +# SPDX-License-Identifier: Apache-2.0 +import datetime +from unittest.mock import mock_open, patch, call, MagicMock + +import pytest + +from spdx.model.document import CreationInfo +from tests.spdx.fixtures import creation_info_fixture, actor_fixture + +from spdx.writer.tagvalue.creation_info_writer import write_creation_info + + +@pytest.mark.parametrize("creation_info, expected_calls", + [(creation_info_fixture(), [call("SPDXVersion: SPDX-2.3\n"), call("DataLicense: CC0-1.0\n"), + call("SPDXID: SPDXRef-DOCUMENT\n"), + call("DocumentName: documentName\n"), + call("DocumentNamespace: https://some.namespace\n"), + call("DocumentComment: documentComment\n"), + call("\n## External Document References\n"), call( + "ExternalDocumentRef: DocumentRef-external https://namespace.com SHA1: 71c4025dd9897b364f3ebbb42c484ff43d00791c\n"), + call("\n"), call("## Creation Information\n"), + call("LicenseListVersion: 3.19\n"), + call("Creator: Person: creatorName (some@mail.com)\n"), + call("Created: 2022-12-01T00:00:00Z\n"), + call("CreatorComment: creatorComment\n")]), + (CreationInfo(spdx_version="SPDX-2.3", spdx_id="SPDXRef-DOCUMENT", creators=[actor_fixture()], + name="Test document", document_namespace="https://namespace.com", + created=datetime.datetime(2022, 3, 10)), + [call("SPDXVersion: SPDX-2.3\n"), call("DataLicense: CC0-1.0\n"), + call("SPDXID: SPDXRef-DOCUMENT\n"), call("DocumentName: Test document\n"), + call("DocumentNamespace: https://namespace.com\n"), call("\n"), + call("## Creation Information\n"), call("Creator: Person: actorName (some@mail.com)\n"), + call("Created: 2022-03-10T00:00:00Z\n")])]) +def test_creation_info_writer(creation_info, expected_calls): + mock: MagicMock = mock_open() + with patch(f"{__name__}.open", mock, create=True): + with open("foo", "w") as file: + write_creation_info(creation_info, file) + + mock.assert_called_once_with("foo", "w") + handle = mock() + handle.write.assert_has_calls(expected_calls) From 08b447dccf77c00ed0ff686f0ffe893ee1745a89 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 10 Mar 2023 14:02:11 +0100 Subject: [PATCH 342/362] [refactor] use f-strings, double quotes and more descriptive variable names Signed-off-by: Meret Behrens --- .../writer/tagvalue/test_package_writer.py | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/tests/spdx/writer/tagvalue/test_package_writer.py b/tests/spdx/writer/tagvalue/test_package_writer.py index feb2608f3..09d869315 100644 --- a/tests/spdx/writer/tagvalue/test_package_writer.py +++ b/tests/spdx/writer/tagvalue/test_package_writer.py @@ -8,7 +8,7 @@ # 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.mock import patch, mock_open, call +from unittest.mock import patch, mock_open, call, MagicMock from tests.spdx.fixtures import package_fixture from spdx.writer.tagvalue.package_writer import write_package @@ -17,40 +17,40 @@ def test_package_writer(): package = package_fixture() - m = mock_open() - with patch('{}.open'.format(__name__), m, create=True): - with open('foo', 'w') as h: - write_package(package, h) + mock: MagicMock = mock_open() + with patch(f"{__name__}.open", mock, create=True): + with open("foo", "w") as file: + write_package(package, file) - m.assert_called_once_with('foo', 'w') - handle = m() + mock.assert_called_once_with("foo", "w") + handle = mock() handle.write.assert_has_calls( - [call('## Package Information\n'), - call('PackageName: packageName\n'), - call('SPDXID: SPDXRef-Package\n'), - call('PackageVersion: 12.2\n'), - call('PackageFileName: ./packageFileName\n'), - call('PackageSupplier: Person: supplierName (some@mail.com)\n'), - call('PackageOriginator: Person: originatorName (some@mail.com)\n'), - call('PackageDownloadLocation: https://download.com\n'), - call('FilesAnalyzed: True\n'), - call('PackageVerificationCode: 85ed0817af83a24ad8da68c2b5094de69833983c (excludes: ./exclude.py)\n'), - call('PackageChecksum: SHA1: 71c4025dd9897b364f3ebbb42c484ff43d00791c\n'), - call('PackageHomePage: https://homepage.com\n'), - call('PackageSourceInfo: sourceInfo\n'), - call('PackageLicenseConcluded: MIT AND GPL-2.0-only\n'), - call('PackageLicenseInfoFromFiles: MIT\n'), - call('PackageLicenseInfoFromFiles: GPL-2.0-only\n'), - call('PackageLicenseDeclared: MIT AND GPL-2.0-only\n'), - call('PackageLicenseComments: packageLicenseComment\n'), - call('PackageCopyrightText: packageCopyrightText\n'), - call('PackageSummary: packageSummary\n'), - call('PackageDescription: packageDescription\n'), - call('PackageComment: packageComment\n'), - call('ExternalRef: PACKAGE-MANAGER maven-central org.apache.tomcat:tomcat:9.0.0.M4\n'), - call('ExternalRefComment: externalPackageRefComment\n'), - call('PackageAttributionText: packageAttributionText\n'), - call('PrimaryPackagePurpose: SOURCE\n'), - call('ReleaseDate: 2022-12-01T00:00:00Z\n'), - call('BuiltDate: 2022-12-02T00:00:00Z\n'), - call('ValidUntilDate: 2022-12-03T00:00:00Z\n')]) + [call("## Package Information\n"), + call("PackageName: packageName\n"), + call("SPDXID: SPDXRef-Package\n"), + call("PackageVersion: 12.2\n"), + call("PackageFileName: ./packageFileName\n"), + call("PackageSupplier: Person: supplierName (some@mail.com)\n"), + call("PackageOriginator: Person: originatorName (some@mail.com)\n"), + call("PackageDownloadLocation: https://download.com\n"), + call("FilesAnalyzed: True\n"), + call("PackageVerificationCode: 85ed0817af83a24ad8da68c2b5094de69833983c (excludes: ./exclude.py)\n"), + call("PackageChecksum: SHA1: 71c4025dd9897b364f3ebbb42c484ff43d00791c\n"), + call("PackageHomePage: https://homepage.com\n"), + call("PackageSourceInfo: sourceInfo\n"), + call("PackageLicenseConcluded: MIT AND GPL-2.0-only\n"), + call("PackageLicenseInfoFromFiles: MIT\n"), + call("PackageLicenseInfoFromFiles: GPL-2.0-only\n"), + call("PackageLicenseDeclared: MIT AND GPL-2.0-only\n"), + call("PackageLicenseComments: packageLicenseComment\n"), + call("PackageCopyrightText: packageCopyrightText\n"), + call("PackageSummary: packageSummary\n"), + call("PackageDescription: packageDescription\n"), + call("PackageComment: packageComment\n"), + call("ExternalRef: PACKAGE-MANAGER maven-central org.apache.tomcat:tomcat:9.0.0.M4\n"), + call("ExternalRefComment: externalPackageRefComment\n"), + call("PackageAttributionText: packageAttributionText\n"), + call("PrimaryPackagePurpose: SOURCE\n"), + call("ReleaseDate: 2022-12-01T00:00:00Z\n"), + call("BuiltDate: 2022-12-02T00:00:00Z\n"), + call("ValidUntilDate: 2022-12-03T00:00:00Z\n")]) From 34d00455729fd96638753ade8cdfc2abcd92fe09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 10 Mar 2023 14:21:28 +0100 Subject: [PATCH 343/362] [issue-518] rename file_type to file_types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- README.md | 2 +- src/spdx/jsonschema/file_converter.py | 2 +- src/spdx/model/file.py | 6 +++--- src/spdx/parser/jsonlikedict/file_parser.py | 2 +- src/spdx/parser/rdf/file_parser.py | 2 +- src/spdx/parser/tagvalue/parser.py | 2 +- src/spdx/writer/rdf/file_writer.py | 2 +- src/spdx/writer/tagvalue/file_writer.py | 2 +- tests/spdx/fixtures.py | 6 +++--- tests/spdx/jsonschema/test_file_converter.py | 4 ++-- tests/spdx/model/test_file.py | 6 +++--- tests/spdx/parser/jsonlikedict/test_file_parser.py | 2 +- tests/spdx/parser/rdf/test_file_parser.py | 2 +- tests/spdx/parser/tagvalue/test_file_parser.py | 2 +- 14 files changed, 21 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index e1681a7c2..cb25a8104 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ 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_type=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") diff --git a/src/spdx/jsonschema/file_converter.py b/src/spdx/jsonschema/file_converter.py index 5806ee604..f93704307 100644 --- a/src/spdx/jsonschema/file_converter.py +++ b/src/spdx/jsonschema/file_converter.py @@ -58,7 +58,7 @@ def _get_property_value(self, file: Any, file_property: FileProperty, document: elif file_property == FileProperty.FILE_NAME: return file.name elif file_property == FileProperty.FILE_TYPES: - return [file_type.name for file_type in file.file_type] or None + return [file_type.name for file_type in file.file_types] or None elif file_property == FileProperty.LICENSE_COMMENTS: return file.license_comment elif file_property == FileProperty.LICENSE_CONCLUDED: diff --git a/src/spdx/model/file.py b/src/spdx/model/file.py index eb9b0d094..1c7e157a1 100644 --- a/src/spdx/model/file.py +++ b/src/spdx/model/file.py @@ -39,7 +39,7 @@ class File: name: str spdx_id: str checksums: List[Checksum] - file_type: List[FileType] = field(default_factory=list) + file_types: List[FileType] = field(default_factory=list) license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None license_info_in_file: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = field( default_factory=list) @@ -55,14 +55,14 @@ class File: # - artifact of (3 properties): replace by an external package reference and a GENERATED_FROM relationship # between the file and this package - def __init__(self, name: str, spdx_id: str, checksums: List[Checksum], file_type: List[FileType] = None, + def __init__(self, name: str, spdx_id: str, checksums: List[Checksum], file_types: List[FileType] = None, license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, license_info_in_file: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = None, license_comment: Optional[str] = None, copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None, comment: str = None, notice: Optional[str] = None, contributors: List[str] = None, attribution_texts: List[str] = None): - file_type = [] if file_type is None else file_type + file_types = [] if file_types is None else file_types license_info_in_file = [] if license_info_in_file is None else license_info_in_file contributors = [] if contributors is None else contributors attribution_texts = [] if attribution_texts is None else attribution_texts diff --git a/src/spdx/parser/jsonlikedict/file_parser.py b/src/spdx/parser/jsonlikedict/file_parser.py index f7cb62030..42d31f852 100644 --- a/src/spdx/parser/jsonlikedict/file_parser.py +++ b/src/spdx/parser/jsonlikedict/file_parser.py @@ -61,7 +61,7 @@ def parse_file(self, file_dict: Dict) -> Optional[File]: file = construct_or_raise_parsing_error(File, dict(name=name, spdx_id=spdx_id, checksums=checksums, attribution_texts=attribution_texts, comment=comment, - copyright_text=copyright_text, file_type=file_types, + copyright_text=copyright_text, file_types=file_types, contributors=file_contributors, license_comment=license_comments, license_concluded=license_concluded, diff --git a/src/spdx/parser/rdf/file_parser.py b/src/spdx/parser/rdf/file_parser.py index 994f50660..37cb2ca50 100644 --- a/src/spdx/parser/rdf/file_parser.py +++ b/src/spdx/parser/rdf/file_parser.py @@ -56,7 +56,7 @@ def parse_file(file_node: URIRef, graph: Graph, doc_namespace: str) -> File: raise_parsing_error_if_logger_has_messages(logger, "File") file = construct_or_raise_parsing_error(File, dict(name=name, spdx_id=spdx_id, checksums=checksums, attribution_texts=attribution_texts, comment=comment, - copyright_text=copyright_text, file_type=file_types, + copyright_text=copyright_text, file_types=file_types, contributors=file_contributors, license_comment=license_comment, license_concluded=license_concluded, diff --git a/src/spdx/parser/tagvalue/parser.py b/src/spdx/parser/tagvalue/parser.py index c1f68dbfc..a7a8ea0c5 100644 --- a/src/spdx/parser/tagvalue/parser.py +++ b/src/spdx/parser/tagvalue/parser.py @@ -293,7 +293,7 @@ def p_file_type(self, p): except KeyError: self.current_element["logger"].append(f"Invalid FileType: {p[2]}. Line {p.lineno(1)}") return - self.current_element.setdefault("file_type", []).append(file_type) + self.current_element.setdefault("file_types", []).append(file_type) @grammar_rule("file_checksum : FILE_CHECKSUM CHECKSUM") def p_file_checksum(self, p): diff --git a/src/spdx/writer/rdf/file_writer.py b/src/spdx/writer/rdf/file_writer.py index 79b82c26a..6441d8c06 100644 --- a/src/spdx/writer/rdf/file_writer.py +++ b/src/spdx/writer/rdf/file_writer.py @@ -25,7 +25,7 @@ def add_file_to_graph(file: File, graph: Graph, doc_namespace: str, file_resource = URIRef(add_namespace_to_spdx_id(file.spdx_id, doc_namespace, external_doc_ref_to_namespace)) graph.add((file_resource, RDF.type, SPDX_NAMESPACE.File)) graph.add((file_resource, SPDX_NAMESPACE.fileName, Literal(file.name))) - for file_type in file.file_type: + for file_type in file.file_types: graph.add((file_resource, SPDX_NAMESPACE.fileType, SPDX_NAMESPACE[f"fileType_{snake_case_to_camel_case(file_type.name)}"])) diff --git a/src/spdx/writer/tagvalue/file_writer.py b/src/spdx/writer/tagvalue/file_writer.py index ee99645eb..87caf0b73 100644 --- a/src/spdx/writer/tagvalue/file_writer.py +++ b/src/spdx/writer/tagvalue/file_writer.py @@ -21,7 +21,7 @@ def write_file(file: File, text_output: TextIO): write_value("FileName", file.name, text_output) write_value("SPDXID", file.spdx_id, text_output) - for file_type in file.file_type: + for file_type in file.file_types: write_value("FileType", file_type.name, text_output) for file_checksum in file.checksums: diff --git a/tests/spdx/fixtures.py b/tests/spdx/fixtures.py index 9c99f17a9..8df947a71 100644 --- a/tests/spdx/fixtures.py +++ b/tests/spdx/fixtures.py @@ -55,17 +55,17 @@ def creation_info_fixture(spdx_version="SPDX-2.3", spdx_id="SPDXRef-DOCUMENT", n external_document_refs, license_list_version, document_comment) -def file_fixture(name="./fileName.py", spdx_id="SPDXRef-File", checksums=None, file_type=None, +def file_fixture(name="./fileName.py", spdx_id="SPDXRef-File", checksums=None, file_types=None, license_concluded=get_spdx_licensing().parse("MIT and GPL-2.0"), license_info_in_file=None, license_comment="licenseComment", copyright_text="copyrightText", comment="fileComment", notice="fileNotice", contributors=None, attribution_texts=None) -> File: checksums = [checksum_fixture()] if checksums is None else checksums - file_type = [FileType.TEXT] if file_type is None else file_type + file_types = [FileType.TEXT] if file_types is None else file_types license_info_in_file = [get_spdx_licensing().parse("MIT"), get_spdx_licensing().parse("GPL-2.0")] if license_info_in_file is None else license_info_in_file contributors = ["fileContributor"] if contributors is None else contributors attribution_texts = ["fileAttributionText"] if attribution_texts is None else attribution_texts - return File(name=name, spdx_id=spdx_id, checksums=checksums, file_type=file_type, + return File(name=name, spdx_id=spdx_id, checksums=checksums, file_types=file_types, license_concluded=license_concluded, license_info_in_file=license_info_in_file, license_comment=license_comment, copyright_text=copyright_text, comment=comment, notice=notice, contributors=contributors, attribution_texts=attribution_texts) diff --git a/tests/spdx/jsonschema/test_file_converter.py b/tests/spdx/jsonschema/test_file_converter.py index d1a08282c..eda157f02 100644 --- a/tests/spdx/jsonschema/test_file_converter.py +++ b/tests/spdx/jsonschema/test_file_converter.py @@ -73,7 +73,7 @@ def test_successful_conversion(converter: FileConverter): converter.annotation_converter.convert.return_value = "mock_converted_annotation" file = File(name="name", spdx_id="spdxId", checksums=[Checksum(ChecksumAlgorithm.SHA224, "sha224"), Checksum(ChecksumAlgorithm.MD2, "md2")], - file_type=[FileType.SPDX, FileType.OTHER], license_concluded=Licensing().parse("MIT and GPL-2.0"), + file_types=[FileType.SPDX, FileType.OTHER], license_concluded=Licensing().parse("MIT and GPL-2.0"), license_info_in_file=[Licensing().parse("MIT"), Licensing().parse("GPL-2.0")], license_comment="licenseComment", copyright_text="copyrightText", comment="comment", notice="notice", contributors=["contributor1", "contributor2"], @@ -104,7 +104,7 @@ def test_successful_conversion(converter: FileConverter): def test_null_values(converter: FileConverter): file = file_fixture(copyright_text=None, license_concluded=None, license_comment=None, comment=None, notice=None, - attribution_texts=[], checksums=[], contributors=[], file_type=[], license_info_in_file=[]) + attribution_texts=[], checksums=[], contributors=[], file_types=[], license_info_in_file=[]) document = Document(creation_info_fixture(), files=[file]) converted_dict = converter.convert(file, document) diff --git a/tests/spdx/model/test_file.py b/tests/spdx/model/test_file.py index 14d6b4c78..75ea6096b 100644 --- a/tests/spdx/model/test_file.py +++ b/tests/spdx/model/test_file.py @@ -15,7 +15,7 @@ def test_correct_initialization(checksum): assert file.name == "name" assert file.spdx_id == "id" assert file.checksums == [checksum, checksum] - assert file.file_type == [FileType.OTHER, FileType.SPDX] + assert file.file_types == [FileType.OTHER, FileType.SPDX] assert file.license_concluded == SpdxNone() assert file.license_info_in_file == SpdxNoAssertion() assert file.license_comment == "comment on license" @@ -32,7 +32,7 @@ def test_correct_initialization_with_default_values(checksum): assert file.name == "name" assert file.spdx_id == "id" assert file.checksums == [checksum, checksum] - assert file.file_type == [] + assert file.file_types == [] assert file.license_concluded is None assert file.license_info_in_file == [] assert file.license_comment is None @@ -64,7 +64,7 @@ def test_wrong_type_in_checksum(): @mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_wrong_type_in_file_type(checksum): with pytest.raises(TypeError): - File("name", "id", [checksum], file_type=FileType.OTHER) + File("name", "id", [checksum], file_types=FileType.OTHER) @mock.patch('spdx.model.checksum.Checksum', autospec=True) diff --git a/tests/spdx/parser/jsonlikedict/test_file_parser.py b/tests/spdx/parser/jsonlikedict/test_file_parser.py index a4faa3ae8..2c0204be9 100644 --- a/tests/spdx/parser/jsonlikedict/test_file_parser.py +++ b/tests/spdx/parser/jsonlikedict/test_file_parser.py @@ -53,7 +53,7 @@ def test_parse_file(): Checksum(ChecksumAlgorithm.MD5, "624c1abb3664f4b35547e7c73864ad24")]) assert file.comment == "The concluded license was taken from the package level that the file was included in.\nThis information was found in the COPYING.txt file in the xyz directory." assert file.copyright_text == "Copyright 2008-2010 John Smith" - assert file.file_type == [FileType.SOURCE] + assert file.file_types == [FileType.SOURCE] TestCase().assertCountEqual(file.contributors, ["The Regents of the University of California", "Modified by Paul Mundt lethal@linux-sh.org", "IBM Corporation"]) assert file.license_concluded == Licensing().parse("(LGPL-2.0-only OR LicenseRef-2)") diff --git a/tests/spdx/parser/rdf/test_file_parser.py b/tests/spdx/parser/rdf/test_file_parser.py index 6ffed17a0..1b265c20a 100644 --- a/tests/spdx/parser/rdf/test_file_parser.py +++ b/tests/spdx/parser/rdf/test_file_parser.py @@ -30,7 +30,7 @@ def test_parse_file(): assert file.name == "./fileName.py" assert file.spdx_id == "SPDXRef-File" assert file.checksums == [Checksum(ChecksumAlgorithm.SHA1, "71c4025dd9897b364f3ebbb42c484ff43d00791c")] - assert file.file_type == [FileType.TEXT] + assert file.file_types == [FileType.TEXT] assert file.comment == "fileComment" assert file.copyright_text == "copyrightText" assert file.contributors == ["fileContributor"] diff --git a/tests/spdx/parser/tagvalue/test_file_parser.py b/tests/spdx/parser/tagvalue/test_file_parser.py index fecc8d77b..0cedc84b6 100644 --- a/tests/spdx/parser/tagvalue/test_file_parser.py +++ b/tests/spdx/parser/tagvalue/test_file_parser.py @@ -37,7 +37,7 @@ def test_parse_file(): spdx_file = document.files[0] assert spdx_file.name == "testfile.java" assert spdx_file.spdx_id == "SPDXRef-File" - assert spdx_file.file_type == [FileType.SOURCE, FileType.TEXT] + assert spdx_file.file_types == [FileType.SOURCE, FileType.TEXT] assert spdx_file.comment == "Very long file" assert spdx_file.attribution_texts == [ "Acknowledgements that might be required to be communicated in some contexts."] From 4a0675ffc146f4ba90970facced6ff0f66a73c7c Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 3 Mar 2023 13:34:03 +0100 Subject: [PATCH 344/362] [issue-503] validate license_expressions only as a List of LicenseExpression, SpdxNone or SpdxNoAssertion Signed-off-by: Meret Behrens --- .../jsonlikedict/license_expression_parser.py | 14 ------- .../license_expression_validator.py | 19 ++++----- .../test_license_expression_parser.py | 40 ++++++------------- .../test_license_expression_validator.py | 5 ++- 4 files changed, 26 insertions(+), 52 deletions(-) diff --git a/src/spdx/parser/jsonlikedict/license_expression_parser.py b/src/spdx/parser/jsonlikedict/license_expression_parser.py index 07a634960..a2e1912a8 100644 --- a/src/spdx/parser/jsonlikedict/license_expression_parser.py +++ b/src/spdx/parser/jsonlikedict/license_expression_parser.py @@ -35,17 +35,3 @@ def parse_license_expression(license_expression_str: str) -> Union[LicenseExpres raise SPDXParsingError([f"Error parsing LicenseExpression: {err.args[0]}: {license_expression_str}"]) return license_expression - - def parse_license_expressions(self, license_expression_str_or_list: Union[str, List[str]]) -> Union[ - LicenseExpression, SpdxNone, SpdxNoAssertion, List[Union[LicenseExpression, SpdxNone, SpdxNoAssertion]]]: - if not isinstance(license_expression_str_or_list, List): - return self.parse_license_expression(license_expression_str_or_list) - - license_expressions = [] - logger = Logger() - for license_expression_str in license_expression_str_or_list: - license_expressions = append_parsed_field_or_log_error(logger, license_expressions, - license_expression_str, - self.parse_license_expression) - raise_parsing_error_if_logger_has_messages(logger) - return license_expressions diff --git a/src/spdx/validation/license_expression_validator.py b/src/spdx/validation/license_expression_validator.py index f2c8ddfbf..789d6c8c4 100644 --- a/src/spdx/validation/license_expression_validator.py +++ b/src/spdx/validation/license_expression_validator.py @@ -19,12 +19,11 @@ from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType -def validate_license_expressions(license_expressions: Optional[ - Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]], document: Document, parent_id: str) -> List[ValidationMessage]: - if license_expressions in [SpdxNoAssertion(), SpdxNone(), None]: - return [] - - context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.LICENSE_EXPRESSION, full_element=license_expressions) +def validate_license_expressions( + license_expressions: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]], + document: Document, parent_id: str) -> List[ValidationMessage]: + context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.LICENSE_EXPRESSION, + full_element=license_expressions) validation_messages = [] for license_expression in license_expressions: @@ -33,13 +32,15 @@ def validate_license_expressions(license_expressions: Optional[ return validation_messages -def validate_license_expression(license_expression: Optional[ - Union[LicenseExpression, SpdxNoAssertion, SpdxNone]], document: Document, parent_id: str, context: ValidationContext = None) -> List[ValidationMessage]: +def validate_license_expression( + license_expression: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]], document: Document, + parent_id: str, context: ValidationContext = None) -> List[ValidationMessage]: if license_expression in [SpdxNoAssertion(), SpdxNone(), None]: return [] if not context: - context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.LICENSE_EXPRESSION, full_element=license_expression) + context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.LICENSE_EXPRESSION, + full_element=license_expression) validation_messages = [] license_ref_ids: List[str] = [license_ref.license_id for license_ref in document.extracted_licensing_info] diff --git a/tests/spdx/parser/jsonlikedict/test_license_expression_parser.py b/tests/spdx/parser/jsonlikedict/test_license_expression_parser.py index 5eddd06d1..b6d45e486 100644 --- a/tests/spdx/parser/jsonlikedict/test_license_expression_parser.py +++ b/tests/spdx/parser/jsonlikedict/test_license_expression_parser.py @@ -11,7 +11,7 @@ from unittest import TestCase import pytest -from license_expression import Licensing +from license_expression import get_spdx_licensing from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone @@ -19,6 +19,18 @@ from spdx.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser +@pytest.mark.parametrize("license_expression_str, expected_license", + [("First License", get_spdx_licensing().parse("First License")), + ("Second License", get_spdx_licensing().parse("Second License")), + ("NOASSERTION", SpdxNoAssertion()), + ("NONE", SpdxNone())]) +def test_parse_license_expression(license_expression_str, expected_license): + license_expression_parser = LicenseExpressionParser() + license_expression = license_expression_parser.parse_license_expression(license_expression_str) + + assert license_expression == expected_license + + @pytest.mark.parametrize("invalid_license_expression,expected_message", [(56, ["Error parsing LicenseExpression: expression must be a string and not: : 56"]), @@ -30,29 +42,3 @@ def test_parse_invalid_license_expression(invalid_license_expression, expected_m license_expression_parser.parse_license_expression(invalid_license_expression) TestCase().assertCountEqual(err.value.get_messages(), expected_message) - - -def test_parse_license_expressions(): - license_expression_parser = LicenseExpressionParser() - license_expressions_list = ["First License", "Second License", "NONE", "NOASSERTION"] - - license_expressions = license_expression_parser.parse_license_expressions(license_expressions_list) - - assert len(license_expressions) == 4 - TestCase().assertCountEqual(license_expressions, - [Licensing().parse("First License"), Licensing().parse("Second License"), - SpdxNone(), SpdxNoAssertion()]) - - -@pytest.mark.parametrize("invalid_license_expressions, expected_message", - [(["First Expression", 4, 6], - ["Error parsing LicenseExpression: expression must be a string and not: : 4", - "Error parsing LicenseExpression: expression must be a string and not: : 6"]) - ]) -def test_parse_invalid_license_expressions(invalid_license_expressions, expected_message): - license_expression_parser = LicenseExpressionParser() - - with pytest.raises(SPDXParsingError) as err: - license_expression_parser.parse_license_expressions(invalid_license_expressions) - - TestCase().assertCountEqual(err.value.get_messages(), expected_message) diff --git a/tests/spdx/validation/test_license_expression_validator.py b/tests/spdx/validation/test_license_expression_validator.py index b6b89ba25..2bcdef5f1 100644 --- a/tests/spdx/validation/test_license_expression_validator.py +++ b/tests/spdx/validation/test_license_expression_validator.py @@ -47,9 +47,10 @@ def test_none_and_no_assertion(expression): @pytest.mark.parametrize("expression_list", - [SpdxNone(), SpdxNoAssertion(), + [[SpdxNone()], [SpdxNoAssertion()], [get_spdx_licensing().parse("MIT and GPL-3.0-only"), - get_spdx_licensing().parse(FIXTURE_LICENSE_ID)] + get_spdx_licensing().parse(FIXTURE_LICENSE_ID)], + [SpdxNone(), get_spdx_licensing().parse("MIT"), SpdxNoAssertion()] ]) def test_valid_license_expressions(expression_list): document: Document = document_fixture() From 66a3355619e8864d1f398291c5f6761c81c69575 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 3 Mar 2023 12:11:02 +0100 Subject: [PATCH 345/362] [issue-503] fix: correct type for LicenseInfoInSnippet in Snippet Signed-off-by: Meret Behrens --- src/spdx/jsonschema/snippet_converter.py | 4 +--- src/spdx/model/snippet.py | 5 +++-- .../parser/jsonlikedict/snippet_parser.py | 20 +++++++++---------- src/spdx/parser/tagvalue/parser.py | 6 +----- .../writer/rdf/license_expression_writer.py | 4 ++-- tests/spdx/fixtures.py | 4 +++- .../spdx/jsonschema/test_snippet_converter.py | 11 +++++----- tests/spdx/model/test_snippet.py | 8 ++++---- .../jsonlikedict/test_snippet_parser.py | 6 ++++-- .../rdf/data/file_to_test_rdf_parser.rdf.xml | 1 + tests/spdx/parser/rdf/test_snippet_parser.py | 4 +++- .../parser/tagvalue/test_snippet_parser.py | 5 +++-- .../json/expected_results/expected.json | 2 +- tests/spdx/writer/rdf/test_snippet_writer.py | 6 ++++-- 14 files changed, 44 insertions(+), 42 deletions(-) diff --git a/src/spdx/jsonschema/snippet_converter.py b/src/spdx/jsonschema/snippet_converter.py index 90dc9883b..30e6ee9cf 100644 --- a/src/spdx/jsonschema/snippet_converter.py +++ b/src/spdx/jsonschema/snippet_converter.py @@ -48,9 +48,7 @@ def _get_property_value(self, snippet: Snippet, snippet_property: SnippetPropert elif snippet_property == SnippetProperty.LICENSE_CONCLUDED: return apply_if_present(str, snippet.license_concluded) elif snippet_property == SnippetProperty.LICENSE_INFO_IN_SNIPPETS: - if isinstance(snippet.license_info_in_snippet, list): - return [str(license_expression) for license_expression in snippet.license_info_in_snippet] or None - return apply_if_present(str, snippet.license_info_in_snippet) + return [str(license_expression) for license_expression in snippet.license_info_in_snippet] or None elif snippet_property == SnippetProperty.NAME: return snippet.name elif snippet_property == SnippetProperty.RANGES: diff --git a/src/spdx/model/snippet.py b/src/spdx/model/snippet.py index 6a6974a6c..1565dba0b 100644 --- a/src/spdx/model/snippet.py +++ b/src/spdx/model/snippet.py @@ -25,7 +25,7 @@ class Snippet: byte_range: Tuple[int, int] line_range: Optional[Tuple[int, int]] = None license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None - license_info_in_snippet: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = None + license_info_in_snippet: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None license_comment: Optional[str] = None copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None comment: Optional[str] = None @@ -35,8 +35,9 @@ class Snippet: def __init__(self, spdx_id: str, file_spdx_id: str, byte_range: Tuple[int, int], line_range: Optional[Tuple[int, int]] = None, license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, - license_info_in_snippet: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = None, + license_info_in_snippet: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, license_comment: Optional[str] = None, copyright_text: Optional[str] = None, comment: Optional[str] = None, name: Optional[str] = None, attribution_texts: List[str] = None): attribution_texts = [] if attribution_texts is None else attribution_texts + license_info_in_snippet = [] if license_info_in_snippet is None else license_info_in_snippet check_types_and_set_values(self, locals()) diff --git a/src/spdx/parser/jsonlikedict/snippet_parser.py b/src/spdx/parser/jsonlikedict/snippet_parser.py index 264301a5f..b942abe07 100644 --- a/src/spdx/parser/jsonlikedict/snippet_parser.py +++ b/src/spdx/parser/jsonlikedict/snippet_parser.py @@ -42,7 +42,7 @@ def parse_snippet(self, snippet_dict: Dict) -> Snippet: spdx_id: Optional[str] = snippet_dict.get("SPDXID") file_spdx_id: Optional[str] = snippet_dict.get("snippetFromFile") name: Optional[str] = snippet_dict.get("name") - + ranges: Dict = parse_field_or_log_error(logger, snippet_dict.get("ranges", []), self.parse_ranges, default={}) byte_range: Optional[Tuple[Union[int, str], Union[int, str]]] = ranges.get(RangeType.BYTE) line_range: Optional[Tuple[Union[int, str], Union[int, str]]] = ranges.get(RangeType.LINE) @@ -53,15 +53,12 @@ def parse_snippet(self, snippet_dict: Dict) -> Snippet: comment: Optional[str] = snippet_dict.get("comment") copyright_text: Optional[str] = snippet_dict.get("copyrightText") license_comment: Optional[str] = snippet_dict.get("licenseComments") - license_concluded: Optional[Union[ - LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger, snippet_dict.get( - "licenseConcluded"), lambda x: parse_field_or_no_assertion_or_none(x, - self.license_expression_parser.parse_license_expression)) - - license_info: Optional[Union[List[ - LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger, snippet_dict.get( - "licenseInfoInSnippets"), lambda x: parse_field_or_no_assertion_or_none(x, - self.license_expression_parser.parse_license_expressions)) + license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( + logger, snippet_dict.get("licenseConcluded"), self.license_expression_parser.parse_license_expression) + + license_info: List[Union[LicenseExpression], SpdxNoAssertion, SpdxNone] = parse_field_or_log_error( + logger, snippet_dict.get("licenseInfoInSnippets"), self.license_expression_parser.parse_license_expression, + field_is_list=True) if logger.has_messages(): raise SPDXParsingError([f"Error while parsing snippet: {logger.get_messages()}"]) @@ -121,7 +118,8 @@ def validate_pointer_and_get_type(pointer: Dict) -> RangeType: return RangeType.BYTE if "offset" in pointer else RangeType.LINE @staticmethod - def convert_range_from_str(_range: Tuple[Union[int, str], Union[int, str]]) -> Tuple[Union[int, str], Union[int, str]]: + def convert_range_from_str(_range: Tuple[Union[int, str], Union[int, str]]) -> Tuple[ + Union[int, str], Union[int, str]]: # XML does not support integers, so we have to convert from string (if possible) if not _range: return _range diff --git a/src/spdx/parser/tagvalue/parser.py b/src/spdx/parser/tagvalue/parser.py index a7a8ea0c5..b79056cde 100644 --- a/src/spdx/parser/tagvalue/parser.py +++ b/src/spdx/parser/tagvalue/parser.py @@ -407,11 +407,7 @@ def p_snippet_attribution_text(self, p): @grammar_rule("snippet_license_info : SNIPPET_LICENSE_INFO license_or_no_assertion_or_none") def p_snippet_license_info(self, p): - if not self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)): - return - if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): - self.current_element["license_info_in_snippet"] = p[2] - else: + if self.check_that_current_element_matches_class_for_value(Snippet, p.lineno(1)): self.current_element.setdefault("license_info_in_snippet", []).append(p[2]) @grammar_rule("snippet_byte_range : SNIPPET_BYTE_RANGE LINE\n snippet_line_range : SNIPPET_LINE_RANGE LINE") diff --git a/src/spdx/writer/rdf/license_expression_writer.py b/src/spdx/writer/rdf/license_expression_writer.py index aa21ba740..cf4edde7e 100644 --- a/src/spdx/writer/rdf/license_expression_writer.py +++ b/src/spdx/writer/rdf/license_expression_writer.py @@ -23,7 +23,7 @@ def add_license_expression_or_none_or_no_assertion(value: Union[ - List[LicenseExpression], LicenseExpression, SpdxNoAssertion, SpdxNone], graph: Graph, parent: Node, predicate: Node, + List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]], LicenseExpression, SpdxNoAssertion, SpdxNone], graph: Graph, parent: Node, predicate: Node, doc_namespace: str): if isinstance(value, SpdxNoAssertion): graph.add((parent, predicate, SPDX_NAMESPACE.noassertion)) @@ -33,7 +33,7 @@ def add_license_expression_or_none_or_no_assertion(value: Union[ return if isinstance(value, list): for license_expression in value: - add_license_expression_to_graph(license_expression, graph, parent, predicate, doc_namespace) + add_license_expression_or_none_or_no_assertion(license_expression, graph, parent, predicate, doc_namespace) if isinstance(value, LicenseExpression): add_license_expression_to_graph(value, graph, parent, predicate, doc_namespace) diff --git a/tests/spdx/fixtures.py b/tests/spdx/fixtures.py index 8df947a71..4e7fb1d4b 100644 --- a/tests/spdx/fixtures.py +++ b/tests/spdx/fixtures.py @@ -23,6 +23,8 @@ ExternalPackageRefCategory from spdx.model.relationship import Relationship, RelationshipType from spdx.model.snippet import Snippet +from spdx.model.spdx_no_assertion import SpdxNoAssertion +from spdx.model.spdx_none import SpdxNone from spdx.model.version import Version """Utility methods to create data model instances. All properties have valid defaults, so they don't need to be @@ -116,7 +118,7 @@ def snippet_fixture(spdx_id="SPDXRef-Snippet", file_spdx_id="SPDXRef-File", byte copyright_text="licenseCopyrightText", comment="snippetComment", name="snippetName", attribution_texts=None) -> Snippet: license_info_in_snippet = [get_spdx_licensing().parse("MIT"), get_spdx_licensing().parse( - "GPL-2.0")] if license_info_in_snippet is None else license_info_in_snippet + "GPL-2.0"), SpdxNone()] if license_info_in_snippet is None else license_info_in_snippet attribution_texts = ["snippetAttributionText"] if attribution_texts is None else attribution_texts return Snippet(spdx_id=spdx_id, file_spdx_id=file_spdx_id, byte_range=byte_range, line_range=line_range, license_concluded=license_concluded, license_info_in_snippet=license_info_in_snippet, diff --git a/tests/spdx/jsonschema/test_snippet_converter.py b/tests/spdx/jsonschema/test_snippet_converter.py index 9a12b34f4..7752a2a6b 100644 --- a/tests/spdx/jsonschema/test_snippet_converter.py +++ b/tests/spdx/jsonschema/test_snippet_converter.py @@ -114,25 +114,24 @@ def test_null_values(converter: SnippetConverter): def test_spdx_no_assertion(converter: SnippetConverter): - snippet = snippet_fixture(license_concluded=SpdxNoAssertion(), license_info_in_snippet=SpdxNoAssertion()) + snippet = snippet_fixture(license_concluded=SpdxNoAssertion(), license_info_in_snippet=[SpdxNoAssertion()]) document = Document(creation_info_fixture(), snippets=[snippet]) converted_dict = converter.convert(snippet, document) assert converted_dict[converter.json_property_name(SnippetProperty.LICENSE_CONCLUDED)] == SPDX_NO_ASSERTION_STRING - assert converted_dict[ - converter.json_property_name(SnippetProperty.LICENSE_INFO_IN_SNIPPETS)] == SPDX_NO_ASSERTION_STRING + assert converted_dict[converter.json_property_name(SnippetProperty.LICENSE_INFO_IN_SNIPPETS)] == [ + SPDX_NO_ASSERTION_STRING] def test_spdx_none(converter: SnippetConverter): - snippet = snippet_fixture(license_concluded=SpdxNone(), license_info_in_snippet=SpdxNone()) + snippet = snippet_fixture(license_concluded=SpdxNone(), license_info_in_snippet=[SpdxNone()]) document = Document(creation_info_fixture(), snippets=[snippet]) converted_dict = converter.convert(snippet, document) assert converted_dict[converter.json_property_name(SnippetProperty.LICENSE_CONCLUDED)] == SPDX_NONE_STRING - assert converted_dict[ - converter.json_property_name(SnippetProperty.LICENSE_INFO_IN_SNIPPETS)] == SPDX_NONE_STRING + assert converted_dict[converter.json_property_name(SnippetProperty.LICENSE_INFO_IN_SNIPPETS)] == [SPDX_NONE_STRING] def test_snippet_annotations(converter: SnippetConverter): diff --git a/tests/spdx/model/test_snippet.py b/tests/spdx/model/test_snippet.py index 23bc2b625..77219c5de 100644 --- a/tests/spdx/model/test_snippet.py +++ b/tests/spdx/model/test_snippet.py @@ -6,14 +6,14 @@ def test_correct_initialization(): - snippet = Snippet("id", "file_id", (200, 400), (20, 40), SpdxNone(), SpdxNoAssertion(), "comment on license", + snippet = Snippet("id", "file_id", (200, 400), (20, 40), SpdxNone(), [SpdxNoAssertion()], "comment on license", "copyright", "comment", "name", ["attribution"]) assert snippet.spdx_id == "id" assert snippet.file_spdx_id == "file_id" assert snippet.byte_range == (200, 400) assert snippet.line_range == (20, 40) assert snippet.license_concluded == SpdxNone() - assert snippet.license_info_in_snippet == SpdxNoAssertion() + assert snippet.license_info_in_snippet == [SpdxNoAssertion()] assert snippet.license_comment == "comment on license" assert snippet.copyright_text == "copyright" assert snippet.comment == "comment" @@ -28,7 +28,7 @@ def test_correct_initialization_with_default_values(): assert snippet.byte_range == (200, 400) assert snippet.line_range is None assert snippet.license_concluded is None - assert snippet.license_info_in_snippet is None + assert snippet.license_info_in_snippet == [] assert snippet.license_comment is None assert snippet.copyright_text is None assert snippet.comment is None @@ -63,7 +63,7 @@ def test_wrong_type_in_license_concluded(): def test_wrong_type_in_license_info_in_snippet(): with pytest.raises(TypeError): - Snippet("id", "file_id", (200, 400), license_info_in_snippet=[SpdxNoAssertion()]) + Snippet("id", "file_id", (200, 400), license_info_in_snippet=SpdxNoAssertion()) def test_wrong_type_in_license_comment(): diff --git a/tests/spdx/parser/jsonlikedict/test_snippet_parser.py b/tests/spdx/parser/jsonlikedict/test_snippet_parser.py index 2663b6e61..27f778caa 100644 --- a/tests/spdx/parser/jsonlikedict/test_snippet_parser.py +++ b/tests/spdx/parser/jsonlikedict/test_snippet_parser.py @@ -13,6 +13,8 @@ import pytest from license_expression import Licensing + +from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.parser.error import SPDXParsingError from spdx.parser.jsonlikedict.snippet_parser import SnippetParser @@ -26,7 +28,7 @@ def test_parse_snippet(): "copyrightText": "Copyright 2008-2010 John Smith", "licenseComments": "The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz.", "licenseConcluded": "GPL-2.0-only", - "licenseInfoInSnippets": ["GPL-2.0-only"], + "licenseInfoInSnippets": ["GPL-2.0-only", "NOASSERTION"], "name": "from linux kernel", "ranges": [{ "endPointer": { @@ -60,7 +62,7 @@ def test_parse_snippet(): assert snippet.byte_range == (310, 420) assert snippet.line_range == (5, 23) assert snippet.file_spdx_id == "SPDXRef-DoapSource" - assert snippet.license_info_in_snippet == [Licensing().parse("GPL-2.0-only")] + assert snippet.license_info_in_snippet == [Licensing().parse("GPL-2.0-only"), SpdxNoAssertion()] assert snippet.license_concluded == Licensing().parse("GPL-2.0-only") assert snippet.attribution_texts == ["Some example attibution text."] diff --git a/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml b/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml index 26e6cb534..4482361df 100644 --- a/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml +++ b/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml @@ -228,6 +228,7 @@ + snippetAttributionText snippetName snippetLicenseComment diff --git a/tests/spdx/parser/rdf/test_snippet_parser.py b/tests/spdx/parser/rdf/test_snippet_parser.py index 1bf80c2ed..b0af0bedf 100644 --- a/tests/spdx/parser/rdf/test_snippet_parser.py +++ b/tests/spdx/parser/rdf/test_snippet_parser.py @@ -15,6 +15,7 @@ from license_expression import get_spdx_licensing from rdflib import Graph, RDF, BNode, Literal +from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.parser.error import SPDXParsingError from spdx.parser.rdf.snippet_parser import parse_snippet, parse_ranges from spdx.rdfschema.namespace import SPDX_NAMESPACE, POINTER_NAMESPACE @@ -33,7 +34,8 @@ def test_parse_snippet(): assert snippet.line_range == (3, 4) assert snippet.license_concluded == get_spdx_licensing().parse("MIT AND GPL-2.0") TestCase().assertCountEqual(snippet.license_info_in_snippet, - [get_spdx_licensing().parse("MIT"), get_spdx_licensing().parse("GPL-2.0")]) + [get_spdx_licensing().parse("MIT"), get_spdx_licensing().parse("GPL-2.0"), + SpdxNoAssertion()]) assert snippet.license_comment == "snippetLicenseComment" assert snippet.copyright_text == "licenseCopyrightText" assert snippet.comment == "snippetComment" diff --git a/tests/spdx/parser/tagvalue/test_snippet_parser.py b/tests/spdx/parser/tagvalue/test_snippet_parser.py index 5ce2b0f74..94c54c39f 100644 --- a/tests/spdx/parser/tagvalue/test_snippet_parser.py +++ b/tests/spdx/parser/tagvalue/test_snippet_parser.py @@ -13,6 +13,7 @@ import pytest from license_expression import get_spdx_licensing +from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.parser.error import SPDXParsingError from spdx.parser.tagvalue.parser import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR @@ -28,7 +29,7 @@ def test_parse_snippet(): "SnippetName: from linux kernel", "SnippetFromFileSPDXID: SPDXRef-DoapSource", "SnippetLicenseConcluded: Apache-2.0", - "LicenseInfoInSnippet: Apache-2.0", + "LicenseInfoInSnippet: NOASSERTION", "SnippetByteRange: 310:420", "SnippetLineRange: 5:23", "SnippetAttributionText: This is a text\nthat spans multiple lines.", @@ -46,7 +47,7 @@ def test_parse_snippet(): assert snippet.license_comment == "Some lic comment." assert snippet.file_spdx_id == "SPDXRef-DoapSource" assert snippet.license_concluded == get_spdx_licensing().parse("Apache-2.0") - assert snippet.license_info_in_snippet == [get_spdx_licensing().parse("Apache-2.0")] + assert snippet.license_info_in_snippet == [SpdxNoAssertion()] assert snippet.byte_range[0] == 310 assert snippet.byte_range[1] == 420 assert snippet.line_range[0] == 5 diff --git a/tests/spdx/writer/json/expected_results/expected.json b/tests/spdx/writer/json/expected_results/expected.json index c441e917d..f94964570 100644 --- a/tests/spdx/writer/json/expected_results/expected.json +++ b/tests/spdx/writer/json/expected_results/expected.json @@ -138,7 +138,7 @@ "copyrightText": "licenseCopyrightText", "licenseComments": "snippetLicenseComment", "licenseConcluded": "MIT AND GPL-2.0-only", - "licenseInfoInSnippets": ["MIT", "GPL-2.0-only"], + "licenseInfoInSnippets": ["MIT", "GPL-2.0-only", "NONE"], "name": "snippetName", "ranges": [ { diff --git a/tests/spdx/writer/rdf/test_snippet_writer.py b/tests/spdx/writer/rdf/test_snippet_writer.py index 03591c2ff..1951563aa 100644 --- a/tests/spdx/writer/rdf/test_snippet_writer.py +++ b/tests/spdx/writer/rdf/test_snippet_writer.py @@ -10,7 +10,7 @@ # limitations under the License. import pytest from rdflib import Graph, URIRef, RDF, Literal, RDFS -from spdx.rdfschema.namespace import SPDX_NAMESPACE, POINTER_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE, POINTER_NAMESPACE, LICENSE_NAMESPACE from spdx.writer.rdf.snippet_writer import add_snippet_to_graph, add_range_to_graph from tests.spdx.fixtures import snippet_fixture @@ -25,7 +25,9 @@ def test_add_snippet_to_graph(): assert (URIRef("docNamespace#SPDXRef-Snippet"), RDF.type, SPDX_NAMESPACE.Snippet) in graph assert (None, SPDX_NAMESPACE.snippetFromFile, URIRef(f"docNamespace#{snippet.file_spdx_id}")) in graph assert (None, SPDX_NAMESPACE.licenseConcluded, None) in graph - assert (None, SPDX_NAMESPACE.licenseInfoInSnippet, None) in graph + assert (None, SPDX_NAMESPACE.licenseInfoInSnippet, LICENSE_NAMESPACE.MIT) in graph + assert (None, SPDX_NAMESPACE.licenseInfoInSnippet, LICENSE_NAMESPACE["GPL-2.0-only"]) in graph + assert (None, SPDX_NAMESPACE.licenseInfoInSnippet, SPDX_NAMESPACE.none) in graph assert (None, SPDX_NAMESPACE.licenseComments, Literal(snippet.license_comment)) in graph assert (None, SPDX_NAMESPACE.copyrightText, Literal(snippet.copyright_text)) in graph assert (None, SPDX_NAMESPACE.name, Literal(snippet.name)) in graph From dcfc4719f7ccfc1c61eb7a9f75ab363b73f32a85 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 3 Mar 2023 14:01:47 +0100 Subject: [PATCH 346/362] [issue-503] fix: correct type for LicenseInfoFromFiles in Package Signed-off-by: Meret Behrens --- src/spdx/jsonschema/package_converter.py | 4 +- src/spdx/model/package.py | 5 +-- .../parser/jsonlikedict/package_parser.py | 21 ++++----- src/spdx/parser/tagvalue/parser.py | 6 +-- tests/spdx/fixtures.py | 2 +- .../spdx/jsonschema/test_package_converter.py | 8 ++-- tests/spdx/model/test_package.py | 9 ++-- .../jsonlikedict/test_package_parser.py | 5 ++- .../rdf/data/file_to_test_rdf_parser.rdf.xml | 45 +------------------ tests/spdx/parser/rdf/test_package_parser.py | 5 ++- .../parser/tagvalue/test_package_parser.py | 7 ++- .../spdx/validation/test_package_validator.py | 8 ++-- .../json/expected_results/expected.json | 2 +- tests/spdx/writer/rdf/test_package_writer.py | 6 ++- .../writer/tagvalue/test_package_writer.py | 3 +- 15 files changed, 46 insertions(+), 90 deletions(-) diff --git a/src/spdx/jsonschema/package_converter.py b/src/spdx/jsonschema/package_converter.py index 5929f7794..c595e076f 100644 --- a/src/spdx/jsonschema/package_converter.py +++ b/src/spdx/jsonschema/package_converter.py @@ -85,9 +85,7 @@ def _get_property_value(self, package: Package, package_property: PackagePropert elif package_property == PackageProperty.LICENSE_DECLARED: return apply_if_present(str, package.license_declared) elif package_property == PackageProperty.LICENSE_INFO_FROM_FILES: - if isinstance(package.license_info_from_files, list): - return [str(license_expression) for license_expression in package.license_info_from_files] or None - return apply_if_present(str, package.license_info_from_files) + return [str(license_expression) for license_expression in package.license_info_from_files] or None elif package_property == PackageProperty.NAME: return package.name elif package_property == PackageProperty.ORIGINATOR: diff --git a/src/spdx/model/package.py b/src/spdx/model/package.py index a6a4eaf4c..f725718d0 100644 --- a/src/spdx/model/package.py +++ b/src/spdx/model/package.py @@ -92,8 +92,7 @@ class Package: homepage: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None source_info: Optional[str] = None license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None - license_info_from_files: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = field( - default_factory=list) + license_info_from_files: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = field(default_factory=list) license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None license_comment: Optional[str] = None copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None @@ -115,7 +114,7 @@ def __init__(self, spdx_id: str, name: str, download_location: Union[str, SpdxNo checksums: List[Checksum] = None, homepage: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None, source_info: Optional[str] = None, license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, - license_info_from_files: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = None, + license_info_from_files: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, license_comment: Optional[str] = None, copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None, diff --git a/src/spdx/parser/jsonlikedict/package_parser.py b/src/spdx/parser/jsonlikedict/package_parser.py index 57300337d..50049092d 100644 --- a/src/spdx/parser/jsonlikedict/package_parser.py +++ b/src/spdx/parser/jsonlikedict/package_parser.py @@ -62,9 +62,9 @@ def parse_package(self, package_dict: Dict) -> Package: files_analyzed: Optional[Union[bool, str]] = package_dict.get("filesAnalyzed") - if files_analyzed is None: # default value is True + if files_analyzed is None: # default value is True files_analyzed = True - elif isinstance(files_analyzed, str): # XML does not support boolean typed values + elif isinstance(files_analyzed, str): # XML does not support boolean typed values if files_analyzed.lower() == "true": files_analyzed = True elif files_analyzed.lower() == "false": @@ -73,19 +73,14 @@ def parse_package(self, package_dict: Dict) -> Package: homepage: Optional[str] = package_dict.get("homepage") license_comments: Optional[str] = package_dict.get("licenseComments") license_concluded = parse_field_or_log_error( - logger, package_dict.get("licenseConcluded"), - lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expression), - None) + logger, package_dict.get("licenseConcluded"), self.license_expression_parser.parse_license_expression) license_declared: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger, package_dict.get("licenseDeclared"), - lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expression)) - - license_info_from_file: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = \ - parse_field_or_log_error( - logger, package_dict.get("licenseInfoFromFiles"), - lambda x: parse_field_or_no_assertion_or_none(x, - self.license_expression_parser.parse_license_expressions)) + logger, package_dict.get("licenseDeclared"), self.license_expression_parser.parse_license_expression) + + license_info_from_file: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( + logger, package_dict.get("licenseInfoFromFiles"), self.license_expression_parser.parse_license_expression, + field_is_list=True) originator: Optional[Union[Actor, SpdxNoAssertion]] = parse_field_or_log_error( logger, package_dict.get("originator"), lambda x: parse_field_or_no_assertion(x, self.actor_parser.parse_actor)) diff --git a/src/spdx/parser/tagvalue/parser.py b/src/spdx/parser/tagvalue/parser.py index b79056cde..e37c62c34 100644 --- a/src/spdx/parser/tagvalue/parser.py +++ b/src/spdx/parser/tagvalue/parser.py @@ -342,11 +342,7 @@ def p_pkg_external_refs(self, p): @grammar_rule("pkg_license_info : PKG_LICENSE_INFO license_or_no_assertion_or_none") def p_pkg_license_info_from_file(self, p): - if not self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)): - return - if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): - self.current_element["license_info_from_files"] = p[2] - else: + if self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)): self.current_element.setdefault("license_info_from_files", []).append(p[2]) @grammar_rule("pkg_checksum : PKG_CHECKSUM CHECKSUM") diff --git a/tests/spdx/fixtures.py b/tests/spdx/fixtures.py index 4e7fb1d4b..86ca2b78f 100644 --- a/tests/spdx/fixtures.py +++ b/tests/spdx/fixtures.py @@ -87,7 +87,7 @@ def package_fixture(spdx_id="SPDXRef-Package", name="packageName", download_loca valid_until_date=datetime(2022, 12, 3)) -> Package: checksums = [checksum_fixture()] if checksums is None else checksums license_info_from_files = [get_spdx_licensing().parse("MIT"), get_spdx_licensing().parse( - "GPL-2.0")] if license_info_from_files is None else license_info_from_files + "GPL-2.0"), SpdxNoAssertion()] if license_info_from_files is None else license_info_from_files external_references = [external_package_ref_fixture()] if external_references is None else external_references attribution_texts = ["packageAttributionText"] if attribution_texts is None else attribution_texts return Package(spdx_id=spdx_id, name=name, download_location=download_location, version=version, diff --git a/tests/spdx/jsonschema/test_package_converter.py b/tests/spdx/jsonschema/test_package_converter.py index eb88da3eb..4c2b18b37 100644 --- a/tests/spdx/jsonschema/test_package_converter.py +++ b/tests/spdx/jsonschema/test_package_converter.py @@ -188,7 +188,7 @@ def test_null_values(converter: PackageConverter): def test_spdx_no_assertion(converter: PackageConverter): package = package_fixture(download_location=SpdxNoAssertion(), supplier=SpdxNoAssertion(), originator=SpdxNoAssertion(), homepage=SpdxNoAssertion(), - license_concluded=SpdxNoAssertion(), license_info_from_files=SpdxNoAssertion(), + license_concluded=SpdxNoAssertion(), license_info_from_files=[SpdxNoAssertion()], license_declared=SpdxNoAssertion(), copyright_text=SpdxNoAssertion()) document = Document(creation_info_fixture(), packages=[package]) @@ -201,14 +201,14 @@ def test_spdx_no_assertion(converter: PackageConverter): assert converted_dict[converter.json_property_name(PackageProperty.HOMEPAGE)] == SPDX_NO_ASSERTION_STRING assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_CONCLUDED)] == SPDX_NO_ASSERTION_STRING assert converted_dict[ - converter.json_property_name(PackageProperty.LICENSE_INFO_FROM_FILES)] == SPDX_NO_ASSERTION_STRING + converter.json_property_name(PackageProperty.LICENSE_INFO_FROM_FILES)] == [SPDX_NO_ASSERTION_STRING] assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_DECLARED)] == SPDX_NO_ASSERTION_STRING assert converted_dict[converter.json_property_name(PackageProperty.COPYRIGHT_TEXT)] == SPDX_NO_ASSERTION_STRING def test_spdx_none(converter: PackageConverter): package = package_fixture(download_location=SpdxNone(), homepage=SpdxNone(), - license_concluded=SpdxNone(), license_info_from_files=SpdxNone(), + license_concluded=SpdxNone(), license_info_from_files=[SpdxNone()], license_declared=SpdxNone(), copyright_text=SpdxNone()) document = Document(creation_info_fixture(), packages=[package]) @@ -218,7 +218,7 @@ def test_spdx_none(converter: PackageConverter): assert converted_dict[converter.json_property_name(PackageProperty.DOWNLOAD_LOCATION)] == SPDX_NONE_STRING assert converted_dict[converter.json_property_name(PackageProperty.HOMEPAGE)] == SPDX_NONE_STRING assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_CONCLUDED)] == SPDX_NONE_STRING - assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_INFO_FROM_FILES)] == SPDX_NONE_STRING + assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_INFO_FROM_FILES)] == [SPDX_NONE_STRING] assert converted_dict[converter.json_property_name(PackageProperty.LICENSE_DECLARED)] == SPDX_NONE_STRING assert converted_dict[converter.json_property_name(PackageProperty.COPYRIGHT_TEXT)] == SPDX_NONE_STRING diff --git a/tests/spdx/model/test_package.py b/tests/spdx/model/test_package.py index cf05cf89f..a2fcf7961 100644 --- a/tests/spdx/model/test_package.py +++ b/tests/spdx/model/test_package.py @@ -16,9 +16,10 @@ @mock.patch('spdx.model.actor.Actor', autospec=True) def test_correct_initialization(actor, verif_code, checksum, ext_ref): package = Package("id", "name", SpdxNoAssertion(), "version", "file_name", SpdxNoAssertion(), actor, True, - verif_code, [checksum], "homepage", "source_info", None, [Licensing().parse("license and expression")], - SpdxNone(), "comment on license", "copyright", "summary", "description", "comment", - [ext_ref, ext_ref], ["text"], PackagePurpose.OTHER, datetime(2022, 1, 1), None, None) + verif_code, [checksum], "homepage", "source_info", None, + [Licensing().parse("license and expression"), SpdxNoAssertion()], SpdxNone(), + "comment on license", "copyright", "summary", "description", "comment", [ext_ref, ext_ref], + ["text"], PackagePurpose.OTHER, datetime(2022, 1, 1), None, None) assert package.spdx_id == "id" assert package.name == "name" assert package.download_location == SpdxNoAssertion() @@ -32,7 +33,7 @@ def test_correct_initialization(actor, verif_code, checksum, ext_ref): assert package.homepage == "homepage" assert package.source_info == "source_info" assert package.license_concluded is None - assert package.license_info_from_files == [Licensing().parse("license and expression")] + assert package.license_info_from_files == [Licensing().parse("license and expression"), SpdxNoAssertion()] assert package.license_declared == SpdxNone() assert package.license_comment == "comment on license" assert package.copyright_text == "copyright" diff --git a/tests/spdx/parser/jsonlikedict/test_package_parser.py b/tests/spdx/parser/jsonlikedict/test_package_parser.py index a9813f2fc..809bab34c 100644 --- a/tests/spdx/parser/jsonlikedict/test_package_parser.py +++ b/tests/spdx/parser/jsonlikedict/test_package_parser.py @@ -17,6 +17,7 @@ from spdx.model.checksum import Checksum, ChecksumAlgorithm from license_expression import Licensing from spdx.model.package import PackageVerificationCode, ExternalPackageRef, ExternalPackageRefCategory, PackagePurpose +from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.parser.error import SPDXParsingError from spdx.parser.jsonlikedict.dict_parsing_functions import parse_list_of_elements from spdx.parser.jsonlikedict.package_parser import PackageParser @@ -62,7 +63,7 @@ def test_parse_package(): "licenseComments": "The license for this project changed with the release of version x.y. The version of the project included here post-dates the license change.", "licenseConcluded": "(LGPL-2.0-only OR LicenseRef-3)", "licenseDeclared": "(LGPL-2.0-only AND LicenseRef-3)", - "licenseInfoFromFiles": ["GPL-2.0-only", "LicenseRef-2", "LicenseRef-1"], + "licenseInfoFromFiles": ["GPL-2.0-only", "LicenseRef-2", "LicenseRef-1", "NOASSERTION"], "name": "glibc", "originator": "Organization: ExampleCodeInspect (contact@example.com)", "packageFileName": "glibc-2.11.1.tar.gz", @@ -104,7 +105,7 @@ def test_parse_package(): assert package.license_concluded == Licensing().parse("(LGPL-2.0-only OR LicenseRef-3)") TestCase().assertCountEqual(package.license_info_from_files, [Licensing().parse("GPL-2.0-only"), Licensing().parse("LicenseRef-2"), - Licensing().parse("LicenseRef-1")]) + Licensing().parse("LicenseRef-1"), SpdxNoAssertion()]) assert package.license_declared == Licensing().parse("(LGPL-2.0-only AND LicenseRef-3)") assert package.license_comment == "The license for this project changed with the release of version x.y. The version of the project included here post-dates the license change." assert package.copyright_text == "Copyright 2008-2010 John Smith" diff --git a/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml b/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml index 4482361df..436fcc4ba 100644 --- a/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml +++ b/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml @@ -93,6 +93,7 @@ https://download.com + Person: originatorName (some@mail.com) @@ -138,48 +139,7 @@ packageName - 2022-12-01T00:00:00Z - Person: supplierName (some@mail.com) - - - - 71c4025dd9897b364f3ebbb42c484ff43d00791c - - - 12.2 - - - 2022-12-03T00:00:00Z - https://homepage.com - true - ./packageFileName - - - - - - - - - ./exclude.py - 85ed0817af83a24ad8da68c2b5094de69833983c - - - - packageCopyrightText http://differentdownload.com - sourceInfo - packageSummary - packageAttributionText - packageDescription - - - - - - - Person: originatorName (some@mail.com) - @@ -188,9 +148,6 @@ - packageComment - 2022-12-02T00:00:00Z - packageLicenseComment diff --git a/tests/spdx/parser/rdf/test_package_parser.py b/tests/spdx/parser/rdf/test_package_parser.py index 5999e2ed8..f219496a2 100644 --- a/tests/spdx/parser/rdf/test_package_parser.py +++ b/tests/spdx/parser/rdf/test_package_parser.py @@ -18,12 +18,14 @@ from spdx.model.actor import Actor, ActorType from spdx.model.checksum import ChecksumAlgorithm, Checksum from spdx.model.package import PackagePurpose, PackageVerificationCode, ExternalPackageRefCategory +from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.parser.rdf.package_parser import parse_package, parse_external_package_ref from spdx.rdfschema.namespace import SPDX_NAMESPACE def test_package_parser(): graph = Graph().parse(os.path.join(os.path.dirname(__file__), "data/file_to_test_rdf_parser.rdf.xml")) + # we have two packages in the test file, graph.value() will return the first package package_node = graph.value(predicate=RDF.type, object=SPDX_NAMESPACE.Package) doc_namespace = "https://some.namespace" @@ -41,7 +43,8 @@ def test_package_parser(): assert package.license_concluded == get_spdx_licensing().parse("MIT AND GPL-2.0") assert package.license_declared == get_spdx_licensing().parse("MIT AND GPL-2.0") TestCase().assertCountEqual(package.license_info_from_files, - [get_spdx_licensing().parse("MIT"), get_spdx_licensing().parse("GPL-2.0")]) + [get_spdx_licensing().parse("MIT"), get_spdx_licensing().parse("GPL-2.0"), + SpdxNoAssertion()]) assert package.license_comment == "packageLicenseComment" assert package.copyright_text == "packageCopyrightText" assert package.verification_code == PackageVerificationCode(value="85ed0817af83a24ad8da68c2b5094de69833983c", diff --git a/tests/spdx/parser/tagvalue/test_package_parser.py b/tests/spdx/parser/tagvalue/test_package_parser.py index 84b61f0d0..39d6eeef3 100644 --- a/tests/spdx/parser/tagvalue/test_package_parser.py +++ b/tests/spdx/parser/tagvalue/test_package_parser.py @@ -15,6 +15,7 @@ from license_expression import get_spdx_licensing from spdx.model.package import ExternalPackageRef, ExternalPackageRefCategory, PackagePurpose +from spdx.model.spdx_none import SpdxNone from spdx.parser.error import SPDXParsingError from spdx.parser.tagvalue.parser import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR @@ -42,6 +43,7 @@ def test_parse_package(): "PackageLicenseConcluded: (LicenseRef-2.0 and Apache-2.0)", "PackageLicenseInfoFromFiles: Apache-1.0", "PackageLicenseInfoFromFiles: Apache-2.0", + "PackageLicenseInfoFromFiles: NONE", "PackageLicenseComments: License Comments", "ExternalRef: SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:", "ExternalRefComment: Some comment about the package.", @@ -57,9 +59,10 @@ def test_parse_package(): assert package.name == "Test" assert package.spdx_id == "SPDXRef-Package" assert package.version == "1:22.36.1-8+deb11u1" - assert len(package.license_info_from_files) == 2 + assert len(package.license_info_from_files) == 3 TestCase().assertCountEqual(package.license_info_from_files, [get_spdx_licensing().parse("Apache-1.0"), - get_spdx_licensing().parse("Apache-2.0")]) + get_spdx_licensing().parse("Apache-2.0"), + SpdxNone()]) assert package.license_concluded == get_spdx_licensing().parse("LicenseRef-2.0 AND Apache-2.0") assert package.files_analyzed is True assert package.comment == "Comment on the package." diff --git a/tests/spdx/validation/test_package_validator.py b/tests/spdx/validation/test_package_validator.py index 72391fc8e..fe054201a 100644 --- a/tests/spdx/validation/test_package_validator.py +++ b/tests/spdx/validation/test_package_validator.py @@ -35,12 +35,12 @@ def test_valid_package(): [(package_fixture(files_analyzed=False, verification_code=package_verification_code_fixture(), license_info_from_files=[]), f'verification_code must be None if files_analyzed is False, but is: {package_verification_code_fixture()}'), - (package_fixture(files_analyzed=False, license_info_from_files=SpdxNone(), + (package_fixture(files_analyzed=False, license_info_from_files=[SpdxNone()], verification_code=None), - 'license_info_from_files must be None if files_analyzed is False, but is: NONE'), - (package_fixture(files_analyzed=False, license_info_from_files=SpdxNoAssertion(), + 'license_info_from_files must be None if files_analyzed is False, but is: [NONE]'), + (package_fixture(files_analyzed=False, license_info_from_files=[SpdxNoAssertion()], verification_code=None), - 'license_info_from_files must be None if files_analyzed is False, but is: NOASSERTION'), + 'license_info_from_files must be None if files_analyzed is False, but is: [NOASSERTION]'), (package_fixture(files_analyzed=False, license_info_from_files=[Licensing().parse("some_license")], verification_code=None), diff --git a/tests/spdx/writer/json/expected_results/expected.json b/tests/spdx/writer/json/expected_results/expected.json index f94964570..8b5b08d5a 100644 --- a/tests/spdx/writer/json/expected_results/expected.json +++ b/tests/spdx/writer/json/expected_results/expected.json @@ -101,7 +101,7 @@ "licenseComments": "packageLicenseComment", "licenseConcluded": "MIT AND GPL-2.0-only", "licenseDeclared": "MIT AND GPL-2.0-only", - "licenseInfoFromFiles": ["MIT", "GPL-2.0-only"], + "licenseInfoFromFiles": ["MIT", "GPL-2.0-only", "NOASSERTION"], "name": "packageName", "originator": "Person: originatorName (some@mail.com)", "packageFileName": "./packageFileName", diff --git a/tests/spdx/writer/rdf/test_package_writer.py b/tests/spdx/writer/rdf/test_package_writer.py index abec88493..7f57a7b4b 100644 --- a/tests/spdx/writer/rdf/test_package_writer.py +++ b/tests/spdx/writer/rdf/test_package_writer.py @@ -15,7 +15,7 @@ from spdx.datetime_conversions import datetime_to_iso_string from spdx.writer.rdf.package_writer import add_package_to_graph, add_external_package_ref_to_graph, \ add_package_verification_code_to_graph -from spdx.rdfschema.namespace import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE, LICENSE_NAMESPACE from tests.spdx.fixtures import package_fixture, external_package_ref_fixture, package_verification_code_fixture @@ -38,7 +38,9 @@ def test_add_package_to_graph(): assert (None, DOAP.homepage, Literal(package.homepage)) in graph assert (None, SPDX_NAMESPACE.sourceInfo, Literal(package.source_info)) in graph assert (None, SPDX_NAMESPACE.licenseConcluded, None) in graph - assert (None, SPDX_NAMESPACE.licenseInfoFromFiles, None) in graph + assert (None, SPDX_NAMESPACE.licenseInfoFromFiles, LICENSE_NAMESPACE.MIT) in graph + assert (None, SPDX_NAMESPACE.licenseInfoFromFiles, LICENSE_NAMESPACE["GPL-2.0-only"]) in graph + assert (None, SPDX_NAMESPACE.licenseInfoFromFiles, SPDX_NAMESPACE.noassertion) in graph assert (None, SPDX_NAMESPACE.licenseDeclared, None) in graph assert (None, SPDX_NAMESPACE.licenseComments, Literal(package.license_comment)) in graph assert (None, SPDX_NAMESPACE.copyrightText, Literal(package.copyright_text)) in graph diff --git a/tests/spdx/writer/tagvalue/test_package_writer.py b/tests/spdx/writer/tagvalue/test_package_writer.py index 09d869315..60570d269 100644 --- a/tests/spdx/writer/tagvalue/test_package_writer.py +++ b/tests/spdx/writer/tagvalue/test_package_writer.py @@ -17,7 +17,7 @@ def test_package_writer(): package = package_fixture() - mock: MagicMock = mock_open() + mock: MagicMock = mock_open() with patch(f"{__name__}.open", mock, create=True): with open("foo", "w") as file: write_package(package, file) @@ -41,6 +41,7 @@ def test_package_writer(): call("PackageLicenseConcluded: MIT AND GPL-2.0-only\n"), call("PackageLicenseInfoFromFiles: MIT\n"), call("PackageLicenseInfoFromFiles: GPL-2.0-only\n"), + call('PackageLicenseInfoFromFiles: NOASSERTION\n'), call("PackageLicenseDeclared: MIT AND GPL-2.0-only\n"), call("PackageLicenseComments: packageLicenseComment\n"), call("PackageCopyrightText: packageCopyrightText\n"), From 44250c1c463c0640103ac0d5194edbccb5227049 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 3 Mar 2023 14:11:43 +0100 Subject: [PATCH 347/362] [issue-503] fix: correct type for LicenseInfoInFile in File Signed-off-by: Meret Behrens --- src/spdx/jsonschema/file_converter.py | 4 +--- src/spdx/model/file.py | 5 ++--- src/spdx/parser/jsonlikedict/file_parser.py | 16 +++++++--------- src/spdx/parser/tagvalue/parser.py | 8 ++------ tests/spdx/fixtures.py | 3 ++- tests/spdx/jsonschema/test_file_converter.py | 12 ++++++------ tests/spdx/model/test_file.py | 4 ++-- .../spdx/parser/jsonlikedict/test_file_parser.py | 7 +++++-- .../rdf/data/file_to_test_rdf_parser.rdf.xml | 1 + tests/spdx/parser/rdf/test_file_parser.py | 4 +++- tests/spdx/parser/tagvalue/test_file_parser.py | 10 +++++++--- .../writer/json/expected_results/expected.json | 2 +- tests/spdx/writer/rdf/test_file_writer.py | 6 ++++-- 13 files changed, 43 insertions(+), 39 deletions(-) diff --git a/src/spdx/jsonschema/file_converter.py b/src/spdx/jsonschema/file_converter.py index f93704307..0d339c890 100644 --- a/src/spdx/jsonschema/file_converter.py +++ b/src/spdx/jsonschema/file_converter.py @@ -64,9 +64,7 @@ def _get_property_value(self, file: Any, file_property: FileProperty, document: elif file_property == FileProperty.LICENSE_CONCLUDED: return apply_if_present(str, file.license_concluded) elif file_property == FileProperty.LICENSE_INFO_IN_FILES: - if isinstance(file.license_info_in_file, list): - return [str(license_expression) for license_expression in file.license_info_in_file] or None - return apply_if_present(str, file.license_info_in_file) + return [str(license_expression) for license_expression in file.license_info_in_file] or None elif file_property == FileProperty.NOTICE_TEXT: return file.notice diff --git a/src/spdx/model/file.py b/src/spdx/model/file.py index 1c7e157a1..50b3576d6 100644 --- a/src/spdx/model/file.py +++ b/src/spdx/model/file.py @@ -41,8 +41,7 @@ class File: checksums: List[Checksum] file_types: List[FileType] = field(default_factory=list) license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None - license_info_in_file: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = field( - default_factory=list) + license_info_in_file: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = field(default_factory=list) license_comment: Optional[str] = None copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None comment: Optional[str] = None @@ -57,7 +56,7 @@ class File: def __init__(self, name: str, spdx_id: str, checksums: List[Checksum], file_types: List[FileType] = None, license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, - license_info_in_file: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = None, + license_info_in_file: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None, license_comment: Optional[str] = None, copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None, comment: str = None, notice: Optional[str] = None, diff --git a/src/spdx/parser/jsonlikedict/file_parser.py b/src/spdx/parser/jsonlikedict/file_parser.py index 42d31f852..e89d9844b 100644 --- a/src/spdx/parser/jsonlikedict/file_parser.py +++ b/src/spdx/parser/jsonlikedict/file_parser.py @@ -16,8 +16,7 @@ from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.model.spdx_none import SpdxNone from spdx.parser.jsonlikedict.checksum_parser import ChecksumParser -from spdx.parser.jsonlikedict.dict_parsing_functions import parse_field_or_log_error, \ - parse_field_or_no_assertion_or_none +from spdx.parser.jsonlikedict.dict_parsing_functions import parse_field_or_log_error from spdx.parser.parsing_functions import construct_or_raise_parsing_error, raise_parsing_error_if_logger_has_messages from spdx.parser.jsonlikedict.license_expression_parser import LicenseExpressionParser from spdx.parser.logger import Logger @@ -38,7 +37,8 @@ def parse_file(self, file_dict: Dict) -> Optional[File]: name: Optional[str] = file_dict.get("fileName") spdx_id: Optional[str] = file_dict.get("SPDXID") checksums_list: List[Dict] = file_dict.get("checksums") - checksums: List[Checksum] = parse_field_or_log_error(logger, checksums_list, self.checksum_parser.parse_checksum, field_is_list=True) + checksums: List[Checksum] = parse_field_or_log_error(logger, checksums_list, + self.checksum_parser.parse_checksum, field_is_list=True) attribution_texts: List[str] = file_dict.get("attributionTexts", []) comment: Optional[str] = file_dict.get("comment") @@ -49,13 +49,11 @@ def parse_file(self, file_dict: Dict) -> Optional[File]: license_comments: Optional[str] = file_dict.get("licenseComments") license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger, file_dict.get("licenseConcluded"), - lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expression)) + logger, file_dict.get("licenseConcluded"), self.license_expression_parser.parse_license_expression) - license_info_in_files: Optional[ - Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( - logger, file_dict.get("licenseInfoInFiles"), - lambda x: parse_field_or_no_assertion_or_none(x, self.license_expression_parser.parse_license_expressions)) + license_info_in_files: List[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error( + logger, file_dict.get("licenseInfoInFiles"), self.license_expression_parser.parse_license_expression, + field_is_list=True) notice_text: Optional[str] = file_dict.get("noticeText") raise_parsing_error_if_logger_has_messages(logger, "File") diff --git a/src/spdx/parser/tagvalue/parser.py b/src/spdx/parser/tagvalue/parser.py index e37c62c34..884f7ebdd 100644 --- a/src/spdx/parser/tagvalue/parser.py +++ b/src/spdx/parser/tagvalue/parser.py @@ -277,12 +277,8 @@ def p_file_attribution_text(self, p): @grammar_rule("file_license_info : FILE_LICENSE_INFO license_or_no_assertion_or_none") def p_file_license_info(self, p): - if not self.check_that_current_element_matches_class_for_value(File, p.lineno(1)): - return - if p[2] == SpdxNone() or p[2] == SpdxNoAssertion(): - self.current_element["license_info_in_file"] = p[2] - return - self.current_element.setdefault("license_info_in_file", []).append(p[2]) + if self.check_that_current_element_matches_class_for_value(File, p.lineno(1)): + self.current_element.setdefault("license_info_in_file", []).append(p[2]) @grammar_rule("file_type : FILE_TYPE LINE") def p_file_type(self, p): diff --git a/tests/spdx/fixtures.py b/tests/spdx/fixtures.py index 86ca2b78f..1158a87ef 100644 --- a/tests/spdx/fixtures.py +++ b/tests/spdx/fixtures.py @@ -64,7 +64,8 @@ def file_fixture(name="./fileName.py", spdx_id="SPDXRef-File", checksums=None, f checksums = [checksum_fixture()] if checksums is None else checksums file_types = [FileType.TEXT] if file_types is None else file_types license_info_in_file = [get_spdx_licensing().parse("MIT"), - get_spdx_licensing().parse("GPL-2.0")] if license_info_in_file is None else license_info_in_file + get_spdx_licensing().parse("GPL-2.0"), + SpdxNoAssertion()] if license_info_in_file is None else license_info_in_file contributors = ["fileContributor"] if contributors is None else contributors attribution_texts = ["fileAttributionText"] if attribution_texts is None else attribution_texts return File(name=name, spdx_id=spdx_id, checksums=checksums, file_types=file_types, diff --git a/tests/spdx/jsonschema/test_file_converter.py b/tests/spdx/jsonschema/test_file_converter.py index eda157f02..51dd84ed0 100644 --- a/tests/spdx/jsonschema/test_file_converter.py +++ b/tests/spdx/jsonschema/test_file_converter.py @@ -74,7 +74,7 @@ def test_successful_conversion(converter: FileConverter): file = File(name="name", spdx_id="spdxId", checksums=[Checksum(ChecksumAlgorithm.SHA224, "sha224"), Checksum(ChecksumAlgorithm.MD2, "md2")], file_types=[FileType.SPDX, FileType.OTHER], license_concluded=Licensing().parse("MIT and GPL-2.0"), - license_info_in_file=[Licensing().parse("MIT"), Licensing().parse("GPL-2.0")], + license_info_in_file=[Licensing().parse("MIT"), Licensing().parse("GPL-2.0"), SpdxNoAssertion()], license_comment="licenseComment", copyright_text="copyrightText", comment="comment", notice="notice", contributors=["contributor1", "contributor2"], attribution_texts=["attributionText1", "attributionText2"]) @@ -97,7 +97,7 @@ def test_successful_conversion(converter: FileConverter): converter.json_property_name(FileProperty.FILE_TYPES): ["SPDX", "OTHER"], converter.json_property_name(FileProperty.LICENSE_COMMENTS): "licenseComment", converter.json_property_name(FileProperty.LICENSE_CONCLUDED): "MIT AND GPL-2.0", - converter.json_property_name(FileProperty.LICENSE_INFO_IN_FILES): ["MIT", "GPL-2.0"], + converter.json_property_name(FileProperty.LICENSE_INFO_IN_FILES): ["MIT", "GPL-2.0", "NOASSERTION"], converter.json_property_name(FileProperty.NOTICE_TEXT): "notice" } @@ -123,7 +123,7 @@ def test_null_values(converter: FileConverter): def test_spdx_no_assertion(converter: FileConverter): - file = file_fixture(license_concluded=SpdxNoAssertion(), license_info_in_file=SpdxNoAssertion(), + file = file_fixture(license_concluded=SpdxNoAssertion(), license_info_in_file=[SpdxNoAssertion()], copyright_text=SpdxNoAssertion()) document = Document(creation_info_fixture(), files=[file]) @@ -132,11 +132,11 @@ def test_spdx_no_assertion(converter: FileConverter): assert converted_dict[ converter.json_property_name(FileProperty.COPYRIGHT_TEXT)] == SPDX_NO_ASSERTION_STRING assert converted_dict[converter.json_property_name(FileProperty.LICENSE_CONCLUDED)] == SPDX_NO_ASSERTION_STRING - assert converted_dict[converter.json_property_name(FileProperty.LICENSE_INFO_IN_FILES)] == SPDX_NO_ASSERTION_STRING + assert converted_dict[converter.json_property_name(FileProperty.LICENSE_INFO_IN_FILES)] == [SPDX_NO_ASSERTION_STRING] def test_spdx_none(converter: FileConverter): - file = file_fixture(license_concluded=SpdxNone(), license_info_in_file=SpdxNone(), copyright_text=SpdxNone()) + file = file_fixture(license_concluded=SpdxNone(), license_info_in_file=[SpdxNone()], copyright_text=SpdxNone()) document = Document(creation_info_fixture(), files=[file]) converted_dict = converter.convert(file, document) @@ -144,7 +144,7 @@ def test_spdx_none(converter: FileConverter): assert converted_dict[ converter.json_property_name(FileProperty.COPYRIGHT_TEXT)] == SPDX_NONE_STRING assert converted_dict[converter.json_property_name(FileProperty.LICENSE_CONCLUDED)] == SPDX_NONE_STRING - assert converted_dict[converter.json_property_name(FileProperty.LICENSE_INFO_IN_FILES)] == SPDX_NONE_STRING + assert converted_dict[converter.json_property_name(FileProperty.LICENSE_INFO_IN_FILES)] == [SPDX_NONE_STRING] def test_file_annotations(converter: FileConverter): diff --git a/tests/spdx/model/test_file.py b/tests/spdx/model/test_file.py index 75ea6096b..70ecab4a6 100644 --- a/tests/spdx/model/test_file.py +++ b/tests/spdx/model/test_file.py @@ -10,14 +10,14 @@ @mock.patch('spdx.model.checksum.Checksum', autospec=True) def test_correct_initialization(checksum): - file = File("name", "id", [checksum, checksum], [FileType.OTHER, FileType.SPDX], SpdxNone(), SpdxNoAssertion(), + file = File("name", "id", [checksum, checksum], [FileType.OTHER, FileType.SPDX], SpdxNone(), [SpdxNoAssertion()], "comment on license", "copyright", "comment", "notice", ["contributor"], ["attribution"]) assert file.name == "name" assert file.spdx_id == "id" assert file.checksums == [checksum, checksum] assert file.file_types == [FileType.OTHER, FileType.SPDX] assert file.license_concluded == SpdxNone() - assert file.license_info_in_file == SpdxNoAssertion() + assert file.license_info_in_file == [SpdxNoAssertion()] assert file.license_comment == "comment on license" assert file.copyright_text == "copyright" assert file.comment == "comment" diff --git a/tests/spdx/parser/jsonlikedict/test_file_parser.py b/tests/spdx/parser/jsonlikedict/test_file_parser.py index 2c0204be9..d08262dd4 100644 --- a/tests/spdx/parser/jsonlikedict/test_file_parser.py +++ b/tests/spdx/parser/jsonlikedict/test_file_parser.py @@ -15,6 +15,8 @@ from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.model.file import FileType from license_expression import Licensing + +from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.parser.error import SPDXParsingError from spdx.parser.jsonlikedict.dict_parsing_functions import parse_list_of_elements from spdx.parser.jsonlikedict.file_parser import FileParser @@ -40,7 +42,7 @@ def test_parse_file(): "fileTypes": ["SOURCE"], "licenseComments": "The concluded license was taken from the package level that the file was included in.", "licenseConcluded": "(LGPL-2.0-only OR LicenseRef-2)", - "licenseInfoInFiles": ["GPL-2.0-only", "LicenseRef-2"], + "licenseInfoInFiles": ["GPL-2.0-only", "LicenseRef-2", "NOASSERTION"], "noticeText": "Copyright (c) 2001 Aaron Lehmann aaroni@vitelus.com\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: \nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." } @@ -58,7 +60,8 @@ def test_parse_file(): "Modified by Paul Mundt lethal@linux-sh.org", "IBM Corporation"]) assert file.license_concluded == Licensing().parse("(LGPL-2.0-only OR LicenseRef-2)") TestCase().assertCountEqual(file.license_info_in_file, - [Licensing().parse("GPL-2.0-only"), Licensing().parse("LicenseRef-2")]) + [Licensing().parse("GPL-2.0-only"), Licensing().parse("LicenseRef-2"), + SpdxNoAssertion()]) assert file.license_comment == "The concluded license was taken from the package level that the file was included in." assert file.attribution_texts == ["Some attribution text."] diff --git a/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml b/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml index 436fcc4ba..2cd671ee9 100644 --- a/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml +++ b/tests/spdx/parser/rdf/data/file_to_test_rdf_parser.rdf.xml @@ -36,6 +36,7 @@ fileAttributionText + diff --git a/tests/spdx/parser/rdf/test_file_parser.py b/tests/spdx/parser/rdf/test_file_parser.py index 1b265c20a..9b9f634d5 100644 --- a/tests/spdx/parser/rdf/test_file_parser.py +++ b/tests/spdx/parser/rdf/test_file_parser.py @@ -16,6 +16,7 @@ from spdx.model.checksum import Checksum, ChecksumAlgorithm from spdx.model.file import FileType +from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.parser.rdf.file_parser import parse_file from spdx.rdfschema.namespace import SPDX_NAMESPACE @@ -36,7 +37,8 @@ def test_parse_file(): assert file.contributors == ["fileContributor"] assert file.license_concluded == get_spdx_licensing().parse("MIT AND GPL-2.0") TestCase().assertCountEqual(file.license_info_in_file, - [get_spdx_licensing().parse("MIT"), get_spdx_licensing().parse("GPL-2.0")]) + [get_spdx_licensing().parse("MIT"), get_spdx_licensing().parse("GPL-2.0"), + SpdxNoAssertion()]) assert file.license_comment == "licenseComment" assert file.notice == "fileNotice" assert file.attribution_texts == ["fileAttributionText"] diff --git a/tests/spdx/parser/tagvalue/test_file_parser.py b/tests/spdx/parser/tagvalue/test_file_parser.py index 0cedc84b6..29399020f 100644 --- a/tests/spdx/parser/tagvalue/test_file_parser.py +++ b/tests/spdx/parser/tagvalue/test_file_parser.py @@ -12,6 +12,7 @@ from license_expression import get_spdx_licensing from spdx.model.file import FileType +from spdx.model.spdx_no_assertion import SpdxNoAssertion from spdx.parser.error import SPDXParsingError from spdx.parser.tagvalue.parser import Parser from tests.spdx.parser.tagvalue.test_creation_info_parser import DOCUMENT_STR @@ -27,6 +28,7 @@ def test_parse_file(): "FileChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", "LicenseConcluded: Apache-2.0", "LicenseInfoInFile: Apache-2.0", + "LicenseInfoInFile: NOASSERTION", "FileCopyrightText: Copyright 2014 Acme Inc.", "FileComment: Very long file", "FileAttributionText: Acknowledgements that might be required to be communicated in some contexts." @@ -41,7 +43,8 @@ def test_parse_file(): assert spdx_file.comment == "Very long file" assert spdx_file.attribution_texts == [ "Acknowledgements that might be required to be communicated in some contexts."] - assert spdx_file.license_info_in_file == [get_spdx_licensing().parse("Apache-2.0")] + assert spdx_file.license_info_in_file == [get_spdx_licensing().parse("Apache-2.0"), + SpdxNoAssertion()] assert spdx_file.license_concluded == get_spdx_licensing().parse("Apache-2.0") @@ -63,5 +66,6 @@ def test_parse_invalid_file(): with pytest.raises(SPDXParsingError) as err: parser.parse(file_str) - assert err.value.get_messages() == ["Error while parsing File: ['Invalid FileType: SOUCE. Line 3', 'Error while " - "parsing FileChecksum: Token did not match specified grammar rule. Line: 5']"] + assert err.value.get_messages() == [ + "Error while parsing File: ['Invalid FileType: SOUCE. Line 3', 'Error while " + "parsing FileChecksum: Token did not match specified grammar rule. Line: 5']"] diff --git a/tests/spdx/writer/json/expected_results/expected.json b/tests/spdx/writer/json/expected_results/expected.json index 8b5b08d5a..b6a364b35 100644 --- a/tests/spdx/writer/json/expected_results/expected.json +++ b/tests/spdx/writer/json/expected_results/expected.json @@ -55,7 +55,7 @@ ], "licenseComments": "licenseComment", "licenseConcluded": "MIT AND GPL-2.0-only", - "licenseInfoInFiles": ["MIT", "GPL-2.0-only"], + "licenseInfoInFiles": ["MIT", "GPL-2.0-only", "NOASSERTION"], "noticeText": "fileNotice" } ], diff --git a/tests/spdx/writer/rdf/test_file_writer.py b/tests/spdx/writer/rdf/test_file_writer.py index 0fb67a884..a8efc647a 100644 --- a/tests/spdx/writer/rdf/test_file_writer.py +++ b/tests/spdx/writer/rdf/test_file_writer.py @@ -11,7 +11,7 @@ from rdflib import Graph, Literal, RDFS, RDF, URIRef from spdx.writer.rdf.file_writer import add_file_to_graph -from spdx.rdfschema.namespace import SPDX_NAMESPACE +from spdx.rdfschema.namespace import SPDX_NAMESPACE, LICENSE_NAMESPACE from tests.spdx.fixtures import file_fixture @@ -26,7 +26,9 @@ def test_add_file_to_graph(): assert (None, SPDX_NAMESPACE.fileType, SPDX_NAMESPACE.fileType_text) in graph assert (None, SPDX_NAMESPACE.licenseComments, Literal(file.license_comment)) in graph assert (None, SPDX_NAMESPACE.licenseConcluded, None) in graph - assert (None, SPDX_NAMESPACE.licenseInfoInFile, None) in graph + assert (None, SPDX_NAMESPACE.licenseInfoInFile, LICENSE_NAMESPACE.MIT) in graph + assert (None, SPDX_NAMESPACE.licenseInfoInFile, LICENSE_NAMESPACE["GPL-2.0-only"]) in graph + assert (None, SPDX_NAMESPACE.licenseInfoInFile, SPDX_NAMESPACE.noassertion) in graph assert (None, SPDX_NAMESPACE.copyrightText, Literal(file.copyright_text)) in graph assert (None, RDFS.comment, Literal(file.comment)) in graph assert (None, SPDX_NAMESPACE.noticeText, Literal(file.notice)) in graph From 8d2f4974b1884e1d397fbbae145f035cb91676ec Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Fri, 3 Mar 2023 14:19:54 +0100 Subject: [PATCH 348/362] [issue-503] fix: delete helper method as we don't need to distinguish cases for LicenseInfoFromFiles, LicenseInfoInFile and LicenseInfoInSnippet Signed-off-by: Meret Behrens --- src/spdx/writer/tagvalue/file_writer.py | 5 +++-- src/spdx/writer/tagvalue/package_writer.py | 5 +++-- src/spdx/writer/tagvalue/snippet_writer.py | 7 +++---- .../writer/tagvalue/tagvalue_writer_helper_functions.py | 9 --------- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/spdx/writer/tagvalue/file_writer.py b/src/spdx/writer/tagvalue/file_writer.py index 87caf0b73..a12473738 100644 --- a/src/spdx/writer/tagvalue/file_writer.py +++ b/src/spdx/writer/tagvalue/file_writer.py @@ -12,7 +12,7 @@ from spdx.model.file import File from spdx.writer.tagvalue.checksum_writer import write_checksum_to_tag_value -from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, write_license_info_list +from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value def write_file(file: File, text_output: TextIO): @@ -28,7 +28,8 @@ def write_file(file: File, text_output: TextIO): write_value("FileChecksum", write_checksum_to_tag_value(file_checksum), text_output) write_value("LicenseConcluded", file.license_concluded, text_output) - write_license_info_list("LicenseInfoInFile", file.license_info_in_file, text_output) + for license_info in file.license_info_in_file: + write_value("LicenseInfoInFile", license_info, text_output) write_text_value("LicenseComments", file.license_comment, text_output) write_text_value("FileCopyrightText", file.copyright_text, text_output) diff --git a/src/spdx/writer/tagvalue/package_writer.py b/src/spdx/writer/tagvalue/package_writer.py index b5abeb1c7..74e4adeb6 100644 --- a/src/spdx/writer/tagvalue/package_writer.py +++ b/src/spdx/writer/tagvalue/package_writer.py @@ -14,7 +14,7 @@ from spdx.model.package import Package, PackageVerificationCode from spdx.writer.tagvalue.checksum_writer import write_checksum_to_tag_value from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, \ - transform_enum_name_to_tv, write_actor, write_license_info_list + transform_enum_name_to_tv, write_actor def write_package(package: Package, text_output: TextIO): @@ -40,7 +40,8 @@ def write_package(package: Package, text_output: TextIO): write_text_value("PackageSourceInfo", package.source_info, text_output) write_value("PackageLicenseConcluded", package.license_concluded, text_output) - write_license_info_list("PackageLicenseInfoFromFiles", package.license_info_from_files, text_output) + for license_info in package.license_info_from_files: + write_value("PackageLicenseInfoFromFiles", license_info, text_output) write_value("PackageLicenseDeclared", package.license_declared, text_output) write_text_value("PackageLicenseComments", package.license_comment, text_output) write_text_value("PackageCopyrightText", package.copyright_text, text_output) diff --git a/src/spdx/writer/tagvalue/snippet_writer.py b/src/spdx/writer/tagvalue/snippet_writer.py index 62355d5d6..eee6b6416 100644 --- a/src/spdx/writer/tagvalue/snippet_writer.py +++ b/src/spdx/writer/tagvalue/snippet_writer.py @@ -11,9 +11,7 @@ from typing import TextIO from spdx.model.snippet import Snippet -from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, write_range, \ - write_license_info_list - +from spdx.writer.tagvalue.tagvalue_writer_helper_functions import write_value, write_text_value, write_range def write_snippet(snippet: Snippet, text_output: TextIO): text_output.write("## Snippet Information\n") @@ -24,7 +22,8 @@ def write_snippet(snippet: Snippet, text_output: TextIO): write_range("SnippetLineRange", snippet.line_range, text_output) write_value("SnippetLicenseConcluded", snippet.license_concluded, text_output) - write_license_info_list("LicenseInfoInSnippet", snippet.license_info_in_snippet, text_output) + for license_info in snippet.license_info_in_snippet: + write_value("LicenseInfoInSnippet", license_info, text_output) write_text_value("SnippetLicenseComments", snippet.license_comment, text_output) write_text_value("SnippetCopyrightText", snippet.copyright_text, text_output) diff --git a/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py b/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py index 846871982..1f30c0f45 100644 --- a/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py +++ b/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py @@ -58,15 +58,6 @@ def write_list_of_elements(list_of_elements: List[Any], write_method: Callable[[ write_separator(text_output) -def write_license_info_list(tag: str, license_infos: Union[SpdxNone, SpdxNoAssertion, List[LicenseExpression]], text_output: TextIO): - if isinstance(license_infos, (SpdxNone, SpdxNoAssertion)): - write_value(tag, license_infos, text_output) - return - - for license_info in license_infos: - write_value(tag, license_info, text_output) - - def write_actor(tag: str, element_to_write: Optional[Union[Actor, SpdxNoAssertion]], text_output: TextIO): if isinstance(element_to_write, Actor): write_value(tag, element_to_write.to_serialized_string(), text_output) From 91b05dc8b3bc767ea0a21f92eeb7af13e13176ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 15 Mar 2023 15:43:22 +0100 Subject: [PATCH 349/362] fixed the version of typeguard as 3.0.0 introduces breaking changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2dd71b8e7..dd52b43eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ ] urls = { Homepage = "https://github.com/spdx/tools-python" } requires-python = ">=3.7" -dependencies = ["click", "pyyaml", "xmltodict", "rdflib", "typeguard", "uritools", "license_expression", "ply"] +dependencies = ["click", "pyyaml", "xmltodict", "rdflib", "typeguard==2.13.3", "uritools", "license_expression", "ply"] dynamic = ["version"] [project.optional-dependencies] From ea92a1c88495b5e5904ca6e94f2319805649708e Mon Sep 17 00:00:00 2001 From: HarshvMahawar Date: Thu, 23 Mar 2023 13:51:51 +0530 Subject: [PATCH 350/362] Added convenience function to extract SPDX elements by their Id Signed-off-by: HarshvMahawar --- src/spdx/document_utils.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/spdx/document_utils.py b/src/spdx/document_utils.py index 10947a719..74a49faf9 100644 --- a/src/spdx/document_utils.py +++ b/src/spdx/document_utils.py @@ -9,8 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. from typing import List +from typing import Union from spdx.model.document import Document +from spdx.model.snippet import Snippet +from spdx.model.package import Package +from spdx.model.file import File def get_contained_spdx_element_ids(document: Document) -> List[str]: @@ -18,3 +22,12 @@ def get_contained_spdx_element_ids(document: Document) -> List[str]: element_ids.extend([package.spdx_id for package in document.packages]) element_ids.extend([snippet.spdx_id for snippet in document.snippets]) return element_ids + + +def get_element_from_spdx_id(document: Document, spdx_id: str) -> Union[Package, File, Snippet, None]: + elements = [file_ for file_ in document.files] + elements.extend([package_ for package_ in document.packages]) + elements.extend([snippet_ for snippet_ in document.snippets]) + for element in elements: + if element.spdx_id == spdx_id: + return element From 9f998b92922e6c63ad802d2be427f7ffdb032295 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 28 Mar 2023 11:23:54 +0200 Subject: [PATCH 351/362] [issue-402] add validation option to tag-value writer Signed-off-by: Meret Behrens --- src/spdx/writer/tagvalue/tagvalue_writer.py | 13 ++++++++++--- src/spdx/writer/write_anything.py | 2 +- tests/spdx/writer/tagvalue/test_tagvalue_writer.py | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/spdx/writer/tagvalue/tagvalue_writer.py b/src/spdx/writer/tagvalue/tagvalue_writer.py index bce0db50d..91e53d6cb 100644 --- a/src/spdx/writer/tagvalue/tagvalue_writer.py +++ b/src/spdx/writer/tagvalue/tagvalue_writer.py @@ -8,10 +8,11 @@ # 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 typing import TextIO +from typing import TextIO, List from spdx.model.document import Document +from spdx.validation.document_validator import validate_full_spdx_document +from spdx.validation.validation_message import ValidationMessage from spdx.writer.tagvalue.annotation_writer import write_annotation from spdx.writer.tagvalue.creation_info_writer import write_creation_info from spdx.writer.tagvalue.extracted_licensing_info_writer import write_extracted_licensing_info @@ -23,7 +24,13 @@ get_file_ids_with_contained_snippets, write_optional_heading, write_list_of_elements -def write_document_to_file(document: Document, file_name: str): +def write_document_to_file(document: Document, file_name: str, validate: bool = True): + if validate: + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, + document.creation_info.spdx_version) + if validation_messages: + raise ValueError(f"Document is not valid. The following errors were detected: {validation_messages}") + with open(file_name, "w") as out: write_document(document, out) diff --git a/src/spdx/writer/write_anything.py b/src/spdx/writer/write_anything.py index 14506a007..7f8c67c21 100644 --- a/src/spdx/writer/write_anything.py +++ b/src/spdx/writer/write_anything.py @@ -27,6 +27,6 @@ def write_file(document: Document, file_name: str, validate: bool = True): elif output_format == FileFormat.XML: xml_writer.write_document_to_file(document, file_name, validate) elif output_format == FileFormat.TAG_VALUE: - tagvalue_writer.write_document_to_file(document, file_name) + tagvalue_writer.write_document_to_file(document, file_name, validate) elif output_format == FileFormat.RDF_XML: rdf_writer.write_document_to_file(document, file_name, validate) diff --git a/tests/spdx/writer/tagvalue/test_tagvalue_writer.py b/tests/spdx/writer/tagvalue/test_tagvalue_writer.py index f289be6de..1936326ab 100644 --- a/tests/spdx/writer/tagvalue/test_tagvalue_writer.py +++ b/tests/spdx/writer/tagvalue/test_tagvalue_writer.py @@ -28,7 +28,7 @@ def temporary_file_path() -> str: def test_write_tag_value(temporary_file_path: str): document = document_fixture() - write_document_to_file(document, temporary_file_path) + write_document_to_file(document, temporary_file_path, False) parsed_document = tagvalue_parser.parse_from_file(temporary_file_path) From d1c01f899a9d656aca01bfd6953fb4df54eeeb6d Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Tue, 28 Mar 2023 11:33:51 +0200 Subject: [PATCH 352/362] [issue-402] drop spdx version as input argument as it is automatically inferred from the creation_info Signed-off-by: Meret Behrens --- src/spdx/writer/rdf/rdf_writer.py | 3 +-- src/spdx/writer/tagvalue/tagvalue_writer.py | 3 +-- src/spdx/writer/xml/xml_writer.py | 6 +++--- src/spdx/writer/yaml/yaml_writer.py | 6 +++--- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/spdx/writer/rdf/rdf_writer.py b/src/spdx/writer/rdf/rdf_writer.py index 9251f57a8..0e7848e86 100644 --- a/src/spdx/writer/rdf/rdf_writer.py +++ b/src/spdx/writer/rdf/rdf_writer.py @@ -28,8 +28,7 @@ def write_document_to_file(document: Document, file_name: str, validate: bool): if validate: - validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, - document.creation_info.spdx_version) + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document) if validation_messages: raise ValueError(f"Document is not valid. The following errors were detected: {validation_messages}") diff --git a/src/spdx/writer/tagvalue/tagvalue_writer.py b/src/spdx/writer/tagvalue/tagvalue_writer.py index 91e53d6cb..92db6f2e1 100644 --- a/src/spdx/writer/tagvalue/tagvalue_writer.py +++ b/src/spdx/writer/tagvalue/tagvalue_writer.py @@ -26,8 +26,7 @@ def write_document_to_file(document: Document, file_name: str, validate: bool = True): if validate: - validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, - document.creation_info.spdx_version) + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document) if validation_messages: raise ValueError(f"Document is not valid. The following errors were detected: {validation_messages}") diff --git a/src/spdx/writer/xml/xml_writer.py b/src/spdx/writer/xml/xml_writer.py index f6bcf738e..369bbfedc 100644 --- a/src/spdx/writer/xml/xml_writer.py +++ b/src/spdx/writer/xml/xml_writer.py @@ -18,15 +18,15 @@ from spdx.validation.validation_message import ValidationMessage -def write_document_to_file(document: Document, file_name: str, validate: bool = True, converter: DocumentConverter = None): +def write_document_to_file(document: Document, file_name: str, validate: bool = True, + converter: DocumentConverter = None): """ Serializes the provided document to XML and writes it to a file with the provided name. Unless validate is set to False, validates the document before serialization. Unless a DocumentConverter instance is provided, a new one is created. """ if validate: - validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, - document.creation_info.spdx_version) + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document) if validation_messages: raise ValueError(f"Document is not valid. The following errors were detected: {validation_messages}") if converter is None: diff --git a/src/spdx/writer/yaml/yaml_writer.py b/src/spdx/writer/yaml/yaml_writer.py index 38dc40ac4..2db63cb8c 100644 --- a/src/spdx/writer/yaml/yaml_writer.py +++ b/src/spdx/writer/yaml/yaml_writer.py @@ -18,15 +18,15 @@ from spdx.validation.validation_message import ValidationMessage -def write_document_to_file(document: Document, file_name: str, validate: bool = True, converter: DocumentConverter = None): +def write_document_to_file(document: Document, file_name: str, validate: bool = True, + converter: DocumentConverter = None): """ Serializes the provided document to yaml and writes it to a file with the provided name. Unless validate is set to False, validates the document before serialization. Unless a DocumentConverter instance is provided, a new one is created. """ if validate: - validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, - document.creation_info.spdx_version) + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document) if validation_messages: raise ValueError(f"Document is not valid. The following errors were detected: {validation_messages}") if converter is None: From 1d84bcbf4642d6b7c8b5707565639343e54674e2 Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 29 Mar 2023 08:23:56 +0200 Subject: [PATCH 353/362] [fix] add python 3.11 to GitHub Action Signed-off-by: Meret Behrens --- .github/workflows/install_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/install_and_test.yml b/.github/workflows/install_and_test.yml index 77078c5f6..e90e67106 100644 --- a/.github/workflows/install_and_test.yml +++ b/.github/workflows/install_and_test.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: os: [ ubuntu-latest, macos-latest, windows-latest ] - python-version: [ "3.7", "3.8", "3.9", "3.10" ] + python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11" ] steps: - uses: actions/checkout@v3 From 22618247df67b56ad5be976a21939b349bc21d7e Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Wed, 29 Mar 2023 08:28:33 +0200 Subject: [PATCH 354/362] [fix] also run GitHub Action after merge Signed-off-by: Meret Behrens --- .github/workflows/install_and_test.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/install_and_test.yml b/.github/workflows/install_and_test.yml index e90e67106..ae48615d0 100644 --- a/.github/workflows/install_and_test.yml +++ b/.github/workflows/install_and_test.yml @@ -1,6 +1,11 @@ name: Install and Test -on: [ workflow_dispatch, pull_request ] +on: + push: + branches: + - refactor-python-tools + pull_request: + workflow_dispatch: jobs: install_and_test: From ba2c5802243413071fd1ed98ec82ef70bf0a2cd9 Mon Sep 17 00:00:00 2001 From: Gary O'Neall Date: Thu, 16 Mar 2023 08:43:56 -0700 Subject: [PATCH 355/362] Create dependabot.yml Signed-off-by: Gary O'Neall --- .github/dependabot.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..91abb11fd --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "pip" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" From b132d32836beafc67f1f7e6126624ec01a361fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 14 Mar 2023 11:38:20 +0100 Subject: [PATCH 356/362] update changelog for v0.7.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee72fb020..81dc51c9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## v0.7.1 (2023-03-14) + +### New features and changes + +* added GitHub Actions workflow +* added requirements.txt +* added uritools for URI validation +* Python >= 3.7 is now required +* json/yaml/xml: added support for empty arrays for hasFiles and licenseInfoFromFiles +* rdf: fixed writing of multiple packages +* tag-value: enhanced parsing of snippet ranges to not mix it up with package version +* tag-value: fixed parsing of whitespaces +* tag-value: duplicates in LicenseInfoInFile are now removed during writing +* account for supplier and originator to be NOASSERTION +* checksum validation now requires lowercase values +* during writing of a file, the encoding can be set (default is utf-8) +* license list updated to version 3.20 + +### Contributors + +This release was made possible by the following contributors. Thank you very much! + +* Christian Decker @chrisdecker1201 +* Marc-Etienne Vargenau @vargenau +* John Vandenberg @jayvdb +* Nicolaus Weidner @nicoweidner +* Meret Behrens @meretp +* Armin Tänzer @armintaenzertng +* Maximilian Huber @maxhbr + ## v0.7.0 (2022-12-08) From de1e86ea57750b797e02369316ebc737ac4d209e Mon Sep 17 00:00:00 2001 From: Meret Behrens Date: Thu, 9 Feb 2023 16:19:56 +0100 Subject: [PATCH 357/362] [issue-468] add a step to fork the repository MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Meret Behrens Signed-off-by: Armin Tänzer --- CONTRIBUTING.md | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 03e43d245..6352e3894 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,11 +26,17 @@ Here's the process to make changes to the codebase: 2. Review [open pull requests](https://github.com/spdx/tools-python/pulls) before committing time to a substantial revision. Work along similar lines may already be in progress. -3. Create a new branch: +3. Fork the repository as described [here](https://docs.github.com/en/get-started/quickstart/fork-a-repo#forking-a-repository) + 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 . ``` -4. Make some changes and commit them to the branch: +5. Make some changes and commit them to the branch: ```sh git commit --signoff -m 'description of my changes' ``` @@ -42,22 +48,22 @@ Here's the process to make changes to the codebase: commits: `git commit -s` or `--signoff` signs a current commit, and `git rebase --signoff ` retroactively signs a range of past commits. -5. Test your changes: +6. Test your changes: ```sh python setup.py test # in the repo root ``` You may use other test runners, such as `pytest` or `nose` at your preference. -6. Push the branch to your fork on GitHub: +7. Push the branch to your fork on GitHub: ```sh git push origin fix-or-improve-something ``` -7. Make a pull request on GitHub. -8. Continue making more changes and commits on the branch, with `git commit --signoff` and `git push`. -9. When done, write a comment on the PR asking for a code review. -10. Some other developer will review your changes and accept your PR. The merge should be done with `rebase`, if +8. Make a pull request on GitHub. +9. Continue making more changes and commits on the branch, with `git commit --signoff` and `git push`. +10. When done, write a comment on the PR asking for a code review. +11. Some other developer will review your changes and accept your PR. The merge should be done with `rebase`, if possible, or with `squash`. -11. The temporary branch on GitHub should be deleted (there is a button for deleting it). -12. Delete the local branch as well: +12. The temporary branch on GitHub should be deleted (there is a button for deleting it). +13. Delete the local branch as well: ```sh git checkout master git pull -p From 3019702492059f2e5569c9441e89ce30c0f69b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 29 Mar 2023 10:46:49 +0200 Subject: [PATCH 358/362] delete unused test data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- tests/spdx/data/doc_parse/SBOMexpected.json | 168 ------- tests/spdx/data/doc_parse/expected.json | 343 ------------- tests/spdx/data/doc_parse/spdx-expected.json | 335 ------------- .../doc_write/json-simple-multi-package.json | 78 --- .../spdx/data/doc_write/json-simple-plus.json | 72 --- tests/spdx/data/doc_write/json-simple.json | 72 --- tests/spdx/data/doc_write/rdf-mini.json | 13 - .../spdx/data/doc_write/rdf-simple-plus.json | 105 ---- tests/spdx/data/doc_write/rdf-simple.json | 105 ---- tests/spdx/data/doc_write/tv-mini.tv | 7 - tests/spdx/data/doc_write/tv-simple-plus.tv | 35 -- tests/spdx/data/doc_write/tv-simple.tv | 35 -- .../doc_write/xml-simple-multi-package.xml | 59 --- tests/spdx/data/doc_write/xml-simple-plus.xml | 51 -- tests/spdx/data/doc_write/xml-simple.xml | 51 -- .../doc_write/yaml-simple-multi-package.yaml | 54 --- .../spdx/data/doc_write/yaml-simple-plus.yaml | 49 -- tests/spdx/data/doc_write/yaml-simple.yaml | 50 -- .../data/formats/SPDXSBOMExample.spdx.yml | 58 --- tests/spdx/data/formats/SPDXSBOMExample.tag | 64 --- tests/spdx/data/formats/SPDXSimpleTag.tag | 65 --- .../data/formats/SPDXTagExample-v2.2.spdx | 329 ------------- tests/spdx/data/formats/SPDXTagExample.tag | 224 --------- .../data/formats/SPDXXMLExample-v2.2.spdx.xml | 443 ----------------- .../data/formats/SPDXXMLExample-v2.3.spdx.xml | 459 ------------------ .../formats/SPDXYAMLExample-2.2.spdx.yaml | 390 --------------- .../formats/SPDXYAMLExample-2.3.spdx.yaml | 406 ---------------- 27 files changed, 4120 deletions(-) delete mode 100644 tests/spdx/data/doc_parse/SBOMexpected.json delete mode 100644 tests/spdx/data/doc_parse/expected.json delete mode 100644 tests/spdx/data/doc_parse/spdx-expected.json delete mode 100644 tests/spdx/data/doc_write/json-simple-multi-package.json delete mode 100644 tests/spdx/data/doc_write/json-simple-plus.json delete mode 100644 tests/spdx/data/doc_write/json-simple.json delete mode 100644 tests/spdx/data/doc_write/rdf-mini.json delete mode 100644 tests/spdx/data/doc_write/rdf-simple-plus.json delete mode 100644 tests/spdx/data/doc_write/rdf-simple.json delete mode 100644 tests/spdx/data/doc_write/tv-mini.tv delete mode 100644 tests/spdx/data/doc_write/tv-simple-plus.tv delete mode 100644 tests/spdx/data/doc_write/tv-simple.tv delete mode 100644 tests/spdx/data/doc_write/xml-simple-multi-package.xml delete mode 100644 tests/spdx/data/doc_write/xml-simple-plus.xml delete mode 100644 tests/spdx/data/doc_write/xml-simple.xml delete mode 100644 tests/spdx/data/doc_write/yaml-simple-multi-package.yaml delete mode 100644 tests/spdx/data/doc_write/yaml-simple-plus.yaml delete mode 100644 tests/spdx/data/doc_write/yaml-simple.yaml delete mode 100644 tests/spdx/data/formats/SPDXSBOMExample.spdx.yml delete mode 100644 tests/spdx/data/formats/SPDXSBOMExample.tag delete mode 100644 tests/spdx/data/formats/SPDXSimpleTag.tag delete mode 100644 tests/spdx/data/formats/SPDXTagExample-v2.2.spdx delete mode 100644 tests/spdx/data/formats/SPDXTagExample.tag delete mode 100644 tests/spdx/data/formats/SPDXXMLExample-v2.2.spdx.xml delete mode 100644 tests/spdx/data/formats/SPDXXMLExample-v2.3.spdx.xml delete mode 100644 tests/spdx/data/formats/SPDXYAMLExample-2.2.spdx.yaml delete mode 100644 tests/spdx/data/formats/SPDXYAMLExample-2.3.spdx.yaml diff --git a/tests/spdx/data/doc_parse/SBOMexpected.json b/tests/spdx/data/doc_parse/SBOMexpected.json deleted file mode 100644 index 3d0504388..000000000 --- a/tests/spdx/data/doc_parse/SBOMexpected.json +++ /dev/null @@ -1,168 +0,0 @@ -{ - "id": "SPDXRef-DOCUMENT", - "specVersion": { - "major": 2, - "minor": 2 - }, - "documentNamespace": "http://spdx.org/spdxdocs/spdx-document-xyz", - "name": "xyz-0.1.0", - "comment": null, - "dataLicense": { - "type": "Single", - "identifier": "CC0-1.0", - "name": "Creative Commons Zero v1.0 Universal" - }, - "licenseListVersion": { - "major": 3, - "minor": 9 - }, - "creators": [ - { - "name": "Example Inc.", - "email": null, - "type": "Organization" - }, - { - "name": "Thomas Steenbergen", - "email": null, - "type": "Person" - } - ], - "created": "2020-07-23T18:30:22Z", - "creatorComment": null, - "files": [], - "packages": [ - { - "id": "SPDXRef-Package-xyz", - "name": "xyz", - "packageFileName": null, - "summary": "Awesome product created by Example Inc.", - "description": null, - "versionInfo": "0.1.0", - "sourceInfo": null, - "downloadLocation": "git+ssh://gitlab.example.com:3389/products/xyz.git@b2c358080011af6a366d2512a25a379fbe7b1f78", - "homepage": "https://example.com/products/xyz", - "originator": null, - "supplier": null, - "licenseConcluded": { - "type": "Single", - "identifier": "NOASSERTION", - "name": "NOASSERTION" - }, - "licenseDeclared": { - "type": "Conjunction", - "identifier": [ - "Apache-2.0", - "LicenseRef-Proprietary-ExampleInc", - "curl" - ], - "name": [ - "Apache License 2.0", - "LicenseRef-Proprietary-ExampleInc", - "curl License" - ] - }, - "copyrightText": "copyright 2004-2020 Example Inc. All Rights Reserved.", - "licenseComment": null, - "checksums": [{ - "identifier": "SHA1", - "value": "SOME-SHA1" - }], - "licenseInfoFromFiles": [], - "verificationCode": { - "value": null, - "excludedFilesNames": [] - } - }, - { - "id": "SPDXRef-Package-curl", - "name": "curl", - "packageFileName": "./libs/curl", - "summary": null, - "description": "A command line tool and library for transferring data with URL syntax, supporting HTTP, HTTPS, FTP, FTPS, GOPHER, TFTP, SCP, SFTP, SMB, TELNET, DICT, LDAP, LDAPS, MQTT, FILE, IMAP, SMTP, POP3, RTSP and RTMP. libcurl offers a myriad of powerful features.", - "versionInfo": "7.70.0", - "sourceInfo": null, - "downloadLocation": "https://github.com/curl/curl/releases/download/curl-7_70_0/curl-7.70.0.tar.gz", - "homepage": "https://curl.haxx.se/", - "originator": null, - "supplier": null, - "licenseConcluded": { - "type": "Single", - "identifier": "NOASSERTION", - "name": "NOASSERTION" - }, - "licenseDeclared": { - "type": "Single", - "identifier": "curl", - "name": "curl License" - }, - "copyrightText": "Copyright (c) 1996 - 2020, Daniel Stenberg, , and many contributors, see the THANKS file.", - "licenseComment": null, - "checksums": [{ - "identifier": "SHA1", - "value": "SOME-SHA1" - }], - "licenseInfoFromFiles": [], - "verificationCode": { - "value": null, - "excludedFilesNames": [] - } - }, - { - "id": "SPDXRef-Package-openssl", - "name": "openssl", - "packageFileName": "./libs/openssl", - "summary": null, - "description": "OpenSSL is a robust, commercial-grade, full-featured Open Source Toolkit for the Transport Layer Security (TLS) protocol formerly known as the Secure Sockets Layer (SSL) protocol. The protocol implementation is based on a full-strength general purpose cryptographic library, which can also be used stand-alone.", - "versionInfo": "1.1.1g", - "sourceInfo": null, - "downloadLocation": "git+ssh://github.com/openssl/openssl.git@e2e09d9fba1187f8d6aafaa34d4172f56f1ffb72", - "homepage": "https://www.openssl.org/", - "originator": null, - "supplier": null, - "licenseConcluded": { - "type": "Single", - "identifier": "NOASSERTION", - "name": "NOASSERTION" - }, - "licenseDeclared": { - "type": "Single", - "identifier": "Apache-2.0", - "name": "Apache License 2.0" - }, - "copyrightText": "copyright 2004-2020 The OpenSSL Project Authors. All Rights Reserved.", - "licenseComment": null, - "checksums": [{ - "identifier": "SHA1", - "value": "SOME-SHA1" - }], - "licenseInfoFromFiles": [], - "verificationCode": { - "value": null, - "excludedFilesNames": [] - } - } - ], - "externalDocumentRefs": [], - "extractedLicenses": [], - "annotations": [], - "reviews": [], - "snippets": [], - "relationships": [ - { - "spdx_element_id": "SPDXRef-Package-xyz", - "relationship_type": "CONTAINS", - "related_spdx_element": "SPDXRef-Package-curl" - }, - { - "spdx_element_id": "SPDXRef-Package-xyz", - "relationship_type": "CONTAINS", - "related_spdx_element": "SPDXRef-Package-openssl" - }, - { - "spdx_element_id": "SPDXRef-DOCUMENT", - "relationship_type": "DESCRIBES", - "related_spdx_element": "SPDXRef-Package-xyz" - } - ] -} diff --git a/tests/spdx/data/doc_parse/expected.json b/tests/spdx/data/doc_parse/expected.json deleted file mode 100644 index 84f6689d1..000000000 --- a/tests/spdx/data/doc_parse/expected.json +++ /dev/null @@ -1,343 +0,0 @@ -{ - "id": "SPDXRef-DOCUMENT", - "specVersion": { - "major": 2, - "minor": 1 - }, - "documentNamespace": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301", - "name": "Sample_Document-V2.1", - "comment": "This is a sample spreadsheet", - "dataLicense": { - "type": "Single", - "identifier": "CC0-1.0", - "name": "Creative Commons Zero v1.0 Universal" - }, - "licenseListVersion": { - "major": 3, - "minor": 6 - }, - "creators": [ - { - "name": "Gary O'Neall", - "email": null, - "type": "Person" - }, - { - "name": "Source Auditor Inc.", - "email": null, - "type": "Organization" - }, - { - "name": "SourceAuditor-V1.2", - "type": "Tool" - } - ], - "created": "2010-02-03T00:00:00Z", - "creatorComment": "This is an example of an SPDX spreadsheet format", - "files": [ - { - "id": "SPDXRef-File1", - "fileName": "Jenna-2.6.3/jena-2.6.3-sources.jar", - "fileTypes": ["ARCHIVE", "OTHER"], - "comment": "This file belongs to Jena", - "licenseConcluded": { - "type": "Single", - "identifier": "LicenseRef-1", - "name": "LicenseRef-1" - }, - "copyrightText": "(c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP", - "licenseComment": "This license is used by Jena", - "notice": null, - "checksums": [{ - "identifier": "SHA1", - "value": "3ab4e1c67a2d28fced849ee1bb76e7391b93f125" - },{ - "identifier": "SHA256", - "value": "3ab4e1c67a2d28fced849ee1bb76e7391b93f1250000000000000000" - }], - "licenseInfoInFiles": [ - { - "type": "Single", - "identifier": "LicenseRef-1", - "name": "LicenseRef-1" - } - ], - "contributors": [], - "dependencies": [], - "artifactOfProjectName": [ - "Jena" - ], - "artifactOfProjectHome": [ - "http://www.openjena.org/" - ], - "artifactOfProjectURI": [ - "http://subversion.apache.org/doap.rdf" - ] - }, - { - "id": "SPDXRef-File2", - "fileName": "src/org/spdx/parser/DOAPProject.java", - "fileTypes": ["SOURCE", "TEXT"], - "comment": null, - "licenseConcluded": { - "type": "Single", - "identifier": "Apache-2.0", - "name": "Apache License 2.0" - }, - "copyrightText": "Copyright 2010, 2011 Source Auditor Inc.", - "licenseComment": null, - "notice": null, - "checksums": [{ - "identifier": "SHA1", - "value": "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12" - }], - "licenseInfoInFiles": [ - { - "type": "Single", - "identifier": "Apache-2.0", - "name": "Apache License 2.0" - } - ], - "contributors": [], - "dependencies": [], - "artifactOfProjectName": [], - "artifactOfProjectHome": [], - "artifactOfProjectURI": [] - } - ], - "packages": [ - { - "id": "SPDXRef-Package", - "name": "SPDX Translator", - "packageFileName": "spdxtranslator-1.0.zip", - "summary": "SPDX Translator utility", - "description": "This utility translates and SPDX RDF XML document to a spreadsheet, translates a spreadsheet to an SPDX RDF XML document and translates an SPDX RDFa document to an SPDX RDF XML document.", - "versionInfo": "Version 0.9.2", - "sourceInfo": "Version 1.0 of the SPDX Translator application", - "downloadLocation": "http://www.spdx.org/tools", - "homepage": null, - "originator": { - "name": "SPDX", - "email": null, - "type": "Organization" - }, - "supplier": { - "name": "Linux Foundation", - "email": null, - "type": "Organization" - }, - "licenseConcluded": { - "type": "Conjunction", - "identifier": [ - "Apache-1.0", - "Apache-2.0", - "LicenseRef-1", - "LicenseRef-2", - "LicenseRef-3", - "LicenseRef-4", - "MPL-1.1" - ], - "name": [ - "Apache License 1.0", - "Apache License 2.0", - "CyberNeko License", - "LicenseRef-1", - "LicenseRef-2", - "LicenseRef-4", - "Mozilla Public License 1.1" - ] - }, - "licenseDeclared": { - "type": "Conjunction", - "identifier": [ - "Apache-2.0", - "LicenseRef-1", - "LicenseRef-2", - "LicenseRef-3", - "LicenseRef-4", - "MPL-1.1" - ], - "name": [ - "Apache License 2.0", - "CyberNeko License", - "LicenseRef-1", - "LicenseRef-2", - "LicenseRef-4", - "Mozilla Public License 1.1" - ] - }, - "copyrightText": " Copyright 2010, 2011 Source Auditor Inc.", - "licenseComment": "The declared license information can be found in the NOTICE file at the root of the archive file", - "checksums": [{ - "identifier": "SHA1", - "value": "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12" - }], - "licenseInfoFromFiles": [ - { - "type": "Single", - "identifier": "Apache-1.0", - "name": "Apache License 1.0" - }, - { - "type": "Single", - "identifier": "Apache-2.0", - "name": "Apache License 2.0" - }, - { - "type": "Single", - "identifier": "LicenseRef-1", - "name": "LicenseRef-1" - }, - { - "type": "Single", - "identifier": "LicenseRef-2", - "name": "LicenseRef-2" - }, - { - "type": "Single", - "identifier": "LicenseRef-3", - "name": "CyberNeko License" - }, - { - "type": "Single", - "identifier": "LicenseRef-4", - "name": "LicenseRef-4" - }, - { - "type": "Single", - "identifier": "MPL-1.1", - "name": "Mozilla Public License 1.1" - } - ], - "verificationCode": { - "value": "4e3211c67a2d28fced849ee1bb76e7391b93feba", - "excludedFilesNames": [ - "SpdxTranslatorSpdx.rdf", - "SpdxTranslatorSpdx.txt" - ] - }, - "builtDate": "2020-01-01T12:00:00Z", - "releaseDate": "2021-01-01T12:00:00Z", - "validUntilDate": "2022-01-01T12:00:00Z", - "primaryPackagePurpose": "OPERATING-SYSTEM"} - ], - "externalDocumentRefs": [ - { - "externalDocumentId": "DocumentRef-spdx-tool-2.1", - "spdxDocument": "https://spdx.org/spdxdocs/spdx-tools-v2.1-3F2504E0-4F89-41D3-9A0C-0305E82C3301", - "checksum": { - "identifier": "SHA1", - "value": "d6a770ba38583ed4bb4525bd96e50461655d2759" - } - } - ], - "extractedLicenses": [ - { - "name": "LicenseRef-1", - "identifier": "LicenseRef-1", - "text": "/*\n * (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * 3. The name of the author may not be used to endorse or promote products\n * derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */", - "comment": null, - "cross_refs": [] - }, - { - "name": "LicenseRef-2", - "identifier": "LicenseRef-2", - "text": "This package includes the GRDDL parser developed by Hewlett Packard under the following license:\n\u00a9 Copyright 2007 Hewlett-Packard Development Company, LP\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\nRedistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\nThe name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.\nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ", - "comment": null, - "cross_refs": [] - }, - { - "name": "CyberNeko License", - "identifier": "LicenseRef-3", - "text": "The CyberNeko Software License, Version 1.0\n\n\n(C) Copyright 2002-2005, Andy Clark. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n1. Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n3. The end-user documentation included with the redistribution,\n if any, must include the following acknowledgment:\n \"This product includes software developed by Andy Clark.\"\n Alternately, this acknowledgment may appear in the software itself,\n if and wherever such third-party acknowledgments normally appear.\n\n4. The names \"CyberNeko\" and \"NekoHTML\" must not be used to endorse\n or promote products derived from this software without prior\n written permission. For written permission, please contact\n andyc@cyberneko.net.\n\n5. Products derived from this software may not be called \"CyberNeko\",\n nor may \"CyberNeko\" appear in their name, without prior written\n permission of the author.\n\nTHIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED\nWARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\nOF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS\nBE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,\nOR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT\nOF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\nBUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\nWHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\nOR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\nEVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.", - "comment": "This is tye CyperNeko License", - "cross_refs": [ - "http://justasample.url.com", - "http://people.apache.org/~andyc/neko/LICENSE" - ] - }, - { - "name": "LicenseRef-4", - "identifier": "LicenseRef-4", - "text": "/*\n * (c) Copyright 2009 University of Bristol\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * 3. The name of the author may not be used to endorse or promote products\n * derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */ ", - "comment": null, - "cross_refs": [] - } - ], - "annotations": [ - { - "id": "SPDXRef-DOCUMENT", - "comment": "This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses", - "type": "REVIEW", - "annotator": { - "name": "Jim Reviewer", - "email": null, - "type": "Person" - }, - "date": "2012-06-13T00:00:00Z" - } - ], - "reviews": [ - { - "comment": "This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses", - "reviewer": { - "name": "Joe Reviewer", - "email": null, - "type": "Person" - }, - "date": "2010-02-10T00:00:00Z" - }, - { - "comment": "Another example reviewer.", - "reviewer": { - "name": "Suzanne Reviewer", - "email": null, - "type": "Person" - }, - "date": "2011-03-13T00:00:00Z" - } - ], - "snippets": [ - { - "id": "SPDXRef-Snippet", - "name": "from linux kernel", - "comment": "This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0-or-later.", - "copyrightText": "Copyright 2008-2010 John Smith", - "licenseComments": "The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz.", - "snippetFromFile": "SPDXRef-DoapSource", - "licenseConcluded": { - "type": "Single", - "identifier": "Apache-2.0", - "name": "Apache License 2.0" - }, - "licenseInfoInSnippets": [ - { - "type": "Single", - "identifier": "Apache-2.0", - "name": "Apache License 2.0"}, - { - "type": "Single", - "identifier": "GPL-2.0-only", - "name": "GNU General Public License v2.0 only" - } - ] - } - ], - "relationships": [ - { - "spdx_element_id": "SPDXRef-Package", - "relationship_type": "CONTAINS", - "related_spdx_element": "SPDXRef-File1" - }, - { - "spdx_element_id": "SPDXRef-Package", - "relationship_type": "CONTAINS", - "related_spdx_element": "SPDXRef-File2" - }, - { - "spdx_element_id": "SPDXRef-DOCUMENT", - "relationship_type": "DESCRIBES", - "related_spdx_element": "SPDXRef-Package" - } - ] -} diff --git a/tests/spdx/data/doc_parse/spdx-expected.json b/tests/spdx/data/doc_parse/spdx-expected.json deleted file mode 100644 index fce55cb2c..000000000 --- a/tests/spdx/data/doc_parse/spdx-expected.json +++ /dev/null @@ -1,335 +0,0 @@ -{ - "id": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DOCUMENT", - "specVersion": { - "major": 2, - "minor": 1 - }, - "documentNamespace": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301", - "name": "Sample_Document-V2.1", - "comment": "This is a sample spreadsheet", - "dataLicense": { - "type": "Single", - "identifier": "CC0-1.0", - "name": "Creative Commons Zero v1.0 Universal" - }, - "licenseListVersion": { - "major": 3, - "minor": 6 - }, - "creators": [ - { - "name": "Gary O'Neall", - "email": null, - "type": "Person" - }, - { - "name": "Source Auditor Inc.", - "email": null, - "type": "Organization" - }, - { - "name": "SourceAuditor-V1.2", - "type": "Tool" - } - ], - "created": "2010-02-03T00:00:00Z", - "creatorComment": "This is an example of an SPDX spreadsheet format", - "files": [ - { - "id": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File1", - "fileName": "Jenna-2.6.3/jena-2.6.3-sources.jar", - "fileTypes": ["ARCHIVE", "OTHER"], - "comment": "This file belongs to Jena", - "licenseConcluded": { - "type": "Single", - "identifier": "LicenseRef-1", - "name": "LicenseRef-1" - }, - "copyrightText": "(c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP", - "licenseComment": "This license is used by Jena", - "notice": null, - "checksums": [ - { - "identifier": "SHA1", - "value": "3ab4e1c67a2d28fced849ee1bb76e7391b93f125" - }, - { - "identifier": "SHA256", - "value": "3ab4e1c67a2d28fced849ee1bb76e7391b93f1250000000000000000" - } - ], - "licenseInfoInFiles": [ - { - "type": "Single", - "identifier": "LicenseRef-1", - "name": "LicenseRef-1" - } - ], - "contributors": [], - "dependencies": [], - "artifactOfProjectName": [ - "Jena" - ], - "artifactOfProjectHome": [ - "http://www.openjena.org/" - ], - "artifactOfProjectURI": [] - }, - { - "id": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File2", - "fileName": "src/org/spdx/parser/DOAPProject.java", - "fileTypes": ["SOURCE", "TEXT"], - "comment": null, - "licenseConcluded": { - "type": "Single", - "identifier": "Apache-2.0", - "name": "Apache License 2.0" - }, - "copyrightText": "Copyright 2010, 2011 Source Auditor Inc.", - "licenseComment": null, - "notice": null, - "checksums": [ - { - "identifier": "SHA1", - "value": "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12" - }, - { - "identifier": "SHA256", - "value": "2fd4e1c67a2d28fced849ee1bb76e7391b93eb120000000000000000" - } - ], - "licenseInfoInFiles": [ - { - "type": "Single", - "identifier": "Apache-2.0", - "name": "Apache License 2.0" - } - ], - "contributors": [], - "dependencies": [], - "artifactOfProjectName": [], - "artifactOfProjectHome": [], - "artifactOfProjectURI": [] - } - ], - "packages": [ - { - "id": "SPDXRef-Package", - "name": "SPDX Translator", - "packageFileName": "spdxtranslator-1.0.zip", - "summary": "SPDX Translator utility", - "description": "This utility translates and SPDX RDF XML document to a spreadsheet, translates a spreadsheet to an SPDX RDF XML document and translates an SPDX RDFa document to an SPDX RDF XML document.", - "versionInfo": "Version 0.9.2", - "sourceInfo": "Version 1.0 of the SPDX Translator application", - "downloadLocation": "http://www.spdx.org/tools", - "homepage": null, - "originator": { - "name": "SPDX", - "email": null, - "type": "Organization" - }, - "supplier": { - "name": "Linux Foundation", - "email": null, - "type": "Organization" - }, - "licenseConcluded": { - "type": "Conjunction", - "identifier": [ - "Apache-1.0", - "Apache-2.0", - "LicenseRef-1", - "LicenseRef-2", - "LicenseRef-3", - "LicenseRef-4", - "MPL-1.1" - ], - "name": [ - "Apache License 1.0", - "Apache License 2.0", - "CyberNeko License", - "LicenseRef-1", - "LicenseRef-2", - "LicenseRef-4", - "Mozilla Public License 1.1" - ] - }, - "licenseDeclared": { - "type": "Conjunction", - "identifier": [ - "Apache-2.0", - "LicenseRef-1", - "LicenseRef-2", - "LicenseRef-3", - "LicenseRef-4", - "MPL-1.1" - ], - "name": [ - "Apache License 2.0", - "CyberNeko License", - "LicenseRef-1", - "LicenseRef-2", - "LicenseRef-4", - "Mozilla Public License 1.1" - ] - }, - "copyrightText": " Copyright 2010, 2011 Source Auditor Inc.", - "licenseComment": "The declared license information can be found in the NOTICE file at the root of the archive file", - "checksums": [ - { - "identifier": "SHA1", - "value": "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12" - } - ], - "licenseInfoFromFiles": [ - { - "type": "Single", - "identifier": "Apache-1.0", - "name": "Apache License 1.0" - }, - { - "type": "Single", - "identifier": "Apache-2.0", - "name": "Apache License 2.0" - }, - { - "type": "Single", - "identifier": "LicenseRef-1", - "name": "LicenseRef-1" - }, - { - "type": "Single", - "identifier": "LicenseRef-2", - "name": "LicenseRef-2" - }, - { - "type": "Single", - "identifier": "LicenseRef-3", - "name": "CyberNeko License" - }, - { - "type": "Single", - "identifier": "LicenseRef-4", - "name": "LicenseRef-4" - }, - { - "type": "Single", - "identifier": "MPL-1.1", - "name": "Mozilla Public License 1.1" - } - ], - "verificationCode": { - "value": "4e3211c67a2d28fced849ee1bb76e7391b93feba", - "excludedFilesNames": [ - "SpdxTranslatorSpdx.rdf", - "SpdxTranslatorSpdx.txt" - ] - } - } - ], - "externalDocumentRefs": [ - { - "externalDocumentId": "DocumentRef-spdx-tool-2.1", - "spdxDocument": "https://spdx.org/spdxdocs/spdx-tools-v2.1-3F2504E0-4F89-41D3-9A0C-0305E82C3301", - "checksum": { - "identifier": "SHA1", - "value": "d6a770ba38583ed4bb4525bd96e50461655d2759" - } - } - ], - "extractedLicenses": [ - { - "name": "LicenseRef-1", - "identifier": "LicenseRef-1", - "text": "/*\n * (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * 3. The name of the author may not be used to endorse or promote products\n * derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */", - "comment": null, - "cross_refs": [] - }, - { - "name": "LicenseRef-2", - "identifier": "LicenseRef-2", - "text": "This package includes the GRDDL parser developed by Hewlett Packard under the following license:\n\u00a9 Copyright 2007 Hewlett-Packard Development Company, LP\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \nRedistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \nThe name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. \nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ", - "comment": null, - "cross_refs": [] - }, - { - "name": "CyberNeko License", - "identifier": "LicenseRef-3", - "text": "The CyberNeko Software License, Version 1.0\n\n \n(C) Copyright 2002-2005, Andy Clark. All rights reserved.\n \nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n1. Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer. \n\n2. Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n3. The end-user documentation included with the redistribution,\n if any, must include the following acknowledgment: \n \"This product includes software developed by Andy Clark.\"\n Alternately, this acknowledgment may appear in the software itself,\n if and wherever such third-party acknowledgments normally appear.\n\n4. The names \"CyberNeko\" and \"NekoHTML\" must not be used to endorse\n or promote products derived from this software without prior \n written permission. For written permission, please contact \n andyc@cyberneko.net.\n\n5. Products derived from this software may not be called \"CyberNeko\",\n nor may \"CyberNeko\" appear in their name, without prior written\n permission of the author.\n\nTHIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED\nWARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\nOF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS\nBE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, \nOR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT \nOF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR \nBUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, \nWHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE \nOR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, \nEVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.", - "comment": "This is tye CyperNeko License", - "cross_refs": [ - "http://justasample.url.com", - "http://people.apache.org/~andyc/neko/LICENSE" - ] - }, - { - "name": "LicenseRef-4", - "identifier": "LicenseRef-4", - "text": "/*\n * (c) Copyright 2009 University of Bristol\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * 3. The name of the author may not be used to endorse or promote products\n * derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */ ", - "comment": null, - "cross_refs": [] - } - ], - "annotations": [ - { - "id": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-45", - "comment": "This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses", - "type": "REVIEW", - "annotator": { - "name": "Jim Reviewer", - "email": null, - "type": "Person" - }, - "date": "2012-06-13T00:00:00Z" - } - ], - "reviews": [ - { - "comment": "This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses", - "reviewer": { - "name": "Joe Reviewer", - "email": null, - "type": "Person" - }, - "date": "2010-02-10T00:00:00Z" - }, - { - "comment": "Another example reviewer.", - "reviewer": { - "name": "Suzanne Reviewer", - "email": null, - "type": "Person" - }, - "date": "2011-03-13T00:00:00Z" - } - ], - "snippets": [ - { - "id": "SPDXRef-Snippet", - "name": "from linux kernel", - "comment": "This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0-or-later.", - "copyrightText": "Copyright 2008-2010 John Smith", - "licenseComments": "The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz.", - "snippetFromFile": "SPDXRef-DoapSource", - "licenseConcluded": { - "type": "Single", - "identifier": "Apache-2.0", - "name": "Apache License 2.0" - }, - "licenseInfoInSnippets": [ - { - "type": "Single", - "identifier": "Apache-2.0", - "name": "Apache License 2.0" - } - ] - } - ], - "relationships": [ - { - "spdx_element_id": "SPDXRef-DOCUMENT", - "relationship_type": "DESCRIBES", - "related_spdx_element": "SPDXRef-Package" - } - ] -} diff --git a/tests/spdx/data/doc_write/json-simple-multi-package.json b/tests/spdx/data/doc_write/json-simple-multi-package.json deleted file mode 100644 index 61e221c7e..000000000 --- a/tests/spdx/data/doc_write/json-simple-multi-package.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "spdxVersion": "SPDX-2.1", - "documentNamespace": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301", - "creationInfo": { - "creators": [ - "Tool: ScanCode" - ], - "created": "2021-10-21T17:09:37Z", - "licenseListVersion": "3.6" - }, - "dataLicense": "CC0-1.0", - "SPDXID": "SPDXRef-DOCUMENT", - "name": "Sample_Document-V2.1", - "documentDescribes": [ - "SPDXRef-Package1", - "SPDXRef-Package2", - "SPDXRef-Package3" - ], - "packages": [ - { - "SPDXID": "SPDXRef-Package1", - "name": "some/path1", - "downloadLocation": "NOASSERTION", - "filesAnalyzed": false, - "licenseConcluded": "NOASSERTION", - "licenseDeclared": "NOASSERTION", - "copyrightText": "Some copyright" - }, - { - "SPDXID": "SPDXRef-Package2", - "name": "some/path2", - "downloadLocation": "NOASSERTION", - "packageVerificationCode": { - "packageVerificationCodeValue": "SOME code" - }, - "licenseInfoFromFiles": [ - "LGPL-2.1-or-later" - ], - "licenseConcluded": "NOASSERTION", - "licenseDeclared": "NOASSERTION", - "copyrightText": "Some copyright", - "hasFiles": [ - "SPDXRef-File" - ] - }, - { - "SPDXID": "SPDXRef-Package3", - "name": "some/path3", - "downloadLocation": "NOASSERTION", - "licenseInfoFromFiles": [ - "LGPL-2.1-or-later" - ], - "licenseConcluded": "NOASSERTION", - "licenseDeclared": "NOASSERTION", - "copyrightText": "Some copyright", - "hasFiles": [ - "SPDXRef-File" - ] - } - ], - "files": [ - { - "fileName": "./some/path/tofile", - "SPDXID": "SPDXRef-File", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "SOME-SHA1" - } - ], - "licenseConcluded": "NOASSERTION", - "licenseInfoInFiles": [ - "LGPL-2.1-or-later" - ], - "copyrightText": "NOASSERTION" - } - ] -} diff --git a/tests/spdx/data/doc_write/json-simple-plus.json b/tests/spdx/data/doc_write/json-simple-plus.json deleted file mode 100644 index e9be761eb..000000000 --- a/tests/spdx/data/doc_write/json-simple-plus.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "spdxVersion": "SPDX-2.1", - "dataLicense": "CC0-1.0", - "name": "Sample_Document-V2.1", - "SPDXID": "SPDXRef-DOCUMENT", - "documentNamespace": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301", - "creationInfo": { - "creators": [ - "Organization: SPDX" - ], - "created": "2021-11-15T00:00:00Z", - "licenseListVersion": "3.6" - }, - "documentDescribes": [ - "SPDXRef-Package" - ], - "packages": [ - { - "SPDXID": "SPDXRef-Package", - "name": "some/path", - "downloadLocation": "NOASSERTION", - "copyrightText": "Some copyright", - "packageVerificationCode": { - "packageVerificationCodeValue": "SOME code" - }, - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "SOME-SHA1" - }, - { - "algorithm": "SHA256", - "checksumValue": "SOME-SHA256" - } - ], - "licenseDeclared": "NOASSERTION", - "licenseConcluded": "NOASSERTION", - "licenseInfoFromFiles": [ - "LGPL-2.1-or-later" - ], - "hasFiles": [ - "SPDXRef-File" - ], - "primaryPackagePurpose": "FILE", - "releaseDate": "2021-01-01T12:00:00Z", - "builtDate": "2021-01-01T12:00:00Z", - "validUntilDate": "2022-01-01T12:00:00Z" - } - ], - "files": [ - { - "SPDXID": "SPDXRef-File", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "SOME-SHA1" - }, - { - "algorithm": "SHA256", - "checksumValue": "SOME-SHA256" - } - ], - "licenseConcluded": "NOASSERTION", - "copyrightText": "NOASSERTION", - "fileName": "./some/path/tofile", - "licenseInfoInFiles": [ - "LGPL-2.1-or-later" - ], - "fileTypes": ["OTHER", "SOURCE"] - } - ] -} diff --git a/tests/spdx/data/doc_write/json-simple.json b/tests/spdx/data/doc_write/json-simple.json deleted file mode 100644 index 30c1b8943..000000000 --- a/tests/spdx/data/doc_write/json-simple.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "spdxVersion": "SPDX-2.1", - "dataLicense": "CC0-1.0", - "name": "Sample_Document-V2.1", - "SPDXID": "SPDXRef-DOCUMENT", - "documentNamespace": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301", - "creationInfo": { - "creators": [ - "Organization: SPDX" - ], - "created": "2021-11-15T00:00:00Z", - "licenseListVersion": "3.6" - }, - "documentDescribes": [ - "SPDXRef-Package" - ], - "packages": [ - { - "SPDXID": "SPDXRef-Package", - "name": "some/path", - "downloadLocation": "NOASSERTION", - "copyrightText": "Some copyright", - "packageVerificationCode": { - "packageVerificationCodeValue": "SOME code" - }, - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "SOME-SHA1" - }, - { - "algorithm": "SHA256", - "checksumValue": "SOME-SHA256" - } - ], - "licenseDeclared": "NOASSERTION", - "licenseConcluded": "NOASSERTION", - "licenseInfoFromFiles": [ - "LGPL-2.1-only" - ], - "hasFiles": [ - "SPDXRef-File" - ], - "primaryPackagePurpose": "FILE", - "releaseDate": "2021-01-01T12:00:00Z", - "builtDate": "2021-01-01T12:00:00Z", - "validUntilDate": "2022-01-01T12:00:00Z" - } - ], - "files": [ - { - "SPDXID": "SPDXRef-File", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "SOME-SHA1" - }, - { - "algorithm": "SHA256", - "checksumValue": "SOME-SHA256" - } - ], - "licenseConcluded": "NOASSERTION", - "copyrightText": "NOASSERTION", - "fileName": "./some/path/tofile", - "licenseInfoInFiles": [ - "LGPL-2.1-only" - ], - "fileTypes": ["OTHER", "SOURCE"] - } - ] -} diff --git a/tests/spdx/data/doc_write/rdf-mini.json b/tests/spdx/data/doc_write/rdf-mini.json deleted file mode 100644 index d413c6aaa..000000000 --- a/tests/spdx/data/doc_write/rdf-mini.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "rdf:RDF": { - "@xmlns:ns1": "http://spdx.org/rdf/terms#", - "@xmlns:rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "ns1:SpdxDocument": { - "@rdf:about": "http://www.spdx.org/tools#SPDXRef-DOCUMENT", - "ns1:specVersion": "SPDX-2.1", - "ns1:dataLicense": { - "@rdf:resource": "http://spdx.org/licenses/CC0-1.0" - } - } - } -} diff --git a/tests/spdx/data/doc_write/rdf-simple-plus.json b/tests/spdx/data/doc_write/rdf-simple-plus.json deleted file mode 100644 index d2b65aeed..000000000 --- a/tests/spdx/data/doc_write/rdf-simple-plus.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "rdf:RDF": { - "@xmlns:ns1": "http://spdx.org/rdf/terms#", - "@xmlns:rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "ns1:SpdxDocument": { - "ns1:describesPackage": { - "ns1:Package": { - "@rdf:about": "http://www.spdx.org/tools#SPDXRef-Package", - "ns1:Package": { - "@rdf:resource": "SPDXRef-Package" - }, - "ns1:hasFile": { - "@rdf:resource": "http://www.spdx.org/files#SPDXRef-File" - }, - "ns1:name": "some/path", - "ns1:licenseDeclared": { - "@rdf:resource": "http://spdx.org/rdf/terms#noassertion" - }, - "ns1:downloadLocation": { - "@rdf:resource": "http://spdx.org/rdf/terms#noassertion" - }, - "ns1:licenseInfoFromFiles": { - "@rdf:resource": "http://spdx.org/licenses/LGPL-2.1-or-later" - }, - "ns1:licenseConcluded": { - "@rdf:resource": "http://spdx.org/rdf/terms#noassertion" - }, - "ns1:copyrightText": "Some copyright", - "ns1:checksum": [ - { - "ns1:Checksum": { - "ns1:algorithm": "http://spdx.org/rdf/terms#checksumAlgorithm_sha1", - "ns1:checksumValue": "SOME-SHA1" - } - }, - { - "ns1:Checksum": { - "ns1:algorithm": "http://spdx.org/rdf/terms#checksumAlgorithm_sha256", - "ns1:checksumValue": "SOME-SHA256" - } - } - ] - } - }, - "ns1:specVersion": "SPDX-2.1", - "ns1:dataLicense": { - "@rdf:resource": "http://spdx.org/licenses/CC0-1.0" - }, - "ns1:name": { - "@rdf:resource": "Sample_Document-V2.1" - }, - "ns1:referencesFile": { - "ns1:File": { - "@rdf:about": "http://www.spdx.org/files#SPDXRef-File", - "ns1:fileName": "./some/path/tofile", - "ns1:checksum": [ - { - "ns1:Checksum": { - "ns1:checksumValue": "SOME-SHA1", - "ns1:algorithm": "http://spdx.org/rdf/terms#checksumAlgorithm_sha1" - } - }, { - "ns1:Checksum": { - "ns1:checksumValue": "SOME-SHA256", - "ns1:algorithm": "http://spdx.org/rdf/terms#checksumAlgorithm_sha256" - } - } - ], - "ns1:licenseConcluded": { - "@rdf:resource": "http://spdx.org/rdf/terms#noassertion" - }, - "ns1:copyrightText": { - "@rdf:resource": "http://spdx.org/rdf/terms#noassertion" - }, - "ns1:licenseInfoInFile": { - "@rdf:resource": "http://spdx.org/licenses/LGPL-2.1-or-later" - }, - "ns1:fileType": [{"@rdf:resource": "http://spdx.org/rdf/terms#fileType_other"}, - {"@rdf:resource": "http://spdx.org/rdf/terms#fileType_source"}] - } - }, - "ns1:relationship": [ - { - "ns1:Relationship": { - "ns1:spdxElementId": "SPDXRef-DOCUMENT", - "ns1:relatedSpdxElement": "SPDXRef-Package", - "ns1:relationshipType": { - "@rdf:resource": "http://spdx.org/rdf/terms#relationshipType_describes" - } - } - }, - { - "ns1:Relationship": { - "ns1:spdxElementId": "SPDXRef-Package", - "ns1:relatedSpdxElement": "SPDXRef-File", - "ns1:relationshipType": { - "@rdf:resource": "http://spdx.org/rdf/terms#relationshipType_contains" - } - } - } - ], - "@rdf:about": "http://www.spdx.org/tools#SPDXRef-DOCUMENT" - } - } -} diff --git a/tests/spdx/data/doc_write/rdf-simple.json b/tests/spdx/data/doc_write/rdf-simple.json deleted file mode 100644 index 00064a345..000000000 --- a/tests/spdx/data/doc_write/rdf-simple.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "rdf:RDF": { - "@xmlns:ns1": "http://spdx.org/rdf/terms#", - "@xmlns:rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "ns1:SpdxDocument": { - "ns1:describesPackage": { - "ns1:Package": { - "@rdf:about": "http://www.spdx.org/tools#SPDXRef-Package", - "ns1:Package": { - "@rdf:resource": "SPDXRef-Package" - }, - "ns1:hasFile": { - "@rdf:resource": "http://www.spdx.org/files#SPDXRef-File" - }, - "ns1:downloadLocation": { - "@rdf:resource": "http://spdx.org/rdf/terms#noassertion" - }, - "ns1:licenseDeclared": { - "@rdf:resource": "http://spdx.org/rdf/terms#noassertion" - }, - "ns1:name": "some/path", - "ns1:licenseInfoFromFiles": { - "@rdf:resource": "http://spdx.org/licenses/LGPL-2.1-only" - }, - "ns1:licenseConcluded": { - "@rdf:resource": "http://spdx.org/rdf/terms#noassertion" - }, - "ns1:copyrightText": "Some copyright", - "ns1:checksum": [ - { - "ns1:Checksum": { - "ns1:algorithm": "http://spdx.org/rdf/terms#checksumAlgorithm_sha1", - "ns1:checksumValue": "SOME-SHA1" - } - }, - { - "ns1:Checksum": { - "ns1:algorithm": "http://spdx.org/rdf/terms#checksumAlgorithm_sha256", - "ns1:checksumValue": "SOME-SHA256" - } - } - ] - } - }, - "ns1:specVersion": "SPDX-2.1", - "ns1:dataLicense": { - "@rdf:resource": "http://spdx.org/licenses/CC0-1.0" - }, - "ns1:name": { - "@rdf:resource": "Sample_Document-V2.1" - }, - "ns1:referencesFile": { - "ns1:File": { - "@rdf:about": "http://www.spdx.org/files#SPDXRef-File", - "ns1:licenseInfoInFile": { - "@rdf:resource": "http://spdx.org/licenses/LGPL-2.1-only" - }, - "ns1:checksum": [ - { - "ns1:Checksum": { - "ns1:checksumValue": "SOME-SHA1", - "ns1:algorithm": "http://spdx.org/rdf/terms#checksumAlgorithm_sha1" - } - }, { - "ns1:Checksum": { - "ns1:checksumValue": "SOME-SHA256", - "ns1:algorithm": "http://spdx.org/rdf/terms#checksumAlgorithm_sha256" - } - } - ], - "ns1:licenseConcluded": { - "@rdf:resource": "http://spdx.org/rdf/terms#noassertion" - }, - "ns1:copyrightText": { - "@rdf:resource": "http://spdx.org/rdf/terms#noassertion" - }, - "ns1:fileName": "./some/path/tofile", - "ns1:fileType": [{"@rdf:resource": "http://spdx.org/rdf/terms#fileType_other"}, - {"@rdf:resource": "http://spdx.org/rdf/terms#fileType_source"}] - } - }, - "ns1:relationship": [ - { - "ns1:Relationship": { - "ns1:spdxElementId": "SPDXRef-DOCUMENT", - "ns1:relatedSpdxElement": "SPDXRef-Package", - "ns1:relationshipType": { - "@rdf:resource": "http://spdx.org/rdf/terms#relationshipType_describes" - } - } - }, - { - "ns1:Relationship": { - "ns1:spdxElementId": "SPDXRef-Package", - "ns1:relatedSpdxElement": "SPDXRef-File", - "ns1:relationshipType": { - "@rdf:resource": "http://spdx.org/rdf/terms#relationshipType_contains" - } - } - } - ], - "@rdf:about": "http://www.spdx.org/tools#SPDXRef-DOCUMENT" - } - } -} diff --git a/tests/spdx/data/doc_write/tv-mini.tv b/tests/spdx/data/doc_write/tv-mini.tv deleted file mode 100644 index 368682998..000000000 --- a/tests/spdx/data/doc_write/tv-mini.tv +++ /dev/null @@ -1,7 +0,0 @@ -# Document Information -SPDXVersion: SPDX-2.1 -DataLicense: CC0-1.0 -LicenseListVersion: 3.6 -SPDXID: SPDXRef-DOCUMENT -# Creation Info - diff --git a/tests/spdx/data/doc_write/tv-simple-plus.tv b/tests/spdx/data/doc_write/tv-simple-plus.tv deleted file mode 100644 index 8e4d5356d..000000000 --- a/tests/spdx/data/doc_write/tv-simple-plus.tv +++ /dev/null @@ -1,35 +0,0 @@ -# Document Information -SPDXVersion: SPDX-2.1 -DataLicense: CC0-1.0 -DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 -DocumentName: Sample_Document-V2.1 -LicenseListVersion: 3.6 -SPDXID: SPDXRef-DOCUMENT -# Creation Info -# Relationships -Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package -# Package -PackageName: some/path -SPDXID: SPDXRef-Package -PackageDownloadLocation: NOASSERTION -PackageChecksum: SHA1: SOME-SHA1 -PackageChecksum: SHA256: SOME-SHA256 -PackageVerificationCode: SOME code -PackageLicenseDeclared: NOASSERTION -PackageLicenseConcluded: NOASSERTION -PackageLicenseInfoFromFiles: LGPL-2.1-or-later -PackageCopyrightText: Some copyright -PrimaryPackagePurpose: FILE -BuiltDate: 2021-01-01T12:00:00Z -ReleaseDate: 2021-01-01T12:00:00Z -ValidUntilDate: 2022-01-01T12:00:00Z -# File -FileName: ./some/path/tofile -SPDXID: SPDXRef-File -FileType: OTHER -FileType: SOURCE -FileChecksum: SHA1: SOME-SHA1 -FileChecksum: SHA256: SOME-SHA256 -LicenseConcluded: NOASSERTION -LicenseInfoInFile: LGPL-2.1-or-later -FileCopyrightText: NOASSERTION diff --git a/tests/spdx/data/doc_write/tv-simple.tv b/tests/spdx/data/doc_write/tv-simple.tv deleted file mode 100644 index 1190f444f..000000000 --- a/tests/spdx/data/doc_write/tv-simple.tv +++ /dev/null @@ -1,35 +0,0 @@ -# Document Information -SPDXVersion: SPDX-2.1 -DataLicense: CC0-1.0 -DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 -DocumentName: Sample_Document-V2.1 -LicenseListVersion: 3.6 -SPDXID: SPDXRef-DOCUMENT -# Creation Info -# Relationships -Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package -# Package -PackageName: some/path -SPDXID: SPDXRef-Package -PackageDownloadLocation: NOASSERTION -PackageChecksum: SHA1: SOME-SHA1 -PackageChecksum: SHA256: SOME-SHA256 -PackageVerificationCode: SOME code -PackageLicenseDeclared: NOASSERTION -PackageLicenseConcluded: NOASSERTION -PackageLicenseInfoFromFiles: LGPL-2.1-only -PackageCopyrightText: Some copyright -PrimaryPackagePurpose: FILE -BuiltDate: 2021-01-01T12:00:00Z -ReleaseDate: 2021-01-01T12:00:00Z -ValidUntilDate: 2022-01-01T12:00:00Z -# File -FileName: ./some/path/tofile -SPDXID: SPDXRef-File -FileType: OTHER -FileType: SOURCE -FileChecksum: SHA1: SOME-SHA1 -FileChecksum: SHA256: SOME-SHA256 -LicenseConcluded: NOASSERTION -LicenseInfoInFile: LGPL-2.1-only -FileCopyrightText: NOASSERTION diff --git a/tests/spdx/data/doc_write/xml-simple-multi-package.xml b/tests/spdx/data/doc_write/xml-simple-multi-package.xml deleted file mode 100644 index 7552c6dcc..000000000 --- a/tests/spdx/data/doc_write/xml-simple-multi-package.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - SPDX-2.1 - https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 - - Tool: ScanCode - 2021-10-21T17:02:23Z - 3.6 - - CC0-1.0 - SPDXRef-DOCUMENT - Sample_Document-V2.1 - SPDXRef-Package1 - SPDXRef-Package2 - SPDXRef-Package3 - - SPDXRef-Package1 - some/path1 - NOASSERTION - false - NOASSERTION - NOASSERTION - Some copyright - - - SPDXRef-Package2 - some/path2 - NOASSERTION - - SOME code - - LGPL-2.1-or-later - NOASSERTION - NOASSERTION - Some copyright - SPDXRef-File - - - SPDXRef-Package3 - some/path3 - NOASSERTION - LGPL-2.1-or-later - NOASSERTION - NOASSERTION - Some copyright - SPDXRef-File - - - ./some/path/tofile - SPDXRef-File - - SHA1 - SOME-SHA1 - - NOASSERTION - LGPL-2.1-or-later - NOASSERTION - - diff --git a/tests/spdx/data/doc_write/xml-simple-plus.xml b/tests/spdx/data/doc_write/xml-simple-plus.xml deleted file mode 100644 index 82f20efe7..000000000 --- a/tests/spdx/data/doc_write/xml-simple-plus.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - SPDX-2.1 - CC0-1.0 - Sample_Document-V2.1 - SPDXRef-DOCUMENT - https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 - SPDXRef-Package - - SPDXRef-Package - some/path - NOASSERTION - Some copyright - - SOME code - - - SOME-SHA1 - SHA1 - - - SOME-SHA256 - SHA256 - - NOASSERTION - NOASSERTION - LGPL-2.1-or-later - SPDXRef-File - FILE - 2021-01-01T12:00:00Z - 2021-01-01T12:00:00Z - 2022-01-01T12:00:00Z - - - ./some/path/tofile - SPDXRef-File - - SOME-SHA1 - SHA1 - - - SOME-SHA256 - SHA256 - - NOASSERTION - NOASSERTION - LGPL-2.1-or-later - SOURCE - OTHER - - diff --git a/tests/spdx/data/doc_write/xml-simple.xml b/tests/spdx/data/doc_write/xml-simple.xml deleted file mode 100644 index 961fb9ad4..000000000 --- a/tests/spdx/data/doc_write/xml-simple.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - SPDX-2.1 - CC0-1.0 - Sample_Document-V2.1 - SPDXRef-DOCUMENT - https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 - SPDXRef-Package - - SPDXRef-Package - some/path - NOASSERTION - Some copyright - - SOME code - - - SOME-SHA1 - SHA1 - - - SOME-SHA256 - SHA256 - - NOASSERTION - NOASSERTION - LGPL-2.1-only - SPDXRef-File - FILE - 2021-01-01T12:00:00Z - 2021-01-01T12:00:00Z - 2022-01-01T12:00:00Z - - - ./some/path/tofile - SPDXRef-File - - SOME-SHA1 - SHA1 - - - SOME-SHA256 - SHA256 - - NOASSERTION - NOASSERTION - LGPL-2.1-only - SOURCE - OTHER - - diff --git a/tests/spdx/data/doc_write/yaml-simple-multi-package.yaml b/tests/spdx/data/doc_write/yaml-simple-multi-package.yaml deleted file mode 100644 index a6d10db5c..000000000 --- a/tests/spdx/data/doc_write/yaml-simple-multi-package.yaml +++ /dev/null @@ -1,54 +0,0 @@ -SPDXID: SPDXRef-DOCUMENT -creationInfo: - created: '2021-10-21T16:46:56Z' - creators: - - 'Tool: ScanCode' - licenseListVersion: '3.6' -dataLicense: CC0-1.0 -documentDescribes: - - SPDXRef-Package1 - - SPDXRef-Package2 - - SPDXRef-Package3 -packages: - - SPDXID: SPDXRef-Package1 - copyrightText: Some copyright - downloadLocation: NOASSERTION - filesAnalyzed: false - licenseConcluded: NOASSERTION - licenseDeclared: NOASSERTION - name: some/path1 - - SPDXID: SPDXRef-Package2 - copyrightText: Some copyright - downloadLocation: NOASSERTION - hasFiles: - - SPDXRef-File - licenseConcluded: NOASSERTION - licenseDeclared: NOASSERTION - licenseInfoFromFiles: - - LGPL-2.1-or-later - name: some/path2 - packageVerificationCode: - packageVerificationCodeValue: SOME code - - SPDXID: SPDXRef-Package3 - copyrightText: Some copyright - downloadLocation: NOASSERTION - hasFiles: - - SPDXRef-File - licenseConcluded: NOASSERTION - licenseDeclared: NOASSERTION - licenseInfoFromFiles: - - LGPL-2.1-or-later - name: some/path3 -documentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 -name: Sample_Document-V2.1 -spdxVersion: SPDX-2.1 -files: - - SPDXID: SPDXRef-File - checksums: - - algorithm: SHA1 - checksumValue: SOME-SHA1 - copyrightText: NOASSERTION - licenseConcluded: NOASSERTION - licenseInfoInFiles: - - LGPL-2.1-or-later - fileName: ./some/path/tofile \ No newline at end of file diff --git a/tests/spdx/data/doc_write/yaml-simple-plus.yaml b/tests/spdx/data/doc_write/yaml-simple-plus.yaml deleted file mode 100644 index 007d6cdd9..000000000 --- a/tests/spdx/data/doc_write/yaml-simple-plus.yaml +++ /dev/null @@ -1,49 +0,0 @@ -SPDXID: SPDXRef-DOCUMENT -creationInfo: - created: '2021-11-15T00:00:00Z' - creators: - - 'Organization: SPDX' - licenseListVersion: '3.6' -dataLicense: CC0-1.0 -documentDescribes: -- SPDXRef-Package -documentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 -files: -- SPDXID: SPDXRef-File - checksums: - - algorithm: SHA1 - checksumValue: SOME-SHA1 - - algorithm: SHA256 - checksumValue: SOME-SHA256 - copyrightText: NOASSERTION - fileName: ./some/path/tofile - licenseConcluded: NOASSERTION - licenseInfoInFiles: - - LGPL-2.1-or-later - fileTypes: - - SOURCE - - OTHER -name: Sample_Document-V2.1 -packages: -- SPDXID: SPDXRef-Package - checksums: - - algorithm: SHA1 - checksumValue: SOME-SHA1 - - algorithm: SHA256 - checksumValue: SOME-SHA256 - copyrightText: Some copyright - downloadLocation: NOASSERTION - hasFiles: - - SPDXRef-File - licenseConcluded: NOASSERTION - licenseDeclared: NOASSERTION - licenseInfoFromFiles: - - LGPL-2.1-or-later - name: some/path - packageVerificationCode: - packageVerificationCodeValue: SOME code - primaryPackagePurpose: FILE - releaseDate: '2021-01-01T12:00:00Z' - builtDate: '2021-01-01T12:00:00Z' - validUntilDate: '2022-01-01T12:00:00Z' -spdxVersion: SPDX-2.1 diff --git a/tests/spdx/data/doc_write/yaml-simple.yaml b/tests/spdx/data/doc_write/yaml-simple.yaml deleted file mode 100644 index 98e6edb42..000000000 --- a/tests/spdx/data/doc_write/yaml-simple.yaml +++ /dev/null @@ -1,50 +0,0 @@ -SPDXID: SPDXRef-DOCUMENT -creationInfo: - created: '2021-11-15T00:00:00Z' - creators: - - 'Organization: SPDX' - licenseListVersion: '3.6' -dataLicense: CC0-1.0 -documentDescribes: -- SPDXRef-Package -documentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 -files: -- SPDXID: SPDXRef-File - checksums: - - algorithm: SHA1 - checksumValue: SOME-SHA1 - - algorithm: SHA256 - checksumValue: SOME-SHA256 - copyrightText: NOASSERTION - fileName: ./some/path/tofile - licenseConcluded: NOASSERTION - licenseInfoInFiles: - - LGPL-2.1-only - fileTypes: - - SOURCE - - OTHER - -name: Sample_Document-V2.1 -packages: -- SPDXID: SPDXRef-Package - checksums: - - algorithm: SHA1 - checksumValue: SOME-SHA1 - - algorithm: SHA256 - checksumValue: SOME-SHA256 - copyrightText: Some copyright - downloadLocation: NOASSERTION - hasFiles: - - SPDXRef-File - licenseConcluded: NOASSERTION - licenseDeclared: NOASSERTION - licenseInfoFromFiles: - - LGPL-2.1-only - name: some/path - packageVerificationCode: - packageVerificationCodeValue: SOME code - primaryPackagePurpose: FILE - releaseDate: '2021-01-01T12:00:00Z' - builtDate: '2021-01-01T12:00:00Z' - validUntilDate: '2022-01-01T12:00:00Z' -spdxVersion: SPDX-2.1 diff --git a/tests/spdx/data/formats/SPDXSBOMExample.spdx.yml b/tests/spdx/data/formats/SPDXSBOMExample.spdx.yml deleted file mode 100644 index d26a7c8d3..000000000 --- a/tests/spdx/data/formats/SPDXSBOMExample.spdx.yml +++ /dev/null @@ -1,58 +0,0 @@ -# example of an SBOM with several packages and filesAnalyzed=False -# from https://github.com/spdx/spdx-spec/issues/439 -SPDXID: "SPDXRef-DOCUMENT" -spdxVersion: "SPDX-2.2" -creationInfo: - created: "2020-07-23T18:30:22Z" - creators: - - "Organization: Example Inc." - - "Person: Thomas Steenbergen" - licenseListVersion: "3.9" -name: "xyz-0.1.0" -dataLicense: "CC0-1.0" -documentNamespace: "http://spdx.org/spdxdocs/spdx-document-xyz" -documentDescribes: -- "SPDXRef-Package-xyz" -packages: -- SPDXID: "SPDXRef-Package-xyz" - summary: "Awesome product created by Example Inc." - copyrightText: "copyright 2004-2020 Example Inc. All Rights Reserved." - downloadLocation: "git+ssh://gitlab.example.com:3389/products/xyz.git@b2c358080011af6a366d2512a25a379fbe7b1f78" - filesAnalyzed: false - homepage: "https://example.com/products/xyz" - licenseConcluded: "NOASSERTION" - licenseDeclared: "Apache-2.0 AND curl AND LicenseRef-Proprietary-ExampleInc" - name: "xyz" - versionInfo: "0.1.0" -- SPDXID: "SPDXRef-Package-curl" - description: "A command line tool and library for transferring data with URL syntax, supporting \ - HTTP, HTTPS, FTP, FTPS, GOPHER, TFTP, SCP, SFTP, SMB, TELNET, DICT, LDAP, LDAPS, MQTT, FILE, \ - IMAP, SMTP, POP3, RTSP and RTMP. libcurl offers a myriad of powerful features." - copyrightText: "Copyright (c) 1996 - 2020, Daniel Stenberg, , and many - contributors, see the THANKS file." - downloadLocation: "https://github.com/curl/curl/releases/download/curl-7_70_0/curl-7.70.0.tar.gz" - filesAnalyzed: false - homepage: "https://curl.haxx.se/" - licenseConcluded: "NOASSERTION" - licenseDeclared: "curl" - name: "curl" - packageFileName: "./libs/curl" - versionInfo: "7.70.0" -- SPDXID: "SPDXRef-Package-openssl" - description: "OpenSSL is a robust, commercial-grade, full-featured Open Source Toolkit for the Transport Layer Security (TLS) protocol formerly known as the Secure Sockets Layer (SSL) protocol. The protocol implementation is based on a full-strength general purpose cryptographic library, which can also be used stand-alone." - copyrightText: "copyright 2004-2020 The OpenSSL Project Authors. All Rights Reserved." - downloadLocation: "git+ssh://github.com/openssl/openssl.git@e2e09d9fba1187f8d6aafaa34d4172f56f1ffb72" - filesAnalyzed: false - homepage: "https://www.openssl.org/" - licenseConcluded: "NOASSERTION" - licenseDeclared: "Apache-2.0" - packageFileName: "./libs/openssl" - name: "openssl" - versionInfo: "1.1.1g" -relationships: -- spdxElementId: "SPDXRef-Package-xyz" - relatedSpdxElement: "SPDXRef-Package-curl" - relationshipType: "CONTAINS" -- spdxElementId: "SPDXRef-Package-xyz" - relatedSpdxElement: "SPDXRef-Package-openssl" - relationshipType: "CONTAINS" diff --git a/tests/spdx/data/formats/SPDXSBOMExample.tag b/tests/spdx/data/formats/SPDXSBOMExample.tag deleted file mode 100644 index c20c9f522..000000000 --- a/tests/spdx/data/formats/SPDXSBOMExample.tag +++ /dev/null @@ -1,64 +0,0 @@ -# Document Information - -SPDXVersion: SPDX-2.2 -DataLicense: CC0-1.0 -DocumentNamespace: http://spdx.org/spdxdocs/spdx-document-xyz -DocumentName: xyz-0.1.0 -SPDXID: SPDXRef-DOCUMENT - - -# Creation Info - -Creator: Organization: Example Inc. -Creator: Person: Thomas Steenbergen -Created: 2020-07-23T18:30:22Z - - -# Relationships - -Relationship: SPDXRef-Package-xyz CONTAINS SPDXRef-Package-curl -Relationship: SPDXRef-Package-xyz CONTAINS SPDXRef-Package-openssl - - -# Package - -PackageName: xyz -SPDXID: SPDXRef-Package-xyz -PackageVersion: 0.1.0 -PackageDownloadLocation: git+ssh://gitlab.example.com:3389/products/xyz.git@b2c358080011af6a366d2512a25a379fbe7b1f78 -FilesAnalyzed: False -PackageSummary: Awesome product created by Example Inc. -PackageLicenseDeclared: (Apache-2.0 AND curl AND LicenseRef-Proprietary-ExampleInc) -PackageLicenseConcluded: NOASSERTION -PackageCopyrightText: copyright 2004-2020 Example Inc. All Rights Reserved. -PackageHomePage: https://example.com/products/xyz - - -# Package - -PackageName: curl -SPDXID: SPDXRef-Package-curl -PackageVersion: 7.70.0 -PackageDownloadLocation: https://github.com/curl/curl/releases/download/curl-7_70_0/curl-7.70.0.tar.gz -FilesAnalyzed: False -PackageFileName: ./libs/curl -PackageDescription: A command line tool and library for transferring data with URL syntax, supporting HTTP, HTTPS, FTP, FTPS, GOPHER, TFTP, SCP, SFTP, SMB, TELNET, DICT, LDAP, LDAPS, MQTT, FILE, IMAP, SMTP, POP3, RTSP and RTMP. libcurl offers a myriad of powerful features. -PackageLicenseDeclared: curl -PackageLicenseConcluded: NOASSERTION -PackageCopyrightText: Copyright (c) 1996 - 2020, Daniel Stenberg, , and many contributors, see the THANKS file. -PackageHomePage: https://curl.haxx.se/ - - -# Package - -PackageName: openssl -SPDXID: SPDXRef-Package-openssl -PackageVersion: 1.1.1g -PackageDownloadLocation: git+ssh://github.com/openssl/openssl.git@e2e09d9fba1187f8d6aafaa34d4172f56f1ffb72 -FilesAnalyzed: False -PackageFileName: ./libs/openssl -PackageDescription: OpenSSL is a robust, commercial-grade, full-featured Open Source Toolkit for the Transport Layer Security (TLS) protocol formerly known as the Secure Sockets Layer (SSL) protocol. The protocol implementation is based on a full-strength general purpose cryptographic library, which can also be used stand-alone. -PackageLicenseDeclared: Apache-2.0 -PackageLicenseConcluded: NOASSERTION -PackageCopyrightText: copyright 2004-2020 The OpenSSL Project Authors. All Rights Reserved. -PackageHomePage: https://www.openssl.org/ diff --git a/tests/spdx/data/formats/SPDXSimpleTag.tag b/tests/spdx/data/formats/SPDXSimpleTag.tag deleted file mode 100644 index 316978006..000000000 --- a/tests/spdx/data/formats/SPDXSimpleTag.tag +++ /dev/null @@ -1,65 +0,0 @@ -# Document info -SPDXVersion: SPDX-2.1 -DataLicense: CC0-1.0 -DocumentName: Sample_Document-V2.1 -SPDXID: SPDXRef-DOCUMENT -DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 -DocumentComment: Sample Comment -ExternalDocumentRef:DocumentRef-spdx-tool-2.1 https://spdx.org/spdxdocs/spdx-tools-v2.1-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759 - -# Creation info -Creator: Person: Bob (bob@example.com) -Creator: Organization: Acme -Created: 2014-02-03T00:00:00Z -CreatorComment: Sample Comment - -# Review #1 -Reviewer: Person: Bob the Reviewer -ReviewDate: 2014-02-10T00:00:00Z -ReviewComment: Bob was Here. - -# Review #2 -Reviewer: Person: Alice the Reviewer -ReviewDate: 2014-04-10T00:00:00Z -ReviewComment: Alice was also here. - - -# Package info -PackageName: Test -SPDXID: SPDXRef-Package -PackageVersion: Version 0.9.2 -PackageDownloadLocation: http://example.com/test -PackageSummary: Test package -PackageSourceInfo: Version 1.0 of test -PackageFileName: test-1.0.zip -PackageSupplier: Organization:ACME -PackageOriginator: Organization:ACME -PackageAttributionText: The GNU C Library is free software. See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed. License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually. -PackageChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 -PackageVerificationCode: 4e3211c67a2d28fced849ee1bb76e7391b93feba (something.rdf, something.txt) -PackageDescription: A package. -PackageAttributionText: The GNU C Library is free software. See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed. License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually. -PackageCopyrightText: Copyright 2010, 2011 Acme Inc. -PackageLicenseDeclared: Apache-2.0 -PackageLicenseConcluded: (LicenseRef-2.0 and Apache-2.0) -PackageLicenseInfoFromFiles: Apache-1.0 -PackageLicenseInfoFromFiles: Apache-2.0 -PackageLicenseComments: License Comments - -# File Info - -FileName: testfile.java -SPDXID: SPDXRef-File -FileType: SOURCE -FileChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 -LicenseConcluded: Apache-2.0 -LicenseInfoInFile: Apache-2.0 -FileCopyrightText: Copyright 2014 Acme Inc. -ArtifactOfProjectName: AcmeTest -ArtifactOfProjectHomePage: http://www.acme.org/ -ArtifactOfProjectURI: http://www.acme.org/ -FileComment: Very long file - - - - diff --git a/tests/spdx/data/formats/SPDXTagExample-v2.2.spdx b/tests/spdx/data/formats/SPDXTagExample-v2.2.spdx deleted file mode 100644 index e8f32ebfd..000000000 --- a/tests/spdx/data/formats/SPDXTagExample-v2.2.spdx +++ /dev/null @@ -1,329 +0,0 @@ -SPDXVersion: SPDX-2.2 -DataLicense: CC0-1.0 -DocumentNamespace: http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 -DocumentName: SPDX-Tools-v2.0 -SPDXID: SPDXRef-DOCUMENT -DocumentComment: This document was created using SPDX 2.0 using licenses from the web site. - -## External Document References -ExternalDocumentRef: DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759 -## Creation Information -Creator: Tool: LicenseFind-1.0 -Creator: Organization: ExampleCodeInspect () -Creator: Person: Jane Doe () -Created: 2010-01-29T18:30:22Z -CreatorComment: This package has been shipped in source and binary form. -The binaries were created with gcc 4.5.1 and expect to link to -compatible system run time libraries. -LicenseListVersion: 3.9 -## Annotations -Annotator: Person: Jane Doe () -AnnotationDate: 2010-01-29T18:30:22Z -AnnotationComment: Document level annotation -AnnotationType: OTHER -SPDXREF: SPDXRef-DOCUMENT -Annotator: Person: Joe Reviewer -AnnotationDate: 2010-02-10T00:00:00Z -AnnotationComment: This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses -AnnotationType: REVIEW -SPDXREF: SPDXRef-DOCUMENT -Annotator: Person: Suzanne Reviewer -AnnotationDate: 2011-03-13T00:00:00Z -AnnotationComment: Another example reviewer. -AnnotationType: REVIEW -SPDXREF: SPDXRef-DOCUMENT -## Relationships -Relationship: SPDXRef-DOCUMENT CONTAINS SPDXRef-Package -Relationship: SPDXRef-DOCUMENT COPY_OF DocumentRef-spdx-tool-1.2:SPDXRef-ToolsElement -Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File -Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package - -FileName: ./package/foo.c -SPDXID: SPDXRef-File -FileComment: The concluded license was taken from the package level that the file was included in. -This information was found in the COPYING.txt file in the xyz directory. -FileType: SOURCE -FileChecksum: SHA1: d6a770ba38583ed4bb4525bd96e50461655d2758 -FileChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 -LicenseConcluded: (LGPL-2.0-only OR LicenseRef-2) -LicenseInfoInFile: GPL-2.0-only -LicenseInfoInFile: LicenseRef-2 -LicenseComments: The concluded license was taken from the package level that the file was included in. -FileCopyrightText: Copyright 2008-2010 John Smith -FileNotice: Copyright (c) 2001 Aaron Lehmann aaroni@vitelus.com - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the �Software�), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED �AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -FileContributor: The Regents of the University of California -FileContributor: Modified by Paul Mundt lethal@linux-sh.org -FileContributor: IBM Corporation -## Annotations -Annotator: Person: File Commenter -AnnotationDate: 2011-01-29T18:30:22Z -AnnotationComment: File level annotation -AnnotationType: OTHER -SPDXREF: SPDXRef-File -## Relationships -Relationship: SPDXRef-File GENERATED_FROM SPDXRef-fromDoap-0 -## Package Information -PackageName: glibc -SPDXID: SPDXRef-Package -PackageVersion: 2.11.1 -PackageFileName: glibc-2.11.1.tar.gz -PackageSupplier: Person: Jane Doe (jane.doe@example.com) -PackageOriginator: Organization: ExampleCodeInspect (contact@example.com) -PackageDownloadLocation: http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz -PackageVerificationCode: d6a770ba38583ed4bb4525bd96e50461655d2758(./package.spdx) -PackageChecksum: MD5: 624c1abb3664f4b35547e7c73864ad24 -PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c -PackageChecksum: SHA256: 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd -PackageHomePage: http://ftp.gnu.org/gnu/glibc -PackageSourceInfo: uses glibc-2_11-branch from git://sourceware.org/git/glibc.git. -PackageLicenseConcluded: (LGPL-2.0-only OR LicenseRef-3) -## License information from files -PackageLicenseInfoFromFiles: GPL-2.0-only -PackageLicenseInfoFromFiles: LicenseRef-2 -PackageLicenseInfoFromFiles: LicenseRef-1 -PackageLicenseDeclared: (LGPL-2.0-only AND LicenseRef-3) -PackageLicenseComments: The license for this project changed with the release of version x.y. The version of the project included here post-dates the license change. -PackageCopyrightText: Copyright 2008-2010 John Smith -PackageSummary: GNU C library. -PackageDescription: The GNU C Library defines functions that are specified by the ISO C standard, as well as additional features specific to POSIX and other derivatives of the Unix operating system, and extensions specific to GNU systems. -PackageAttributionText: The GNU C Library is free software. See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed. License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually. -ExternalRef: SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* -ExternalRef: OTHER LocationRef-acmeforge acmecorp/acmenator/4.1.3-alpha -ExternalRefComment: This is the external ref for Acme -## Annotations -Annotator: Person: Package Commenter -AnnotationDate: 2011-01-29T18:30:22Z -AnnotationComment: Package level annotation -AnnotationType: OTHER -SPDXREF: SPDXRef-Package -## Relationships -Relationship: SPDXRef-Package CONTAINS SPDXRef-JenaLib -Relationship: SPDXRef-Package DYNAMIC_LINK SPDXRef-Saxon - -## File Information -FileName: ./lib-source/commons-lang3-3.1-sources.jar -SPDXID: SPDXRef-CommonsLangSrc -FileComment: This file is used by Jena -FileType: ARCHIVE -FileChecksum: SHA1: c2b4e1c67a2d28fced849ee1bb76e7391b93f125 -LicenseConcluded: Apache-2.0 -LicenseInfoInFile: Apache-2.0 -FileCopyrightText: Copyright 2001-2011 The Apache Software Foundation -FileNotice: Apache Commons Lang -Copyright 2001-2011 The Apache Software Foundation - -This product includes software developed by -The Apache Software Foundation (http://www.apache.org/). - -This product includes software from the Spring Framework, -under the Apache License 2.0 (see: StringUtils.containsWhitespace()) -FileContributor: Apache Software Foundation -## Relationships -Relationship: SPDXRef-CommonsLangSrc GENERATED_FROM NOASSERTION - -FileName: ./lib-source/jena-2.6.3-sources.jar -SPDXID: SPDXRef-JenaLib -FileComment: This file belongs to Jena -FileType: ARCHIVE -FileChecksum: SHA1: 3ab4e1c67a2d28fced849ee1bb76e7391b93f125 -LicenseConcluded: LicenseRef-1 -LicenseInfoInFile: LicenseRef-1 -LicenseComments: This license is used by Jena -FileCopyrightText: (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP -FileContributor: Apache Software Foundation -FileContributor: Hewlett Packard Inc. -## Relationships -Relationship: SPDXRef-JenaLib CONTAINS SPDXRef-Package - -FileName: ./src/org/spdx/parser/DOAPProject.java -SPDXID: SPDXRef-DoapSource -FileType: SOURCE -FileChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 -LicenseConcluded: Apache-2.0 -LicenseInfoInFile: Apache-2.0 -FileCopyrightText: Copyright 2010, 2011 Source Auditor Inc. -FileContributor: Protecode Inc. -FileContributor: SPDX Technical Team Members -FileContributor: Open Logic Inc. -FileContributor: Source Auditor Inc. -FileContributor: Black Duck Software In.c - -## Package Information -PackageName: Apache Commons Lang -SPDXID: SPDXRef-fromDoap-1 -PackageDownloadLocation: NOASSERTION -PackageHomePage: http://commons.apache.org/proper/commons-lang/ -PackageLicenseConcluded: NOASSERTION -PackageLicenseDeclared: NOASSERTION -PackageCopyrightText: NOASSERTION -FilesAnalyzed: false - -## Package Information -PackageName: Jena -SPDXID: SPDXRef-fromDoap-0 -PackageVersion: 3.12.0 -PackageDownloadLocation: https://search.maven.org/remotecontent?filepath=org/apache/jena/apache-jena/3.12.0/apache-jena-3.12.0.tar.gz -PackageHomePage: http://www.openjena.org/ -PackageLicenseConcluded: NOASSERTION -PackageLicenseDeclared: NOASSERTION -PackageCopyrightText: NOASSERTION -ExternalRef: PACKAGE-MANAGER purl pkg:maven/org.apache.jena/apache-jena@3.12.0 -FilesAnalyzed: false - -## Package Information -PackageName: Saxon -SPDXID: SPDXRef-Saxon -PackageVersion: 8.8 -PackageFileName: saxonB-8.8.zip -PackageDownloadLocation: https://sourceforge.net/projects/saxon/files/Saxon-B/8.8.0.7/saxonb8-8-0-7j.zip/download -PackageChecksum: SHA1: 85ed0817af83a24ad8da68c2b5094de69833983c -PackageHomePage: http://saxon.sourceforge.net/ -PackageLicenseConcluded: MPL-1.0 -PackageLicenseDeclared: MPL-1.0 -PackageLicenseComments: Other versions available for a commercial license -PackageCopyrightText: Copyright Saxonica Ltd -PackageDescription: The Saxon package is a collection of tools for processing XML documents. -FilesAnalyzed: false - -## Snippet Information -SnippetSPDXID: SPDXRef-Snippet -SnippetFromFileSPDXID: SPDXRef-DoapSource -SnippetByteRange: 310:420 -SnippetLineRange: 5:23 -SnippetLicenseConcluded: GPL-2.0-only -LicenseInfoInSnippet: GPL-2.0-only -SnippetLicenseComments: The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz. -SnippetCopyrightText: Copyright 2008-2010 John Smith -SnippetComment: This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0. -SnippetName: from linux kernel - - -## License Information -LicenseID: LicenseRef-1 -ExtractedText: /* - * (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -LicenseID: LicenseRef-2 -ExtractedText: This package includes the GRDDL parser developed by Hewlett Packard under the following license: -� Copyright 2007 Hewlett-Packard Development Company, LP - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -LicenseID: LicenseRef-4 -ExtractedText: /* - * (c) Copyright 2009 University of Bristol - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -LicenseID: LicenseRef-Beerware-4.2 -ExtractedText: "THE BEER-WARE LICENSE" (Revision 42): -phk@FreeBSD.ORG wrote this file. As long as you retain this notice you -can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp -LicenseName: Beer-Ware License (Version 42) -LicenseCrossReference: http://people.freebsd.org/~phk/ -LicenseComment: The beerware license has a couple of other standard variants. - -LicenseID: LicenseRef-3 -ExtractedText: The CyberNeko Software License, Version 1.0 - - -(C) Copyright 2002-2005, Andy Clark. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - -3. The end-user documentation included with the redistribution, - if any, must include the following acknowledgment: - "This product includes software developed by Andy Clark." - Alternately, this acknowledgment may appear in the software itself, - if and wherever such third-party acknowledgments normally appear. - -4. The names "CyberNeko" and "NekoHTML" must not be used to endorse - or promote products derived from this software without prior - written permission. For written permission, please contact - andyc@cyberneko.net. - -5. Products derived from this software may not be called "CyberNeko", - nor may "CyberNeko" appear in their name, without prior written - permission of the author. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -LicenseName: CyberNeko License -LicenseCrossReference: http://people.apache.org/~andyc/neko/LICENSE, http://justasample.url.com -LicenseComment: This is tye CyperNeko License - diff --git a/tests/spdx/data/formats/SPDXTagExample.tag b/tests/spdx/data/formats/SPDXTagExample.tag deleted file mode 100644 index 855ba417a..000000000 --- a/tests/spdx/data/formats/SPDXTagExample.tag +++ /dev/null @@ -1,224 +0,0 @@ -SPDXVersion: SPDX-2.1 -DataLicense: CC0-1.0 -DocumentName: Sample_Document-V2.1 -SPDXID: SPDXRef-DOCUMENT -DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 -DocumentComment: This is a sample spreadsheet - -## Creation Information -Creator: Person: Gary O'Neall -Creator: Organization: Source Auditor Inc. -Creator: Tool: SourceAuditor-V1.2 -Created: 2010-02-03T00:00:00Z -CreatorComment: This is an example of an SPDX spreadsheet format - -## Review Information -Reviewer: Person: Joe Reviewer -ReviewDate: 2010-02-10T00:00:00Z -ReviewComment: This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses - -Reviewer: Person: Suzanne Reviewer -ReviewDate: 2011-03-13T00:00:00Z -ReviewComment: Another example reviewer. - -## Annotation Information -Annotator: Person: Jim Annotator -AnnotationType: REVIEW -AnnotationDate: 2012-03-11T00:00:00Z -AnnotationComment: An example annotation comment. -SPDXREF: SPDXRef-45 - -## Relationships -Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-File -Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package -Relationship: SPDXRef-DOCUMENT COPY_OF DocumentRef-spdx-tool-1.2:SPDXRef-ToolsElement -Relationship: SPDXRef-DOCUMENT CONTAINS SPDXRef-Package - -## Package Information -PackageName: SPDX Translator -SPDXID: SPDXRef-Package -PackageVersion: Version 0.9.2 -PackageDownloadLocation: http://www.spdx.org/tools -PackageSummary: SPDX Translator utility -PackageSourceInfo: Version 1.0 of the SPDX Translator application -PackageFileName: spdxtranslator-1.0.zip -PackageAttributionText: The GNU C Library is free software. See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed. License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually. -PackageSupplier: Organization:Linux Foundation -PackageOriginator: Organization:SPDX -PackageChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 -PackageVerificationCode: 4e3211c67a2d28fced849ee1bb76e7391b93feba (SpdxTranslatorSpdx.rdf, SpdxTranslatorSpdx.txt) -PackageDescription: This utility translates and SPDX RDF XML document to a spreadsheet, translates a spreadsheet to an SPDX RDF XML document and translates an SPDX RDFa document to an SPDX RDF XML document. -PackageAttributionText: The GNU C Library is free software. See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed. License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually. -PackageComment: This package includes several sub-packages. - -PackageCopyrightText: Copyright 2010, 2011 Source Auditor Inc. - -PackageLicenseDeclared: (LicenseRef-3 AND LicenseRef-4 AND Apache-2.0 AND MPL-1.1 AND LicenseRef-1 AND LicenseRef-2) -PackageLicenseConcluded: (LicenseRef-3 AND LicenseRef-4 AND Apache-1.0 AND Apache-2.0 AND MPL-1.1 AND LicenseRef-1 AND LicenseRef-2) - -PackageLicenseInfoFromFiles: Apache-1.0 -PackageLicenseInfoFromFiles: LicenseRef-3 -PackageLicenseInfoFromFiles: Apache-2.0 -PackageLicenseInfoFromFiles: LicenseRef-4 -PackageLicenseInfoFromFiles: LicenseRef-2 -PackageLicenseInfoFromFiles: LicenseRef-1 -PackageLicenseInfoFromFiles: MPL-1.1 -PackageLicenseComments: The declared license information can be found in the NOTICE file at the root of the archive file - -ExternalRef: SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*: -ExternalRefComment: NIST National Vulnerability Database (NVD) describes security vulnerabilities (CVEs) which affect Vendor Product Version acmecorp:acmenator:6.6.6. - -## File Information -FileName: src/org/spdx/parser/DOAPProject.java -SPDXID: SPDXRef-File1 -FileType: SOURCE -FileType: TEXT -FileChecksum: SHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eB12 -FileChecksum: SHA256: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb120000000000000000 -LicenseConcluded: Apache-2.0 -LicenseInfoInFile: Apache-2.0 -FileCopyrightText: Copyright 2010, 2011 Source Auditor Inc. - -FileName: Jenna-2.6.3/jena-2.6.3-sources.jar -SPDXID: SPDXRef-File2 -FileType: ARCHIVE -FileType: OTHER -FileChecksum: SHA1: 3ab4e1c67a2d28fced849ee1bb76e7391b93f125 -FileChecksum: SHA256: 3ab4e1c67a2d28fced849ee1bb76e7391b93f1250000000000000000 -LicenseConcluded: LicenseRef-1 -LicenseInfoInFile: LicenseRef-1 -LicenseComments: This license is used by Jena -FileCopyrightText: (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP -ArtifactOfProjectName: Jena -ArtifactOfProjectHomePage: http://www.openjena.org/ -ArtifactOfProjectURI: http://www.openjena.org/doap.rdf -FileComment: This file belongs to Jena - -## Snippet Information -SnippetSPDXID: SPDXRef-Snippet -SnippetFromFileSPDXID: SPDXRef-DoapSource -SnippetLicenseComments: The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz. -SnippetCopyrightText: Copyright 2008-2010 John Smith -SnippetComment: This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0-or-later. -SnippetName: from linux kernel -SnippetLicenseConcluded: Apache-2.0 -LicenseInfoInSnippet: Apache-2.0 -SnippetByteRange: 310:420 - -## License Information -LicenseID: LicenseRef-3 -ExtractedText: The CyberNeko Software License, Version 1.0 - - -(C) Copyright 2002-2005, Andy Clark. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - -3. The end-user documentation included with the redistribution, - if any, must include the following acknowledgment: - "This product includes software developed by Andy Clark." - Alternately, this acknowledgment may appear in the software itself, - if and wherever such third-party acknowledgments normally appear. - -4. The names "CyberNeko" and "NekoHTML" must not be used to endorse - or promote products derived from this software without prior - written permission. For written permission, please contact - andyc@cyberneko.net. - -5. Products derived from this software may not be called "CyberNeko", - nor may "CyberNeko" appear in their name, without prior written - permission of the author. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -LicenseName: CyberNeko License -LicenseCrossReference: http://people.apache.org/~andyc/neko/LICENSE -LicenseCrossReference: http://justasample.url.com -LicenseComment: This is tye CyperNeko License - -LicenseID: LicenseRef-1 -ExtractedText: /* - * (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -LicenseID: LicenseRef-2 -ExtractedText: This package includes the GRDDL parser developed by Hewlett Packard under the following license: -© Copyright 2007 Hewlett-Packard Development Company, LP - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -LicenseID: LicenseRef-4 -ExtractedText: /* - * (c) Copyright 2009 University of Bristol - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - diff --git a/tests/spdx/data/formats/SPDXXMLExample-v2.2.spdx.xml b/tests/spdx/data/formats/SPDXXMLExample-v2.2.spdx.xml deleted file mode 100644 index 80e0527a2..000000000 --- a/tests/spdx/data/formats/SPDXXMLExample-v2.2.spdx.xml +++ /dev/null @@ -1,443 +0,0 @@ - - - SPDXRef-DOCUMENT - SPDX-2.2 - - This package has been shipped in source and binary form. -The binaries were created with gcc 4.5.1 and expect to link to -compatible system run time libraries. - 2010-01-29T18:30:22Z - Tool: LicenseFind-1.0 - Organization: ExampleCodeInspect () - Person: Jane Doe () - 3.9 - - SPDX-Tools-v2.0 - CC0-1.0 - This document was created using SPDX 2.0 using licenses from the web site. - - DocumentRef-spdx-tool-1.2 - - SHA1 - d6a770ba38583ed4bb4525bd96e50461655d2759 - - http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 - - - LicenseRef-1 - /* - * (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - - - LicenseRef-2 - This package includes the GRDDL parser developed by Hewlett Packard under the following license: -� Copyright 2007 Hewlett-Packard Development Company, LP - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - LicenseRef-4 - /* - * (c) Copyright 2009 University of Bristol - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - - - LicenseRef-Beerware-4.2 - The beerware license has a couple of other standard variants. - "THE BEER-WARE LICENSE" (Revision 42): -phk@FreeBSD.ORG wrote this file. As long as you retain this notice you -can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp - Beer-Ware License (Version 42) - http://people.freebsd.org/~phk/ - - - LicenseRef-3 - This is tye CyperNeko License - The CyberNeko Software License, Version 1.0 - - -(C) Copyright 2002-2005, Andy Clark. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - -3. The end-user documentation included with the redistribution, - if any, must include the following acknowledgment: - "This product includes software developed by Andy Clark." - Alternately, this acknowledgment may appear in the software itself, - if and wherever such third-party acknowledgments normally appear. - -4. The names "CyberNeko" and "NekoHTML" must not be used to endorse - or promote products derived from this software without prior - written permission. For written permission, please contact - andyc@cyberneko.net. - -5. Products derived from this software may not be called "CyberNeko", - nor may "CyberNeko" appear in their name, without prior written - permission of the author. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - CyberNeko License - http://people.apache.org/~andyc/neko/LICENSE - http://justasample.url.com - - - 2010-01-29T18:30:22Z - OTHER - Person: Jane Doe () - Document level annotation - - - 2010-02-10T00:00:00Z - REVIEW - Person: Joe Reviewer - This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses - - - 2011-03-13T00:00:00Z - REVIEW - Person: Suzanne Reviewer - Another example reviewer. - - http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 - SPDXRef-File - SPDXRef-Package - - SPDXRef-Package - - 2011-01-29T18:30:22Z - OTHER - Person: Package Commenter - Package level annotation - - The GNU C Library is free software. See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed. License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually. - - MD5 - 624c1abb3664f4b35547e7c73864ad24 - - - SHA1 - 85ed0817af83a24ad8da68c2b5094de69833983c - - - SHA256 - 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd - - Copyright 2008-2010 John Smith - The GNU C Library defines functions that are specified by the ISO C standard, as well as additional features specific to POSIX and other derivatives of the Unix operating system, and extensions specific to GNU systems. - http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz - - SECURITY - cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* - cpe23Type - - - This is the external ref for Acme - OTHER - acmecorp/acmenator/4.1.3-alpha - http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge - - true - SPDXRef-CommonsLangSrc - SPDXRef-JenaLib - SPDXRef-DoapSource - http://ftp.gnu.org/gnu/glibc - The license for this project changed with the release of version x.y. The version of the project included here post-dates the license change. - (LGPL-2.0-only OR LicenseRef-3) - (LGPL-2.0-only AND LicenseRef-3) - GPL-2.0-only - LicenseRef-2 - LicenseRef-1 - glibc - Organization: ExampleCodeInspect (contact@example.com) - glibc-2.11.1.tar.gz - - ./package.spdx - d6a770ba38583ed4bb4525bd96e50461655d2758 - - uses glibc-2_11-branch from git://sourceware.org/git/glibc.git. - GNU C library. - Person: Jane Doe (jane.doe@example.com) - 2.11.1 - - - SPDXRef-fromDoap-1 - NOASSERTION - NOASSERTION - false - http://commons.apache.org/proper/commons-lang/ - NOASSERTION - NOASSERTION - Apache Commons Lang - - - SPDXRef-fromDoap-0 - NOASSERTION - https://search.maven.org/remotecontent?filepath=org/apache/jena/apache-jena/3.12.0/apache-jena-3.12.0.tar.gz - - PACKAGE_MANAGER - pkg:maven/org.apache.jena/apache-jena@3.12.0 - purl - - false - http://www.openjena.org/ - NOASSERTION - NOASSERTION - Jena - 3.12.0 - - - SPDXRef-Saxon - - SHA1 - 85ed0817af83a24ad8da68c2b5094de69833983c - - Copyright Saxonica Ltd - The Saxon package is a collection of tools for processing XML documents. - https://sourceforge.net/projects/saxon/files/Saxon-B/8.8.0.7/saxonb8-8-0-7j.zip/download - false - http://saxon.sourceforge.net/ - Other versions available for a commercial license - MPL-1.0 - MPL-1.0 - Saxon - saxonB-8.8.zip - 8.8 - - - SPDXRef-DoapSource - - SHA1 - 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 - - Copyright 2010, 2011 Source Auditor Inc. - Protecode Inc. - SPDX Technical Team Members - Open Logic Inc. - Source Auditor Inc. - Black Duck Software In.c - ./src/org/spdx/parser/DOAPProject.java - SOURCE - Apache-2.0 - Apache-2.0 - - - SPDXRef-CommonsLangSrc - - SHA1 - c2b4e1c67a2d28fced849ee1bb76e7391b93f125 - - This file is used by Jena - Copyright 2001-2011 The Apache Software Foundation - Apache Software Foundation - ./lib-source/commons-lang3-3.1-sources.jar - ARCHIVE - Apache-2.0 - Apache-2.0 - Apache Commons Lang -Copyright 2001-2011 The Apache Software Foundation - -This product includes software developed by -The Apache Software Foundation (http://www.apache.org/). - -This product includes software from the Spring Framework, -under the Apache License 2.0 (see: StringUtils.containsWhitespace()) - - - SPDXRef-JenaLib - - SHA1 - 3ab4e1c67a2d28fced849ee1bb76e7391b93f125 - - This file belongs to Jena - (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP - Apache Software Foundation - Hewlett Packard Inc. - ./lib-source/jena-2.6.3-sources.jar - ARCHIVE - This license is used by Jena - LicenseRef-1 - LicenseRef-1 - - - SPDXRef-File - - 2011-01-29T18:30:22Z - OTHER - Person: File Commenter - File level annotation - - - SHA1 - d6a770ba38583ed4bb4525bd96e50461655d2758 - - - MD5 - 624c1abb3664f4b35547e7c73864ad24 - - The concluded license was taken from the package level that the file was included in. -This information was found in the COPYING.txt file in the xyz directory. - Copyright 2008-2010 John Smith - The Regents of the University of California - Modified by Paul Mundt lethal@linux-sh.org - IBM Corporation - ./package/foo.c - SOURCE - The concluded license was taken from the package level that the file was included in. - (LGPL-2.0-only OR LicenseRef-2) - GPL-2.0-only - LicenseRef-2 - Copyright (c) 2001 Aaron Lehmann aaroni@vitelus.com - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the �Software�), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED �AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - SPDXRef-Snippet - This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0. - Copyright 2008-2010 John Smith - The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz. - GPL-2.0-only - GPL-2.0-only - from linux kernel - - - 420 - SPDXRef-DoapSource - - - 310 - SPDXRef-DoapSource - - - - - 23 - SPDXRef-DoapSource - - - 5 - SPDXRef-DoapSource - - - SPDXRef-DoapSource - - - SPDXRef-DOCUMENT - SPDXRef-Package - CONTAINS - - - SPDXRef-DOCUMENT - DocumentRef-spdx-tool-1.2:SPDXRef-ToolsElement - COPY_OF - - - SPDXRef-DOCUMENT - SPDXRef-File - DESCRIBES - - - SPDXRef-DOCUMENT - SPDXRef-Package - DESCRIBES - - - SPDXRef-Package - SPDXRef-JenaLib - CONTAINS - - - SPDXRef-Package - SPDXRef-Saxon - DYNAMIC_LINK - - - SPDXRef-CommonsLangSrc - NOASSERTION - GENERATED_FROM - - - SPDXRef-JenaLib - SPDXRef-Package - CONTAINS - - - SPDXRef-File - SPDXRef-fromDoap-0 - GENERATED_FROM - - diff --git a/tests/spdx/data/formats/SPDXXMLExample-v2.3.spdx.xml b/tests/spdx/data/formats/SPDXXMLExample-v2.3.spdx.xml deleted file mode 100644 index 609ac5125..000000000 --- a/tests/spdx/data/formats/SPDXXMLExample-v2.3.spdx.xml +++ /dev/null @@ -1,459 +0,0 @@ - - - SPDXRef-DOCUMENT - SPDX-2.3 - - This package has been shipped in source and binary form. -The binaries were created with gcc 4.5.1 and expect to link to -compatible system run time libraries. - 2010-01-29T18:30:22Z - Tool: LicenseFind-1.0 - Organization: ExampleCodeInspect () - Person: Jane Doe () - 3.17 - - SPDX-Tools-v2.0 - CC0-1.0 - This document was created using SPDX 2.0 using licenses from the web site. - - DocumentRef-spdx-tool-1.2 - - SHA1 - d6a770ba38583ed4bb4525bd96e50461655d2759 - - http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 - - - LicenseRef-1 - /* - * (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - - - LicenseRef-2 - This package includes the GRDDL parser developed by Hewlett Packard under the following license: -© Copyright 2007 Hewlett-Packard Development Company, LP - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - LicenseRef-4 - /* - * (c) Copyright 2009 University of Bristol - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - - - LicenseRef-Beerware-4.2 - The beerware license has a couple of other standard variants. - "THE BEER-WARE LICENSE" (Revision 42): -phk@FreeBSD.ORG wrote this file. As long as you retain this notice you -can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp - Beer-Ware License (Version 42) - http://people.freebsd.org/~phk/ - - - LicenseRef-3 - This is tye CyperNeko License - The CyberNeko Software License, Version 1.0 - - -(C) Copyright 2002-2005, Andy Clark. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - -3. The end-user documentation included with the redistribution, - if any, must include the following acknowledgment: - "This product includes software developed by Andy Clark." - Alternately, this acknowledgment may appear in the software itself, - if and wherever such third-party acknowledgments normally appear. - -4. The names "CyberNeko" and "NekoHTML" must not be used to endorse - or promote products derived from this software without prior - written permission. For written permission, please contact - andyc@cyberneko.net. - -5. Products derived from this software may not be called "CyberNeko", - nor may "CyberNeko" appear in their name, without prior written - permission of the author. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - CyberNeko License - http://people.apache.org/~andyc/neko/LICENSE - http://justasample.url.com - - - 2010-01-29T18:30:22Z - OTHER - Person: Jane Doe () - Document level annotation - - - 2010-02-10T00:00:00Z - REVIEW - Person: Joe Reviewer - This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses - - - 2011-03-13T00:00:00Z - REVIEW - Person: Suzanne Reviewer - Another example reviewer. - - SPDXRef-File - SPDXRef-Package - http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301 - - SPDXRef-Package - - 2011-01-29T18:30:22Z - OTHER - Person: Package Commenter - Package level annotation - - The GNU C Library is free software. See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed. License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually. - 2011-01-29T18:30:22Z - - MD5 - 624c1abb3664f4b35547e7c73864ad24 - - - SHA1 - 85ed0817af83a24ad8da68c2b5094de69833983c - - - SHA256 - 11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd - - - BLAKE2b-384 - aaabd89c926ab525c242e6621f2f5fa73aa4afe3d9e24aed727faaadd6af38b620bdb623dd2b4788b1c8086984af8706 - - Copyright 2008-2010 John Smith - The GNU C Library defines functions that are specified by the ISO C standard, as well as additional features specific to POSIX and other derivatives of the Unix operating system, and extensions specific to GNU systems. - http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz - - SECURITY - cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* - cpe23Type - - - This is the external ref for Acme - OTHER - acmecorp/acmenator/4.1.3-alpha - http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge - - true - http://ftp.gnu.org/gnu/glibc - The license for this project changed with the release of version x.y. The version of the project included here post-dates the license change. - (LGPL-2.0-only OR LicenseRef-3) - (LGPL-2.0-only AND LicenseRef-3) - GPL-2.0-only - LicenseRef-2 - LicenseRef-1 - glibc - Organization: ExampleCodeInspect (contact@example.com) - glibc-2.11.1.tar.gz - - ./package.spdx - d6a770ba38583ed4bb4525bd96e50461655d2758 - - SOURCE - SPDXRef-Specification - SPDXRef-Specification - SPDXRef-CommonsLangSrc - SPDXRef-Specification - SPDXRef-CommonsLangSrc - SPDXRef-JenaLib - SPDXRef-Specification - SPDXRef-CommonsLangSrc - SPDXRef-JenaLib - SPDXRef-DoapSource - SPDXRef-Specification - SPDXRef-CommonsLangSrc - SPDXRef-JenaLib - SPDXRef-DoapSource - 2012-01-29T18:30:22Z - uses glibc-2_11-branch from git://sourceware.org/git/glibc.git. - GNU C library. - Person: Jane Doe (jane.doe@example.com) - 2014-01-29T18:30:22Z - 2.11.1 - - - SPDXRef-fromDoap-1 - NOASSERTION - NOASSERTION - false - http://commons.apache.org/proper/commons-lang/ - NOASSERTION - NOASSERTION - Apache Commons Lang - - - SPDXRef-fromDoap-0 - https://search.maven.org/remotecontent?filepath=org/apache/jena/apache-jena/3.12.0/apache-jena-3.12.0.tar.gz - - PACKAGE-MANAGER - pkg:maven/org.apache.jena/apache-jena@3.12.0 - purl - - false - http://www.openjena.org/ - Jena - 3.12.0 - - - SPDXRef-Saxon - - SHA1 - 85ed0817af83a24ad8da68c2b5094de69833983c - - Copyright Saxonica Ltd - The Saxon package is a collection of tools for processing XML documents. - https://sourceforge.net/projects/saxon/files/Saxon-B/8.8.0.7/saxonb8-8-0-7j.zip/download - false - http://saxon.sourceforge.net/ - Other versions available for a commercial license - MPL-1.0 - MPL-1.0 - Saxon - saxonB-8.8.zip - 8.8 - - - SPDXRef-DoapSource - - SHA1 - 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 - - Copyright 2010, 2011 Source Auditor Inc. - Protecode Inc. - SPDX Technical Team Members - Open Logic Inc. - Source Auditor Inc. - Black Duck Software In.c - ./src/org/spdx/parser/DOAPProject.java - SOURCE - Apache-2.0 - Apache-2.0 - - - SPDXRef-CommonsLangSrc - - SHA1 - c2b4e1c67a2d28fced849ee1bb76e7391b93f125 - - This file is used by Jena - Copyright 2001-2011 The Apache Software Foundation - Apache Software Foundation - ./lib-source/commons-lang3-3.1-sources.jar - ARCHIVE - Apache-2.0 - Apache-2.0 - Apache Commons Lang -Copyright 2001-2011 The Apache Software Foundation - -This product includes software developed by -The Apache Software Foundation (http://www.apache.org/). - -This product includes software from the Spring Framework, -under the Apache License 2.0 (see: StringUtils.containsWhitespace()) - - - SPDXRef-JenaLib - - SHA1 - 3ab4e1c67a2d28fced849ee1bb76e7391b93f125 - - This file belongs to Jena - (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP - Apache Software Foundation - Hewlett Packard Inc. - ./lib-source/jena-2.6.3-sources.jar - ARCHIVE - This license is used by Jena - LicenseRef-1 - LicenseRef-1 - - - SPDXRef-Specification - - SHA1 - fff4e1c67a2d28fced849ee1bb76e7391b93f125 - - Specification Documentation - ./docs/myspec.pdf - DOCUMENTATION - - - SPDXRef-File - - 2011-01-29T18:30:22Z - OTHER - Person: File Commenter - File level annotation - - - SHA1 - d6a770ba38583ed4bb4525bd96e50461655d2758 - - - MD5 - 624c1abb3664f4b35547e7c73864ad24 - - The concluded license was taken from the package level that the file was included in. -This information was found in the COPYING.txt file in the xyz directory. - Copyright 2008-2010 John Smith - The Regents of the University of California - Modified by Paul Mundt lethal@linux-sh.org - IBM Corporation - ./package/foo.c - SOURCE - The concluded license was taken from the package level that the file was included in. - (LGPL-2.0-only OR LicenseRef-2) - GPL-2.0-only - LicenseRef-2 - Copyright (c) 2001 Aaron Lehmann aaroni@vitelus.com - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - SPDXRef-Snippet - This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0. - Copyright 2008-2010 John Smith - The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz. - GPL-2.0-only - GPL-2.0-only - from linux kernel - - - 420 - SPDXRef-DoapSource - - - 310 - SPDXRef-DoapSource - - - - - 23 - SPDXRef-DoapSource - - - 5 - SPDXRef-DoapSource - - - SPDXRef-DoapSource - - - SPDXRef-DOCUMENT - CONTAINS - SPDXRef-Package - - - SPDXRef-DOCUMENT - COPY_OF - DocumentRef-spdx-tool-1.2:SPDXRef-ToolsElement - - - SPDXRef-Package - DYNAMIC_LINK - SPDXRef-Saxon - - - SPDXRef-CommonsLangSrc - GENERATED_FROM - NOASSERTION - - - SPDXRef-JenaLib - CONTAINS - SPDXRef-Package - - - SPDXRef-Specification - SPECIFICATION_FOR - SPDXRef-fromDoap-0 - - - SPDXRef-File - GENERATED_FROM - SPDXRef-fromDoap-0 - - diff --git a/tests/spdx/data/formats/SPDXYAMLExample-2.2.spdx.yaml b/tests/spdx/data/formats/SPDXYAMLExample-2.2.spdx.yaml deleted file mode 100644 index d58cf229c..000000000 --- a/tests/spdx/data/formats/SPDXYAMLExample-2.2.spdx.yaml +++ /dev/null @@ -1,390 +0,0 @@ ---- -SPDXID: "SPDXRef-DOCUMENT" -spdxVersion: "SPDX-2.2" -creationInfo: - comment: "This package has been shipped in source and binary form.\nThe binaries\ - \ were created with gcc 4.5.1 and expect to link to\ncompatible system run time\ - \ libraries." - created: "2010-01-29T18:30:22Z" - creators: - - "Tool: LicenseFind-1.0" - - "Organization: ExampleCodeInspect ()" - - "Person: Jane Doe ()" - licenseListVersion: "3.9" -name: "SPDX-Tools-v2.0" -dataLicense: "CC0-1.0" -comment: "This document was created using SPDX 2.0 using licenses from the web site." -externalDocumentRefs: -- externalDocumentId: "DocumentRef-spdx-tool-1.2" - checksum: - algorithm: "SHA1" - checksumValue: "d6a770ba38583ed4bb4525bd96e50461655d2759" - spdxDocument: "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301" -hasExtractedLicensingInfos: -- licenseId: "LicenseRef-1" - extractedText: "/*\n * (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,\ - \ 2008, 2009 Hewlett-Packard Development Company, LP\n * All rights reserved.\n\ - \ *\n * Redistribution and use in source and binary forms, with or without\n *\ - \ modification, are permitted provided that the following conditions\n * are met:\n\ - \ * 1. Redistributions of source code must retain the above copyright\n * notice,\ - \ this list of conditions and the following disclaimer.\n * 2. Redistributions\ - \ in binary form must reproduce the above copyright\n * notice, this list of\ - \ conditions and the following disclaimer in the\n * documentation and/or other\ - \ materials provided with the distribution.\n * 3. The name of the author may\ - \ not be used to endorse or promote products\n * derived from this software\ - \ without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED\ - \ BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES, INCLUDING,\ - \ BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY AND FITNESS\ - \ FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE AUTHOR BE\ - \ LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\ - \ DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\ - \ OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\ - \ CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\ - \ OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\ - \ USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\ - */" -- licenseId: "LicenseRef-2" - extractedText: "This package includes the GRDDL parser developed by Hewlett Packard\ - \ under the following license:\n� Copyright 2007 Hewlett-Packard Development Company,\ - \ LP\n\nRedistribution and use in source and binary forms, with or without modification,\ - \ are permitted provided that the following conditions are met: \n\nRedistributions\ - \ of source code must retain the above copyright notice, this list of conditions\ - \ and the following disclaimer. \nRedistributions in binary form must reproduce\ - \ the above copyright notice, this list of conditions and the following disclaimer\ - \ in the documentation and/or other materials provided with the distribution.\ - \ \nThe name of the author may not be used to endorse or promote products derived\ - \ from this software without specific prior written permission. \nTHIS SOFTWARE\ - \ IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,\ - \ BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\ - \ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE\ - \ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\ - \ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\ - \ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\ - \ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\ - \ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\ - \ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -- licenseId: "LicenseRef-4" - extractedText: "/*\n * (c) Copyright 2009 University of Bristol\n * All rights reserved.\n\ - \ *\n * Redistribution and use in source and binary forms, with or without\n *\ - \ modification, are permitted provided that the following conditions\n * are met:\n\ - \ * 1. Redistributions of source code must retain the above copyright\n * notice,\ - \ this list of conditions and the following disclaimer.\n * 2. Redistributions\ - \ in binary form must reproduce the above copyright\n * notice, this list of\ - \ conditions and the following disclaimer in the\n * documentation and/or other\ - \ materials provided with the distribution.\n * 3. The name of the author may\ - \ not be used to endorse or promote products\n * derived from this software\ - \ without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED\ - \ BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES, INCLUDING,\ - \ BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY AND FITNESS\ - \ FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE AUTHOR BE\ - \ LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\ - \ DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\ - \ OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\ - \ CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\ - \ OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\ - \ USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\ - */" -- licenseId: "LicenseRef-Beerware-4.2" - comment: "The beerware license has a couple of other standard variants." - extractedText: "\"THE BEER-WARE LICENSE\" (Revision 42):\nphk@FreeBSD.ORG wrote\ - \ this file. As long as you retain this notice you\ncan do whatever you want with\ - \ this stuff. If we meet some day, and you think this stuff is worth it, you can\ - \ buy me a beer in return Poul-Henning Kamp" - name: "Beer-Ware License (Version 42)" - seeAlsos: - - "http://people.freebsd.org/~phk/" -- licenseId: "LicenseRef-3" - comment: "This is tye CyperNeko License" - extractedText: "The CyberNeko Software License, Version 1.0\n\n \n(C) Copyright\ - \ 2002-2005, Andy Clark. All rights reserved.\n \nRedistribution and use in source\ - \ and binary forms, with or without\nmodification, are permitted provided that\ - \ the following conditions\nare met:\n\n1. Redistributions of source code must\ - \ retain the above copyright\n notice, this list of conditions and the following\ - \ disclaimer. \n\n2. Redistributions in binary form must reproduce the above copyright\n\ - \ notice, this list of conditions and the following disclaimer in\n the documentation\ - \ and/or other materials provided with the\n distribution.\n\n3. The end-user\ - \ documentation included with the redistribution,\n if any, must include the\ - \ following acknowledgment: \n \"This product includes software developed\ - \ by Andy Clark.\"\n Alternately, this acknowledgment may appear in the software\ - \ itself,\n if and wherever such third-party acknowledgments normally appear.\n\ - \n4. The names \"CyberNeko\" and \"NekoHTML\" must not be used to endorse\n \ - \ or promote products derived from this software without prior \n written permission.\ - \ For written permission, please contact \n andyc@cyberneko.net.\n\n5. Products\ - \ derived from this software may not be called \"CyberNeko\",\n nor may \"CyberNeko\"\ - \ appear in their name, without prior written\n permission of the author.\n\n\ - THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED\nWARRANTIES,\ - \ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\nOF MERCHANTABILITY AND\ - \ FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE AUTHOR\ - \ OR OTHER CONTRIBUTORS\nBE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\ - \ EXEMPLARY, \nOR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT\ - \ \nOF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR \nBUSINESS\ - \ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, \nWHETHER IN CONTRACT,\ - \ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE \nOR OTHERWISE) ARISING IN ANY\ - \ WAY OUT OF THE USE OF THIS SOFTWARE, \nEVEN IF ADVISED OF THE POSSIBILITY OF\ - \ SUCH DAMAGE." - name: "CyberNeko License" - seeAlsos: - - "http://people.apache.org/~andyc/neko/LICENSE" - - "http://justasample.url.com" -annotations: -- annotationDate: "2010-01-29T18:30:22Z" - annotationType: "OTHER" - annotator: "Person: Jane Doe ()" - comment: "Document level annotation" -- annotationDate: "2010-02-10T00:00:00Z" - annotationType: "REVIEW" - annotator: "Person: Joe Reviewer" - comment: "This is just an example. Some of the non-standard licenses look like\ - \ they are actually BSD 3 clause licenses" -- annotationDate: "2011-03-13T00:00:00Z" - annotationType: "REVIEW" - annotator: "Person: Suzanne Reviewer" - comment: "Another example reviewer." -documentNamespace: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301" -documentDescribes: -- "SPDXRef-File" -- "SPDXRef-Package" -packages: -- SPDXID: "SPDXRef-Package" - annotations: - - annotationDate: "2011-01-29T18:30:22Z" - annotationType: "OTHER" - annotator: "Person: Package Commenter" - comment: "Package level annotation" - attributionTexts: - - "The GNU C Library is free software. See the file COPYING.LIB for copying conditions,\ - \ and LICENSES for notices about a few contributions that require these additional\ - \ notices to be distributed. License copyright years may be listed using range\ - \ notation, e.g., 1996-2015, indicating that every year in the range, inclusive,\ - \ is a copyrightable year that would otherwise be listed individually." - checksums: - - algorithm: "MD5" - checksumValue: "624c1abb3664f4b35547e7c73864ad24" - - algorithm: "SHA1" - checksumValue: "85ed0817af83a24ad8da68c2b5094de69833983c" - - algorithm: "SHA256" - checksumValue: "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd" - copyrightText: "Copyright 2008-2010 John Smith" - description: "The GNU C Library defines functions that are specified by the ISO\ - \ C standard, as well as additional features specific to POSIX and other derivatives\ - \ of the Unix operating system, and extensions specific to GNU systems." - downloadLocation: "http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz" - externalRefs: - - referenceCategory: "SECURITY" - referenceLocator: "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" - referenceType: "cpe23Type" - - comment: "This is the external ref for Acme" - referenceCategory: "OTHER" - referenceLocator: "acmecorp/acmenator/4.1.3-alpha" - referenceType: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge" - filesAnalyzed: true - hasFiles: - - "SPDXRef-CommonsLangSrc" - - "SPDXRef-JenaLib" - - "SPDXRef-DoapSource" - homepage: "http://ftp.gnu.org/gnu/glibc" - licenseComments: "The license for this project changed with the release of version\ - \ x.y. The version of the project included here post-dates the license change." - licenseConcluded: "(LGPL-2.0-only OR LicenseRef-3)" - licenseDeclared: "(LGPL-2.0-only AND LicenseRef-3)" - licenseInfoFromFiles: - - "GPL-2.0-only" - - "LicenseRef-2" - - "LicenseRef-1" - name: "glibc" - originator: "Organization: ExampleCodeInspect (contact@example.com)" - packageFileName: "glibc-2.11.1.tar.gz" - packageVerificationCode: - packageVerificationCodeExcludedFiles: - - "./package.spdx" - packageVerificationCodeValue: "d6a770ba38583ed4bb4525bd96e50461655d2758" - sourceInfo: "uses glibc-2_11-branch from git://sourceware.org/git/glibc.git." - summary: "GNU C library." - supplier: "Person: Jane Doe (jane.doe@example.com)" - versionInfo: "2.11.1" -- SPDXID: "SPDXRef-fromDoap-1" - copyrightText: "NOASSERTION" - downloadLocation: "NOASSERTION" - filesAnalyzed: false - homepage: "http://commons.apache.org/proper/commons-lang/" - licenseConcluded: "NOASSERTION" - licenseDeclared: "NOASSERTION" - name: "Apache Commons Lang" -- SPDXID: "SPDXRef-fromDoap-0" - copyrightText: "NOASSERTION" - downloadLocation: "https://search.maven.org/remotecontent?filepath=org/apache/jena/apache-jena/3.12.0/apache-jena-3.12.0.tar.gz" - externalRefs: - - referenceCategory: "PACKAGE_MANAGER" - referenceLocator: "pkg:maven/org.apache.jena/apache-jena@3.12.0" - referenceType: "purl" - filesAnalyzed: false - homepage: "http://www.openjena.org/" - licenseConcluded: "NOASSERTION" - licenseDeclared: "NOASSERTION" - name: "Jena" - versionInfo: "3.12.0" -- SPDXID: "SPDXRef-Saxon" - checksums: - - algorithm: "SHA1" - checksumValue: "85ed0817af83a24ad8da68c2b5094de69833983c" - copyrightText: "Copyright Saxonica Ltd" - description: "The Saxon package is a collection of tools for processing XML documents." - downloadLocation: "https://sourceforge.net/projects/saxon/files/Saxon-B/8.8.0.7/saxonb8-8-0-7j.zip/download" - filesAnalyzed: false - homepage: "http://saxon.sourceforge.net/" - licenseComments: "Other versions available for a commercial license" - licenseConcluded: "MPL-1.0" - licenseDeclared: "MPL-1.0" - name: "Saxon" - packageFileName: "saxonB-8.8.zip" - versionInfo: "8.8" -files: -- SPDXID: "SPDXRef-DoapSource" - checksums: - - algorithm: "SHA1" - checksumValue: "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12" - copyrightText: "Copyright 2010, 2011 Source Auditor Inc." - fileContributors: - - "Protecode Inc." - - "SPDX Technical Team Members" - - "Open Logic Inc." - - "Source Auditor Inc." - - "Black Duck Software In.c" - fileName: "./src/org/spdx/parser/DOAPProject.java" - fileTypes: - - "SOURCE" - licenseConcluded: "Apache-2.0" - licenseInfoInFiles: - - "Apache-2.0" -- SPDXID: "SPDXRef-CommonsLangSrc" - checksums: - - algorithm: "SHA1" - checksumValue: "c2b4e1c67a2d28fced849ee1bb76e7391b93f125" - comment: "This file is used by Jena" - copyrightText: "Copyright 2001-2011 The Apache Software Foundation" - fileContributors: - - "Apache Software Foundation" - fileName: "./lib-source/commons-lang3-3.1-sources.jar" - fileTypes: - - "ARCHIVE" - licenseConcluded: "Apache-2.0" - licenseInfoInFiles: - - "Apache-2.0" - noticeText: "Apache Commons Lang\nCopyright 2001-2011 The Apache Software Foundation\n\ - \nThis product includes software developed by\nThe Apache Software Foundation\ - \ (http://www.apache.org/).\n\nThis product includes software from the Spring\ - \ Framework,\nunder the Apache License 2.0 (see: StringUtils.containsWhitespace())" -- SPDXID: "SPDXRef-JenaLib" - checksums: - - algorithm: "SHA1" - checksumValue: "3ab4e1c67a2d28fced849ee1bb76e7391b93f125" - comment: "This file belongs to Jena" - copyrightText: "(c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,\ - \ 2009 Hewlett-Packard Development Company, LP" - fileContributors: - - "Apache Software Foundation" - - "Hewlett Packard Inc." - fileName: "./lib-source/jena-2.6.3-sources.jar" - fileTypes: - - "ARCHIVE" - licenseComments: "This license is used by Jena" - licenseConcluded: "LicenseRef-1" - licenseInfoInFiles: - - "LicenseRef-1" -- SPDXID: "SPDXRef-File" - annotations: - - annotationDate: "2011-01-29T18:30:22Z" - annotationType: "OTHER" - annotator: "Person: File Commenter" - comment: "File level annotation" - checksums: - - algorithm: "SHA1" - checksumValue: "d6a770ba38583ed4bb4525bd96e50461655d2758" - - algorithm: "MD5" - checksumValue: "624c1abb3664f4b35547e7c73864ad24" - comment: "The concluded license was taken from the package level that the file was\ - \ included in.\nThis information was found in the COPYING.txt file in the xyz\ - \ directory." - copyrightText: "Copyright 2008-2010 John Smith" - fileContributors: - - "The Regents of the University of California" - - "Modified by Paul Mundt lethal@linux-sh.org" - - "IBM Corporation" - fileName: "./package/foo.c" - fileTypes: - - "SOURCE" - licenseComments: "The concluded license was taken from the package level that the\ - \ file was included in." - licenseConcluded: "(LGPL-2.0-only OR LicenseRef-2)" - licenseInfoInFiles: - - "GPL-2.0-only" - - "LicenseRef-2" - noticeText: "Copyright (c) 2001 Aaron Lehmann aaroni@vitelus.com\n\nPermission is\ - \ hereby granted, free of charge, to any person obtaining a copy of this software\ - \ and associated documentation files (the �Software�), to deal in the Software\ - \ without restriction, including without limitation the rights to use, copy, modify,\ - \ merge, publish, distribute, sublicense, and/or sell copies of the Software,\ - \ and to permit persons to whom the Software is furnished to do so, subject to\ - \ the following conditions: \nThe above copyright notice and this permission notice\ - \ shall be included in all copies or substantial portions of the Software.\n\n\ - THE SOFTWARE IS PROVIDED �AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\ - \ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR\ - \ A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\ - \ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\ - \ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\ - \ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." -snippets: -- SPDXID: "SPDXRef-Snippet" - comment: "This snippet was identified as significant and highlighted in this Apache-2.0\ - \ file, when a commercial scanner identified it as being derived from file foo.c\ - \ in package xyz which is licensed under GPL-2.0." - copyrightText: "Copyright 2008-2010 John Smith" - licenseComments: "The concluded license was taken from package xyz, from which the\ - \ snippet was copied into the current file. The concluded license information\ - \ was found in the COPYING.txt file in package xyz." - licenseConcluded: "GPL-2.0-only" - licenseInfoInSnippets: - - "GPL-2.0-only" - name: "from linux kernel" - ranges: - - endPointer: - offset: 420 - reference: "SPDXRef-DoapSource" - startPointer: - offset: 310 - reference: "SPDXRef-DoapSource" - - endPointer: - lineNumber: 23 - reference: "SPDXRef-DoapSource" - startPointer: - lineNumber: 5 - reference: "SPDXRef-DoapSource" - snippetFromFile: "SPDXRef-DoapSource" -relationships: -- spdxElementId: "SPDXRef-DOCUMENT" - relatedSpdxElement: "SPDXRef-Package" - relationshipType: "CONTAINS" -- spdxElementId: "SPDXRef-DOCUMENT" - relatedSpdxElement: "DocumentRef-spdx-tool-1.2:SPDXRef-ToolsElement" - relationshipType: "COPY_OF" -- spdxElementId: "SPDXRef-DOCUMENT" - relatedSpdxElement: "SPDXRef-File" - relationshipType: "DESCRIBES" -- spdxElementId: "SPDXRef-DOCUMENT" - relatedSpdxElement: "SPDXRef-Package" - relationshipType: "DESCRIBES" -- spdxElementId: "SPDXRef-Package" - relatedSpdxElement: "SPDXRef-JenaLib" - relationshipType: "CONTAINS" -- spdxElementId: "SPDXRef-Package" - relatedSpdxElement: "SPDXRef-Saxon" - relationshipType: "DYNAMIC_LINK" -- spdxElementId: "SPDXRef-CommonsLangSrc" - relatedSpdxElement: "NOASSERTION" - relationshipType: "GENERATED_FROM" -- spdxElementId: "SPDXRef-JenaLib" - relatedSpdxElement: "SPDXRef-Package" - relationshipType: "CONTAINS" -- spdxElementId: "SPDXRef-File" - relatedSpdxElement: "SPDXRef-fromDoap-0" - relationshipType: "GENERATED_FROM" diff --git a/tests/spdx/data/formats/SPDXYAMLExample-2.3.spdx.yaml b/tests/spdx/data/formats/SPDXYAMLExample-2.3.spdx.yaml deleted file mode 100644 index 9770f71dd..000000000 --- a/tests/spdx/data/formats/SPDXYAMLExample-2.3.spdx.yaml +++ /dev/null @@ -1,406 +0,0 @@ ---- -SPDXID: "SPDXRef-DOCUMENT" -spdxVersion: "SPDX-2.3" -creationInfo: - comment: "This package has been shipped in source and binary form.\nThe binaries\ - \ were created with gcc 4.5.1 and expect to link to\ncompatible system run time\ - \ libraries." - created: "2010-01-29T18:30:22Z" - creators: - - "Tool: LicenseFind-1.0" - - "Organization: ExampleCodeInspect ()" - - "Person: Jane Doe ()" - licenseListVersion: "3.17" -name: "SPDX-Tools-v2.0" -dataLicense: "CC0-1.0" -comment: "This document was created using SPDX 2.0 using licenses from the web site." -externalDocumentRefs: -- externalDocumentId: "DocumentRef-spdx-tool-1.2" - checksum: - algorithm: "SHA1" - checksumValue: "d6a770ba38583ed4bb4525bd96e50461655d2759" - spdxDocument: "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301" -hasExtractedLicensingInfos: -- licenseId: "LicenseRef-1" - extractedText: "/*\n * (c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,\ - \ 2008, 2009 Hewlett-Packard Development Company, LP\n * All rights reserved.\n\ - \ *\n * Redistribution and use in source and binary forms, with or without\n *\ - \ modification, are permitted provided that the following conditions\n * are met:\n\ - \ * 1. Redistributions of source code must retain the above copyright\n * notice,\ - \ this list of conditions and the following disclaimer.\n * 2. Redistributions\ - \ in binary form must reproduce the above copyright\n * notice, this list of\ - \ conditions and the following disclaimer in the\n * documentation and/or other\ - \ materials provided with the distribution.\n * 3. The name of the author may\ - \ not be used to endorse or promote products\n * derived from this software\ - \ without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED\ - \ BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES, INCLUDING,\ - \ BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY AND FITNESS\ - \ FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE AUTHOR BE\ - \ LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\ - \ DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\ - \ OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\ - \ CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\ - \ OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\ - \ USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\ - */" -- licenseId: "LicenseRef-2" - extractedText: "This package includes the GRDDL parser developed by Hewlett Packard\ - \ under the following license:\n© Copyright 2007 Hewlett-Packard Development Company,\ - \ LP\n\nRedistribution and use in source and binary forms, with or without modification,\ - \ are permitted provided that the following conditions are met: \n\nRedistributions\ - \ of source code must retain the above copyright notice, this list of conditions\ - \ and the following disclaimer. \nRedistributions in binary form must reproduce\ - \ the above copyright notice, this list of conditions and the following disclaimer\ - \ in the documentation and/or other materials provided with the distribution.\ - \ \nThe name of the author may not be used to endorse or promote products derived\ - \ from this software without specific prior written permission. \nTHIS SOFTWARE\ - \ IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,\ - \ BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\ - \ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE\ - \ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\ - \ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\ - \ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\ - \ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\ - \ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\ - \ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -- licenseId: "LicenseRef-4" - extractedText: "/*\n * (c) Copyright 2009 University of Bristol\n * All rights reserved.\n\ - \ *\n * Redistribution and use in source and binary forms, with or without\n *\ - \ modification, are permitted provided that the following conditions\n * are met:\n\ - \ * 1. Redistributions of source code must retain the above copyright\n * notice,\ - \ this list of conditions and the following disclaimer.\n * 2. Redistributions\ - \ in binary form must reproduce the above copyright\n * notice, this list of\ - \ conditions and the following disclaimer in the\n * documentation and/or other\ - \ materials provided with the distribution.\n * 3. The name of the author may\ - \ not be used to endorse or promote products\n * derived from this software\ - \ without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED\ - \ BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES, INCLUDING,\ - \ BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY AND FITNESS\ - \ FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE AUTHOR BE\ - \ LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\ - \ DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\ - \ OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\ - \ CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\ - \ OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\ - \ USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\ - */" -- licenseId: "LicenseRef-Beerware-4.2" - comment: "The beerware license has a couple of other standard variants." - extractedText: "\"THE BEER-WARE LICENSE\" (Revision 42):\nphk@FreeBSD.ORG wrote\ - \ this file. As long as you retain this notice you\ncan do whatever you want with\ - \ this stuff. If we meet some day, and you think this stuff is worth it, you can\ - \ buy me a beer in return Poul-Henning Kamp" - name: "Beer-Ware License (Version 42)" - seeAlsos: - - "http://people.freebsd.org/~phk/" -- licenseId: "LicenseRef-3" - comment: "This is tye CyperNeko License" - extractedText: "The CyberNeko Software License, Version 1.0\n\n \n(C) Copyright\ - \ 2002-2005, Andy Clark. All rights reserved.\n \nRedistribution and use in source\ - \ and binary forms, with or without\nmodification, are permitted provided that\ - \ the following conditions\nare met:\n\n1. Redistributions of source code must\ - \ retain the above copyright\n notice, this list of conditions and the following\ - \ disclaimer. \n\n2. Redistributions in binary form must reproduce the above copyright\n\ - \ notice, this list of conditions and the following disclaimer in\n the documentation\ - \ and/or other materials provided with the\n distribution.\n\n3. The end-user\ - \ documentation included with the redistribution,\n if any, must include the\ - \ following acknowledgment: \n \"This product includes software developed\ - \ by Andy Clark.\"\n Alternately, this acknowledgment may appear in the software\ - \ itself,\n if and wherever such third-party acknowledgments normally appear.\n\ - \n4. The names \"CyberNeko\" and \"NekoHTML\" must not be used to endorse\n \ - \ or promote products derived from this software without prior \n written permission.\ - \ For written permission, please contact \n andyc@cyberneko.net.\n\n5. Products\ - \ derived from this software may not be called \"CyberNeko\",\n nor may \"CyberNeko\"\ - \ appear in their name, without prior written\n permission of the author.\n\n\ - THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED\nWARRANTIES,\ - \ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\nOF MERCHANTABILITY AND\ - \ FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE AUTHOR\ - \ OR OTHER CONTRIBUTORS\nBE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\ - \ EXEMPLARY, \nOR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT\ - \ \nOF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR \nBUSINESS\ - \ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, \nWHETHER IN CONTRACT,\ - \ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE \nOR OTHERWISE) ARISING IN ANY\ - \ WAY OUT OF THE USE OF THIS SOFTWARE, \nEVEN IF ADVISED OF THE POSSIBILITY OF\ - \ SUCH DAMAGE." - name: "CyberNeko License" - seeAlsos: - - "http://people.apache.org/~andyc/neko/LICENSE" - - "http://justasample.url.com" -annotations: -- annotationDate: "2010-01-29T18:30:22Z" - annotationType: "OTHER" - annotator: "Person: Jane Doe ()" - comment: "Document level annotation" -- annotationDate: "2010-02-10T00:00:00Z" - annotationType: "REVIEW" - annotator: "Person: Joe Reviewer" - comment: "This is just an example. Some of the non-standard licenses look like\ - \ they are actually BSD 3 clause licenses" -- annotationDate: "2011-03-13T00:00:00Z" - annotationType: "REVIEW" - annotator: "Person: Suzanne Reviewer" - comment: "Another example reviewer." -documentDescribes: -- "SPDXRef-File" -- "SPDXRef-Package" -documentNamespace: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301" -packages: -- SPDXID: "SPDXRef-Package" - annotations: - - annotationDate: "2011-01-29T18:30:22Z" - annotationType: "OTHER" - annotator: "Person: Package Commenter" - comment: "Package level annotation" - attributionTexts: - - "The GNU C Library is free software. See the file COPYING.LIB for copying conditions,\ - \ and LICENSES for notices about a few contributions that require these additional\ - \ notices to be distributed. License copyright years may be listed using range\ - \ notation, e.g., 1996-2015, indicating that every year in the range, inclusive,\ - \ is a copyrightable year that would otherwise be listed individually." - builtDate: "2011-01-29T18:30:22Z" - checksums: - - algorithm: "MD5" - checksumValue: "624c1abb3664f4b35547e7c73864ad24" - - algorithm: "SHA1" - checksumValue: "85ed0817af83a24ad8da68c2b5094de69833983c" - - algorithm: "SHA256" - checksumValue: "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd" - - algorithm: "BLAKE2b-384" - checksumValue: "aaabd89c926ab525c242e6621f2f5fa73aa4afe3d9e24aed727faaadd6af38b620bdb623dd2b4788b1c8086984af8706" - copyrightText: "Copyright 2008-2010 John Smith" - description: "The GNU C Library defines functions that are specified by the ISO\ - \ C standard, as well as additional features specific to POSIX and other derivatives\ - \ of the Unix operating system, and extensions specific to GNU systems." - downloadLocation: "http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz" - externalRefs: - - referenceCategory: "SECURITY" - referenceLocator: "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" - referenceType: "cpe23Type" - - comment: "This is the external ref for Acme" - referenceCategory: "OTHER" - referenceLocator: "acmecorp/acmenator/4.1.3-alpha" - referenceType: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge" - filesAnalyzed: true - homepage: "http://ftp.gnu.org/gnu/glibc" - licenseComments: "The license for this project changed with the release of version\ - \ x.y. The version of the project included here post-dates the license change." - licenseConcluded: "(LGPL-2.0-only OR LicenseRef-3)" - licenseDeclared: "(LGPL-2.0-only AND LicenseRef-3)" - licenseInfoFromFiles: - - "GPL-2.0-only" - - "LicenseRef-2" - - "LicenseRef-1" - name: "glibc" - originator: "Organization: ExampleCodeInspect (contact@example.com)" - packageFileName: "glibc-2.11.1.tar.gz" - packageVerificationCode: - packageVerificationCodeExcludedFiles: - - "./package.spdx" - packageVerificationCodeValue: "d6a770ba38583ed4bb4525bd96e50461655d2758" - primaryPackagePurpose: "SOURCE" - hasFiles: - - "SPDXRef-Specification" - - "SPDXRef-Specification" - - "SPDXRef-CommonsLangSrc" - - "SPDXRef-Specification" - - "SPDXRef-CommonsLangSrc" - - "SPDXRef-JenaLib" - - "SPDXRef-Specification" - - "SPDXRef-CommonsLangSrc" - - "SPDXRef-JenaLib" - - "SPDXRef-DoapSource" - - "SPDXRef-Specification" - - "SPDXRef-CommonsLangSrc" - - "SPDXRef-JenaLib" - - "SPDXRef-DoapSource" - releaseDate: "2012-01-29T18:30:22Z" - sourceInfo: "uses glibc-2_11-branch from git://sourceware.org/git/glibc.git." - summary: "GNU C library." - supplier: "Person: Jane Doe (jane.doe@example.com)" - validUntilDate: "2014-01-29T18:30:22Z" - versionInfo: "2.11.1" -- SPDXID: "SPDXRef-fromDoap-1" - copyrightText: "NOASSERTION" - downloadLocation: "NOASSERTION" - filesAnalyzed: false - homepage: "http://commons.apache.org/proper/commons-lang/" - licenseConcluded: "NOASSERTION" - licenseDeclared: "NOASSERTION" - name: "Apache Commons Lang" -- SPDXID: "SPDXRef-fromDoap-0" - downloadLocation: "https://search.maven.org/remotecontent?filepath=org/apache/jena/apache-jena/3.12.0/apache-jena-3.12.0.tar.gz" - externalRefs: - - referenceCategory: "PACKAGE-MANAGER" - referenceLocator: "pkg:maven/org.apache.jena/apache-jena@3.12.0" - referenceType: "purl" - filesAnalyzed: false - homepage: "http://www.openjena.org/" - name: "Jena" - versionInfo: "3.12.0" -- SPDXID: "SPDXRef-Saxon" - checksums: - - algorithm: "SHA1" - checksumValue: "85ed0817af83a24ad8da68c2b5094de69833983c" - copyrightText: "Copyright Saxonica Ltd" - description: "The Saxon package is a collection of tools for processing XML documents." - downloadLocation: "https://sourceforge.net/projects/saxon/files/Saxon-B/8.8.0.7/saxonb8-8-0-7j.zip/download" - filesAnalyzed: false - homepage: "http://saxon.sourceforge.net/" - licenseComments: "Other versions available for a commercial license" - licenseConcluded: "MPL-1.0" - licenseDeclared: "MPL-1.0" - name: "Saxon" - packageFileName: "saxonB-8.8.zip" - versionInfo: "8.8" -files: -- SPDXID: "SPDXRef-DoapSource" - checksums: - - algorithm: "SHA1" - checksumValue: "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12" - copyrightText: "Copyright 2010, 2011 Source Auditor Inc." - fileContributors: - - "Protecode Inc." - - "SPDX Technical Team Members" - - "Open Logic Inc." - - "Source Auditor Inc." - - "Black Duck Software In.c" - fileName: "./src/org/spdx/parser/DOAPProject.java" - fileTypes: - - "SOURCE" - licenseConcluded: "Apache-2.0" - licenseInfoInFiles: - - "Apache-2.0" -- SPDXID: "SPDXRef-CommonsLangSrc" - checksums: - - algorithm: "SHA1" - checksumValue: "c2b4e1c67a2d28fced849ee1bb76e7391b93f125" - comment: "This file is used by Jena" - copyrightText: "Copyright 2001-2011 The Apache Software Foundation" - fileContributors: - - "Apache Software Foundation" - fileName: "./lib-source/commons-lang3-3.1-sources.jar" - fileTypes: - - "ARCHIVE" - licenseConcluded: "Apache-2.0" - licenseInfoInFiles: - - "Apache-2.0" - noticeText: "Apache Commons Lang\nCopyright 2001-2011 The Apache Software Foundation\n\ - \nThis product includes software developed by\nThe Apache Software Foundation\ - \ (http://www.apache.org/).\n\nThis product includes software from the Spring\ - \ Framework,\nunder the Apache License 2.0 (see: StringUtils.containsWhitespace())" -- SPDXID: "SPDXRef-JenaLib" - checksums: - - algorithm: "SHA1" - checksumValue: "3ab4e1c67a2d28fced849ee1bb76e7391b93f125" - comment: "This file belongs to Jena" - copyrightText: "(c) Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,\ - \ 2009 Hewlett-Packard Development Company, LP" - fileContributors: - - "Apache Software Foundation" - - "Hewlett Packard Inc." - fileName: "./lib-source/jena-2.6.3-sources.jar" - fileTypes: - - "ARCHIVE" - licenseComments: "This license is used by Jena" - licenseConcluded: "LicenseRef-1" - licenseInfoInFiles: - - "LicenseRef-1" -- SPDXID: "SPDXRef-Specification" - checksums: - - algorithm: "SHA1" - checksumValue: "fff4e1c67a2d28fced849ee1bb76e7391b93f125" - comment: "Specification Documentation" - fileName: "./docs/myspec.pdf" - fileTypes: - - "DOCUMENTATION" -- SPDXID: "SPDXRef-File" - annotations: - - annotationDate: "2011-01-29T18:30:22Z" - annotationType: "OTHER" - annotator: "Person: File Commenter" - comment: "File level annotation" - checksums: - - algorithm: "SHA1" - checksumValue: "d6a770ba38583ed4bb4525bd96e50461655d2758" - - algorithm: "MD5" - checksumValue: "624c1abb3664f4b35547e7c73864ad24" - comment: "The concluded license was taken from the package level that the file was\ - \ included in.\nThis information was found in the COPYING.txt file in the xyz\ - \ directory." - copyrightText: "Copyright 2008-2010 John Smith" - fileContributors: - - "The Regents of the University of California" - - "Modified by Paul Mundt lethal@linux-sh.org" - - "IBM Corporation" - fileName: "./package/foo.c" - fileTypes: - - "SOURCE" - licenseComments: "The concluded license was taken from the package level that the\ - \ file was included in." - licenseConcluded: "(LGPL-2.0-only OR LicenseRef-2)" - licenseInfoInFiles: - - "GPL-2.0-only" - - "LicenseRef-2" - noticeText: "Copyright (c) 2001 Aaron Lehmann aaroni@vitelus.com\n\nPermission is\ - \ hereby granted, free of charge, to any person obtaining a copy of this software\ - \ and associated documentation files (the \"Software\"), to deal in the Software\ - \ without restriction, including without limitation the rights to use, copy, modify,\ - \ merge, publish, distribute, sublicense, and/or sell copies of the Software,\ - \ and to permit persons to whom the Software is furnished to do so, subject to\ - \ the following conditions: \nThe above copyright notice and this permission notice\ - \ shall be included in all copies or substantial portions of the Software.\n\n\ - THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\ - \ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR\ - \ A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\ - \ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\ - \ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\ - \ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." -snippets: -- SPDXID: "SPDXRef-Snippet" - comment: "This snippet was identified as significant and highlighted in this Apache-2.0\ - \ file, when a commercial scanner identified it as being derived from file foo.c\ - \ in package xyz which is licensed under GPL-2.0." - copyrightText: "Copyright 2008-2010 John Smith" - licenseComments: "The concluded license was taken from package xyz, from which the\ - \ snippet was copied into the current file. The concluded license information\ - \ was found in the COPYING.txt file in package xyz." - licenseConcluded: "GPL-2.0-only" - licenseInfoInSnippets: - - "GPL-2.0-only" - name: "from linux kernel" - ranges: - - endPointer: - offset: 420 - reference: "SPDXRef-DoapSource" - startPointer: - offset: 310 - reference: "SPDXRef-DoapSource" - - endPointer: - lineNumber: 23 - reference: "SPDXRef-DoapSource" - startPointer: - lineNumber: 5 - reference: "SPDXRef-DoapSource" - snippetFromFile: "SPDXRef-DoapSource" -relationships: -- spdxElementId: "SPDXRef-DOCUMENT" - relationshipType: "CONTAINS" - relatedSpdxElement: "SPDXRef-Package" -- spdxElementId: "SPDXRef-DOCUMENT" - relationshipType: "COPY_OF" - relatedSpdxElement: "DocumentRef-spdx-tool-1.2:SPDXRef-ToolsElement" -- spdxElementId: "SPDXRef-Package" - relationshipType: "DYNAMIC_LINK" - relatedSpdxElement: "SPDXRef-Saxon" -- spdxElementId: "SPDXRef-CommonsLangSrc" - relationshipType: "GENERATED_FROM" - relatedSpdxElement: "NOASSERTION" -- spdxElementId: "SPDXRef-JenaLib" - relationshipType: "CONTAINS" - relatedSpdxElement: "SPDXRef-Package" -- spdxElementId: "SPDXRef-Specification" - relationshipType: "SPECIFICATION_FOR" - relatedSpdxElement: "SPDXRef-fromDoap-0" -- spdxElementId: "SPDXRef-File" - relationshipType: "GENERATED_FROM" - relatedSpdxElement: "SPDXRef-fromDoap-0" From 9158ecb27b375ede278756e329a018a8c7afe2ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 29 Mar 2023 10:57:56 +0200 Subject: [PATCH 359/362] delete unused license lists and corresponding files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/config.py | 70 - src/spdx/exceptions.json | 408 --- src/spdx/licenses.json | 4974 ------------------------------ src/spdx/model/license.py | 45 - tests/spdx/model/test_license.py | 31 - 5 files changed, 5528 deletions(-) delete mode 100644 src/spdx/config.py delete mode 100644 src/spdx/exceptions.json delete mode 100644 src/spdx/licenses.json delete mode 100644 src/spdx/model/license.py delete mode 100644 tests/spdx/model/test_license.py diff --git a/src/spdx/config.py b/src/spdx/config.py deleted file mode 100644 index 3830fbfe3..000000000 --- a/src/spdx/config.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 2014 Ahmed H. Ismail -# 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 codecs -import json -import os - -from spdx.model.version import Version - -_base_dir = os.path.dirname(__file__) -_licenses = os.path.join(_base_dir, "licenses.json") -_exceptions = os.path.join(_base_dir, "exceptions.json") - - -def _load_list(file_name, object_type="licenses", id_attribute="licenseId"): - """ - Return a list version tuple and a mapping of licenses - name->id and id->name loaded from a JSON file - from https://github.com/spdx/license-list-data - """ - licenses_map = {} - with codecs.open(file_name, "rb", encoding="utf-8") as lics: - licenses = json.load(lics) - version = tuple(licenses["licenseListVersion"].split(".")) - for lic in licenses[object_type]: - if lic.get("isDeprecatedLicenseId"): - continue - name = lic["name"] - identifier = lic[id_attribute] - licenses_map[name] = identifier - licenses_map[identifier] = name - return version, licenses_map - - -def load_license_list(file_name): - """ - Return the licenses list version tuple and a mapping of licenses - name->id and id->name loaded from a JSON file - from https://github.com/spdx/license-list-data - """ - return _load_list(file_name, object_type="licenses", id_attribute="licenseId") - - -def load_exception_list(file_name): - """ - Return the exceptions list version tuple and a mapping of exceptions - name->id and id->name loaded from a JSON file - from https://github.com/spdx/license-list-data - """ - return _load_list( - file_name, object_type="exceptions", id_attribute="licenseExceptionId" - ) - - -(_lmajor, _lminor), LICENSE_MAP = load_license_list(_licenses) -LICENSE_LIST_VERSION = Version(major=_lmajor, minor=_lminor) - -(_emajor, _eminor), EXCEPTION_MAP = load_exception_list(_exceptions) -EXCEPTION_LIST_VERSION = Version(major=_emajor, minor=_eminor) - -assert LICENSE_LIST_VERSION == EXCEPTION_LIST_VERSION -del _emajor, _eminor, EXCEPTION_LIST_VERSION diff --git a/src/spdx/exceptions.json b/src/spdx/exceptions.json deleted file mode 100644 index 709a7d614..000000000 --- a/src/spdx/exceptions.json +++ /dev/null @@ -1,408 +0,0 @@ -{ - "licenseListVersion": "3.6", - "releaseDate": "2019-07-10", - "exceptions": [ - { - "reference": "./Libtool-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Libtool-exception.json", - "referenceNumber": "1", - "name": "Libtool Exception", - "seeAlso": [ - "http://git.savannah.gnu.org/cgit/libtool.git/tree/m4/libtool.m4" - ], - "licenseExceptionId": "Libtool-exception" - }, - { - "reference": "./Linux-syscall-note.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Linux-syscall-note.json", - "referenceNumber": "2", - "name": "Linux Syscall Note", - "seeAlso": [ - "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/COPYING" - ], - "licenseExceptionId": "Linux-syscall-note" - }, - { - "reference": "./Autoconf-exception-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Autoconf-exception-3.0.json", - "referenceNumber": "3", - "name": "Autoconf exception 3.0", - "seeAlso": [ - "http://www.gnu.org/licenses/autoconf-exception-3.0.html" - ], - "licenseExceptionId": "Autoconf-exception-3.0" - }, - { - "reference": "./OCCT-exception-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OCCT-exception-1.0.json", - "referenceNumber": "4", - "name": "Open CASCADE Exception 1.0", - "seeAlso": [ - "http://www.opencascade.com/content/licensing" - ], - "licenseExceptionId": "OCCT-exception-1.0" - }, - { - "reference": "./openvpn-openssl-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/openvpn-openssl-exception.json", - "referenceNumber": "5", - "name": "OpenVPN OpenSSL Exception", - "seeAlso": [ - "http://openvpn.net/index.php/license.html" - ], - "licenseExceptionId": "openvpn-openssl-exception" - }, - { - "reference": "./gnu-javamail-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/gnu-javamail-exception.json", - "referenceNumber": "6", - "name": "GNU JavaMail exception", - "seeAlso": [ - "http://www.gnu.org/software/classpathx/javamail/javamail.html" - ], - "licenseExceptionId": "gnu-javamail-exception" - }, - { - "reference": "./OpenJDK-assembly-exception-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OpenJDK-assembly-exception-1.0.json", - "referenceNumber": "7", - "name": "OpenJDK Assembly exception 1.0", - "seeAlso": [ - "http://openjdk.java.net/legal/assembly-exception.html" - ], - "licenseExceptionId": "OpenJDK-assembly-exception-1.0" - }, - { - "reference": "./Bison-exception-2.2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Bison-exception-2.2.json", - "referenceNumber": "8", - "name": "Bison exception 2.2", - "seeAlso": [ - "http://git.savannah.gnu.org/cgit/bison.git/tree/data/yacc.c?id\u003d193d7c7054ba7197b0789e14965b739162319b5e#n141" - ], - "licenseExceptionId": "Bison-exception-2.2" - }, - { - "reference": "./i2p-gpl-java-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/i2p-gpl-java-exception.json", - "referenceNumber": "9", - "name": "i2p GPL+Java Exception", - "seeAlso": [ - "http://geti2p.net/en/get-involved/develop/licenses#java_exception" - ], - "licenseExceptionId": "i2p-gpl-java-exception" - }, - { - "reference": "./Universal-FOSS-exception-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Universal-FOSS-exception-1.0.json", - "referenceNumber": "10", - "name": "Universal FOSS Exception, Version 1.0", - "seeAlso": [ - "https://oss.oracle.com/licenses/universal-foss-exception/" - ], - "licenseExceptionId": "Universal-FOSS-exception-1.0" - }, - { - "reference": "./Qt-LGPL-exception-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Qt-LGPL-exception-1.1.json", - "referenceNumber": "11", - "name": "Qt LGPL exception 1.1", - "seeAlso": [ - "http://code.qt.io/cgit/qt/qtbase.git/tree/LGPL_EXCEPTION.txt" - ], - "licenseExceptionId": "Qt-LGPL-exception-1.1" - }, - { - "reference": "./389-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/389-exception.json", - "referenceNumber": "12", - "name": "389 Directory Server Exception", - "seeAlso": [ - "http://directory.fedoraproject.org/wiki/GPL_Exception_License_Text" - ], - "licenseExceptionId": "389-exception" - }, - { - "reference": "./Classpath-exception-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Classpath-exception-2.0.json", - "referenceNumber": "13", - "name": "Classpath exception 2.0", - "seeAlso": [ - "http://www.gnu.org/software/classpath/license.html", - "https://fedoraproject.org/wiki/Licensing/GPL_Classpath_Exception" - ], - "licenseExceptionId": "Classpath-exception-2.0" - }, - { - "reference": "./Fawkes-Runtime-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Fawkes-Runtime-exception.json", - "referenceNumber": "14", - "name": "Fawkes Runtime Exception", - "seeAlso": [ - "http://www.fawkesrobotics.org/about/license/" - ], - "licenseExceptionId": "Fawkes-Runtime-exception" - }, - { - "reference": "./PS-or-PDF-font-exception-20170817.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/PS-or-PDF-font-exception-20170817.json", - "referenceNumber": "15", - "name": "PS/PDF font exception (2017-08-17)", - "seeAlso": [ - "https://github.com/ArtifexSoftware/urw-base35-fonts/blob/65962e27febc3883a17e651cdb23e783668c996f/LICENSE" - ], - "licenseExceptionId": "PS-or-PDF-font-exception-20170817" - }, - { - "reference": "./Qt-GPL-exception-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Qt-GPL-exception-1.0.json", - "referenceNumber": "16", - "name": "Qt GPL exception 1.0", - "seeAlso": [ - "http://code.qt.io/cgit/qt/qtbase.git/tree/LICENSE.GPL3-EXCEPT" - ], - "licenseExceptionId": "Qt-GPL-exception-1.0" - }, - { - "reference": "./LZMA-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LZMA-exception.json", - "referenceNumber": "17", - "name": "LZMA exception", - "seeAlso": [ - "http://nsis.sourceforge.net/Docs/AppendixI.html#I.6" - ], - "licenseExceptionId": "LZMA-exception" - }, - { - "reference": "./freertos-exception-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/freertos-exception-2.0.json", - "referenceNumber": "18", - "name": "FreeRTOS Exception 2.0", - "seeAlso": [ - "https://web.archive.org/web/20060809182744/http://www.freertos.org/a00114.html" - ], - "licenseExceptionId": "freertos-exception-2.0" - }, - { - "reference": "./Qwt-exception-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Qwt-exception-1.0.json", - "referenceNumber": "19", - "name": "Qwt exception 1.0", - "seeAlso": [ - "http://qwt.sourceforge.net/qwtlicense.html" - ], - "licenseExceptionId": "Qwt-exception-1.0" - }, - { - "reference": "./CLISP-exception-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CLISP-exception-2.0.json", - "referenceNumber": "20", - "name": "CLISP exception 2.0", - "seeAlso": [ - "http://sourceforge.net/p/clisp/clisp/ci/default/tree/COPYRIGHT" - ], - "licenseExceptionId": "CLISP-exception-2.0" - }, - { - "reference": "./FLTK-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/FLTK-exception.json", - "referenceNumber": "21", - "name": "FLTK exception", - "seeAlso": [ - "http://www.fltk.org/COPYING.php" - ], - "licenseExceptionId": "FLTK-exception" - }, - { - "reference": "./Bootloader-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Bootloader-exception.json", - "referenceNumber": "22", - "name": "Bootloader Distribution Exception", - "seeAlso": [ - "https://github.com/pyinstaller/pyinstaller/blob/develop/COPYING.txt" - ], - "licenseExceptionId": "Bootloader-exception" - }, - { - "reference": "./Nokia-Qt-exception-1.1.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/Nokia-Qt-exception-1.1.json", - "referenceNumber": "23", - "name": "Nokia Qt LGPL exception 1.1", - "seeAlso": [ - "https://www.keepassx.org/dev/projects/keepassx/repository/revisions/b8dfb9cc4d5133e0f09cd7533d15a4f1c19a40f2/entry/LICENSE.NOKIA-LGPL-EXCEPTION" - ], - "licenseExceptionId": "Nokia-Qt-exception-1.1" - }, - { - "reference": "./LLVM-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LLVM-exception.json", - "referenceNumber": "24", - "name": "LLVM Exception", - "seeAlso": [ - "http://llvm.org/foundation/relicensing/LICENSE.txt" - ], - "licenseExceptionId": "LLVM-exception" - }, - { - "reference": "./WxWindows-exception-3.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/WxWindows-exception-3.1.json", - "referenceNumber": "25", - "name": "WxWindows Library Exception 3.1", - "seeAlso": [ - "http://www.opensource.org/licenses/WXwindows" - ], - "licenseExceptionId": "WxWindows-exception-3.1" - }, - { - "reference": "./DigiRule-FOSS-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/DigiRule-FOSS-exception.json", - "referenceNumber": "26", - "name": "DigiRule FOSS License Exception", - "seeAlso": [ - "http://www.digirulesolutions.com/drupal/foss" - ], - "licenseExceptionId": "DigiRule-FOSS-exception" - }, - { - "reference": "./Swift-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Swift-exception.json", - "referenceNumber": "27", - "name": "Swift Exception", - "seeAlso": [ - "https://swift.org/LICENSE.txt", - "https://github.com/apple/swift-package-manager/blob/7ab2275f447a5eb37497ed63a9340f8a6d1e488b/LICENSE.txt#L205" - ], - "licenseExceptionId": "Swift-exception" - }, - { - "reference": "./GCC-exception-3.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/GCC-exception-3.1.json", - "referenceNumber": "28", - "name": "GCC Runtime Library exception 3.1", - "seeAlso": [ - "http://www.gnu.org/licenses/gcc-exception-3.1.html" - ], - "licenseExceptionId": "GCC-exception-3.1" - }, - { - "reference": "./eCos-exception-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/eCos-exception-2.0.json", - "referenceNumber": "29", - "name": "eCos exception 2.0", - "seeAlso": [ - "http://ecos.sourceware.org/license-overview.html" - ], - "licenseExceptionId": "eCos-exception-2.0" - }, - { - "reference": "./Autoconf-exception-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Autoconf-exception-2.0.json", - "referenceNumber": "30", - "name": "Autoconf exception 2.0", - "seeAlso": [ - "http://ac-archive.sourceforge.net/doc/copyright.html", - "http://ftp.gnu.org/gnu/autoconf/autoconf-2.59.tar.gz" - ], - "licenseExceptionId": "Autoconf-exception-2.0" - }, - { - "reference": "./GPL-CC-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/GPL-CC-1.0.json", - "referenceNumber": "31", - "name": "GPL Cooperation Commitment 1.0", - "seeAlso": [ - "https://github.com/gplcc/gplcc/blob/master/Project/COMMITMENT", - "https://gplcc.github.io/gplcc/Project/README-PROJECT.html" - ], - "licenseExceptionId": "GPL-CC-1.0" - }, - { - "reference": "./Font-exception-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Font-exception-2.0.json", - "referenceNumber": "32", - "name": "Font exception 2.0", - "seeAlso": [ - "http://www.gnu.org/licenses/gpl-faq.html#FontException" - ], - "licenseExceptionId": "Font-exception-2.0" - }, - { - "reference": "./u-boot-exception-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/u-boot-exception-2.0.json", - "referenceNumber": "33", - "name": "U-Boot exception 2.0", - "seeAlso": [ - "http://git.denx.de/?p\u003du-boot.git;a\u003dblob;f\u003dLicenses/Exceptions" - ], - "licenseExceptionId": "u-boot-exception-2.0" - }, - { - "reference": "./GCC-exception-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/GCC-exception-2.0.json", - "referenceNumber": "34", - "name": "GCC Runtime Library exception 2.0", - "seeAlso": [ - "https://gcc.gnu.org/git/?p\u003dgcc.git;a\u003dblob;f\u003dgcc/libgcc1.c;h\u003d762f5143fc6eed57b6797c82710f3538aa52b40b;hb\u003dcb143a3ce4fb417c68f5fa2691a1b1b1053dfba9#l10" - ], - "licenseExceptionId": "GCC-exception-2.0" - }, - { - "reference": "./mif-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/mif-exception.json", - "referenceNumber": "35", - "name": "Macros and Inline Functions Exception", - "seeAlso": [ - "http://www.scs.stanford.edu/histar/src/lib/cppsup/exception", - "http://dev.bertos.org/doxygen/", - "https://www.threadingbuildingblocks.org/licensing" - ], - "licenseExceptionId": "mif-exception" - }, - { - "reference": "./OCaml-LGPL-linking-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OCaml-LGPL-linking-exception.json", - "referenceNumber": "36", - "name": "OCaml LGPL Linking Exception", - "seeAlso": [ - "https://caml.inria.fr/ocaml/license.en.html" - ], - "licenseExceptionId": "OCaml-LGPL-linking-exception" - } - ] -} diff --git a/src/spdx/licenses.json b/src/spdx/licenses.json deleted file mode 100644 index 5a78e2b05..000000000 --- a/src/spdx/licenses.json +++ /dev/null @@ -1,4974 +0,0 @@ -{ - "licenseListVersion": "3.6", - "licenses": [ - { - "reference": "./0BSD.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/0BSD.json", - "referenceNumber": "319", - "name": "BSD Zero Clause License", - "licenseId": "0BSD", - "seeAlso": [ - "http://landley.net/toybox/license.html" - ], - "isOsiApproved": true - }, - { - "reference": "./AAL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/AAL.json", - "referenceNumber": "21", - "name": "Attribution Assurance License", - "licenseId": "AAL", - "seeAlso": [ - "https://opensource.org/licenses/attribution" - ], - "isOsiApproved": true - }, - { - "reference": "./ADSL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/ADSL.json", - "referenceNumber": "19", - "name": "Amazon Digital Services License", - "licenseId": "ADSL", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/AmazonDigitalServicesLicense" - ], - "isOsiApproved": false - }, - { - "reference": "./AFL-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AFL-1.1.json", - "referenceNumber": "118", - "name": "Academic Free License v1.1", - "licenseId": "AFL-1.1", - "seeAlso": [ - "http://opensource.linux-mirror.org/licenses/afl-1.1.txt", - "http://wayback.archive.org/web/20021004124254/http://www.opensource.org/licenses/academic.php" - ], - "isOsiApproved": true - }, - { - "reference": "./AFL-1.2.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AFL-1.2.json", - "referenceNumber": "136", - "name": "Academic Free License v1.2", - "licenseId": "AFL-1.2", - "seeAlso": [ - "http://opensource.linux-mirror.org/licenses/afl-1.2.txt", - "http://wayback.archive.org/web/20021204204652/http://www.opensource.org/licenses/academic.php" - ], - "isOsiApproved": true - }, - { - "reference": "./AFL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AFL-2.0.json", - "referenceNumber": "115", - "name": "Academic Free License v2.0", - "licenseId": "AFL-2.0", - "seeAlso": [ - "http://wayback.archive.org/web/20060924134533/http://www.opensource.org/licenses/afl-2.0.txt" - ], - "isOsiApproved": true - }, - { - "reference": "./AFL-2.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AFL-2.1.json", - "referenceNumber": "251", - "name": "Academic Free License v2.1", - "licenseId": "AFL-2.1", - "seeAlso": [ - "http://opensource.linux-mirror.org/licenses/afl-2.1.txt" - ], - "isOsiApproved": true - }, - { - "reference": "./AFL-3.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AFL-3.0.json", - "referenceNumber": "216", - "name": "Academic Free License v3.0", - "licenseId": "AFL-3.0", - "seeAlso": [ - "http://www.rosenlaw.com/AFL3.0.htm", - "https://opensource.org/licenses/afl-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./AGPL-1.0.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AGPL-1.0.json", - "referenceNumber": "335", - "name": "Affero General Public License v1.0", - "licenseId": "AGPL-1.0", - "seeAlso": [ - "http://www.affero.org/oagpl.html" - ], - "isOsiApproved": false - }, - { - "reference": "./AGPL-1.0-only.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/AGPL-1.0-only.json", - "referenceNumber": "384", - "name": "Affero General Public License v1.0 only", - "licenseId": "AGPL-1.0-only", - "seeAlso": [ - "http://www.affero.org/oagpl.html" - ], - "isOsiApproved": false - }, - { - "reference": "./AGPL-1.0-or-later.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/AGPL-1.0-or-later.json", - "referenceNumber": "332", - "name": "Affero General Public License v1.0 or later", - "licenseId": "AGPL-1.0-or-later", - "seeAlso": [ - "http://www.affero.org/oagpl.html" - ], - "isOsiApproved": false - }, - { - "reference": "./AGPL-3.0.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AGPL-3.0.json", - "referenceNumber": "229", - "name": "GNU Affero General Public License v3.0", - "licenseId": "AGPL-3.0", - "seeAlso": [ - "https://www.gnu.org/licenses/agpl.txt", - "https://opensource.org/licenses/AGPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./AGPL-3.0-only.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AGPL-3.0-only.json", - "referenceNumber": "95", - "name": "GNU Affero General Public License v3.0 only", - "licenseId": "AGPL-3.0-only", - "seeAlso": [ - "https://www.gnu.org/licenses/agpl.txt", - "https://opensource.org/licenses/AGPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./AGPL-3.0-or-later.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/AGPL-3.0-or-later.json", - "referenceNumber": "155", - "name": "GNU Affero General Public License v3.0 or later", - "licenseId": "AGPL-3.0-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/agpl.txt", - "https://opensource.org/licenses/AGPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./AMDPLPA.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/AMDPLPA.json", - "referenceNumber": "33", - "name": "AMD\u0027s plpa_map.c License", - "licenseId": "AMDPLPA", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/AMD_plpa_map_License" - ], - "isOsiApproved": false - }, - { - "reference": "./AML.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/AML.json", - "referenceNumber": "148", - "name": "Apple MIT License", - "licenseId": "AML", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Apple_MIT_License" - ], - "isOsiApproved": false - }, - { - "reference": "./AMPAS.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/AMPAS.json", - "referenceNumber": "191", - "name": "Academy of Motion Picture Arts and Sciences BSD", - "licenseId": "AMPAS", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/BSD#AMPASBSD" - ], - "isOsiApproved": false - }, - { - "reference": "./ANTLR-PD.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/ANTLR-PD.json", - "referenceNumber": "395", - "name": "ANTLR Software Rights Notice", - "licenseId": "ANTLR-PD", - "seeAlso": [ - "http://www.antlr2.org/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./APAFML.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/APAFML.json", - "referenceNumber": "195", - "name": "Adobe Postscript AFM License", - "licenseId": "APAFML", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/AdobePostscriptAFM" - ], - "isOsiApproved": false - }, - { - "reference": "./APL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/APL-1.0.json", - "referenceNumber": "252", - "name": "Adaptive Public License 1.0", - "licenseId": "APL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/APL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./APSL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/APSL-1.0.json", - "referenceNumber": "354", - "name": "Apple Public Source License 1.0", - "licenseId": "APSL-1.0", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Apple_Public_Source_License_1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./APSL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/APSL-1.1.json", - "referenceNumber": "324", - "name": "Apple Public Source License 1.1", - "licenseId": "APSL-1.1", - "seeAlso": [ - "http://www.opensource.apple.com/source/IOSerialFamily/IOSerialFamily-7/APPLE_LICENSE" - ], - "isOsiApproved": true - }, - { - "reference": "./APSL-1.2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/APSL-1.2.json", - "referenceNumber": "34", - "name": "Apple Public Source License 1.2", - "licenseId": "APSL-1.2", - "seeAlso": [ - "http://www.samurajdata.se/opensource/mirror/licenses/apsl.php" - ], - "isOsiApproved": true - }, - { - "reference": "./APSL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/APSL-2.0.json", - "referenceNumber": "109", - "name": "Apple Public Source License 2.0", - "licenseId": "APSL-2.0", - "seeAlso": [ - "http://www.opensource.apple.com/license/apsl/" - ], - "isOsiApproved": true - }, - { - "reference": "./Abstyles.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Abstyles.json", - "referenceNumber": "80", - "name": "Abstyles License", - "licenseId": "Abstyles", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Abstyles" - ], - "isOsiApproved": false - }, - { - "reference": "./Adobe-2006.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Adobe-2006.json", - "referenceNumber": "285", - "name": "Adobe Systems Incorporated Source Code License Agreement", - "licenseId": "Adobe-2006", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/AdobeLicense" - ], - "isOsiApproved": false - }, - { - "reference": "./Adobe-Glyph.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Adobe-Glyph.json", - "referenceNumber": "107", - "name": "Adobe Glyph List License", - "licenseId": "Adobe-Glyph", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/MIT#AdobeGlyph" - ], - "isOsiApproved": false - }, - { - "reference": "./Afmparse.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Afmparse.json", - "referenceNumber": "42", - "name": "Afmparse License", - "licenseId": "Afmparse", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Afmparse" - ], - "isOsiApproved": false - }, - { - "reference": "./Aladdin.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Aladdin.json", - "referenceNumber": "258", - "name": "Aladdin Free Public License", - "licenseId": "Aladdin", - "seeAlso": [ - "http://pages.cs.wisc.edu/~ghost/doc/AFPL/6.01/Public.htm" - ], - "isOsiApproved": false - }, - { - "reference": "./Apache-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Apache-1.0.json", - "referenceNumber": "237", - "name": "Apache License 1.0", - "licenseId": "Apache-1.0", - "seeAlso": [ - "http://www.apache.org/licenses/LICENSE-1.0" - ], - "isOsiApproved": false - }, - { - "reference": "./Apache-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Apache-1.1.json", - "referenceNumber": "84", - "name": "Apache License 1.1", - "licenseId": "Apache-1.1", - "seeAlso": [ - "http://apache.org/licenses/LICENSE-1.1", - "https://opensource.org/licenses/Apache-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./Apache-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Apache-2.0.json", - "referenceNumber": "26", - "name": "Apache License 2.0", - "licenseId": "Apache-2.0", - "seeAlso": [ - "http://www.apache.org/licenses/LICENSE-2.0", - "https://opensource.org/licenses/Apache-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Artistic-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Artistic-1.0.json", - "referenceNumber": "165", - "name": "Artistic License 1.0", - "licenseId": "Artistic-1.0", - "seeAlso": [ - "https://opensource.org/licenses/Artistic-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Artistic-1.0-Perl.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Artistic-1.0-Perl.json", - "referenceNumber": "377", - "name": "Artistic License 1.0 (Perl)", - "licenseId": "Artistic-1.0-Perl", - "seeAlso": [ - "http://dev.perl.org/licenses/artistic.html" - ], - "isOsiApproved": true - }, - { - "reference": "./Artistic-1.0-cl8.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Artistic-1.0-cl8.json", - "referenceNumber": "13", - "name": "Artistic License 1.0 w/clause 8", - "licenseId": "Artistic-1.0-cl8", - "seeAlso": [ - "https://opensource.org/licenses/Artistic-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Artistic-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Artistic-2.0.json", - "referenceNumber": "189", - "name": "Artistic License 2.0", - "licenseId": "Artistic-2.0", - "seeAlso": [ - "http://www.perlfoundation.org/artistic_license_2_0", - "https://opensource.org/licenses/artistic-license-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./BSD-1-Clause.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-1-Clause.json", - "referenceNumber": "358", - "name": "BSD 1-Clause License", - "licenseId": "BSD-1-Clause", - "seeAlso": [ - "https://svnweb.freebsd.org/base/head/include/ifaddrs.h?revision\u003d326823" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-2-Clause.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-2-Clause.json", - "referenceNumber": "325", - "name": "BSD 2-Clause \"Simplified\" License", - "licenseId": "BSD-2-Clause", - "seeAlso": [ - "https://opensource.org/licenses/BSD-2-Clause" - ], - "isOsiApproved": true - }, - { - "reference": "./BSD-2-Clause-FreeBSD.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/BSD-2-Clause-FreeBSD.json", - "referenceNumber": "121", - "name": "BSD 2-Clause FreeBSD License", - "licenseId": "BSD-2-Clause-FreeBSD", - "seeAlso": [ - "http://www.freebsd.org/copyright/freebsd-license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-2-Clause-NetBSD.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-2-Clause-NetBSD.json", - "referenceNumber": "381", - "name": "BSD 2-Clause NetBSD License", - "licenseId": "BSD-2-Clause-NetBSD", - "seeAlso": [ - "http://www.netbsd.org/about/redistribution.html#default" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-2-Clause-Patent.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-2-Clause-Patent.json", - "referenceNumber": "169", - "name": "BSD-2-Clause Plus Patent License", - "licenseId": "BSD-2-Clause-Patent", - "seeAlso": [ - "https://opensource.org/licenses/BSDplusPatent" - ], - "isOsiApproved": true - }, - { - "reference": "./BSD-3-Clause.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause.json", - "referenceNumber": "270", - "name": "BSD 3-Clause \"New\" or \"Revised\" License", - "licenseId": "BSD-3-Clause", - "seeAlso": [ - "https://opensource.org/licenses/BSD-3-Clause" - ], - "isOsiApproved": true - }, - { - "reference": "./BSD-3-Clause-Attribution.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-Attribution.json", - "referenceNumber": "39", - "name": "BSD with attribution", - "licenseId": "BSD-3-Clause-Attribution", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/BSD_with_Attribution" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-3-Clause-Clear.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-Clear.json", - "referenceNumber": "212", - "name": "BSD 3-Clause Clear License", - "licenseId": "BSD-3-Clause-Clear", - "seeAlso": [ - "http://labs.metacarta.com/license-explanation.html#license" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-3-Clause-LBNL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-LBNL.json", - "referenceNumber": "337", - "name": "Lawrence Berkeley National Labs BSD variant license", - "licenseId": "BSD-3-Clause-LBNL", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/LBNLBSD" - ], - "isOsiApproved": true - }, - { - "reference": "./BSD-3-Clause-No-Nuclear-License.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.json", - "referenceNumber": "12", - "name": "BSD 3-Clause No Nuclear License", - "licenseId": "BSD-3-Clause-No-Nuclear-License", - "seeAlso": [ - "http://download.oracle.com/otn-pub/java/licenses/bsd.txt?AuthParam\u003d1467140197_43d516ce1776bd08a58235a7785be1cc" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-3-Clause-No-Nuclear-License-2014.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.json", - "referenceNumber": "137", - "name": "BSD 3-Clause No Nuclear License 2014", - "licenseId": "BSD-3-Clause-No-Nuclear-License-2014", - "seeAlso": [ - "https://java.net/projects/javaeetutorial/pages/BerkeleyLicense" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-3-Clause-No-Nuclear-Warranty.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.json", - "referenceNumber": "44", - "name": "BSD 3-Clause No Nuclear Warranty", - "licenseId": "BSD-3-Clause-No-Nuclear-Warranty", - "seeAlso": [ - "https://jogamp.org/git/?p\u003dgluegen.git;a\u003dblob_plain;f\u003dLICENSE.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-3-Clause-Open-MPI.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-3-Clause-Open-MPI.json", - "referenceNumber": "349", - "name": "BSD 3-Clause Open MPI variant", - "licenseId": "BSD-3-Clause-Open-MPI", - "seeAlso": [ - "https://www.open-mpi.org/community/license.php", - "http://www.netlib.org/lapack/LICENSE.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-4-Clause.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/BSD-4-Clause.json", - "referenceNumber": "162", - "name": "BSD 4-Clause \"Original\" or \"Old\" License", - "licenseId": "BSD-4-Clause", - "seeAlso": [ - "http://directory.fsf.org/wiki/License:BSD_4Clause" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-4-Clause-UC.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-4-Clause-UC.json", - "referenceNumber": "203", - "name": "BSD-4-Clause (University of California-Specific)", - "licenseId": "BSD-4-Clause-UC", - "seeAlso": [ - "http://www.freebsd.org/copyright/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-Protection.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-Protection.json", - "referenceNumber": "119", - "name": "BSD Protection License", - "licenseId": "BSD-Protection", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/BSD_Protection_License" - ], - "isOsiApproved": false - }, - { - "reference": "./BSD-Source-Code.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BSD-Source-Code.json", - "referenceNumber": "308", - "name": "BSD Source Code Attribution", - "licenseId": "BSD-Source-Code", - "seeAlso": [ - "https://github.com/robbiehanson/CocoaHTTPServer/blob/master/LICENSE.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./BSL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/BSL-1.0.json", - "referenceNumber": "224", - "name": "Boost Software License 1.0", - "licenseId": "BSL-1.0", - "seeAlso": [ - "http://www.boost.org/LICENSE_1_0.txt", - "https://opensource.org/licenses/BSL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Bahyph.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Bahyph.json", - "referenceNumber": "366", - "name": "Bahyph License", - "licenseId": "Bahyph", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Bahyph" - ], - "isOsiApproved": false - }, - { - "reference": "./Barr.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Barr.json", - "referenceNumber": "333", - "name": "Barr License", - "licenseId": "Barr", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Barr" - ], - "isOsiApproved": false - }, - { - "reference": "./Beerware.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Beerware.json", - "referenceNumber": "17", - "name": "Beerware License", - "licenseId": "Beerware", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Beerware", - "https://people.freebsd.org/~phk/" - ], - "isOsiApproved": false - }, - { - "reference": "./BitTorrent-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BitTorrent-1.0.json", - "referenceNumber": "218", - "name": "BitTorrent Open Source License v1.0", - "licenseId": "BitTorrent-1.0", - "seeAlso": [ - "http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/licenses/BitTorrent?r1\u003d1.1\u0026r2\u003d1.1.1.1\u0026diff_format\u003ds" - ], - "isOsiApproved": false - }, - { - "reference": "./BitTorrent-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/BitTorrent-1.1.json", - "referenceNumber": "179", - "name": "BitTorrent Open Source License v1.1", - "licenseId": "BitTorrent-1.1", - "seeAlso": [ - "http://directory.fsf.org/wiki/License:BitTorrentOSL1.1" - ], - "isOsiApproved": false - }, - { - "reference": "./BlueOak-1.0.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/BlueOak-1.0.0.json", - "referenceNumber": "23", - "name": "Blue Oak Model License 1.0.0", - "licenseId": "BlueOak-1.0.0", - "seeAlso": [ - "https://blueoakcouncil.org/license/1.0.0" - ], - "isOsiApproved": false - }, - { - "reference": "./Borceux.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Borceux.json", - "referenceNumber": "311", - "name": "Borceux license", - "licenseId": "Borceux", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Borceux" - ], - "isOsiApproved": false - }, - { - "reference": "./CATOSL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CATOSL-1.1.json", - "referenceNumber": "262", - "name": "Computer Associates Trusted Open Source License 1.1", - "licenseId": "CATOSL-1.1", - "seeAlso": [ - "https://opensource.org/licenses/CATOSL-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./CC-BY-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-1.0.json", - "referenceNumber": "128", - "name": "Creative Commons Attribution 1.0 Generic", - "licenseId": "CC-BY-1.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by/1.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-2.0.json", - "referenceNumber": "232", - "name": "Creative Commons Attribution 2.0 Generic", - "licenseId": "CC-BY-2.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by/2.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-2.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-2.5.json", - "referenceNumber": "129", - "name": "Creative Commons Attribution 2.5 Generic", - "licenseId": "CC-BY-2.5", - "seeAlso": [ - "https://creativecommons.org/licenses/by/2.5/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-3.0.json", - "referenceNumber": "256", - "name": "Creative Commons Attribution 3.0 Unported", - "licenseId": "CC-BY-3.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by/3.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-4.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CC-BY-4.0.json", - "referenceNumber": "330", - "name": "Creative Commons Attribution 4.0 International", - "licenseId": "CC-BY-4.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by/4.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-1.0.json", - "referenceNumber": "130", - "name": "Creative Commons Attribution Non Commercial 1.0 Generic", - "licenseId": "CC-BY-NC-1.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc/1.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-2.0.json", - "referenceNumber": "244", - "name": "Creative Commons Attribution Non Commercial 2.0 Generic", - "licenseId": "CC-BY-NC-2.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc/2.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-2.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-2.5.json", - "referenceNumber": "1", - "name": "Creative Commons Attribution Non Commercial 2.5 Generic", - "licenseId": "CC-BY-NC-2.5", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc/2.5/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-3.0.json", - "referenceNumber": "255", - "name": "Creative Commons Attribution Non Commercial 3.0 Unported", - "licenseId": "CC-BY-NC-3.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc/3.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-4.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-4.0.json", - "referenceNumber": "186", - "name": "Creative Commons Attribution Non Commercial 4.0 International", - "licenseId": "CC-BY-NC-4.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc/4.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-ND-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-1.0.json", - "referenceNumber": "59", - "name": "Creative Commons Attribution Non Commercial No Derivatives 1.0 Generic", - "licenseId": "CC-BY-NC-ND-1.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nd-nc/1.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-ND-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-2.0.json", - "referenceNumber": "36", - "name": "Creative Commons Attribution Non Commercial No Derivatives 2.0 Generic", - "licenseId": "CC-BY-NC-ND-2.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-nd/2.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-ND-2.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-2.5.json", - "referenceNumber": "158", - "name": "Creative Commons Attribution Non Commercial No Derivatives 2.5 Generic", - "licenseId": "CC-BY-NC-ND-2.5", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-nd/2.5/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-ND-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-3.0.json", - "referenceNumber": "48", - "name": "Creative Commons Attribution Non Commercial No Derivatives 3.0 Unported", - "licenseId": "CC-BY-NC-ND-3.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-nd/3.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-ND-4.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-ND-4.0.json", - "referenceNumber": "281", - "name": "Creative Commons Attribution Non Commercial No Derivatives 4.0 International", - "licenseId": "CC-BY-NC-ND-4.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-SA-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-1.0.json", - "referenceNumber": "178", - "name": "Creative Commons Attribution Non Commercial Share Alike 1.0 Generic", - "licenseId": "CC-BY-NC-SA-1.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-sa/1.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-SA-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-2.0.json", - "referenceNumber": "81", - "name": "Creative Commons Attribution Non Commercial Share Alike 2.0 Generic", - "licenseId": "CC-BY-NC-SA-2.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-sa/2.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-SA-2.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-2.5.json", - "referenceNumber": "62", - "name": "Creative Commons Attribution Non Commercial Share Alike 2.5 Generic", - "licenseId": "CC-BY-NC-SA-2.5", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-sa/2.5/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-SA-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-3.0.json", - "referenceNumber": "22", - "name": "Creative Commons Attribution Non Commercial Share Alike 3.0 Unported", - "licenseId": "CC-BY-NC-SA-3.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-sa/3.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-NC-SA-4.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-NC-SA-4.0.json", - "referenceNumber": "47", - "name": "Creative Commons Attribution Non Commercial Share Alike 4.0 International", - "licenseId": "CC-BY-NC-SA-4.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-ND-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-1.0.json", - "referenceNumber": "50", - "name": "Creative Commons Attribution No Derivatives 1.0 Generic", - "licenseId": "CC-BY-ND-1.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nd/1.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-ND-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-2.0.json", - "referenceNumber": "287", - "name": "Creative Commons Attribution No Derivatives 2.0 Generic", - "licenseId": "CC-BY-ND-2.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nd/2.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-ND-2.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-2.5.json", - "referenceNumber": "68", - "name": "Creative Commons Attribution No Derivatives 2.5 Generic", - "licenseId": "CC-BY-ND-2.5", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nd/2.5/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-ND-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-3.0.json", - "referenceNumber": "393", - "name": "Creative Commons Attribution No Derivatives 3.0 Unported", - "licenseId": "CC-BY-ND-3.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nd/3.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-ND-4.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-ND-4.0.json", - "referenceNumber": "132", - "name": "Creative Commons Attribution No Derivatives 4.0 International", - "licenseId": "CC-BY-ND-4.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-nd/4.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-SA-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-1.0.json", - "referenceNumber": "322", - "name": "Creative Commons Attribution Share Alike 1.0 Generic", - "licenseId": "CC-BY-SA-1.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-sa/1.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-SA-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-2.0.json", - "referenceNumber": "142", - "name": "Creative Commons Attribution Share Alike 2.0 Generic", - "licenseId": "CC-BY-SA-2.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-sa/2.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-SA-2.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-2.5.json", - "referenceNumber": "306", - "name": "Creative Commons Attribution Share Alike 2.5 Generic", - "licenseId": "CC-BY-SA-2.5", - "seeAlso": [ - "https://creativecommons.org/licenses/by-sa/2.5/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-SA-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-3.0.json", - "referenceNumber": "394", - "name": "Creative Commons Attribution Share Alike 3.0 Unported", - "licenseId": "CC-BY-SA-3.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-sa/3.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-BY-SA-4.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CC-BY-SA-4.0.json", - "referenceNumber": "32", - "name": "Creative Commons Attribution Share Alike 4.0 International", - "licenseId": "CC-BY-SA-4.0", - "seeAlso": [ - "https://creativecommons.org/licenses/by-sa/4.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CC-PDDC.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CC-PDDC.json", - "referenceNumber": "371", - "name": "Creative Commons Public Domain Dedication and Certification", - "licenseId": "CC-PDDC", - "seeAlso": [ - "https://creativecommons.org/licenses/publicdomain/" - ], - "isOsiApproved": false - }, - { - "reference": "./CC0-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CC0-1.0.json", - "referenceNumber": "213", - "name": "Creative Commons Zero v1.0 Universal", - "licenseId": "CC0-1.0", - "seeAlso": [ - "https://creativecommons.org/publicdomain/zero/1.0/legalcode" - ], - "isOsiApproved": false - }, - { - "reference": "./CDDL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CDDL-1.0.json", - "referenceNumber": "138", - "name": "Common Development and Distribution License 1.0", - "licenseId": "CDDL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/cddl1" - ], - "isOsiApproved": true - }, - { - "reference": "./CDDL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CDDL-1.1.json", - "referenceNumber": "376", - "name": "Common Development and Distribution License 1.1", - "licenseId": "CDDL-1.1", - "seeAlso": [ - "http://glassfish.java.net/public/CDDL+GPL_1_1.html", - "https://javaee.github.io/glassfish/LICENSE" - ], - "isOsiApproved": false - }, - { - "reference": "./CDLA-Permissive-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CDLA-Permissive-1.0.json", - "referenceNumber": "250", - "name": "Community Data License Agreement Permissive 1.0", - "licenseId": "CDLA-Permissive-1.0", - "seeAlso": [ - "https://cdla.io/permissive-1-0" - ], - "isOsiApproved": false - }, - { - "reference": "./CDLA-Sharing-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CDLA-Sharing-1.0.json", - "referenceNumber": "310", - "name": "Community Data License Agreement Sharing 1.0", - "licenseId": "CDLA-Sharing-1.0", - "seeAlso": [ - "https://cdla.io/sharing-1-0" - ], - "isOsiApproved": false - }, - { - "reference": "./CECILL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CECILL-1.0.json", - "referenceNumber": "223", - "name": "CeCILL Free Software License Agreement v1.0", - "licenseId": "CECILL-1.0", - "seeAlso": [ - "http://www.cecill.info/licences/Licence_CeCILL_V1-fr.html" - ], - "isOsiApproved": false - }, - { - "reference": "./CECILL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CECILL-1.1.json", - "referenceNumber": "300", - "name": "CeCILL Free Software License Agreement v1.1", - "licenseId": "CECILL-1.1", - "seeAlso": [ - "http://www.cecill.info/licences/Licence_CeCILL_V1.1-US.html" - ], - "isOsiApproved": false - }, - { - "reference": "./CECILL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CECILL-2.0.json", - "referenceNumber": "352", - "name": "CeCILL Free Software License Agreement v2.0", - "licenseId": "CECILL-2.0", - "seeAlso": [ - "http://www.cecill.info/licences/Licence_CeCILL_V2-en.html" - ], - "isOsiApproved": false - }, - { - "reference": "./CECILL-2.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CECILL-2.1.json", - "referenceNumber": "120", - "name": "CeCILL Free Software License Agreement v2.1", - "licenseId": "CECILL-2.1", - "seeAlso": [ - "http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.html" - ], - "isOsiApproved": true - }, - { - "reference": "./CECILL-B.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CECILL-B.json", - "referenceNumber": "340", - "name": "CeCILL-B Free Software License Agreement", - "licenseId": "CECILL-B", - "seeAlso": [ - "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html" - ], - "isOsiApproved": false - }, - { - "reference": "./CECILL-C.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CECILL-C.json", - "referenceNumber": "77", - "name": "CeCILL-C Free Software License Agreement", - "licenseId": "CECILL-C", - "seeAlso": [ - "http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html" - ], - "isOsiApproved": false - }, - { - "reference": "./CERN-OHL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CERN-OHL-1.1.json", - "referenceNumber": "341", - "name": "CERN Open Hardware License v1.1", - "licenseId": "CERN-OHL-1.1", - "seeAlso": [ - "https://www.ohwr.org/project/licenses/wikis/cern-ohl-v1.1" - ], - "isOsiApproved": false - }, - { - "reference": "./CERN-OHL-1.2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CERN-OHL-1.2.json", - "referenceNumber": "3", - "name": "CERN Open Hardware Licence v1.2", - "licenseId": "CERN-OHL-1.2", - "seeAlso": [ - "https://www.ohwr.org/project/licenses/wikis/cern-ohl-v1.2" - ], - "isOsiApproved": false - }, - { - "reference": "./CNRI-Jython.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CNRI-Jython.json", - "referenceNumber": "94", - "name": "CNRI Jython License", - "licenseId": "CNRI-Jython", - "seeAlso": [ - "http://www.jython.org/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./CNRI-Python.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CNRI-Python.json", - "referenceNumber": "45", - "name": "CNRI Python License", - "licenseId": "CNRI-Python", - "seeAlso": [ - "https://opensource.org/licenses/CNRI-Python" - ], - "isOsiApproved": true - }, - { - "reference": "./CNRI-Python-GPL-Compatible.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CNRI-Python-GPL-Compatible.json", - "referenceNumber": "202", - "name": "CNRI Python Open Source GPL Compatible License Agreement", - "licenseId": "CNRI-Python-GPL-Compatible", - "seeAlso": [ - "http://www.python.org/download/releases/1.6.1/download_win/" - ], - "isOsiApproved": false - }, - { - "reference": "./CPAL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CPAL-1.0.json", - "referenceNumber": "170", - "name": "Common Public Attribution License 1.0", - "licenseId": "CPAL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/CPAL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./CPL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/CPL-1.0.json", - "referenceNumber": "172", - "name": "Common Public License 1.0", - "licenseId": "CPL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/CPL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./CPOL-1.02.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CPOL-1.02.json", - "referenceNumber": "28", - "name": "Code Project Open License 1.02", - "licenseId": "CPOL-1.02", - "seeAlso": [ - "http://www.codeproject.com/info/cpol10.aspx" - ], - "isOsiApproved": false - }, - { - "reference": "./CUA-OPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CUA-OPL-1.0.json", - "referenceNumber": "365", - "name": "CUA Office Public License v1.0", - "licenseId": "CUA-OPL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/CUA-OPL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Caldera.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Caldera.json", - "referenceNumber": "108", - "name": "Caldera License", - "licenseId": "Caldera", - "seeAlso": [ - "http://www.lemis.com/grog/UNIX/ancient-source-all.pdf" - ], - "isOsiApproved": false - }, - { - "reference": "./ClArtistic.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/ClArtistic.json", - "referenceNumber": "271", - "name": "Clarified Artistic License", - "licenseId": "ClArtistic", - "seeAlso": [ - "http://gianluca.dellavedova.org/2011/01/03/clarified-artistic-license/", - "http://www.ncftp.com/ncftp/doc/LICENSE.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./Condor-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Condor-1.1.json", - "referenceNumber": "307", - "name": "Condor Public License v1.1", - "licenseId": "Condor-1.1", - "seeAlso": [ - "http://research.cs.wisc.edu/condor/license.html#condor", - "http://web.archive.org/web/20111123062036/http://research.cs.wisc.edu/condor/license.html#condor" - ], - "isOsiApproved": false - }, - { - "reference": "./Crossword.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Crossword.json", - "referenceNumber": "363", - "name": "Crossword License", - "licenseId": "Crossword", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Crossword" - ], - "isOsiApproved": false - }, - { - "reference": "./CrystalStacker.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/CrystalStacker.json", - "referenceNumber": "168", - "name": "CrystalStacker License", - "licenseId": "CrystalStacker", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing:CrystalStacker?rd\u003dLicensing/CrystalStacker" - ], - "isOsiApproved": false - }, - { - "reference": "./Cube.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Cube.json", - "referenceNumber": "370", - "name": "Cube License", - "licenseId": "Cube", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Cube" - ], - "isOsiApproved": false - }, - { - "reference": "./D-FSL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/D-FSL-1.0.json", - "referenceNumber": "182", - "name": "Deutsche Freie Software Lizenz", - "licenseId": "D-FSL-1.0", - "seeAlso": [ - "http://www.dipp.nrw.de/d-fsl/lizenzen/", - "http://www.dipp.nrw.de/d-fsl/index_html/lizenzen/de/D-FSL-1_0_de.txt", - "http://www.dipp.nrw.de/d-fsl/index_html/lizenzen/en/D-FSL-1_0_en.txt", - "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl", - "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/deutsche-freie-software-lizenz", - "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/german-free-software-license", - "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/D-FSL-1_0_de.txt/at_download/file", - "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/D-FSL-1_0_en.txt/at_download/file" - ], - "isOsiApproved": false - }, - { - "reference": "./DOC.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/DOC.json", - "referenceNumber": "160", - "name": "DOC License", - "licenseId": "DOC", - "seeAlso": [ - "http://www.cs.wustl.edu/~schmidt/ACE-copying.html" - ], - "isOsiApproved": false - }, - { - "reference": "./DSDP.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/DSDP.json", - "referenceNumber": "141", - "name": "DSDP License", - "licenseId": "DSDP", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/DSDP" - ], - "isOsiApproved": false - }, - { - "reference": "./Dotseqn.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Dotseqn.json", - "referenceNumber": "390", - "name": "Dotseqn License", - "licenseId": "Dotseqn", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Dotseqn" - ], - "isOsiApproved": false - }, - { - "reference": "./ECL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/ECL-1.0.json", - "referenceNumber": "396", - "name": "Educational Community License v1.0", - "licenseId": "ECL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/ECL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./ECL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/ECL-2.0.json", - "referenceNumber": "298", - "name": "Educational Community License v2.0", - "licenseId": "ECL-2.0", - "seeAlso": [ - "https://opensource.org/licenses/ECL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./EFL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/EFL-1.0.json", - "referenceNumber": "150", - "name": "Eiffel Forum License v1.0", - "licenseId": "EFL-1.0", - "seeAlso": [ - "http://www.eiffel-nice.org/license/forum.txt", - "https://opensource.org/licenses/EFL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./EFL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/EFL-2.0.json", - "referenceNumber": "161", - "name": "Eiffel Forum License v2.0", - "licenseId": "EFL-2.0", - "seeAlso": [ - "http://www.eiffel-nice.org/license/eiffel-forum-license-2.html", - "https://opensource.org/licenses/EFL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./EPL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/EPL-1.0.json", - "referenceNumber": "214", - "name": "Eclipse Public License 1.0", - "licenseId": "EPL-1.0", - "seeAlso": [ - "http://www.eclipse.org/legal/epl-v10.html", - "https://opensource.org/licenses/EPL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./EPL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/EPL-2.0.json", - "referenceNumber": "134", - "name": "Eclipse Public License 2.0", - "licenseId": "EPL-2.0", - "seeAlso": [ - "https://www.eclipse.org/legal/epl-2.0", - "https://www.opensource.org/licenses/EPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./EUDatagrid.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/EUDatagrid.json", - "referenceNumber": "192", - "name": "EU DataGrid Software License", - "licenseId": "EUDatagrid", - "seeAlso": [ - "http://eu-datagrid.web.cern.ch/eu-datagrid/license.html", - "https://opensource.org/licenses/EUDatagrid" - ], - "isOsiApproved": true - }, - { - "reference": "./EUPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/EUPL-1.0.json", - "referenceNumber": "173", - "name": "European Union Public License 1.0", - "licenseId": "EUPL-1.0", - "seeAlso": [ - "http://ec.europa.eu/idabc/en/document/7330.html", - "http://ec.europa.eu/idabc/servlets/Doc027f.pdf?id\u003d31096" - ], - "isOsiApproved": false - }, - { - "reference": "./EUPL-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/EUPL-1.1.json", - "referenceNumber": "92", - "name": "European Union Public License 1.1", - "licenseId": "EUPL-1.1", - "seeAlso": [ - "https://joinup.ec.europa.eu/software/page/eupl/licence-eupl", - "https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl1.1.-licence-en_0.pdf", - "https://opensource.org/licenses/EUPL-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./EUPL-1.2.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/EUPL-1.2.json", - "referenceNumber": "387", - "name": "European Union Public License 1.2", - "licenseId": "EUPL-1.2", - "seeAlso": [ - "https://joinup.ec.europa.eu/page/eupl-text-11-12", - "https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl_v1.2_en.pdf", - "https://joinup.ec.europa.eu/sites/default/files/inline-files/EUPL%20v1_2%20EN(1).txt", - "http://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri\u003dCELEX:32017D0863", - "https://opensource.org/licenses/EUPL-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./Entessa.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Entessa.json", - "referenceNumber": "99", - "name": "Entessa Public License v1.0", - "licenseId": "Entessa", - "seeAlso": [ - "https://opensource.org/licenses/Entessa" - ], - "isOsiApproved": true - }, - { - "reference": "./ErlPL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/ErlPL-1.1.json", - "referenceNumber": "157", - "name": "Erlang Public License v1.1", - "licenseId": "ErlPL-1.1", - "seeAlso": [ - "http://www.erlang.org/EPLICENSE" - ], - "isOsiApproved": false - }, - { - "reference": "./Eurosym.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Eurosym.json", - "referenceNumber": "113", - "name": "Eurosym License", - "licenseId": "Eurosym", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Eurosym" - ], - "isOsiApproved": false - }, - { - "reference": "./FSFAP.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/FSFAP.json", - "referenceNumber": "114", - "name": "FSF All Permissive License", - "licenseId": "FSFAP", - "seeAlso": [ - "https://www.gnu.org/prep/maintain/html_node/License-Notices-for-Other-Files.html" - ], - "isOsiApproved": false - }, - { - "reference": "./FSFUL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/FSFUL.json", - "referenceNumber": "193", - "name": "FSF Unlimited License", - "licenseId": "FSFUL", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/FSF_Unlimited_License" - ], - "isOsiApproved": false - }, - { - "reference": "./FSFULLR.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/FSFULLR.json", - "referenceNumber": "43", - "name": "FSF Unlimited License (with License Retention)", - "licenseId": "FSFULLR", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/FSF_Unlimited_License#License_Retention_Variant" - ], - "isOsiApproved": false - }, - { - "reference": "./FTL.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/FTL.json", - "referenceNumber": "240", - "name": "Freetype Project License", - "licenseId": "FTL", - "seeAlso": [ - "http://freetype.fis.uniroma2.it/FTL.TXT", - "http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT" - ], - "isOsiApproved": false - }, - { - "reference": "./Fair.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Fair.json", - "referenceNumber": "297", - "name": "Fair License", - "licenseId": "Fair", - "seeAlso": [ - "http://fairlicense.org/", - "https://opensource.org/licenses/Fair" - ], - "isOsiApproved": true - }, - { - "reference": "./Frameworx-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Frameworx-1.0.json", - "referenceNumber": "389", - "name": "Frameworx Open License 1.0", - "licenseId": "Frameworx-1.0", - "seeAlso": [ - "https://opensource.org/licenses/Frameworx-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./FreeImage.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/FreeImage.json", - "referenceNumber": "277", - "name": "FreeImage Public License v1.0", - "licenseId": "FreeImage", - "seeAlso": [ - "http://freeimage.sourceforge.net/freeimage-license.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.1.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.1.json", - "referenceNumber": "98", - "name": "GNU Free Documentation License v1.1", - "licenseId": "GFDL-1.1", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.1-only.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.1-only.json", - "referenceNumber": "102", - "name": "GNU Free Documentation License v1.1 only", - "licenseId": "GFDL-1.1-only", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.1-or-later.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.1-or-later.json", - "referenceNumber": "348", - "name": "GNU Free Documentation License v1.1 or later", - "licenseId": "GFDL-1.1-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.2.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.2.json", - "referenceNumber": "197", - "name": "GNU Free Documentation License v1.2", - "licenseId": "GFDL-1.2", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.2-only.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.2-only.json", - "referenceNumber": "236", - "name": "GNU Free Documentation License v1.2 only", - "licenseId": "GFDL-1.2-only", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.2-or-later.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.2-or-later.json", - "referenceNumber": "215", - "name": "GNU Free Documentation License v1.2 or later", - "licenseId": "GFDL-1.2-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.3.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.3.json", - "referenceNumber": "112", - "name": "GNU Free Documentation License v1.3", - "licenseId": "GFDL-1.3", - "seeAlso": [ - "https://www.gnu.org/licenses/fdl-1.3.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.3-only.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.3-only.json", - "referenceNumber": "69", - "name": "GNU Free Documentation License v1.3 only", - "licenseId": "GFDL-1.3-only", - "seeAlso": [ - "https://www.gnu.org/licenses/fdl-1.3.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GFDL-1.3-or-later.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GFDL-1.3-or-later.json", - "referenceNumber": "4", - "name": "GNU Free Documentation License v1.3 or later", - "licenseId": "GFDL-1.3-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/fdl-1.3.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./GL2PS.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/GL2PS.json", - "referenceNumber": "124", - "name": "GL2PS License", - "licenseId": "GL2PS", - "seeAlso": [ - "http://www.geuz.org/gl2ps/COPYING.GL2PS" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-1.0.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-1.0.json", - "referenceNumber": "79", - "name": "GNU General Public License v1.0 only", - "licenseId": "GPL-1.0", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-1.0+.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-1.0+.json", - "referenceNumber": "175", - "name": "GNU General Public License v1.0 or later", - "licenseId": "GPL-1.0+", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-1.0-only.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/GPL-1.0-only.json", - "referenceNumber": "15", - "name": "GNU General Public License v1.0 only", - "licenseId": "GPL-1.0-only", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-1.0-or-later.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/GPL-1.0-or-later.json", - "referenceNumber": "357", - "name": "GNU General Public License v1.0 or later", - "licenseId": "GPL-1.0-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-2.0.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0.json", - "referenceNumber": "147", - "name": "GNU General Public License v2.0 only", - "licenseId": "GPL-2.0", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", - "https://opensource.org/licenses/GPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-2.0+.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0+.json", - "referenceNumber": "75", - "name": "GNU General Public License v2.0 or later", - "licenseId": "GPL-2.0+", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", - "https://opensource.org/licenses/GPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-2.0-only.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0-only.json", - "referenceNumber": "233", - "name": "GNU General Public License v2.0 only", - "licenseId": "GPL-2.0-only", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", - "https://opensource.org/licenses/GPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-2.0-or-later.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0-or-later.json", - "referenceNumber": "56", - "name": "GNU General Public License v2.0 or later", - "licenseId": "GPL-2.0-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", - "https://opensource.org/licenses/GPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-2.0-with-GCC-exception.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-GCC-exception.json", - "referenceNumber": "117", - "name": "GNU General Public License v2.0 w/GCC Runtime Library exception", - "licenseId": "GPL-2.0-with-GCC-exception", - "seeAlso": [ - "https://gcc.gnu.org/git/?p\u003dgcc.git;a\u003dblob;f\u003dgcc/libgcc1.c;h\u003d762f5143fc6eed57b6797c82710f3538aa52b40b;hb\u003dcb143a3ce4fb417c68f5fa2691a1b1b1053dfba9#l10" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-2.0-with-autoconf-exception.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-autoconf-exception.json", - "referenceNumber": "355", - "name": "GNU General Public License v2.0 w/Autoconf exception", - "licenseId": "GPL-2.0-with-autoconf-exception", - "seeAlso": [ - "http://ac-archive.sourceforge.net/doc/copyright.html" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-2.0-with-bison-exception.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-bison-exception.json", - "referenceNumber": "378", - "name": "GNU General Public License v2.0 w/Bison exception", - "licenseId": "GPL-2.0-with-bison-exception", - "seeAlso": [ - "http://git.savannah.gnu.org/cgit/bison.git/tree/data/yacc.c?id\u003d193d7c7054ba7197b0789e14965b739162319b5e#n141" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-2.0-with-classpath-exception.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-classpath-exception.json", - "referenceNumber": "60", - "name": "GNU General Public License v2.0 w/Classpath exception", - "licenseId": "GPL-2.0-with-classpath-exception", - "seeAlso": [ - "https://www.gnu.org/software/classpath/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-2.0-with-font-exception.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-2.0-with-font-exception.json", - "referenceNumber": "375", - "name": "GNU General Public License v2.0 w/Font exception", - "licenseId": "GPL-2.0-with-font-exception", - "seeAlso": [ - "https://www.gnu.org/licenses/gpl-faq.html#FontException" - ], - "isOsiApproved": false - }, - { - "reference": "./GPL-3.0.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GPL-3.0.json", - "referenceNumber": "242", - "name": "GNU General Public License v3.0 only", - "licenseId": "GPL-3.0", - "seeAlso": [ - "https://www.gnu.org/licenses/gpl-3.0-standalone.html", - "https://opensource.org/licenses/GPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-3.0+.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GPL-3.0+.json", - "referenceNumber": "73", - "name": "GNU General Public License v3.0 or later", - "licenseId": "GPL-3.0+", - "seeAlso": [ - "https://www.gnu.org/licenses/gpl-3.0-standalone.html", - "https://opensource.org/licenses/GPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-3.0-only.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GPL-3.0-only.json", - "referenceNumber": "206", - "name": "GNU General Public License v3.0 only", - "licenseId": "GPL-3.0-only", - "seeAlso": [ - "https://www.gnu.org/licenses/gpl-3.0-standalone.html", - "https://opensource.org/licenses/GPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-3.0-or-later.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/GPL-3.0-or-later.json", - "referenceNumber": "196", - "name": "GNU General Public License v3.0 or later", - "licenseId": "GPL-3.0-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/gpl-3.0-standalone.html", - "https://opensource.org/licenses/GPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-3.0-with-GCC-exception.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-3.0-with-GCC-exception.json", - "referenceNumber": "221", - "name": "GNU General Public License v3.0 w/GCC Runtime Library exception", - "licenseId": "GPL-3.0-with-GCC-exception", - "seeAlso": [ - "https://www.gnu.org/licenses/gcc-exception-3.1.html" - ], - "isOsiApproved": true - }, - { - "reference": "./GPL-3.0-with-autoconf-exception.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/GPL-3.0-with-autoconf-exception.json", - "referenceNumber": "235", - "name": "GNU General Public License v3.0 w/Autoconf exception", - "licenseId": "GPL-3.0-with-autoconf-exception", - "seeAlso": [ - "https://www.gnu.org/licenses/autoconf-exception-3.0.html" - ], - "isOsiApproved": false - }, - { - "reference": "./Giftware.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Giftware.json", - "referenceNumber": "369", - "name": "Giftware License", - "licenseId": "Giftware", - "seeAlso": [ - "http://liballeg.org/license.html#allegro-4-the-giftware-license" - ], - "isOsiApproved": false - }, - { - "reference": "./Glide.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Glide.json", - "referenceNumber": "374", - "name": "3dfx Glide License", - "licenseId": "Glide", - "seeAlso": [ - "http://www.users.on.net/~triforce/glidexp/COPYING.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./Glulxe.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Glulxe.json", - "referenceNumber": "93", - "name": "Glulxe License", - "licenseId": "Glulxe", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Glulxe" - ], - "isOsiApproved": false - }, - { - "reference": "./HPND.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/HPND.json", - "referenceNumber": "264", - "name": "Historical Permission Notice and Disclaimer", - "licenseId": "HPND", - "seeAlso": [ - "https://opensource.org/licenses/HPND" - ], - "isOsiApproved": true - }, - { - "reference": "./HPND-sell-variant.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/HPND-sell-variant.json", - "referenceNumber": "145", - "name": "Historical Permission Notice and Disclaimer - sell variant", - "licenseId": "HPND-sell-variant", - "seeAlso": [ - "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/sunrpc/auth_gss/gss_generic_token.c?h\u003dv4.19" - ], - "isOsiApproved": false - }, - { - "reference": "./HaskellReport.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/HaskellReport.json", - "referenceNumber": "122", - "name": "Haskell Language Report License", - "licenseId": "HaskellReport", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Haskell_Language_Report_License" - ], - "isOsiApproved": false - }, - { - "reference": "./IBM-pibs.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/IBM-pibs.json", - "referenceNumber": "207", - "name": "IBM PowerPC Initialization and Boot Software", - "licenseId": "IBM-pibs", - "seeAlso": [ - "http://git.denx.de/?p\u003du-boot.git;a\u003dblob;f\u003darch/powerpc/cpu/ppc4xx/miiphy.c;h\u003d297155fdafa064b955e53e9832de93bfb0cfb85b;hb\u003d9fab4bf4cc077c21e43941866f3f2c196f28670d" - ], - "isOsiApproved": false - }, - { - "reference": "./ICU.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/ICU.json", - "referenceNumber": "194", - "name": "ICU License", - "licenseId": "ICU", - "seeAlso": [ - "http://source.icu-project.org/repos/icu/icu/trunk/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./IJG.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/IJG.json", - "referenceNumber": "55", - "name": "Independent JPEG Group License", - "licenseId": "IJG", - "seeAlso": [ - "http://dev.w3.org/cvsweb/Amaya/libjpeg/Attic/README?rev\u003d1.2" - ], - "isOsiApproved": false - }, - { - "reference": "./IPA.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/IPA.json", - "referenceNumber": "312", - "name": "IPA Font License", - "licenseId": "IPA", - "seeAlso": [ - "https://opensource.org/licenses/IPA" - ], - "isOsiApproved": true - }, - { - "reference": "./IPL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/IPL-1.0.json", - "referenceNumber": "31", - "name": "IBM Public License v1.0", - "licenseId": "IPL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/IPL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./ISC.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/ISC.json", - "referenceNumber": "110", - "name": "ISC License", - "licenseId": "ISC", - "seeAlso": [ - "https://www.isc.org/downloads/software-support-policy/isc-license/", - "https://opensource.org/licenses/ISC" - ], - "isOsiApproved": true - }, - { - "reference": "./ImageMagick.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/ImageMagick.json", - "referenceNumber": "231", - "name": "ImageMagick License", - "licenseId": "ImageMagick", - "seeAlso": [ - "http://www.imagemagick.org/script/license.php" - ], - "isOsiApproved": false - }, - { - "reference": "./Imlib2.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Imlib2.json", - "referenceNumber": "257", - "name": "Imlib2 License", - "licenseId": "Imlib2", - "seeAlso": [ - "http://trac.enlightenment.org/e/browser/trunk/imlib2/COPYING", - "https://git.enlightenment.org/legacy/imlib2.git/tree/COPYING" - ], - "isOsiApproved": false - }, - { - "reference": "./Info-ZIP.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Info-ZIP.json", - "referenceNumber": "104", - "name": "Info-ZIP License", - "licenseId": "Info-ZIP", - "seeAlso": [ - "http://www.info-zip.org/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./Intel.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Intel.json", - "referenceNumber": "167", - "name": "Intel Open Source License", - "licenseId": "Intel", - "seeAlso": [ - "https://opensource.org/licenses/Intel" - ], - "isOsiApproved": true - }, - { - "reference": "./Intel-ACPI.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Intel-ACPI.json", - "referenceNumber": "88", - "name": "Intel ACPI Software License Agreement", - "licenseId": "Intel-ACPI", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Intel_ACPI_Software_License_Agreement" - ], - "isOsiApproved": false - }, - { - "reference": "./Interbase-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Interbase-1.0.json", - "referenceNumber": "83", - "name": "Interbase Public License v1.0", - "licenseId": "Interbase-1.0", - "seeAlso": [ - "https://web.archive.org/web/20060319014854/http://info.borland.com/devsupport/interbase/opensource/IPL.html" - ], - "isOsiApproved": false - }, - { - "reference": "./JPNIC.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/JPNIC.json", - "referenceNumber": "105", - "name": "Japan Network Information Center License", - "licenseId": "JPNIC", - "seeAlso": [ - "https://gitlab.isc.org/isc-projects/bind9/blob/master/COPYRIGHT#L366" - ], - "isOsiApproved": false - }, - { - "reference": "./JSON.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/JSON.json", - "referenceNumber": "372", - "name": "JSON License", - "licenseId": "JSON", - "seeAlso": [ - "http://www.json.org/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./JasPer-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/JasPer-2.0.json", - "referenceNumber": "239", - "name": "JasPer License", - "licenseId": "JasPer-2.0", - "seeAlso": [ - "http://www.ece.uvic.ca/~mdadams/jasper/LICENSE" - ], - "isOsiApproved": false - }, - { - "reference": "./LAL-1.2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LAL-1.2.json", - "referenceNumber": "380", - "name": "Licence Art Libre 1.2", - "licenseId": "LAL-1.2", - "seeAlso": [ - "http://artlibre.org/licence/lal/licence-art-libre-12/" - ], - "isOsiApproved": false - }, - { - "reference": "./LAL-1.3.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LAL-1.3.json", - "referenceNumber": "156", - "name": "Licence Art Libre 1.3", - "licenseId": "LAL-1.3", - "seeAlso": [ - "http://artlibre.org/" - ], - "isOsiApproved": false - }, - { - "reference": "./LGPL-2.0.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-2.0.json", - "referenceNumber": "268", - "name": "GNU Library General Public License v2 only", - "licenseId": "LGPL-2.0", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-2.0+.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-2.0+.json", - "referenceNumber": "52", - "name": "GNU Library General Public License v2 or later", - "licenseId": "LGPL-2.0+", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-2.0-only.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LGPL-2.0-only.json", - "referenceNumber": "276", - "name": "GNU Library General Public License v2 only", - "licenseId": "LGPL-2.0-only", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-2.0-or-later.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LGPL-2.0-or-later.json", - "referenceNumber": "217", - "name": "GNU Library General Public License v2 or later", - "licenseId": "LGPL-2.0-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-2.1.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-2.1.json", - "referenceNumber": "166", - "name": "GNU Lesser General Public License v2.1 only", - "licenseId": "LGPL-2.1", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", - "https://opensource.org/licenses/LGPL-2.1" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-2.1+.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-2.1+.json", - "referenceNumber": "64", - "name": "GNU Library General Public License v2.1 or later", - "licenseId": "LGPL-2.1+", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", - "https://opensource.org/licenses/LGPL-2.1" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-2.1-only.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-2.1-only.json", - "referenceNumber": "2", - "name": "GNU Lesser General Public License v2.1 only", - "licenseId": "LGPL-2.1-only", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", - "https://opensource.org/licenses/LGPL-2.1" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-2.1-or-later.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-2.1-or-later.json", - "referenceNumber": "338", - "name": "GNU Lesser General Public License v2.1 or later", - "licenseId": "LGPL-2.1-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", - "https://opensource.org/licenses/LGPL-2.1" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-3.0.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-3.0.json", - "referenceNumber": "210", - "name": "GNU Lesser General Public License v3.0 only", - "licenseId": "LGPL-3.0", - "seeAlso": [ - "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", - "https://opensource.org/licenses/LGPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-3.0+.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-3.0+.json", - "referenceNumber": "152", - "name": "GNU Lesser General Public License v3.0 or later", - "licenseId": "LGPL-3.0+", - "seeAlso": [ - "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", - "https://opensource.org/licenses/LGPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-3.0-only.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-3.0-only.json", - "referenceNumber": "254", - "name": "GNU Lesser General Public License v3.0 only", - "licenseId": "LGPL-3.0-only", - "seeAlso": [ - "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", - "https://opensource.org/licenses/LGPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPL-3.0-or-later.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LGPL-3.0-or-later.json", - "referenceNumber": "301", - "name": "GNU Lesser General Public License v3.0 or later", - "licenseId": "LGPL-3.0-or-later", - "seeAlso": [ - "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", - "https://opensource.org/licenses/LGPL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./LGPLLR.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LGPLLR.json", - "referenceNumber": "103", - "name": "Lesser General Public License For Linguistic Resources", - "licenseId": "LGPLLR", - "seeAlso": [ - "http://www-igm.univ-mlv.fr/~unitex/lgpllr.html" - ], - "isOsiApproved": false - }, - { - "reference": "./LPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LPL-1.0.json", - "referenceNumber": "89", - "name": "Lucent Public License Version 1.0", - "licenseId": "LPL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/LPL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./LPL-1.02.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LPL-1.02.json", - "referenceNumber": "131", - "name": "Lucent Public License v1.02", - "licenseId": "LPL-1.02", - "seeAlso": [ - "http://plan9.bell-labs.com/plan9/license.html", - "https://opensource.org/licenses/LPL-1.02" - ], - "isOsiApproved": true - }, - { - "reference": "./LPPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LPPL-1.0.json", - "referenceNumber": "259", - "name": "LaTeX Project Public License v1.0", - "licenseId": "LPPL-1.0", - "seeAlso": [ - "http://www.latex-project.org/lppl/lppl-1-0.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./LPPL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LPPL-1.1.json", - "referenceNumber": "309", - "name": "LaTeX Project Public License v1.1", - "licenseId": "LPPL-1.1", - "seeAlso": [ - "http://www.latex-project.org/lppl/lppl-1-1.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./LPPL-1.2.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LPPL-1.2.json", - "referenceNumber": "392", - "name": "LaTeX Project Public License v1.2", - "licenseId": "LPPL-1.2", - "seeAlso": [ - "http://www.latex-project.org/lppl/lppl-1-2.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./LPPL-1.3a.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/LPPL-1.3a.json", - "referenceNumber": "305", - "name": "LaTeX Project Public License v1.3a", - "licenseId": "LPPL-1.3a", - "seeAlso": [ - "http://www.latex-project.org/lppl/lppl-1-3a.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./LPPL-1.3c.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LPPL-1.3c.json", - "referenceNumber": "326", - "name": "LaTeX Project Public License v1.3c", - "licenseId": "LPPL-1.3c", - "seeAlso": [ - "http://www.latex-project.org/lppl/lppl-1-3c.txt", - "https://opensource.org/licenses/LPPL-1.3c" - ], - "isOsiApproved": true - }, - { - "reference": "./Latex2e.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Latex2e.json", - "referenceNumber": "283", - "name": "Latex2e License", - "licenseId": "Latex2e", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Latex2e" - ], - "isOsiApproved": false - }, - { - "reference": "./Leptonica.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Leptonica.json", - "referenceNumber": "159", - "name": "Leptonica License", - "licenseId": "Leptonica", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Leptonica" - ], - "isOsiApproved": false - }, - { - "reference": "./LiLiQ-P-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LiLiQ-P-1.1.json", - "referenceNumber": "379", - "name": "Licence Libre du Québec – Permissive version 1.1", - "licenseId": "LiLiQ-P-1.1", - "seeAlso": [ - "https://forge.gouv.qc.ca/licence/fr/liliq-v1-1/", - "http://opensource.org/licenses/LiLiQ-P-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./LiLiQ-R-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LiLiQ-R-1.1.json", - "referenceNumber": "286", - "name": "Licence Libre du Québec – Réciprocité version 1.1", - "licenseId": "LiLiQ-R-1.1", - "seeAlso": [ - "https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-en-francais/licence-libre-du-quebec-reciprocite-liliq-r-v1-1/", - "http://opensource.org/licenses/LiLiQ-R-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./LiLiQ-Rplus-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/LiLiQ-Rplus-1.1.json", - "referenceNumber": "139", - "name": "Licence Libre du Québec – Réciprocité forte version 1.1", - "licenseId": "LiLiQ-Rplus-1.1", - "seeAlso": [ - "https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-en-francais/licence-libre-du-quebec-reciprocite-forte-liliq-r-v1-1/", - "http://opensource.org/licenses/LiLiQ-Rplus-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./Libpng.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Libpng.json", - "referenceNumber": "101", - "name": "libpng License", - "licenseId": "Libpng", - "seeAlso": [ - "http://www.libpng.org/pub/png/src/libpng-LICENSE.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./Linux-OpenIB.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Linux-OpenIB.json", - "referenceNumber": "5", - "name": "Linux Kernel Variant of OpenIB.org license", - "licenseId": "Linux-OpenIB", - "seeAlso": [ - "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/sa.h" - ], - "isOsiApproved": false - }, - { - "reference": "./MIT.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/MIT.json", - "referenceNumber": "201", - "name": "MIT License", - "licenseId": "MIT", - "seeAlso": [ - "https://opensource.org/licenses/MIT" - ], - "isOsiApproved": true - }, - { - "reference": "./MIT-0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MIT-0.json", - "referenceNumber": "6", - "name": "MIT No Attribution", - "licenseId": "MIT-0", - "seeAlso": [ - "https://github.com/aws/mit-0", - "https://romanrm.net/mit-zero", - "https://github.com/awsdocs/aws-cloud9-user-guide/blob/master/LICENSE-SAMPLECODE" - ], - "isOsiApproved": true - }, - { - "reference": "./MIT-CMU.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MIT-CMU.json", - "referenceNumber": "9", - "name": "CMU License", - "licenseId": "MIT-CMU", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing:MIT?rd\u003dLicensing/MIT#CMU_Style", - "https://github.com/python-pillow/Pillow/blob/fffb426092c8db24a5f4b6df243a8a3c01fb63cd/LICENSE" - ], - "isOsiApproved": false - }, - { - "reference": "./MIT-advertising.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MIT-advertising.json", - "referenceNumber": "8", - "name": "Enlightenment License (e16)", - "licenseId": "MIT-advertising", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/MIT_With_Advertising" - ], - "isOsiApproved": false - }, - { - "reference": "./MIT-enna.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MIT-enna.json", - "referenceNumber": "25", - "name": "enna License", - "licenseId": "MIT-enna", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/MIT#enna" - ], - "isOsiApproved": false - }, - { - "reference": "./MIT-feh.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MIT-feh.json", - "referenceNumber": "38", - "name": "feh License", - "licenseId": "MIT-feh", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/MIT#feh" - ], - "isOsiApproved": false - }, - { - "reference": "./MITNFA.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MITNFA.json", - "referenceNumber": "294", - "name": "MIT +no-false-attribs license", - "licenseId": "MITNFA", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/MITNFA" - ], - "isOsiApproved": false - }, - { - "reference": "./MPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MPL-1.0.json", - "referenceNumber": "49", - "name": "Mozilla Public License 1.0", - "licenseId": "MPL-1.0", - "seeAlso": [ - "http://www.mozilla.org/MPL/MPL-1.0.html", - "https://opensource.org/licenses/MPL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./MPL-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/MPL-1.1.json", - "referenceNumber": "304", - "name": "Mozilla Public License 1.1", - "licenseId": "MPL-1.1", - "seeAlso": [ - "http://www.mozilla.org/MPL/MPL-1.1.html", - "https://opensource.org/licenses/MPL-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./MPL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/MPL-2.0.json", - "referenceNumber": "234", - "name": "Mozilla Public License 2.0", - "licenseId": "MPL-2.0", - "seeAlso": [ - "http://www.mozilla.org/MPL/2.0/", - "https://opensource.org/licenses/MPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./MPL-2.0-no-copyleft-exception.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MPL-2.0-no-copyleft-exception.json", - "referenceNumber": "303", - "name": "Mozilla Public License 2.0 (no copyleft exception)", - "licenseId": "MPL-2.0-no-copyleft-exception", - "seeAlso": [ - "http://www.mozilla.org/MPL/2.0/", - "https://opensource.org/licenses/MPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./MS-PL.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/MS-PL.json", - "referenceNumber": "336", - "name": "Microsoft Public License", - "licenseId": "MS-PL", - "seeAlso": [ - "http://www.microsoft.com/opensource/licenses.mspx", - "https://opensource.org/licenses/MS-PL" - ], - "isOsiApproved": true - }, - { - "reference": "./MS-RL.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/MS-RL.json", - "referenceNumber": "280", - "name": "Microsoft Reciprocal License", - "licenseId": "MS-RL", - "seeAlso": [ - "http://www.microsoft.com/opensource/licenses.mspx", - "https://opensource.org/licenses/MS-RL" - ], - "isOsiApproved": true - }, - { - "reference": "./MTLL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MTLL.json", - "referenceNumber": "181", - "name": "Matrix Template Library License", - "licenseId": "MTLL", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Matrix_Template_Library_License" - ], - "isOsiApproved": false - }, - { - "reference": "./MakeIndex.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MakeIndex.json", - "referenceNumber": "187", - "name": "MakeIndex License", - "licenseId": "MakeIndex", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/MakeIndex" - ], - "isOsiApproved": false - }, - { - "reference": "./MirOS.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/MirOS.json", - "referenceNumber": "299", - "name": "MirOS License", - "licenseId": "MirOS", - "seeAlso": [ - "https://opensource.org/licenses/MirOS" - ], - "isOsiApproved": true - }, - { - "reference": "./Motosoto.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Motosoto.json", - "referenceNumber": "317", - "name": "Motosoto License", - "licenseId": "Motosoto", - "seeAlso": [ - "https://opensource.org/licenses/Motosoto" - ], - "isOsiApproved": true - }, - { - "reference": "./Multics.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Multics.json", - "referenceNumber": "63", - "name": "Multics License", - "licenseId": "Multics", - "seeAlso": [ - "https://opensource.org/licenses/Multics" - ], - "isOsiApproved": true - }, - { - "reference": "./Mup.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Mup.json", - "referenceNumber": "353", - "name": "Mup License", - "licenseId": "Mup", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Mup" - ], - "isOsiApproved": false - }, - { - "reference": "./NASA-1.3.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NASA-1.3.json", - "referenceNumber": "87", - "name": "NASA Open Source Agreement 1.3", - "licenseId": "NASA-1.3", - "seeAlso": [ - "http://ti.arc.nasa.gov/opensource/nosa/", - "https://opensource.org/licenses/NASA-1.3" - ], - "isOsiApproved": true - }, - { - "reference": "./NBPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NBPL-1.0.json", - "referenceNumber": "361", - "name": "Net Boolean Public License v1", - "licenseId": "NBPL-1.0", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d37b4b3f6cc4bf34e1d3dec61e69914b9819d8894" - ], - "isOsiApproved": false - }, - { - "reference": "./NCSA.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/NCSA.json", - "referenceNumber": "58", - "name": "University of Illinois/NCSA Open Source License", - "licenseId": "NCSA", - "seeAlso": [ - "http://otm.illinois.edu/uiuc_openSource", - "https://opensource.org/licenses/NCSA" - ], - "isOsiApproved": true - }, - { - "reference": "./NGPL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NGPL.json", - "referenceNumber": "71", - "name": "Nethack General Public License", - "licenseId": "NGPL", - "seeAlso": [ - "https://opensource.org/licenses/NGPL" - ], - "isOsiApproved": true - }, - { - "reference": "./NLOD-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NLOD-1.0.json", - "referenceNumber": "209", - "name": "Norwegian Licence for Open Government Data", - "licenseId": "NLOD-1.0", - "seeAlso": [ - "http://data.norge.no/nlod/en/1.0" - ], - "isOsiApproved": false - }, - { - "reference": "./NLPL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NLPL.json", - "referenceNumber": "344", - "name": "No Limit Public License", - "licenseId": "NLPL", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/NLPL" - ], - "isOsiApproved": false - }, - { - "reference": "./NOSL.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/NOSL.json", - "referenceNumber": "383", - "name": "Netizen Open Source License", - "licenseId": "NOSL", - "seeAlso": [ - "http://bits.netizen.com.au/licenses/NOSL/nosl.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./NPL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/NPL-1.0.json", - "referenceNumber": "328", - "name": "Netscape Public License v1.0", - "licenseId": "NPL-1.0", - "seeAlso": [ - "http://www.mozilla.org/MPL/NPL/1.0/" - ], - "isOsiApproved": false - }, - { - "reference": "./NPL-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/NPL-1.1.json", - "referenceNumber": "185", - "name": "Netscape Public License v1.1", - "licenseId": "NPL-1.1", - "seeAlso": [ - "http://www.mozilla.org/MPL/NPL/1.1/" - ], - "isOsiApproved": false - }, - { - "reference": "./NPOSL-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NPOSL-3.0.json", - "referenceNumber": "222", - "name": "Non-Profit Open Software License 3.0", - "licenseId": "NPOSL-3.0", - "seeAlso": [ - "https://opensource.org/licenses/NOSL3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./NRL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NRL.json", - "referenceNumber": "53", - "name": "NRL License", - "licenseId": "NRL", - "seeAlso": [ - "http://web.mit.edu/network/isakmp/nrllicense.html" - ], - "isOsiApproved": false - }, - { - "reference": "./NTP.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NTP.json", - "referenceNumber": "261", - "name": "NTP License", - "licenseId": "NTP", - "seeAlso": [ - "https://opensource.org/licenses/NTP" - ], - "isOsiApproved": true - }, - { - "reference": "./Naumen.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Naumen.json", - "referenceNumber": "278", - "name": "Naumen Public License", - "licenseId": "Naumen", - "seeAlso": [ - "https://opensource.org/licenses/Naumen" - ], - "isOsiApproved": true - }, - { - "reference": "./Net-SNMP.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Net-SNMP.json", - "referenceNumber": "284", - "name": "Net-SNMP License", - "licenseId": "Net-SNMP", - "seeAlso": [ - "http://net-snmp.sourceforge.net/about/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./NetCDF.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/NetCDF.json", - "referenceNumber": "46", - "name": "NetCDF license", - "licenseId": "NetCDF", - "seeAlso": [ - "http://www.unidata.ucar.edu/software/netcdf/copyright.html" - ], - "isOsiApproved": false - }, - { - "reference": "./Newsletr.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Newsletr.json", - "referenceNumber": "279", - "name": "Newsletr License", - "licenseId": "Newsletr", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Newsletr" - ], - "isOsiApproved": false - }, - { - "reference": "./Nokia.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Nokia.json", - "referenceNumber": "327", - "name": "Nokia Open Source License", - "licenseId": "Nokia", - "seeAlso": [ - "https://opensource.org/licenses/nokia" - ], - "isOsiApproved": true - }, - { - "reference": "./Noweb.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Noweb.json", - "referenceNumber": "364", - "name": "Noweb License", - "licenseId": "Noweb", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Noweb" - ], - "isOsiApproved": false - }, - { - "reference": "./Nunit.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Nunit.json", - "referenceNumber": "288", - "name": "Nunit License", - "licenseId": "Nunit", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Nunit" - ], - "isOsiApproved": false - }, - { - "reference": "./OCCT-PL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OCCT-PL.json", - "referenceNumber": "282", - "name": "Open CASCADE Technology Public License", - "licenseId": "OCCT-PL", - "seeAlso": [ - "http://www.opencascade.com/content/occt-public-license" - ], - "isOsiApproved": false - }, - { - "reference": "./OCLC-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OCLC-2.0.json", - "referenceNumber": "111", - "name": "OCLC Research Public License 2.0", - "licenseId": "OCLC-2.0", - "seeAlso": [ - "http://www.oclc.org/research/activities/software/license/v2final.htm", - "https://opensource.org/licenses/OCLC-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./ODC-By-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/ODC-By-1.0.json", - "referenceNumber": "144", - "name": "Open Data Commons Attribution License v1.0", - "licenseId": "ODC-By-1.0", - "seeAlso": [ - "https://opendatacommons.org/licenses/by/1.0/" - ], - "isOsiApproved": false - }, - { - "reference": "./ODbL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/ODbL-1.0.json", - "referenceNumber": "246", - "name": "ODC Open Database License v1.0", - "licenseId": "ODbL-1.0", - "seeAlso": [ - "http://www.opendatacommons.org/licenses/odbl/1.0/" - ], - "isOsiApproved": false - }, - { - "reference": "./OFL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OFL-1.0.json", - "referenceNumber": "153", - "name": "SIL Open Font License 1.0", - "licenseId": "OFL-1.0", - "seeAlso": [ - "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL10_web" - ], - "isOsiApproved": false - }, - { - "reference": "./OFL-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OFL-1.1.json", - "referenceNumber": "315", - "name": "SIL Open Font License 1.1", - "licenseId": "OFL-1.1", - "seeAlso": [ - "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL_web", - "https://opensource.org/licenses/OFL-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./OGL-UK-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OGL-UK-1.0.json", - "referenceNumber": "116", - "name": "Open Government Licence v1.0", - "licenseId": "OGL-UK-1.0", - "seeAlso": [ - "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/1/" - ], - "isOsiApproved": false - }, - { - "reference": "./OGL-UK-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OGL-UK-2.0.json", - "referenceNumber": "289", - "name": "Open Government Licence v2.0", - "licenseId": "OGL-UK-2.0", - "seeAlso": [ - "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/2/" - ], - "isOsiApproved": false - }, - { - "reference": "./OGL-UK-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OGL-UK-3.0.json", - "referenceNumber": "226", - "name": "Open Government Licence v3.0", - "licenseId": "OGL-UK-3.0", - "seeAlso": [ - "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/" - ], - "isOsiApproved": false - }, - { - "reference": "./OGTSL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OGTSL.json", - "referenceNumber": "125", - "name": "Open Group Test Suite License", - "licenseId": "OGTSL", - "seeAlso": [ - "http://www.opengroup.org/testing/downloads/The_Open_Group_TSL.txt", - "https://opensource.org/licenses/OGTSL" - ], - "isOsiApproved": true - }, - { - "reference": "./OLDAP-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-1.1.json", - "referenceNumber": "97", - "name": "Open LDAP Public License v1.1", - "licenseId": "OLDAP-1.1", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d806557a5ad59804ef3a44d5abfbe91d706b0791f" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-1.2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-1.2.json", - "referenceNumber": "190", - "name": "Open LDAP Public License v1.2", - "licenseId": "OLDAP-1.2", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d42b0383c50c299977b5893ee695cf4e486fb0dc7" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-1.3.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-1.3.json", - "referenceNumber": "106", - "name": "Open LDAP Public License v1.3", - "licenseId": "OLDAP-1.3", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003de5f8117f0ce088d0bd7a8e18ddf37eaa40eb09b1" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-1.4.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-1.4.json", - "referenceNumber": "30", - "name": "Open LDAP Public License v1.4", - "licenseId": "OLDAP-1.4", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dc9f95c2f3f2ffb5e0ae55fe7388af75547660941" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.0.json", - "referenceNumber": "266", - "name": "Open LDAP Public License v2.0 (or possibly 2.0A and 2.0B)", - "licenseId": "OLDAP-2.0", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dcbf50f4e1185a21abd4c0a54d3f4341fe28f36ea" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.0.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.0.1.json", - "referenceNumber": "350", - "name": "Open LDAP Public License v2.0.1", - "licenseId": "OLDAP-2.0.1", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003db6d68acd14e51ca3aab4428bf26522aa74873f0e" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.1.json", - "referenceNumber": "154", - "name": "Open LDAP Public License v2.1", - "licenseId": "OLDAP-2.1", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003db0d176738e96a0d3b9f85cb51e140a86f21be715" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.2.json", - "referenceNumber": "362", - "name": "Open LDAP Public License v2.2", - "licenseId": "OLDAP-2.2", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d470b0c18ec67621c85881b2733057fecf4a1acc3" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.2.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.2.1.json", - "referenceNumber": "339", - "name": "Open LDAP Public License v2.2.1", - "licenseId": "OLDAP-2.2.1", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d4bc786f34b50aa301be6f5600f58a980070f481e" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.2.2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.2.2.json", - "referenceNumber": "199", - "name": "Open LDAP Public License 2.2.2", - "licenseId": "OLDAP-2.2.2", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003ddf2cc1e21eb7c160695f5b7cffd6296c151ba188" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.3.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.3.json", - "referenceNumber": "164", - "name": "Open LDAP Public License v2.3", - "licenseId": "OLDAP-2.3", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dd32cf54a32d581ab475d23c810b0a7fbaf8d63c3" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.4.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.4.json", - "referenceNumber": "66", - "name": "Open LDAP Public License v2.4", - "licenseId": "OLDAP-2.4", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dcd1284c4a91a8a380d904eee68d1583f989ed386" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.5.json", - "referenceNumber": "183", - "name": "Open LDAP Public License v2.5", - "licenseId": "OLDAP-2.5", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d6852b9d90022e8593c98205413380536b1b5a7cf" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.6.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.6.json", - "referenceNumber": "61", - "name": "Open LDAP Public License v2.6", - "licenseId": "OLDAP-2.6", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d1cae062821881f41b73012ba816434897abf4205" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.7.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.7.json", - "referenceNumber": "123", - "name": "Open LDAP Public License v2.7", - "licenseId": "OLDAP-2.7", - "seeAlso": [ - "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d47c2415c1df81556eeb39be6cad458ef87c534a2" - ], - "isOsiApproved": false - }, - { - "reference": "./OLDAP-2.8.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OLDAP-2.8.json", - "referenceNumber": "37", - "name": "Open LDAP Public License v2.8", - "licenseId": "OLDAP-2.8", - "seeAlso": [ - "http://www.openldap.org/software/release/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./OML.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OML.json", - "referenceNumber": "65", - "name": "Open Market License", - "licenseId": "OML", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Open_Market_License" - ], - "isOsiApproved": false - }, - { - "reference": "./OPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OPL-1.0.json", - "referenceNumber": "343", - "name": "Open Public License v1.0", - "licenseId": "OPL-1.0", - "seeAlso": [ - "http://old.koalateam.com/jackaroo/OPL_1_0.TXT", - "https://fedoraproject.org/wiki/Licensing/Open_Public_License" - ], - "isOsiApproved": false - }, - { - "reference": "./OSET-PL-2.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/OSET-PL-2.1.json", - "referenceNumber": "291", - "name": "OSET Public License version 2.1", - "licenseId": "OSET-PL-2.1", - "seeAlso": [ - "http://www.osetfoundation.org/public-license", - "https://opensource.org/licenses/OPL-2.1" - ], - "isOsiApproved": true - }, - { - "reference": "./OSL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OSL-1.0.json", - "referenceNumber": "85", - "name": "Open Software License 1.0", - "licenseId": "OSL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/OSL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./OSL-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OSL-1.1.json", - "referenceNumber": "334", - "name": "Open Software License 1.1", - "licenseId": "OSL-1.1", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/OSL1.1" - ], - "isOsiApproved": false - }, - { - "reference": "./OSL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OSL-2.0.json", - "referenceNumber": "20", - "name": "Open Software License 2.0", - "licenseId": "OSL-2.0", - "seeAlso": [ - "http://web.archive.org/web/20041020171434/http://www.rosenlaw.com/osl2.0.html" - ], - "isOsiApproved": true - }, - { - "reference": "./OSL-2.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OSL-2.1.json", - "referenceNumber": "24", - "name": "Open Software License 2.1", - "licenseId": "OSL-2.1", - "seeAlso": [ - "http://web.archive.org/web/20050212003940/http://www.rosenlaw.com/osl21.htm", - "https://opensource.org/licenses/OSL-2.1" - ], - "isOsiApproved": true - }, - { - "reference": "./OSL-3.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OSL-3.0.json", - "referenceNumber": "100", - "name": "Open Software License 3.0", - "licenseId": "OSL-3.0", - "seeAlso": [ - "https://web.archive.org/web/20120101081418/http://rosenlaw.com:80/OSL3.0.htm", - "https://opensource.org/licenses/OSL-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./OpenSSL.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/OpenSSL.json", - "referenceNumber": "249", - "name": "OpenSSL License", - "licenseId": "OpenSSL", - "seeAlso": [ - "http://www.openssl.org/source/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./PDDL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/PDDL-1.0.json", - "referenceNumber": "14", - "name": "ODC Public Domain Dedication \u0026 License 1.0", - "licenseId": "PDDL-1.0", - "seeAlso": [ - "http://opendatacommons.org/licenses/pddl/1.0/" - ], - "isOsiApproved": false - }, - { - "reference": "./PHP-3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/PHP-3.0.json", - "referenceNumber": "385", - "name": "PHP License v3.0", - "licenseId": "PHP-3.0", - "seeAlso": [ - "http://www.php.net/license/3_0.txt", - "https://opensource.org/licenses/PHP-3.0" - ], - "isOsiApproved": true - }, - { - "reference": "./PHP-3.01.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/PHP-3.01.json", - "referenceNumber": "316", - "name": "PHP License v3.01", - "licenseId": "PHP-3.01", - "seeAlso": [ - "http://www.php.net/license/3_01.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./Parity-6.0.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Parity-6.0.0.json", - "referenceNumber": "91", - "name": "The Parity Public License 6.0.0", - "licenseId": "Parity-6.0.0", - "seeAlso": [ - "https://paritylicense.com/versions/6.0.0.html" - ], - "isOsiApproved": false - }, - { - "reference": "./Plexus.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Plexus.json", - "referenceNumber": "225", - "name": "Plexus Classworlds License", - "licenseId": "Plexus", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Plexus_Classworlds_License" - ], - "isOsiApproved": false - }, - { - "reference": "./PostgreSQL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/PostgreSQL.json", - "referenceNumber": "247", - "name": "PostgreSQL License", - "licenseId": "PostgreSQL", - "seeAlso": [ - "http://www.postgresql.org/about/licence", - "https://opensource.org/licenses/PostgreSQL" - ], - "isOsiApproved": true - }, - { - "reference": "./Python-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Python-2.0.json", - "referenceNumber": "35", - "name": "Python License 2.0", - "licenseId": "Python-2.0", - "seeAlso": [ - "https://opensource.org/licenses/Python-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./QPL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/QPL-1.0.json", - "referenceNumber": "27", - "name": "Q Public License 1.0", - "licenseId": "QPL-1.0", - "seeAlso": [ - "http://doc.qt.nokia.com/3.3/license.html", - "https://opensource.org/licenses/QPL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Qhull.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Qhull.json", - "referenceNumber": "67", - "name": "Qhull License", - "licenseId": "Qhull", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Qhull" - ], - "isOsiApproved": false - }, - { - "reference": "./RHeCos-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/RHeCos-1.1.json", - "referenceNumber": "149", - "name": "Red Hat eCos Public License v1.1", - "licenseId": "RHeCos-1.1", - "seeAlso": [ - "http://ecos.sourceware.org/old-license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./RPL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/RPL-1.1.json", - "referenceNumber": "269", - "name": "Reciprocal Public License 1.1", - "licenseId": "RPL-1.1", - "seeAlso": [ - "https://opensource.org/licenses/RPL-1.1" - ], - "isOsiApproved": true - }, - { - "reference": "./RPL-1.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/RPL-1.5.json", - "referenceNumber": "227", - "name": "Reciprocal Public License 1.5", - "licenseId": "RPL-1.5", - "seeAlso": [ - "https://opensource.org/licenses/RPL-1.5" - ], - "isOsiApproved": true - }, - { - "reference": "./RPSL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/RPSL-1.0.json", - "referenceNumber": "273", - "name": "RealNetworks Public Source License v1.0", - "licenseId": "RPSL-1.0", - "seeAlso": [ - "https://helixcommunity.org/content/rpsl", - "https://opensource.org/licenses/RPSL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./RSA-MD.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/RSA-MD.json", - "referenceNumber": "82", - "name": "RSA Message-Digest License ", - "licenseId": "RSA-MD", - "seeAlso": [ - "http://www.faqs.org/rfcs/rfc1321.html" - ], - "isOsiApproved": false - }, - { - "reference": "./RSCPL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/RSCPL.json", - "referenceNumber": "211", - "name": "Ricoh Source Code Public License", - "licenseId": "RSCPL", - "seeAlso": [ - "http://wayback.archive.org/web/20060715140826/http://www.risource.org/RPL/RPL-1.0A.shtml", - "https://opensource.org/licenses/RSCPL" - ], - "isOsiApproved": true - }, - { - "reference": "./Rdisc.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Rdisc.json", - "referenceNumber": "295", - "name": "Rdisc License", - "licenseId": "Rdisc", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Rdisc_License" - ], - "isOsiApproved": false - }, - { - "reference": "./Ruby.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Ruby.json", - "referenceNumber": "263", - "name": "Ruby License", - "licenseId": "Ruby", - "seeAlso": [ - "http://www.ruby-lang.org/en/LICENSE.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./SAX-PD.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SAX-PD.json", - "referenceNumber": "140", - "name": "Sax Public Domain Notice", - "licenseId": "SAX-PD", - "seeAlso": [ - "http://www.saxproject.org/copying.html" - ], - "isOsiApproved": false - }, - { - "reference": "./SCEA.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SCEA.json", - "referenceNumber": "16", - "name": "SCEA Shared Source License", - "licenseId": "SCEA", - "seeAlso": [ - "http://research.scea.com/scea_shared_source_license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./SGI-B-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SGI-B-1.0.json", - "referenceNumber": "90", - "name": "SGI Free Software License B v1.0", - "licenseId": "SGI-B-1.0", - "seeAlso": [ - "http://oss.sgi.com/projects/FreeB/SGIFreeSWLicB.1.0.html" - ], - "isOsiApproved": false - }, - { - "reference": "./SGI-B-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SGI-B-1.1.json", - "referenceNumber": "241", - "name": "SGI Free Software License B v1.1", - "licenseId": "SGI-B-1.1", - "seeAlso": [ - "http://oss.sgi.com/projects/FreeB/" - ], - "isOsiApproved": false - }, - { - "reference": "./SGI-B-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/SGI-B-2.0.json", - "referenceNumber": "272", - "name": "SGI Free Software License B v2.0", - "licenseId": "SGI-B-2.0", - "seeAlso": [ - "http://oss.sgi.com/projects/FreeB/SGIFreeSWLicB.2.0.pdf" - ], - "isOsiApproved": false - }, - { - "reference": "./SHL-0.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SHL-0.5.json", - "referenceNumber": "72", - "name": "Solderpad Hardware License v0.5", - "licenseId": "SHL-0.5", - "seeAlso": [ - "https://solderpad.org/licenses/SHL-0.5/" - ], - "isOsiApproved": false - }, - { - "reference": "./SHL-0.51.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SHL-0.51.json", - "referenceNumber": "314", - "name": "Solderpad Hardware License, Version 0.51", - "licenseId": "SHL-0.51", - "seeAlso": [ - "https://solderpad.org/licenses/SHL-0.51/" - ], - "isOsiApproved": false - }, - { - "reference": "./SISSL.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/SISSL.json", - "referenceNumber": "74", - "name": "Sun Industry Standards Source License v1.1", - "licenseId": "SISSL", - "seeAlso": [ - "http://www.openoffice.org/licenses/sissl_license.html", - "https://opensource.org/licenses/SISSL" - ], - "isOsiApproved": true - }, - { - "reference": "./SISSL-1.2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SISSL-1.2.json", - "referenceNumber": "7", - "name": "Sun Industry Standards Source License v1.2", - "licenseId": "SISSL-1.2", - "seeAlso": [ - "http://gridscheduler.sourceforge.net/Gridengine_SISSL_license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./SMLNJ.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/SMLNJ.json", - "referenceNumber": "296", - "name": "Standard ML of New Jersey License", - "licenseId": "SMLNJ", - "seeAlso": [ - "https://www.smlnj.org/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./SMPPL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SMPPL.json", - "referenceNumber": "127", - "name": "Secure Messaging Protocol Public License", - "licenseId": "SMPPL", - "seeAlso": [ - "https://github.com/dcblake/SMP/blob/master/Documentation/License.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./SNIA.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SNIA.json", - "referenceNumber": "230", - "name": "SNIA Public License 1.1", - "licenseId": "SNIA", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/SNIA_Public_License" - ], - "isOsiApproved": false - }, - { - "reference": "./SPL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/SPL-1.0.json", - "referenceNumber": "54", - "name": "Sun Public License v1.0", - "licenseId": "SPL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/SPL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./SSPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SSPL-1.0.json", - "referenceNumber": "356", - "name": "Server Side Public License, v 1", - "licenseId": "SSPL-1.0", - "seeAlso": [ - "https://www.mongodb.com/licensing/server-side-public-license" - ], - "isOsiApproved": false - }, - { - "reference": "./SWL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SWL.json", - "referenceNumber": "208", - "name": "Scheme Widget Library (SWL) Software License Agreement", - "licenseId": "SWL", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/SWL" - ], - "isOsiApproved": false - }, - { - "reference": "./Saxpath.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Saxpath.json", - "referenceNumber": "18", - "name": "Saxpath License", - "licenseId": "Saxpath", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Saxpath_License" - ], - "isOsiApproved": false - }, - { - "reference": "./Sendmail.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Sendmail.json", - "referenceNumber": "151", - "name": "Sendmail License", - "licenseId": "Sendmail", - "seeAlso": [ - "http://www.sendmail.com/pdfs/open_source/sendmail_license.pdf", - "https://web.archive.org/web/20160322142305/https://www.sendmail.com/pdfs/open_source/sendmail_license.pdf" - ], - "isOsiApproved": false - }, - { - "reference": "./Sendmail-8.23.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Sendmail-8.23.json", - "referenceNumber": "41", - "name": "Sendmail License 8.23", - "licenseId": "Sendmail-8.23", - "seeAlso": [ - "https://www.proofpoint.com/sites/default/files/sendmail-license.pdf", - "https://web.archive.org/web/20181003101040/https://www.proofpoint.com/sites/default/files/sendmail-license.pdf" - ], - "isOsiApproved": false - }, - { - "reference": "./SimPL-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SimPL-2.0.json", - "referenceNumber": "184", - "name": "Simple Public License 2.0", - "licenseId": "SimPL-2.0", - "seeAlso": [ - "https://opensource.org/licenses/SimPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Sleepycat.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Sleepycat.json", - "referenceNumber": "290", - "name": "Sleepycat License", - "licenseId": "Sleepycat", - "seeAlso": [ - "https://opensource.org/licenses/Sleepycat" - ], - "isOsiApproved": true - }, - { - "reference": "./Spencer-86.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Spencer-86.json", - "referenceNumber": "313", - "name": "Spencer License 86", - "licenseId": "Spencer-86", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Henry_Spencer_Reg-Ex_Library_License" - ], - "isOsiApproved": false - }, - { - "reference": "./Spencer-94.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Spencer-94.json", - "referenceNumber": "29", - "name": "Spencer License 94", - "licenseId": "Spencer-94", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Henry_Spencer_Reg-Ex_Library_License" - ], - "isOsiApproved": false - }, - { - "reference": "./Spencer-99.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Spencer-99.json", - "referenceNumber": "386", - "name": "Spencer License 99", - "licenseId": "Spencer-99", - "seeAlso": [ - "http://www.opensource.apple.com/source/tcl/tcl-5/tcl/generic/regfronts.c" - ], - "isOsiApproved": false - }, - { - "reference": "./StandardML-NJ.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/StandardML-NJ.json", - "referenceNumber": "219", - "name": "Standard ML of New Jersey License", - "licenseId": "StandardML-NJ", - "seeAlso": [ - "http://www.smlnj.org//license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./SugarCRM-1.1.3.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/SugarCRM-1.1.3.json", - "referenceNumber": "292", - "name": "SugarCRM Public License v1.1.3", - "licenseId": "SugarCRM-1.1.3", - "seeAlso": [ - "http://www.sugarcrm.com/crm/SPL" - ], - "isOsiApproved": false - }, - { - "reference": "./TAPR-OHL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/TAPR-OHL-1.0.json", - "referenceNumber": "267", - "name": "TAPR Open Hardware License v1.0", - "licenseId": "TAPR-OHL-1.0", - "seeAlso": [ - "https://www.tapr.org/OHL" - ], - "isOsiApproved": false - }, - { - "reference": "./TCL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/TCL.json", - "referenceNumber": "265", - "name": "TCL/TK License", - "licenseId": "TCL", - "seeAlso": [ - "http://www.tcl.tk/software/tcltk/license.html", - "https://fedoraproject.org/wiki/Licensing/TCL" - ], - "isOsiApproved": false - }, - { - "reference": "./TCP-wrappers.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/TCP-wrappers.json", - "referenceNumber": "274", - "name": "TCP Wrappers License", - "licenseId": "TCP-wrappers", - "seeAlso": [ - "http://rc.quest.com/topics/openssh/license.php#tcpwrappers" - ], - "isOsiApproved": false - }, - { - "reference": "./TMate.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/TMate.json", - "referenceNumber": "253", - "name": "TMate Open Source License", - "licenseId": "TMate", - "seeAlso": [ - "http://svnkit.com/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./TORQUE-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/TORQUE-1.1.json", - "referenceNumber": "171", - "name": "TORQUE v2.5+ Software License v1.1", - "licenseId": "TORQUE-1.1", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/TORQUEv1.1" - ], - "isOsiApproved": false - }, - { - "reference": "./TOSL.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/TOSL.json", - "referenceNumber": "360", - "name": "Trusster Open Source License", - "licenseId": "TOSL", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/TOSL" - ], - "isOsiApproved": false - }, - { - "reference": "./TU-Berlin-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/TU-Berlin-1.0.json", - "referenceNumber": "373", - "name": "Technische Universitaet Berlin License 1.0", - "licenseId": "TU-Berlin-1.0", - "seeAlso": [ - "https://github.com/swh/ladspa/blob/7bf6f3799fdba70fda297c2d8fd9f526803d9680/gsm/COPYRIGHT" - ], - "isOsiApproved": false - }, - { - "reference": "./TU-Berlin-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/TU-Berlin-2.0.json", - "referenceNumber": "391", - "name": "Technische Universitaet Berlin License 2.0", - "licenseId": "TU-Berlin-2.0", - "seeAlso": [ - "https://github.com/CorsixTH/deps/blob/fd339a9f526d1d9c9f01ccf39e438a015da50035/licences/libgsm.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./UPL-1.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/UPL-1.0.json", - "referenceNumber": "205", - "name": "Universal Permissive License v1.0", - "licenseId": "UPL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/UPL" - ], - "isOsiApproved": true - }, - { - "reference": "./Unicode-DFS-2015.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Unicode-DFS-2015.json", - "referenceNumber": "11", - "name": "Unicode License Agreement - Data Files and Software (2015)", - "licenseId": "Unicode-DFS-2015", - "seeAlso": [ - "https://web.archive.org/web/20151224134844/http://unicode.org/copyright.html" - ], - "isOsiApproved": false - }, - { - "reference": "./Unicode-DFS-2016.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Unicode-DFS-2016.json", - "referenceNumber": "382", - "name": "Unicode License Agreement - Data Files and Software (2016)", - "licenseId": "Unicode-DFS-2016", - "seeAlso": [ - "http://www.unicode.org/copyright.html" - ], - "isOsiApproved": false - }, - { - "reference": "./Unicode-TOU.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Unicode-TOU.json", - "referenceNumber": "70", - "name": "Unicode Terms of Use", - "licenseId": "Unicode-TOU", - "seeAlso": [ - "http://www.unicode.org/copyright.html" - ], - "isOsiApproved": false - }, - { - "reference": "./Unlicense.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Unlicense.json", - "referenceNumber": "293", - "name": "The Unlicense", - "licenseId": "Unlicense", - "seeAlso": [ - "http://unlicense.org/" - ], - "isOsiApproved": false - }, - { - "reference": "./VOSTROM.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/VOSTROM.json", - "referenceNumber": "228", - "name": "VOSTROM Public License for Open Source", - "licenseId": "VOSTROM", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/VOSTROM" - ], - "isOsiApproved": false - }, - { - "reference": "./VSL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/VSL-1.0.json", - "referenceNumber": "180", - "name": "Vovida Software License v1.0", - "licenseId": "VSL-1.0", - "seeAlso": [ - "https://opensource.org/licenses/VSL-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Vim.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Vim.json", - "referenceNumber": "133", - "name": "Vim License", - "licenseId": "Vim", - "seeAlso": [ - "http://vimdoc.sourceforge.net/htmldoc/uganda.html" - ], - "isOsiApproved": false - }, - { - "reference": "./W3C.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/W3C.json", - "referenceNumber": "351", - "name": "W3C Software Notice and License (2002-12-31)", - "licenseId": "W3C", - "seeAlso": [ - "http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231.html", - "https://opensource.org/licenses/W3C" - ], - "isOsiApproved": true - }, - { - "reference": "./W3C-19980720.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/W3C-19980720.json", - "referenceNumber": "323", - "name": "W3C Software Notice and License (1998-07-20)", - "licenseId": "W3C-19980720", - "seeAlso": [ - "http://www.w3.org/Consortium/Legal/copyright-software-19980720.html" - ], - "isOsiApproved": false - }, - { - "reference": "./W3C-20150513.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/W3C-20150513.json", - "referenceNumber": "51", - "name": "W3C Software Notice and Document License (2015-05-13)", - "licenseId": "W3C-20150513", - "seeAlso": [ - "https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document" - ], - "isOsiApproved": false - }, - { - "reference": "./WTFPL.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/WTFPL.json", - "referenceNumber": "368", - "name": "Do What The F*ck You Want To Public License", - "licenseId": "WTFPL", - "seeAlso": [ - "http://sam.zoy.org/wtfpl/COPYING" - ], - "isOsiApproved": false - }, - { - "reference": "./Watcom-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Watcom-1.0.json", - "referenceNumber": "177", - "name": "Sybase Open Watcom Public License 1.0", - "licenseId": "Watcom-1.0", - "seeAlso": [ - "https://opensource.org/licenses/Watcom-1.0" - ], - "isOsiApproved": true - }, - { - "reference": "./Wsuipa.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Wsuipa.json", - "referenceNumber": "135", - "name": "Wsuipa License", - "licenseId": "Wsuipa", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Wsuipa" - ], - "isOsiApproved": false - }, - { - "reference": "./X11.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/X11.json", - "referenceNumber": "188", - "name": "X11 License", - "licenseId": "X11", - "seeAlso": [ - "http://www.xfree86.org/3.3.6/COPYRIGHT2.html#3" - ], - "isOsiApproved": false - }, - { - "reference": "./XFree86-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/XFree86-1.1.json", - "referenceNumber": "243", - "name": "XFree86 License 1.1", - "licenseId": "XFree86-1.1", - "seeAlso": [ - "http://www.xfree86.org/current/LICENSE4.html" - ], - "isOsiApproved": false - }, - { - "reference": "./XSkat.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/XSkat.json", - "referenceNumber": "96", - "name": "XSkat License", - "licenseId": "XSkat", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/XSkat_License" - ], - "isOsiApproved": false - }, - { - "reference": "./Xerox.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Xerox.json", - "referenceNumber": "163", - "name": "Xerox License", - "licenseId": "Xerox", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Xerox" - ], - "isOsiApproved": false - }, - { - "reference": "./Xnet.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Xnet.json", - "referenceNumber": "388", - "name": "X.Net License", - "licenseId": "Xnet", - "seeAlso": [ - "https://opensource.org/licenses/Xnet" - ], - "isOsiApproved": true - }, - { - "reference": "./YPL-1.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/YPL-1.0.json", - "referenceNumber": "174", - "name": "Yahoo! Public License v1.0", - "licenseId": "YPL-1.0", - "seeAlso": [ - "http://www.zimbra.com/license/yahoo_public_license_1.0.html" - ], - "isOsiApproved": false - }, - { - "reference": "./YPL-1.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/YPL-1.1.json", - "referenceNumber": "57", - "name": "Yahoo! Public License v1.1", - "licenseId": "YPL-1.1", - "seeAlso": [ - "http://www.zimbra.com/license/yahoo_public_license_1.1.html" - ], - "isOsiApproved": false - }, - { - "reference": "./ZPL-1.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/ZPL-1.1.json", - "referenceNumber": "359", - "name": "Zope Public License 1.1", - "licenseId": "ZPL-1.1", - "seeAlso": [ - "http://old.zope.org/Resources/License/ZPL-1.1" - ], - "isOsiApproved": false - }, - { - "reference": "./ZPL-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/ZPL-2.0.json", - "referenceNumber": "78", - "name": "Zope Public License 2.0", - "licenseId": "ZPL-2.0", - "seeAlso": [ - "http://old.zope.org/Resources/License/ZPL-2.0", - "https://opensource.org/licenses/ZPL-2.0" - ], - "isOsiApproved": true - }, - { - "reference": "./ZPL-2.1.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/ZPL-2.1.json", - "referenceNumber": "345", - "name": "Zope Public License 2.1", - "licenseId": "ZPL-2.1", - "seeAlso": [ - "http://old.zope.org/Resources/ZPL/" - ], - "isOsiApproved": false - }, - { - "reference": "./Zed.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Zed.json", - "referenceNumber": "248", - "name": "Zed License", - "licenseId": "Zed", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Zed" - ], - "isOsiApproved": false - }, - { - "reference": "./Zend-2.0.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Zend-2.0.json", - "referenceNumber": "198", - "name": "Zend License v2.0", - "licenseId": "Zend-2.0", - "seeAlso": [ - "https://web.archive.org/web/20130517195954/http://www.zend.com/license/2_00.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./Zimbra-1.3.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Zimbra-1.3.json", - "referenceNumber": "40", - "name": "Zimbra Public License v1.3", - "licenseId": "Zimbra-1.3", - "seeAlso": [ - "http://web.archive.org/web/20100302225219/http://www.zimbra.com/license/zimbra-public-license-1-3.html" - ], - "isOsiApproved": false - }, - { - "reference": "./Zimbra-1.4.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/Zimbra-1.4.json", - "referenceNumber": "238", - "name": "Zimbra Public License v1.4", - "licenseId": "Zimbra-1.4", - "seeAlso": [ - "http://www.zimbra.com/legal/zimbra-public-license-1-4" - ], - "isOsiApproved": false - }, - { - "reference": "./Zlib.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/Zlib.json", - "referenceNumber": "320", - "name": "zlib License", - "licenseId": "Zlib", - "seeAlso": [ - "http://www.zlib.net/zlib_license.html", - "https://opensource.org/licenses/Zlib" - ], - "isOsiApproved": true - }, - { - "reference": "./blessing.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/blessing.json", - "referenceNumber": "331", - "name": "SQLite Blessing", - "licenseId": "blessing", - "seeAlso": [ - "https://www.sqlite.org/src/artifact/e33a4df7e32d742a?ln\u003d4-9", - "https://sqlite.org/src/artifact/df5091916dbb40e6" - ], - "isOsiApproved": false - }, - { - "reference": "./bzip2-1.0.5.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/bzip2-1.0.5.json", - "referenceNumber": "200", - "name": "bzip2 and libbzip2 License v1.0.5", - "licenseId": "bzip2-1.0.5", - "seeAlso": [ - "http://bzip.org/1.0.5/bzip2-manual-1.0.5.html" - ], - "isOsiApproved": false - }, - { - "reference": "./bzip2-1.0.6.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/bzip2-1.0.6.json", - "referenceNumber": "302", - "name": "bzip2 and libbzip2 License v1.0.6", - "licenseId": "bzip2-1.0.6", - "seeAlso": [ - "https://github.com/asimonov-im/bzip2/blob/master/LICENSE" - ], - "isOsiApproved": false - }, - { - "reference": "./copyleft-next-0.3.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/copyleft-next-0.3.0.json", - "referenceNumber": "176", - "name": "copyleft-next 0.3.0", - "licenseId": "copyleft-next-0.3.0", - "seeAlso": [ - "https://github.com/copyleft-next/copyleft-next/blob/master/Releases/copyleft-next-0.3.0" - ], - "isOsiApproved": false - }, - { - "reference": "./copyleft-next-0.3.1.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/copyleft-next-0.3.1.json", - "referenceNumber": "347", - "name": "copyleft-next 0.3.1", - "licenseId": "copyleft-next-0.3.1", - "seeAlso": [ - "https://github.com/copyleft-next/copyleft-next/blob/master/Releases/copyleft-next-0.3.1" - ], - "isOsiApproved": false - }, - { - "reference": "./curl.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/curl.json", - "referenceNumber": "260", - "name": "curl License", - "licenseId": "curl", - "seeAlso": [ - "https://github.com/bagder/curl/blob/master/COPYING" - ], - "isOsiApproved": false - }, - { - "reference": "./diffmark.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/diffmark.json", - "referenceNumber": "367", - "name": "diffmark license", - "licenseId": "diffmark", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/diffmark" - ], - "isOsiApproved": false - }, - { - "reference": "./dvipdfm.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/dvipdfm.json", - "referenceNumber": "143", - "name": "dvipdfm License", - "licenseId": "dvipdfm", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/dvipdfm" - ], - "isOsiApproved": false - }, - { - "reference": "./eCos-2.0.html", - "isDeprecatedLicenseId": true, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/eCos-2.0.json", - "referenceNumber": "329", - "name": "eCos license version 2.0", - "licenseId": "eCos-2.0", - "seeAlso": [ - "https://www.gnu.org/licenses/ecos-license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./eGenix.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/eGenix.json", - "referenceNumber": "204", - "name": "eGenix.com Public License 1.1.0", - "licenseId": "eGenix", - "seeAlso": [ - "http://www.egenix.com/products/eGenix.com-Public-License-1.1.0.pdf", - "https://fedoraproject.org/wiki/Licensing/eGenix.com_Public_License_1.1.0" - ], - "isOsiApproved": false - }, - { - "reference": "./gSOAP-1.3b.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/gSOAP-1.3b.json", - "referenceNumber": "346", - "name": "gSOAP Public License v1.3b", - "licenseId": "gSOAP-1.3b", - "seeAlso": [ - "http://www.cs.fsu.edu/~engelen/license.html" - ], - "isOsiApproved": false - }, - { - "reference": "./gnuplot.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/gnuplot.json", - "referenceNumber": "10", - "name": "gnuplot License", - "licenseId": "gnuplot", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Gnuplot" - ], - "isOsiApproved": false - }, - { - "reference": "./iMatix.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/iMatix.json", - "referenceNumber": "342", - "name": "iMatix Standard Function Library Agreement", - "licenseId": "iMatix", - "seeAlso": [ - "http://legacy.imatix.com/html/sfl/sfl4.htm#license" - ], - "isOsiApproved": false - }, - { - "reference": "./libpng-2.0.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/libpng-2.0.json", - "referenceNumber": "76", - "name": "PNG Reference Library version 2", - "licenseId": "libpng-2.0", - "seeAlso": [ - "http://www.libpng.org/pub/png/src/libpng-LICENSE.txt" - ], - "isOsiApproved": false - }, - { - "reference": "./libtiff.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/libtiff.json", - "referenceNumber": "220", - "name": "libtiff License", - "licenseId": "libtiff", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/libtiff" - ], - "isOsiApproved": false - }, - { - "reference": "./mpich2.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/mpich2.json", - "referenceNumber": "318", - "name": "mpich2 License", - "licenseId": "mpich2", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/MIT" - ], - "isOsiApproved": false - }, - { - "reference": "./psfrag.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/psfrag.json", - "referenceNumber": "245", - "name": "psfrag License", - "licenseId": "psfrag", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/psfrag" - ], - "isOsiApproved": false - }, - { - "reference": "./psutils.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/psutils.json", - "referenceNumber": "126", - "name": "psutils License", - "licenseId": "psutils", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/psutils" - ], - "isOsiApproved": false - }, - { - "reference": "./wxWindows.html", - "isDeprecatedLicenseId": true, - "detailsUrl": "http://spdx.org/licenses/wxWindows.json", - "referenceNumber": "86", - "name": "wxWindows Library License", - "licenseId": "wxWindows", - "seeAlso": [ - "https://opensource.org/licenses/WXwindows" - ], - "isOsiApproved": false - }, - { - "reference": "./xinetd.html", - "isDeprecatedLicenseId": false, - "isFsfLibre": true, - "detailsUrl": "http://spdx.org/licenses/xinetd.json", - "referenceNumber": "146", - "name": "xinetd License", - "licenseId": "xinetd", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/Xinetd_License" - ], - "isOsiApproved": false - }, - { - "reference": "./xpp.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/xpp.json", - "referenceNumber": "275", - "name": "XPP License", - "licenseId": "xpp", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/xpp" - ], - "isOsiApproved": false - }, - { - "reference": "./zlib-acknowledgement.html", - "isDeprecatedLicenseId": false, - "detailsUrl": "http://spdx.org/licenses/zlib-acknowledgement.json", - "referenceNumber": "321", - "name": "zlib/libpng License with Acknowledgement", - "licenseId": "zlib-acknowledgement", - "seeAlso": [ - "https://fedoraproject.org/wiki/Licensing/ZlibWithAcknowledgement" - ], - "isOsiApproved": false - } - ], - "releaseDate": "2019-07-10" -} diff --git a/src/spdx/model/license.py b/src/spdx/model/license.py deleted file mode 100644 index f0ea53ac1..000000000 --- a/src/spdx/model/license.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 spdx import config - - -def determine_full_name(identifier: str, full_name: str): - if full_name is not None: - return full_name - # Note: the license map contains both the ids and names of licenses as keys, with the name resp. id as value - if identifier in config.LICENSE_MAP: - return config.LICENSE_MAP[identifier] - return identifier - - -def determine_identifier(identifier: str, full_name: str): - if identifier is not None: - return identifier - # Note: the license map contains both the ids and names of licenses as keys, with the name resp. id as value - if full_name in config.LICENSE_MAP: - return config.LICENSE_MAP[full_name] - return full_name - - -class License: - identifier: str - full_name: str - - def __init__(self, identifier: str = None, full_name: str = None): - """Create a new license from identifier, full name or both. If only either identifier or full name is - provided, we try to retrieve the other value from the list of known licenses. If the license is unknown and - only one value is provided, both identifier and full name are set to this value.""" - if identifier is None and full_name is None: - raise ValueError("Must provide either identifier or full name for a license") - self.identifier = determine_identifier(identifier, full_name) - self.full_name = determine_full_name(identifier, full_name) diff --git a/tests/spdx/model/test_license.py b/tests/spdx/model/test_license.py deleted file mode 100644 index 69bf7fa88..000000000 --- a/tests/spdx/model/test_license.py +++ /dev/null @@ -1,31 +0,0 @@ -# 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. - - -import pytest - -from spdx.model.license import determine_full_name, determine_identifier - - -@pytest.mark.parametrize("identifier,full_name,expected", - [("0BSD", "full_name", "full_name"), (None, "full_name", "full_name"), - (None, "BSD Zero Clause License", "BSD Zero Clause License"), - ("0BSD", None, "BSD Zero Clause License"), ("identifier", None, "identifier")]) -def test_determine_full_name(identifier, full_name, expected): - assert determine_full_name(identifier, full_name) == expected - - -@pytest.mark.parametrize("identifier,full_name,expected", - [("identifier", "BSD Zero Clause License", "identifier"), (None, "full_name", "full_name"), - (None, "BSD Zero Clause License", "0BSD"), ("0BSD", None, "0BSD"), - ("identifier", None, "identifier")]) -def test_determine_identifier(identifier, full_name, expected): - assert determine_identifier(identifier, full_name) == expected From 69e938a63016d451ed4d212256673c7b495ad2f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 24 Mar 2023 17:46:24 +0100 Subject: [PATCH 360/362] change DATE to ISO8601_DATE, don't inherit from object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx/parser/tagvalue/lexer.py | 6 +++--- src/spdx/parser/tagvalue/parser.py | 12 ++++++------ tests/spdx/parser/tagvalue/test_tag_value_lexer.py | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/spdx/parser/tagvalue/lexer.py b/src/spdx/parser/tagvalue/lexer.py index 9229ad64e..8a103833f 100644 --- a/src/spdx/parser/tagvalue/lexer.py +++ b/src/spdx/parser/tagvalue/lexer.py @@ -14,7 +14,7 @@ from ply.lex import TOKEN -class SPDXLexer(object): +class SPDXLexer: reserved = { # Top level fields "SPDXVersion": "DOC_VERSION", @@ -107,7 +107,7 @@ class SPDXLexer(object): "UNKNOWN_TAG", "ORGANIZATION_VALUE", "PERSON_VALUE", - "DATE", + "ISO8601_DATE", "LINE", "CHECKSUM" ] + list(reserved.values()) @@ -158,7 +158,7 @@ def t_PERSON_VALUE(self, t): return t @TOKEN(r":\s*\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ") - def t_DATE(self, t): + def t_ISO8601_DATE(self, t): t.value = t.value[1:].strip() return t diff --git a/src/spdx/parser/tagvalue/parser.py b/src/spdx/parser/tagvalue/parser.py index 884f7ebdd..9ee5a3ad8 100644 --- a/src/spdx/parser/tagvalue/parser.py +++ b/src/spdx/parser/tagvalue/parser.py @@ -44,7 +44,7 @@ Snippet="SnippetSPDXID", Package="PackageName", ExtractedLicensingInfo="LicenseID") -class Parser(object): +class Parser: tokens: List[str] logger: Logger current_element: Dict[str, Any] @@ -169,7 +169,7 @@ def p_generic_value(self, p): if self.check_that_current_element_matches_class_for_value(TAG_DATA_MODEL_FIELD[p[1]][0], p.lineno(1)): set_value(p, self.current_element) - @grammar_rule("unknown_tag : UNKNOWN_TAG text_or_line\n | UNKNOWN_TAG DATE\n | UNKNOWN_TAG PERSON_VALUE \n" + @grammar_rule("unknown_tag : UNKNOWN_TAG text_or_line\n | UNKNOWN_TAG ISO8601_DATE\n | UNKNOWN_TAG PERSON_VALUE \n" "| UNKNOWN_TAG") def p_unknown_tag(self, p): self.logger.append(f"Unknown tag provided in line {p.lineno(1)}") @@ -252,7 +252,7 @@ def p_external_document_ref(self, p): def p_creator(self, p): self.creation_info.setdefault("creators", []).append(ActorParser.parse_actor(p[2])) - @grammar_rule("created : CREATED DATE") + @grammar_rule("created : CREATED ISO8601_DATE") def p_created(self, p): set_value(p, self.creation_info, method_to_apply=datetime_from_str) @@ -384,8 +384,8 @@ def p_primary_package_purpose(self, p): if self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)): set_value(p, self.current_element, method_to_apply=lambda x: PackagePurpose[x.replace("-", "_")]) - @grammar_rule("built_date : BUILT_DATE DATE\n release_date : RELEASE_DATE DATE\n " - "valid_until_date : VALID_UNTIL_DATE DATE") + @grammar_rule("built_date : BUILT_DATE ISO8601_DATE\n release_date : RELEASE_DATE ISO8601_DATE\n " + "valid_until_date : VALID_UNTIL_DATE ISO8601_DATE") def p_package_dates(self, p): if self.check_that_current_element_matches_class_for_value(Package, p.lineno(1)): set_value(p, self.current_element, method_to_apply=datetime_from_str) @@ -428,7 +428,7 @@ def p_annotator(self, p): self.initialize_new_current_element(Annotation) set_value(p, self.current_element, method_to_apply=ActorParser.parse_actor) - @grammar_rule("annotation_date : ANNOTATION_DATE DATE") + @grammar_rule("annotation_date : ANNOTATION_DATE ISO8601_DATE") def p_annotation_date(self, p): if self.check_that_current_element_matches_class_for_value(Annotation, p.lineno(1)): set_value(p, self.current_element, method_to_apply=datetime_from_str) diff --git a/tests/spdx/parser/tagvalue/test_tag_value_lexer.py b/tests/spdx/parser/tagvalue/test_tag_value_lexer.py index 0aaf0d864..5eab39bea 100644 --- a/tests/spdx/parser/tagvalue/test_tag_value_lexer.py +++ b/tests/spdx/parser/tagvalue/test_tag_value_lexer.py @@ -120,7 +120,7 @@ def test_tokenization_of_creation_info(lexer): token_assert_helper(lexer.token(), "CREATOR", "Creator", 2) token_assert_helper(lexer.token(), "ORGANIZATION_VALUE", "Organization: Acme.", 2) token_assert_helper(lexer.token(), "CREATED", "Created", 3) - token_assert_helper(lexer.token(), "DATE", "2010-02-03T00:00:00Z", 3) + token_assert_helper(lexer.token(), "ISO8601_DATE", "2010-02-03T00:00:00Z", 3) token_assert_helper(lexer.token(), "CREATOR_COMMENT", "CreatorComment", 4) token_assert_helper(lexer.token(), "TEXT", "Sample Comment", 4) @@ -205,11 +205,11 @@ def test_tokenization_of_package(lexer): token_assert_helper(lexer.token(), "PRIMARY_PACKAGE_PURPOSE", "PrimaryPackagePurpose", 23) token_assert_helper(lexer.token(), "LINE", "OPERATING-SYSTEM", 23) token_assert_helper(lexer.token(), "BUILT_DATE", "BuiltDate", 24) - token_assert_helper(lexer.token(), "DATE", "2020-01-01T12:00:00Z", 24) + token_assert_helper(lexer.token(), "ISO8601_DATE", "2020-01-01T12:00:00Z", 24) token_assert_helper(lexer.token(), "RELEASE_DATE", "ReleaseDate", 25) - token_assert_helper(lexer.token(), "DATE", "2021-01-01T12:00:00Z", 25) + token_assert_helper(lexer.token(), "ISO8601_DATE", "2021-01-01T12:00:00Z", 25) token_assert_helper(lexer.token(), "VALID_UNTIL_DATE", "ValidUntilDate", 26) - token_assert_helper(lexer.token(), "DATE", "2022-01-01T12:00:00Z", 26) + token_assert_helper(lexer.token(), "ISO8601_DATE", "2022-01-01T12:00:00Z", 26) def test_tokenization_of_unknown_tag(lexer): @@ -269,7 +269,7 @@ def test_tokenization_of_annotation(lexer): token_assert_helper(lexer.token(), "ANNOTATOR", "Annotator", 1) token_assert_helper(lexer.token(), "PERSON_VALUE", "Person: Jane Doe()", 1) token_assert_helper(lexer.token(), "ANNOTATION_DATE", "AnnotationDate", 2) - token_assert_helper(lexer.token(), "DATE", "2010-01-29T18:30:22Z", 2) + token_assert_helper(lexer.token(), "ISO8601_DATE", "2010-01-29T18:30:22Z", 2) token_assert_helper(lexer.token(), "ANNOTATION_COMMENT", "AnnotationComment", 3) token_assert_helper(lexer.token(), "TEXT", "Document level annotation", 3) token_assert_helper(lexer.token(), "ANNOTATION_TYPE", "AnnotationType", 4) From 563196d638332db0ead99ef289ea88b6b109cdf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Wed, 29 Mar 2023 12:42:45 +0200 Subject: [PATCH 361/362] update README for when the refactor branch is merged to main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- README.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cb25a8104..998dce902 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,22 @@ CI status (Linux, macOS and Windows): [![Install and Test][1]][2] [2]: https://github.com/spdx/tools-python/actions/workflows/install_and_test.yml -# Current state +# Current state, please read! This repository was subject to a major refactoring recently to get ready for the upcoming SPDX v3.0 release. Therefore, we'd like to encourage you to post any and all issues you find at https://github.com/spdx/tools-python/issues. -If you prefer a version that has been longer in use, please check out -the [latest release](https://github.com/spdx/tools-python/releases/tag/v0.7.0). +If you are looking for the source code of the [current PyPI release](https://pypi.python.org/pypi/spdx-tools), check out +the [v0.7.1 branch](https://github.com/spdx/tools-python/tree/release/v0.7.1). Note, though, that this will only receive bug fixes but no new features. +We encourage you to use the new, refactored version (on the main branch) if you +- want to use the soon-to-be released SPDX v3.0 in the future +- want to perform full validation of your SPDX documents against the v2.2 and v2.3 specification +- want to use the RDF format of SPDX with all v2.3 features. + +If you are planning to migrate from v0.7.x of these tools, +please have a look at the [migration guide](https://github.com/spdx/tools-python/wiki/How-to-migrate-from-0.7-to-1.0). + # Information This library implements SPDX parsers, convertors, validators and handlers in Python. From 63dc18a65e557cb8d6383c45dba31637540a6aad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 24 Mar 2023 09:19:50 +0100 Subject: [PATCH 362/362] [issue-487] add tests for the document_utils module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- tests/spdx/test_document_utils.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/spdx/test_document_utils.py diff --git a/tests/spdx/test_document_utils.py b/tests/spdx/test_document_utils.py new file mode 100644 index 000000000..f2b0fb2ad --- /dev/null +++ b/tests/spdx/test_document_utils.py @@ -0,0 +1,28 @@ +# SPDX-FileCopyrightText: 2023 spdx contributors +# +# SPDX-License-Identifier: Apache-2.0 +from unittest import TestCase + +import pytest + +from spdx.document_utils import get_element_from_spdx_id, get_contained_spdx_element_ids +from tests.spdx.fixtures import document_fixture, snippet_fixture, package_fixture, file_fixture + + +@pytest.fixture +def variables(): + return document_fixture(), package_fixture(), file_fixture(), snippet_fixture() + + +def test_contained_element_ids(variables): + document, package, file, snippet = variables + element_ids = get_contained_spdx_element_ids(document) + TestCase().assertCountEqual(element_ids, [package.spdx_id, file.spdx_id, snippet.spdx_id]) + + +def test_get_element_from_spdx_id(variables): + document, package, file, snippet = variables + assert get_element_from_spdx_id(document, package.spdx_id) == package + assert get_element_from_spdx_id(document, file.spdx_id) == file + assert get_element_from_spdx_id(document, snippet.spdx_id) == snippet + assert get_element_from_spdx_id(document, "unknown_id") is None