Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions cyclonedx/model/bom.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from .._internal.compare import ComparableTuple as _ComparableTuple
from .._internal.time import get_now_utc as _get_now_utc
from ..exception.model import LicenseExpressionAlongWithOthersException, UnknownComponentDependencyException
from ..schema.deprecation import SchemaDeprecationWarning1Dot6
from ..schema.schema import (
SchemaVersion1Dot0,
SchemaVersion1Dot1,
Expand Down Expand Up @@ -291,10 +292,7 @@ def manufacture(self, manufacture: Optional[OrganizationalEntity]) -> None:
we should set this data on `.component.manufacturer`.
"""
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

@property
Expand Down
7 changes: 3 additions & 4 deletions cyclonedx/model/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
SerializationOfUnexpectedValueException,
SerializationOfUnsupportedComponentTypeException,
)
from ..schema.deprecation import SchemaDeprecationWarning1Dot3, SchemaDeprecationWarning1Dot6
from ..schema.schema import (
SchemaVersion1Dot0,
SchemaVersion1Dot1,
Expand Down Expand Up @@ -1185,8 +1186,7 @@ def author(self) -> Optional[str]:
@author.setter
def author(self, author: Optional[str]) -> None:
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

@property
Expand Down Expand Up @@ -1467,8 +1467,7 @@ def modified(self) -> bool:
@modified.setter
def modified(self, modified: bool) -> None:
if modified:
warn('`@.modified` is deprecated from CycloneDX v1.3 onwards. '
'Please use `@.pedigree` instead.', DeprecationWarning)
SchemaDeprecationWarning1Dot3._warn('@.modified', '@.pedigree')
self._modified = modified

@property
Expand Down
6 changes: 2 additions & 4 deletions cyclonedx/model/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
from collections.abc import Iterable
from itertools import chain
from typing import TYPE_CHECKING, Any, Optional, Union
from warnings import warn
from xml.etree.ElementTree import Element # nosec B405

import py_serializable as serializable
Expand All @@ -28,6 +27,7 @@

from .._internal.compare import ComparableTuple as _ComparableTuple
from ..schema import SchemaVersion
from ..schema.deprecation import SchemaDeprecationWarning1Dot5
from ..schema.schema import SchemaVersion1Dot4, SchemaVersion1Dot5, SchemaVersion1Dot6, SchemaVersion1Dot7
from . import ExternalReference, HashType, _HashTypeRepositorySerializationHelper
from .component import Component
Expand Down Expand Up @@ -240,9 +240,7 @@ def tools(self) -> 'SortedSet[Tool]':
@tools.setter
def tools(self, tools: Iterable[Tool]) -> None:
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)

def __len__(self) -> int:
Expand Down
129 changes: 129 additions & 0 deletions cyclonedx/schema/deprecation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# 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
14 changes: 10 additions & 4 deletions examples/complex_deserialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# Copyright (c) OWASP Foundation. All Rights Reserved.

import sys
import warnings
from json import loads as json_loads
from typing import TYPE_CHECKING

Expand All @@ -24,6 +25,7 @@
from cyclonedx.exception import MissingOptionalDependencyException
from cyclonedx.model.bom import Bom
from cyclonedx.schema import OutputFormat, SchemaVersion
from cyclonedx.schema.deprecation import BaseSchemaDeprecationWarning
from cyclonedx.validation import make_schemabased_validator
from cyclonedx.validation.json import JsonStrictValidator

Expand Down Expand Up @@ -154,8 +156,10 @@
print('JSON valid')
except MissingOptionalDependencyException as error:
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))

# endregion JSON
Expand Down Expand Up @@ -255,8 +259,10 @@
print('XML valid')
except MissingOptionalDependencyException as error:
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))

# endregion XML
Expand Down
Loading