cyclonedx-python-lib
Advanced tools
| # This file is part of CycloneDX Python Library | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # Copyright (c) OWASP Foundation. All Rights Reserved. | ||
| """ | ||
| CycloneDX Schema Deprecation Warnings | ||
| ===================================== | ||
| This module provides warning classes for deprecated features in CycloneDX schemas. | ||
| Each warning class corresponds to a specific schema version, enabling downstream | ||
| code to catch, filter, or otherwise handle schema-specific deprecation warnings. | ||
| Intended Usage | ||
| -------------- | ||
| Downstream consumers can manage warnings using Python's ``warnings`` module. | ||
| Common scenarios include: | ||
| - Filtering by schema version | ||
| - Suppressing warnings in tests or batch processing | ||
| - Logging or reporting deprecation warnings without raising exceptions | ||
| Example | ||
| ------- | ||
| .. code-block:: python | ||
| import warnings | ||
| from cyclonedx.schema.deprecation import ( | ||
| BaseSchemaDeprecationWarning, | ||
| SchemaDeprecationWarning1Dot7, | ||
| ) | ||
| # Suppress all CycloneDX schema deprecation warnings | ||
| warnings.filterwarnings("ignore", category=BaseSchemaDeprecationWarning) | ||
| # Suppress only warnings specific to schema version 1.7 | ||
| warnings.filterwarnings("ignore", category=SchemaDeprecationWarning1Dot7) | ||
| Notes | ||
| ----- | ||
| - All deprecation warnings inherit from :class:`BaseSchemaDeprecationWarning`. | ||
| - The ``SCHEMA_VERSION`` class variable indicates the CycloneDX schema version | ||
| where the feature became deprecated. | ||
| - These warning classes are designed for downstream **filtering and logging**, | ||
| not for raising exceptions. | ||
| """ | ||
| from abc import ABC | ||
| from typing import ClassVar, Literal, Optional | ||
| from warnings import warn | ||
| from . import SchemaVersion | ||
| __all__ = [ | ||
| 'BaseSchemaDeprecationWarning', | ||
| 'SchemaDeprecationWarning1Dot1', | ||
| 'SchemaDeprecationWarning1Dot2', | ||
| 'SchemaDeprecationWarning1Dot3', | ||
| 'SchemaDeprecationWarning1Dot4', | ||
| 'SchemaDeprecationWarning1Dot5', | ||
| 'SchemaDeprecationWarning1Dot6', | ||
| 'SchemaDeprecationWarning1Dot7', | ||
| ] | ||
| class BaseSchemaDeprecationWarning(DeprecationWarning, ABC): | ||
| """Base class for warnings about deprecated schema features.""" | ||
| SCHEMA_VERSION: ClassVar[SchemaVersion] | ||
| @classmethod | ||
| def _warn(cls, deprecated: str, instead: Optional[str] = None, *, stacklevel: int = 1) -> None: | ||
| """Internal API. Not part of the public interface.""" | ||
| msg = f'`{deprecated}` is deprecated from CycloneDX v{cls.SCHEMA_VERSION.to_version()} onwards.' | ||
| if instead: | ||
| msg += f' Please use `{instead}` instead.' | ||
| warn(msg, category=cls, stacklevel=stacklevel + 1) | ||
| class SchemaDeprecationWarning1Dot7(BaseSchemaDeprecationWarning): | ||
| """Class for warnings about deprecated schema features in CycloneDX 1.7""" | ||
| SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_7]] = SchemaVersion.V1_7 | ||
| class SchemaDeprecationWarning1Dot6(BaseSchemaDeprecationWarning): | ||
| """Class for warnings about deprecated schema features in CycloneDX 1.6""" | ||
| SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_6]] = SchemaVersion.V1_6 | ||
| class SchemaDeprecationWarning1Dot5(BaseSchemaDeprecationWarning): | ||
| """Class for warnings about deprecated schema features in CycloneDX 1.5""" | ||
| SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_5]] = SchemaVersion.V1_5 | ||
| class SchemaDeprecationWarning1Dot4(BaseSchemaDeprecationWarning): | ||
| """Class for warnings about deprecated schema features in CycloneDX 1.4""" | ||
| SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_4]] = SchemaVersion.V1_4 | ||
| class SchemaDeprecationWarning1Dot3(BaseSchemaDeprecationWarning): | ||
| """Class for warnings about deprecated schema features in CycloneDX 1.3""" | ||
| SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_3]] = SchemaVersion.V1_3 | ||
| class SchemaDeprecationWarning1Dot2(BaseSchemaDeprecationWarning): | ||
| """Class for warnings about deprecated schema features in CycloneDX 1.2""" | ||
| SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_2]] = SchemaVersion.V1_2 | ||
| class SchemaDeprecationWarning1Dot1(BaseSchemaDeprecationWarning): | ||
| """Class for warnings about deprecated schema features in CycloneDX 1.1""" | ||
| SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_1]] = SchemaVersion.V1_1 |
| # This file is part of CycloneDX Python Library | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # Copyright (c) OWASP Foundation. All Rights Reserved. | ||
| import json | ||
| import sys | ||
| from typing import TYPE_CHECKING, Optional | ||
| from cyclonedx.exception import MissingOptionalDependencyException | ||
| from cyclonedx.schema import OutputFormat, SchemaVersion | ||
| from cyclonedx.validation import make_schemabased_validator | ||
| if TYPE_CHECKING: | ||
| from cyclonedx.validation.json import JsonValidator | ||
| from cyclonedx.validation.xml import XmlValidator | ||
| """ | ||
| This example demonstrates how to validate CycloneDX documents (both JSON and XML). | ||
| Make sure to have the needed dependencies installed - install the library's extra 'validation' for that. | ||
| """ | ||
| # region Sample SBOMs | ||
| JSON_SBOM = """ | ||
| { | ||
| "bomFormat": "CycloneDX", | ||
| "specVersion": "1.5", | ||
| "version": 1, | ||
| "metadata": { | ||
| "component": { | ||
| "type": "application", | ||
| "name": "my-app", | ||
| "version": "1.0.0" | ||
| } | ||
| }, | ||
| "components": [] | ||
| } | ||
| """ | ||
| XML_SBOM = """<?xml version="1.0" encoding="UTF-8"?> | ||
| <bom xmlns="http://cyclonedx.org/schema/bom/1.5" version="1"> | ||
| <metadata> | ||
| <component type="application"> | ||
| <name>my-app</name> | ||
| <version>1.0.0</version> | ||
| </component> | ||
| </metadata> | ||
| </bom> | ||
| """ | ||
| INVALID_JSON_SBOM = """ | ||
| { | ||
| "bomFormat": "CycloneDX", | ||
| "specVersion": "1.5", | ||
| "metadata": { | ||
| "component": { | ||
| "type": "invalid-type", | ||
| "name": "my-app" | ||
| } | ||
| } | ||
| } | ||
| """ | ||
| # endregion Sample SBOMs | ||
| # region JSON Validation | ||
| print('--- JSON Validation ---') | ||
| # Create a JSON validator for a specific schema version | ||
| json_validator: 'JsonValidator' = make_schemabased_validator(OutputFormat.JSON, SchemaVersion.V1_5) | ||
| # 1. Validate valid SBOM | ||
| try: | ||
| validation_errors = json_validator.validate_str(JSON_SBOM) | ||
| except MissingOptionalDependencyException as error: | ||
| print('JSON validation was skipped:', error) | ||
| else: | ||
| if validation_errors: | ||
| print('JSON SBOM is unexpectedly invalid!', file=sys.stderr) | ||
| else: | ||
| print('JSON SBOM is valid') | ||
| # 2. Validate invalid SBOM and inspect details | ||
| print('\nChecking invalid JSON SBOM...') | ||
| try: | ||
| validation_errors = json_validator.validate_str(INVALID_JSON_SBOM) | ||
| except MissingOptionalDependencyException as error: | ||
| print('JSON validation was skipped:', error) | ||
| else: | ||
| if validation_errors: | ||
| print('Validation failed as expected.') | ||
| print(f'Error Message: {validation_errors.data.message}') | ||
| print(f'JSON Path: {validation_errors.data.json_path}') | ||
| print(f'Invalid Data: {validation_errors.data.instance}') | ||
| # endregion JSON Validation | ||
| print('\n' + '=' * 30 + '\n') | ||
| # region XML Validation | ||
| print('--- XML Validation ---') | ||
| xml_validator: 'XmlValidator' = make_schemabased_validator(OutputFormat.XML, SchemaVersion.V1_5) | ||
| try: | ||
| xml_validation_errors = xml_validator.validate_str(XML_SBOM) | ||
| if xml_validation_errors: | ||
| print('XML SBOM is invalid!', file=sys.stderr) | ||
| else: | ||
| print('XML SBOM is valid') | ||
| except MissingOptionalDependencyException as error: | ||
| print('XML validation was skipped:', error) | ||
| # endregion XML Validation | ||
| print('\n' + '=' * 30 + '\n') | ||
| # region Dynamic version detection | ||
| print('--- Dynamic Validation ---') | ||
| def _detect_json_format(raw_data: str) -> Optional[tuple[OutputFormat, SchemaVersion]]: | ||
| """Detect JSON format and extract schema version.""" | ||
| try: | ||
| data = json.loads(raw_data) | ||
| except json.JSONDecodeError: | ||
| return None | ||
| spec_version_str = data.get('specVersion') | ||
| try: | ||
| schema_version = SchemaVersion.from_version(spec_version_str) | ||
| except Exception: | ||
| print('failed to detect schema_version from', repr(spec_version_str), file=sys.stderr) | ||
| return None | ||
| return (OutputFormat.JSON, schema_version) | ||
| def _detect_xml_format(raw_data: str) -> Optional[tuple[OutputFormat, SchemaVersion]]: | ||
| try: | ||
| from lxml import etree # type: ignore[import-untyped] | ||
| except ImportError: | ||
| return None | ||
| try: | ||
| xml_tree = etree.fromstring(raw_data.encode('utf-8')) | ||
| except etree.XMLSyntaxError: | ||
| return None | ||
| for ns in xml_tree.nsmap.values(): | ||
| if ns and ns.startswith('http://cyclonedx.org/schema/bom/'): | ||
| version_str = ns.split('/')[-1] | ||
| try: | ||
| return (OutputFormat.XML, SchemaVersion.from_version(version_str)) | ||
| except Exception: | ||
| print('failed to detect schema_version from namespace', repr(ns), file=sys.stderr) | ||
| return None | ||
| print('failed to detect CycloneDX namespace in XML document', file=sys.stderr) | ||
| return None | ||
| def validate_sbom(raw_data: str) -> bool: | ||
| """Validate an SBOM by detecting its format and version.""" | ||
| # Detect format and version | ||
| format_info = _detect_json_format(raw_data) or _detect_xml_format(raw_data) | ||
| if not format_info: | ||
| return False | ||
| input_format, schema_version = format_info | ||
| try: | ||
| validator = make_schemabased_validator(input_format, schema_version) | ||
| errors = validator.validate_str(raw_data) | ||
| if errors: | ||
| print(f'Validation failed ({input_format.name} {schema_version.to_version()}): {errors}', | ||
| file=sys.stderr) | ||
| return False | ||
| print(f'Valid {input_format.name} SBOM (schema {schema_version.to_version()})') | ||
| return True | ||
| except MissingOptionalDependencyException as e: | ||
| print(f'Validation skipped (missing dependencies): {e}') | ||
| return False | ||
| # Execute dynamic validation | ||
| validate_sbom(JSON_SBOM) | ||
| validate_sbom(XML_SBOM) | ||
| # endregion Dynamic version detection |
@@ -25,2 +25,2 @@ # This file is part of CycloneDX Python Library | ||
| # do not use typing here, or else `semantic_release` might have issues finding the variable | ||
| __version__ = "11.6.0" # noqa:Q000 | ||
| __version__ = "11.7.0" # noqa:Q000 |
@@ -33,2 +33,3 @@ # This file is part of CycloneDX Python Library | ||
| from ..exception.model import LicenseExpressionAlongWithOthersException, UnknownComponentDependencyException | ||
| from ..schema.deprecation import SchemaDeprecationWarning1Dot6 | ||
| from ..schema.schema import ( | ||
@@ -295,6 +296,3 @@ SchemaVersion1Dot0, | ||
| if manufacture is not None: | ||
| warn( | ||
| '`bom.metadata.manufacture` is deprecated from CycloneDX v1.6 onwards. ' | ||
| 'Please use `bom.metadata.component.manufacturer` instead.', | ||
| DeprecationWarning) | ||
| SchemaDeprecationWarning1Dot6._warn('bom.metadata.manufacture', 'bom.metadata.component.manufacturer') | ||
| self._manufacture = manufacture | ||
@@ -301,0 +299,0 @@ |
@@ -43,2 +43,3 @@ # This file is part of CycloneDX Python Library | ||
| ) | ||
| from ..schema.deprecation import SchemaDeprecationWarning1Dot3, SchemaDeprecationWarning1Dot6 | ||
| from ..schema.schema import ( | ||
@@ -1189,4 +1190,3 @@ SchemaVersion1Dot0, | ||
| if author is not None: | ||
| warn('`@.author` is deprecated from CycloneDX v1.6 onwards. ' | ||
| 'Please use `@.authors` or `@.manufacturer` instead.', DeprecationWarning) | ||
| SchemaDeprecationWarning1Dot6._warn('@.author', '@.authors` or `@.manufacturer') | ||
| self._author = author | ||
@@ -1472,4 +1472,3 @@ | ||
| if modified: | ||
| warn('`@.modified` is deprecated from CycloneDX v1.3 onwards. ' | ||
| 'Please use `@.pedigree` instead.', DeprecationWarning) | ||
| SchemaDeprecationWarning1Dot3._warn('@.modified', '@.pedigree') | ||
| self._modified = modified | ||
@@ -1476,0 +1475,0 @@ |
@@ -23,2 +23,3 @@ # This file is part of CycloneDX Python Library | ||
| from collections.abc import Iterable | ||
| from enum import Enum | ||
@@ -38,3 +39,3 @@ from json import loads as json_loads | ||
| from ..schema.schema import SchemaVersion1Dot5, SchemaVersion1Dot6, SchemaVersion1Dot7 | ||
| from . import AttachedText, XsUri | ||
| from . import AttachedText, Property, XsUri | ||
| from .bom_ref import BomRef | ||
@@ -90,2 +91,3 @@ | ||
| acknowledgement: Optional[LicenseAcknowledgement] = None, | ||
| properties: Optional[Iterable[Property]] = None, | ||
| ) -> None: | ||
@@ -105,2 +107,3 @@ if not id and not name: | ||
| self._acknowledgement = acknowledgement | ||
| self._properties = SortedSet(properties or []) | ||
@@ -207,14 +210,22 @@ @property | ||
| # @property | ||
| # ... | ||
| # @serializable.view(SchemaVersion1Dot5) | ||
| # @serializable.view(SchemaVersion1Dot6) | ||
| # @serializable.xml_sequence(6) | ||
| # def properties(self) -> ...: | ||
| # ... # TODO since CDX1.5 | ||
| # | ||
| # @licensing.setter | ||
| # def properties(self, ...) -> None: | ||
| # ... # TODO since CDX1.5 | ||
| @property | ||
| @serializable.view(SchemaVersion1Dot5) | ||
| @serializable.view(SchemaVersion1Dot6) | ||
| @serializable.view(SchemaVersion1Dot7) | ||
| @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'property') | ||
| @serializable.xml_sequence(6) | ||
| def properties(self) -> 'SortedSet[Property]': | ||
| """ | ||
| Provides the ability to document properties in a key/value store. This provides flexibility to include data not | ||
| officially supported in the standard without having to use additional namespaces or create extensions. | ||
| Return: | ||
| Set of `Property` | ||
| """ | ||
| return self._properties | ||
| @properties.setter | ||
| def properties(self, properties: Iterable[Property]) -> None: | ||
| self._properties = SortedSet(properties) | ||
| @property | ||
@@ -253,2 +264,3 @@ @serializable.view(SchemaVersion1Dot6) | ||
| self._bom_ref.value, | ||
| _ComparableTuple(self._properties), | ||
| )) | ||
@@ -255,0 +267,0 @@ |
@@ -22,3 +22,2 @@ # This file is part of CycloneDX Python Library | ||
| from typing import TYPE_CHECKING, Any, Optional, Union | ||
| from warnings import warn | ||
| from xml.etree.ElementTree import Element # nosec B405 | ||
@@ -32,2 +31,3 @@ | ||
| from ..schema import SchemaVersion | ||
| from ..schema.deprecation import SchemaDeprecationWarning1Dot5 | ||
| from ..schema.schema import SchemaVersion1Dot4, SchemaVersion1Dot5, SchemaVersion1Dot6, SchemaVersion1Dot7 | ||
@@ -245,5 +245,3 @@ from . import ExternalReference, HashType, _HashTypeRepositorySerializationHelper | ||
| if tools: | ||
| warn('`@.tools` is deprecated from CycloneDX v1.5 onwards. ' | ||
| 'Please use `@.components` and `@.services` instead.', | ||
| DeprecationWarning) | ||
| SchemaDeprecationWarning1Dot5._warn('@.tools', '@.components` and `@.services') | ||
| self._tools = SortedSet(tools) | ||
@@ -250,0 +248,0 @@ |
@@ -26,4 +26,4 @@ # This file is part of CycloneDX Python Library | ||
| class BaseSchemaVersion(ABC, ViewType): | ||
| class BaseSchemaVersion(ViewType, ABC): | ||
| """Base class for schema version views.""" | ||
| @property | ||
@@ -39,3 +39,3 @@ @abstractmethod | ||
| class SchemaVersion1Dot7(BaseSchemaVersion): | ||
| """Schema version views 1.7""" | ||
| @property | ||
@@ -47,3 +47,3 @@ def schema_version_enum(self) -> Literal[SchemaVersion.V1_7]: | ||
| class SchemaVersion1Dot6(BaseSchemaVersion): | ||
| """Schema version views 1.6""" | ||
| @property | ||
@@ -55,3 +55,3 @@ def schema_version_enum(self) -> Literal[SchemaVersion.V1_6]: | ||
| class SchemaVersion1Dot5(BaseSchemaVersion): | ||
| """Schema version views 1.5""" | ||
| @property | ||
@@ -63,3 +63,3 @@ def schema_version_enum(self) -> Literal[SchemaVersion.V1_5]: | ||
| class SchemaVersion1Dot4(BaseSchemaVersion): | ||
| """Schema version views 1.4""" | ||
| @property | ||
@@ -71,3 +71,3 @@ def schema_version_enum(self) -> Literal[SchemaVersion.V1_4]: | ||
| class SchemaVersion1Dot3(BaseSchemaVersion): | ||
| """Schema version views 1.3""" | ||
| @property | ||
@@ -79,3 +79,3 @@ def schema_version_enum(self) -> Literal[SchemaVersion.V1_3]: | ||
| class SchemaVersion1Dot2(BaseSchemaVersion): | ||
| """Schema version views 1.2""" | ||
| @property | ||
@@ -87,3 +87,3 @@ def schema_version_enum(self) -> Literal[SchemaVersion.V1_2]: | ||
| class SchemaVersion1Dot1(BaseSchemaVersion): | ||
| """Schema version views 1.1""" | ||
| @property | ||
@@ -95,3 +95,3 @@ def schema_version_enum(self) -> Literal[SchemaVersion.V1_1]: | ||
| class SchemaVersion1Dot0(BaseSchemaVersion): | ||
| """Schema version views 1.0""" | ||
| @property | ||
@@ -98,0 +98,0 @@ def schema_version_enum(self) -> Literal[SchemaVersion.V1_0]: |
+1
-1
@@ -26,3 +26,3 @@ # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # !! version is managed by semantic_release | ||
| release = '11.6.0' | ||
| release = '11.7.0' | ||
@@ -29,0 +29,0 @@ # -- General configuration --------------------------------------------------- |
@@ -32,1 +32,9 @@ .. # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| :linenos: | ||
| Complex Validation | ||
| ------------------ | ||
| .. literalinclude:: ../examples/complex_validation.py | ||
| :language: python | ||
| :linenos: |
| m2r2>=0.3.2 | ||
| sphinx>=7.2.6,<9 | ||
| sphinx-autoapi>=3.0.0,<4 | ||
| sphinx-rtd-theme>=2.0.0,<3 | ||
| sphinx>=8,<9 | ||
| sphinx-autoapi>=3,<4 | ||
| sphinx-rtd-theme>=3,<4 |
@@ -19,2 +19,3 @@ # This file is part of CycloneDX Python Library | ||
| import sys | ||
| import warnings | ||
| from json import loads as json_loads | ||
@@ -28,2 +29,3 @@ from typing import TYPE_CHECKING | ||
| from cyclonedx.schema import OutputFormat, SchemaVersion | ||
| from cyclonedx.schema.deprecation import BaseSchemaDeprecationWarning | ||
| from cyclonedx.validation import make_schemabased_validator | ||
@@ -159,4 +161,6 @@ from cyclonedx.validation.json import JsonStrictValidator | ||
| print('JSON-validation was skipped due to', error) | ||
| bom_from_json = Bom.from_json( # type: ignore[attr-defined] | ||
| json_loads(json_data)) | ||
| with warnings.catch_warnings(): | ||
| warnings.filterwarnings('ignore', category=BaseSchemaDeprecationWarning) | ||
| bom_from_json = Bom.from_json( # type: ignore[attr-defined] | ||
| json_loads(json_data)) | ||
| print('bom_from_json', repr(bom_from_json)) | ||
@@ -261,4 +265,6 @@ | ||
| print('XML-validation was skipped due to', error) | ||
| bom_from_xml = Bom.from_xml( # type: ignore[attr-defined] | ||
| SafeElementTree.fromstring(xml_data)) | ||
| with warnings.catch_warnings(): | ||
| warnings.filterwarnings('ignore', category=BaseSchemaDeprecationWarning) | ||
| bom_from_xml = Bom.from_xml( # type: ignore[attr-defined] | ||
| SafeElementTree.fromstring(xml_data)) | ||
| print('bom_from_xml', repr(bom_from_xml)) | ||
@@ -265,0 +271,0 @@ |
+1
-1
| Metadata-Version: 2.4 | ||
| Name: cyclonedx-python-lib | ||
| Version: 11.6.0 | ||
| Version: 11.7.0 | ||
| Summary: Python library for CycloneDX | ||
@@ -5,0 +5,0 @@ License: Apache-2.0 |
+6
-3
@@ -8,3 +8,3 @@ [build-system] | ||
| # !! version is managed by semantic_release | ||
| version = "11.6.0" | ||
| version = "11.7.0" | ||
| description = "Python library for CycloneDX" | ||
@@ -101,3 +101,3 @@ authors = [ | ||
| autopep8 = "2.3.2" | ||
| mypy = "1.19.0" | ||
| mypy = "1.19.1" | ||
| tomli = { version = "2.3.0", python = "<3.11" } | ||
@@ -107,3 +107,6 @@ tox = "4.30.3" | ||
| bandit = "1.8.6" | ||
| pyupgrade = "3.21.0" | ||
| pyupgrade = [ | ||
| { version = "3.21.0", python = "<3.10" }, | ||
| { version = "3.21.2", python = ">=3.10" }, | ||
| ] | ||
| deptry = "0.23.1" | ||
@@ -110,0 +113,0 @@ # for tests, use the GPL-version of jsonschema format validators - they are faster |
@@ -1085,2 +1085,10 @@ # This file is part of CycloneDX Python Library | ||
| ]), | ||
| Component(name='c-with-license-properties', type=ComponentType.LIBRARY, bom_ref='C4', | ||
| licenses=[ | ||
| DisjunctiveLicense(id='Apache-2.0', | ||
| properties=[Property(name='key1', value='val1'), | ||
| Property(name='key2', value='val2')]), | ||
| DisjunctiveLicense(name='some other license', | ||
| properties=[Property(name='myname', value='proprietary')]), | ||
| ]), | ||
| ], | ||
@@ -1087,0 +1095,0 @@ services=[ |
@@ -24,3 +24,3 @@ # This file is part of CycloneDX Python Library | ||
| from cyclonedx.exception.model import MutuallyExclusivePropertiesException | ||
| from cyclonedx.model import AttachedText, XsUri | ||
| from cyclonedx.model import AttachedText, Property, XsUri | ||
| from cyclonedx.model.license import DisjunctiveLicense, LicenseExpression | ||
@@ -85,3 +85,21 @@ from tests import reorder | ||
| def test_create_with_properties(self) -> None: | ||
| properties = [Property(name='key1', value='value1')] | ||
| license = DisjunctiveLicense(id='MIT', properties=properties) | ||
| self.assertEqual(1, len(license.properties)) | ||
| def test_set_properties(self) -> None: | ||
| license = DisjunctiveLicense(id='MIT') | ||
| self.assertEqual(0, len(license.properties)) | ||
| license.properties = [Property(name='key1', value='value1')] | ||
| self.assertEqual(1, len(license.properties)) | ||
| def test_equal_with_properties(self) -> None: | ||
| a = DisjunctiveLicense(id='MIT', properties=[Property(name='key1', value='value1')]) | ||
| b = DisjunctiveLicense(id='MIT', properties=[Property(name='key1', value='value1')]) | ||
| c = DisjunctiveLicense(id='MIT') | ||
| self.assertEqual(a, b) | ||
| self.assertNotEqual(a, c) | ||
| class TestModelLicenseExpression(TestCase): | ||
@@ -88,0 +106,0 @@ def test_create(self) -> None: |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
8180496
0.26%1673
0.12%62982
0.48%