Skip to content

Commit 15a9023

Browse files
authored
feat: Moved non‑standard implementations to Contrib area (#916)
* Added - New sub-package `contrib`. * Changed - Moved non‑standard implementations to Contrib area. * Deprecated - Certain exports have been deprecated; downstream imports should be updated to the new locations. Note: the symbols themselves remain supported. See documentation and the "Refactored" section below for details. - Some (trivial) non-standard functionality was deprecated: - `model.bom.Bom.get_component_by_purl()` - `model.bom.Bom.get_urn_uuid()` - `model.bom.Bom.has_component()` - `model.bom.Bom.get_vulnerabilities_for_bom_ref()` - `model.bom.Bom.has_vulnerabilities()` - `model.bom.Bom.urn()` * Refactored * The following symbols were moved. The symbols are still import-able through their old location. * **OLD** -> **NEW** - `builder.this.this_component()` -> `contrib.this.builders.this_component()` - `builder.this.this_tool()` -> `contrib.this.builders.this_tool()` - `exception.factory.*` -> `contrib.license.exceptions.*` - `factory.license.LicenseFactory` -> `contrib.license.factories.LicenseFactory` - `model.HashType.from_hashlib_alg()` -> `contrib.hash.factories.HashTypeFactory.from_hashlib_alg()` - `model.HashType.from_composite_str()` -> `contrib.hash.factories.HashTypeFactory.from_composite_str()` - `model.component.Component.for_file()` -> `contrib.component.builders.ComponentBuilder.make_for_file()` - `model.vulnerability.VulnerabilitySeverity.get_from_cvss_scores()` -> `contrib.vulnerability.cvss.vs_from_cvss_scores()` --------- Signed-off-by: Jan Kowalleck <jan.kowalleck@gmail.com>
1 parent 7e6771b commit 15a9023

33 files changed

+842
-338
lines changed

cyclonedx/builder/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@
1717

1818
"""
1919
Builders used in this library.
20+
21+
.. deprecated:: next
2022
"""

cyclonedx/builder/this.py

Lines changed: 41 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -15,69 +15,51 @@
1515
# SPDX-License-Identifier: Apache-2.0
1616
# Copyright (c) OWASP Foundation. All Rights Reserved.
1717

18-
"""Representation of this very python library."""
18+
"""Representation of this very python library.
1919
20-
__all__ = ['this_component', 'this_tool', ]
20+
.. deprecated:: next
21+
"""
2122

22-
from .. import __version__ as __ThisVersion # noqa: N812
23-
from ..model import ExternalReference, ExternalReferenceType, XsUri
24-
from ..model.component import Component, ComponentType
25-
from ..model.license import DisjunctiveLicense, LicenseAcknowledgement
26-
from ..model.tool import Tool
23+
__all__ = ['this_component', 'this_tool']
2724

28-
# !!! keep this file in sync with `pyproject.toml`
25+
import sys
26+
from typing import TYPE_CHECKING
2927

28+
if sys.version_info >= (3, 13):
29+
from warnings import deprecated
30+
else:
31+
from typing_extensions import deprecated
3032

31-
def this_component() -> Component:
32-
"""Representation of this very python library as a :class:`Component`."""
33-
return Component(
34-
type=ComponentType.LIBRARY,
35-
group='CycloneDX',
36-
name='cyclonedx-python-lib',
37-
version=__ThisVersion or 'UNKNOWN',
38-
description='Python library for CycloneDX',
39-
licenses=(DisjunctiveLicense(id='Apache-2.0',
40-
acknowledgement=LicenseAcknowledgement.DECLARED),),
41-
external_references=(
42-
# let's assume this is not a fork
43-
ExternalReference(
44-
type=ExternalReferenceType.WEBSITE,
45-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/#readme')
46-
),
47-
ExternalReference(
48-
type=ExternalReferenceType.DOCUMENTATION,
49-
url=XsUri('https://cyclonedx-python-library.readthedocs.io/')
50-
),
51-
ExternalReference(
52-
type=ExternalReferenceType.VCS,
53-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib')
54-
),
55-
ExternalReference(
56-
type=ExternalReferenceType.BUILD_SYSTEM,
57-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/actions')
58-
),
59-
ExternalReference(
60-
type=ExternalReferenceType.ISSUE_TRACKER,
61-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/issues')
62-
),
63-
ExternalReference(
64-
type=ExternalReferenceType.LICENSE,
65-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE')
66-
),
67-
ExternalReference(
68-
type=ExternalReferenceType.RELEASE_NOTES,
69-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md')
70-
),
71-
# we cannot assert where the lib was fetched from, but we can give a hint
72-
ExternalReference(
73-
type=ExternalReferenceType.DISTRIBUTION,
74-
url=XsUri('https://pypi.org/project/cyclonedx-python-lib/')
75-
),
76-
),
77-
# to be extended...
78-
)
33+
from ..contrib.this.builders import this_component as _this_component, this_tool as _this_tool
7934

35+
# region deprecated re-export
8036

81-
def this_tool() -> Tool:
82-
"""Representation of this very python library as a :class:`Tool`."""
83-
return Tool.from_component(this_component())
37+
if TYPE_CHECKING:
38+
from ..model.component import Component
39+
from ..model.tool import Tool
40+
41+
42+
@deprecated('Deprecated re-export location - see docstring of "this_component" for details.')
43+
def this_component() -> 'Component':
44+
"""Deprecated — Alias of :func:`cyclonedx.contrib.this.builders.this_component`.
45+
46+
.. deprecated:: next
47+
This re-export location is deprecated.
48+
Use ``from cyclonedx.contrib.this.builders import this_component`` instead.
49+
The exported symbol itself is NOT deprecated — only this import path.
50+
"""
51+
return _this_component()
52+
53+
54+
@deprecated('Deprecated re-export location - see docstring of "this_tool" for details.')
55+
def this_tool() -> 'Tool':
56+
"""Deprecated — Alias of :func:`cyclonedx.contrib.this.builders.this_tool`.
57+
58+
.. deprecated:: next
59+
This re-export location is deprecated.
60+
Use ``from cyclonedx.contrib.this.builders import this_tool`` instead.
61+
The exported symbol itself is NOT deprecated — only this import path.
62+
"""
63+
return _this_tool()
64+
65+
# endregion deprecated re-export

cyclonedx/contrib/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# CycloneDX Contrib Extensions
2+
3+
This directory contains community-contributed functionality that extends the capabilities of the CycloneDX core library.
4+
Unlike the modules in `../`, these features are not part of the official CycloneDX specification and may vary in stability, scope, or compatibility.
5+
6+
## Contents
7+
- Utilities, helpers, and experimental features developed by the community
8+
- Optional add-ons that may facilitate or enhance use of the CycloneDX core library
9+
- Code that evolves independently of the CycloneDX specification
10+
11+
## Notes
12+
- Contrib modules are optional and not required for strict compliance with the CycloneDX standard.
13+
- They may change more frequently than the core and are not guaranteed to follow the same versioning rules.
14+
- Users should evaluate these modules carefully and consult documentation or source comments for details.
15+
16+
## Contributing
17+
Contributions are welcome. To add an extension:
18+
1. Follow the contribution guidelines in the main repository.
19+
2. Place your code in a clearly named subfolder or file under `contrib/`.
20+
3. Provide documentation and tests to ensure clarity and maintainability.

cyclonedx/_internal/hash.py renamed to cyclonedx/contrib/__init__.py

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,11 @@
1717

1818

1919
"""
20-
!!! ALL SYMBOLS IN HERE ARE INTERNAL.
21-
Everything might change without any notice.
20+
Some features in this library are marked as contrib.
21+
These are community-provided extensions and are not part of the official standard.
22+
They are optional and may evolve independently from the core.
2223
"""
2324

24-
25-
from hashlib import sha1
26-
27-
28-
def file_sha1sum(filename: str) -> str:
29-
"""
30-
Generate a SHA1 hash of the provided file.
31-
32-
Args:
33-
filename:
34-
Absolute path to file to hash as `str`
35-
36-
Returns:
37-
SHA-1 hash
38-
"""
39-
h = sha1() # nosec B303, B324
40-
with open(filename, 'rb') as f:
41-
for byte_block in iter(lambda: f.read(4096), b''):
42-
h.update(byte_block)
43-
return h.hexdigest()
25+
__all__ = [
26+
# there is no intention to export anything in here.
27+
]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# This file is part of CycloneDX Python Library
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
# SPDX-License-Identifier: Apache-2.0
16+
# Copyright (c) OWASP Foundation. All Rights Reserved.
17+
18+
"""Component related functionality"""
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# This file is part of CycloneDX Python Library
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
# SPDX-License-Identifier: Apache-2.0
16+
# Copyright (c) OWASP Foundation. All Rights Reserved.
17+
18+
"""Component related builders"""
19+
20+
__all__ = ['ComponentBuilder']
21+
22+
from hashlib import sha1
23+
from os.path import exists
24+
from typing import Optional
25+
26+
from ...model import HashAlgorithm, HashType
27+
from ...model.component import Component, ComponentType
28+
29+
30+
class ComponentBuilder:
31+
32+
def make_for_file(self, absolute_file_path: str, *,
33+
name: Optional[str]) -> Component:
34+
"""
35+
Helper method to create a :class:`cyclonedx.model.component.Component`
36+
that represents the provided local file as a Component.
37+
38+
Args:
39+
absolute_file_path:
40+
Absolute path to the file you wish to represent
41+
name:
42+
Optionally, if supplied this is the name that will be used for the component.
43+
Defaults to arg ``absolute_file_path``.
44+
45+
Returns:
46+
`Component` representing the supplied file
47+
"""
48+
if not exists(absolute_file_path):
49+
raise FileExistsError(f'Supplied file path {absolute_file_path!r} does not exist')
50+
51+
return Component(
52+
type=ComponentType.FILE,
53+
name=name or absolute_file_path,
54+
hashes=[
55+
HashType(alg=HashAlgorithm.SHA_1, content=self._file_sha1sum(absolute_file_path))
56+
]
57+
)
58+
59+
@staticmethod
60+
def _file_sha1sum(filename: str) -> str:
61+
"""
62+
Generate a SHA1 hash of the provided file.
63+
64+
Args:
65+
filename:
66+
Absolute path to file to hash as `str`
67+
68+
Returns:
69+
SHA-1 hash
70+
"""
71+
h = sha1() # nosec B303, B324
72+
with open(filename, 'rb') as f:
73+
for byte_block in iter(lambda: f.read(4096), b''):
74+
h.update(byte_block)
75+
return h.hexdigest()

cyclonedx/contrib/hash/__init__.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# This file is part of CycloneDX Python Library
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
# SPDX-License-Identifier: Apache-2.0
16+
# Copyright (c) OWASP Foundation. All Rights Reserved.
17+
18+
"""Hash related functionality"""

0 commit comments

Comments
 (0)