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..2e3f41c11 100644 --- a/src/spdx/parser/jsonlikedict/license_expression_parser.py +++ b/src/spdx/parser/jsonlikedict/license_expression_parser.py @@ -10,31 +10,42 @@ # 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: - 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()) + 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/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..ee99645eb 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, write_license_info_list def write_file(file: File, text_output: TextIO): @@ -28,8 +27,8 @@ 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) + 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 045e509e4..b5abeb1c7 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, write_license_info_list def write_package(package: Package, text_output: TextIO): @@ -39,9 +39,9 @@ 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) + 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 dc8553e9f..62355d5d6 100644 --- a/src/spdx/writer/tagvalue/snippet_writer.py +++ b/src/spdx/writer/tagvalue/snippet_writer.py @@ -12,24 +12,24 @@ 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 + 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_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) + 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) diff --git a/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py b/src/spdx/writer/tagvalue/tagvalue_writer_helper_functions.py index 445b6c207..4bf7c71c3 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 @@ -24,8 +24,8 @@ def write_separator(out: TextIO): out.write("\n") -def write_value(tag: str, value: Optional[Union[bool, str, SpdxNone, SpdxNoAssertion]], out: TextIO): - if value: +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) @@ -65,17 +74,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'),