Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f6430ec
feat: prepare "contrib" area
jkowalleck Nov 26, 2025
a8a4190
feat: prepare "contrib" area
jkowalleck Nov 27, 2025
0e68f6e
feat: prepare "contrib" area
jkowalleck Nov 27, 2025
3c2ad7d
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
3947eb7
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
885d47e
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
bd37a9e
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
119690d
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
562049c
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
ef83a07
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
7d14191
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
b88d831
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
28b94a7
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
66d199c
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
c1abdc3
feat: prep contrib
jkowalleck Nov 30, 2025
f8c43e6
feat: prepare "contrib" area
jkowalleck Nov 30, 2025
c0ca9bc
feat: prepare "contrib" area
jkowalleck Dec 1, 2025
be8d607
feat: prepare "contrib" area
jkowalleck Dec 1, 2025
840fbd4
feat: prepare "contrib" area
jkowalleck Dec 1, 2025
f61abe9
feat: prepare "contrib" area
jkowalleck Dec 1, 2025
5b98ab4
feat: prepare "contrib" area
jkowalleck Dec 1, 2025
07c2a70
feat: prepare "contrib" area
jkowalleck Dec 1, 2025
0e36aac
feat: prepare "contrib" area
jkowalleck Dec 1, 2025
bccb7e3
feat: prepare "contrib" area
jkowalleck Dec 1, 2025
ff0e11d
typos
jkowalleck Dec 2, 2025
9436747
typos
jkowalleck Dec 2, 2025
0d0443d
typos
jkowalleck Dec 2, 2025
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
95 changes: 37 additions & 58 deletions cyclonedx/builder/this.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,67 +17,46 @@

"""Representation of this very python library."""

__all__ = ['this_component', 'this_tool', ]
__all__ = ['this_component', 'this_tool']

from .. import __version__ as __ThisVersion # noqa: N812
from ..model import ExternalReference, ExternalReferenceType, XsUri
from ..model.component import Component, ComponentType
from ..model.license import DisjunctiveLicense, LicenseAcknowledgement
from ..model.tool import Tool
import sys
from typing import TYPE_CHECKING

# !!! keep this file in sync with `pyproject.toml`
if sys.version_info >= (3, 13):
from warnings import deprecated
else:
from typing_extensions import deprecated

from ..contrib.this.builders import this_component as _this_component, this_tool as _this_tool

def this_component() -> Component:
"""Representation of this very python library as a :class:`Component`."""
return Component(
type=ComponentType.LIBRARY,
group='CycloneDX',
name='cyclonedx-python-lib',
version=__ThisVersion or 'UNKNOWN',
description='Python library for CycloneDX',
licenses=(DisjunctiveLicense(id='Apache-2.0',
acknowledgement=LicenseAcknowledgement.DECLARED),),
external_references=(
# let's assume this is not a fork
ExternalReference(
type=ExternalReferenceType.WEBSITE,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/#readme')
),
ExternalReference(
type=ExternalReferenceType.DOCUMENTATION,
url=XsUri('https://cyclonedx-python-library.readthedocs.io/')
),
ExternalReference(
type=ExternalReferenceType.VCS,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib')
),
ExternalReference(
type=ExternalReferenceType.BUILD_SYSTEM,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/actions')
),
ExternalReference(
type=ExternalReferenceType.ISSUE_TRACKER,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/issues')
),
ExternalReference(
type=ExternalReferenceType.LICENSE,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE')
),
ExternalReference(
type=ExternalReferenceType.RELEASE_NOTES,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md')
),
# we cannot assert where the lib was fetched from, but we can give a hint
ExternalReference(
type=ExternalReferenceType.DISTRIBUTION,
url=XsUri('https://pypi.org/project/cyclonedx-python-lib/')
),
),
# to be extended...
)
# region deprecated re-export

if TYPE_CHECKING:
from ..model.component import Component
from ..model.tool import Tool

def this_tool() -> Tool:
"""Representation of this very python library as a :class:`Tool`."""
return Tool.from_component(this_component())

@deprecated('Deprecated re-export location - see docstring of "this_component" for details.')
def this_component() -> 'Component':
"""Deprecated — Alias of :func:`cyclonedx.contrib.this.builders.this_component`.

.. deprecated:: next
This re-export location is deprecated.
Use ``from cyclonedx.contrib.this.builders import this_component`` instead.
The exported symbol itself is NOT deprecated — only this import path.
"""
return _this_component()


@deprecated('Deprecated re-export location - see docstring of "this_tool" for details.')
def this_tool() -> 'Tool':
"""Deprecated — Alias of :func:`cyclonedx.contrib.this.builders.this_tool`.

.. deprecated:: next
This re-export location is deprecated.
Use ``from cyclonedx.contrib.this.builders import this_tool`` instead.
The exported symbol itself is NOT deprecated — only this import path.
"""
return _this_tool()

# endregion deprecated re-export
20 changes: 20 additions & 0 deletions cyclonedx/contrib/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# CycloneDX Contrib Extensions

This directory contains community-contributed functionality that extends the capabilities of the CycloneDX core library.
Unlike the modules in `../`, these features are not part of the official CycloneDX specification and may vary in stability, scope, or compatibility.

## Contents
- Utilities, helpers, and experimental features developed by the community
- Optional add-ons that may facilitate or enhance use of the CycloneDX core library
- Code that evolves independently of the CycloneDX specification

## Notes
- Contrib modules are optional and not required for strict compliance with the CycloneDX standard.
- They may change more frequently than the core and are not guaranteed to follow the same versioning rules.
- Users should evaluate these modules carefully and consult documentation or source comments for details.

## Contributing
Contributions are welcome. To add an extension:
1. Follow the contribution guidelines in the main repository.
2. Place your code in a clearly named subfolder or file under `contrib/`.
3. Provide documentation and tests to ensure clarity and maintainability.
26 changes: 26 additions & 0 deletions cyclonedx/contrib/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# 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.


"""
Some features in this library are marked as contrib. These are community-provided extensions and are not part of the
official standard. They are optional and may evolve independently from the core.
"""

__all__ = [
# there is no intention to export anything in here.
]
16 changes: 16 additions & 0 deletions cyclonedx/contrib/license/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# 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.
90 changes: 90 additions & 0 deletions cyclonedx/contrib/license/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# 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.

__all__ = ['LicenseFactory']

from typing import TYPE_CHECKING, Optional

from ...exception.factory import InvalidLicenseExpressionException, InvalidSpdxLicenseException
from ...model.license import DisjunctiveLicense, LicenseExpression
from ...spdx import fixup_id as spdx_fixup, is_expression as is_spdx_expression

if TYPE_CHECKING: # pragma: no cover
from ...model import AttachedText, XsUri
from ...model.license import License, LicenseAcknowledgement


class LicenseFactory:
"""Factory for :class:`cyclonedx.model.license.License`."""

def make_from_string(self, value: str, *,
license_text: Optional['AttachedText'] = None,
license_url: Optional['XsUri'] = None,
license_acknowledgement: Optional['LicenseAcknowledgement'] = None
) -> 'License':
"""Make a :class:`cyclonedx.model.license.License` from a string."""
try:
return self.make_with_id(value,
text=license_text,
url=license_url,
acknowledgement=license_acknowledgement)
except InvalidSpdxLicenseException:
pass
try:
return self.make_with_expression(value,
acknowledgement=license_acknowledgement)
except InvalidLicenseExpressionException:
pass
return self.make_with_name(value,
text=license_text,
url=license_url,
acknowledgement=license_acknowledgement)

def make_with_expression(self, expression: str, *,
acknowledgement: Optional['LicenseAcknowledgement'] = None
) -> LicenseExpression:
"""Make a :class:`cyclonedx.model.license.LicenseExpression` with a compound expression.

Utilizes :func:`cyclonedx.spdx.is_expression`.

:raises InvalidLicenseExpressionException: if param `value` is not known/supported license expression
"""
if is_spdx_expression(expression):
return LicenseExpression(expression, acknowledgement=acknowledgement)
raise InvalidLicenseExpressionException(expression)

def make_with_id(self, spdx_id: str, *,
text: Optional['AttachedText'] = None,
url: Optional['XsUri'] = None,
acknowledgement: Optional['LicenseAcknowledgement'] = None
) -> DisjunctiveLicense:
"""Make a :class:`cyclonedx.model.license.DisjunctiveLicense` from an SPDX-ID.

:raises InvalidSpdxLicenseException: if param `spdx_id` was not known/supported SPDX-ID
"""
spdx_license_id = spdx_fixup(spdx_id)
if spdx_license_id is None:
raise InvalidSpdxLicenseException(spdx_id)
return DisjunctiveLicense(id=spdx_license_id, text=text, url=url, acknowledgement=acknowledgement)

def make_with_name(self, name: str, *,
text: Optional['AttachedText'] = None,
url: Optional['XsUri'] = None,
acknowledgement: Optional['LicenseAcknowledgement'] = None
) -> DisjunctiveLicense:
"""Make a :class:`cyclonedx.model.license.DisjunctiveLicense` with a name."""
return DisjunctiveLicense(name=name, text=text, url=url, acknowledgement=acknowledgement)
16 changes: 16 additions & 0 deletions cyclonedx/contrib/this/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# 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.
83 changes: 83 additions & 0 deletions cyclonedx/contrib/this/builders.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# 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.

"""Representation of this very python library."""

__all__ = ['this_component', 'this_tool', ]

from ... import __version__ as __ThisVersion # noqa: N812
from ...model import ExternalReference, ExternalReferenceType, XsUri
from ...model.component import Component, ComponentType
from ...model.license import DisjunctiveLicense, LicenseAcknowledgement
from ...model.tool import Tool

# !!! keep this file in sync with `pyproject.toml`


def this_component() -> Component:
"""Representation of this very python library as a :class:`Component`."""
return Component(
type=ComponentType.LIBRARY,
group='CycloneDX',
name='cyclonedx-python-lib',
version=__ThisVersion or 'UNKNOWN',
description='Python library for CycloneDX',
licenses=(DisjunctiveLicense(id='Apache-2.0',
acknowledgement=LicenseAcknowledgement.DECLARED),),
external_references=(
# let's assume this is not a fork
ExternalReference(
type=ExternalReferenceType.WEBSITE,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/#readme')
),
ExternalReference(
type=ExternalReferenceType.DOCUMENTATION,
url=XsUri('https://cyclonedx-python-library.readthedocs.io/')
),
ExternalReference(
type=ExternalReferenceType.VCS,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib')
),
ExternalReference(
type=ExternalReferenceType.BUILD_SYSTEM,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/actions')
),
ExternalReference(
type=ExternalReferenceType.ISSUE_TRACKER,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/issues')
),
ExternalReference(
type=ExternalReferenceType.LICENSE,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE')
),
ExternalReference(
type=ExternalReferenceType.RELEASE_NOTES,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md')
),
# we cannot assert where the lib was fetched from, but we can give a hint
ExternalReference(
type=ExternalReferenceType.DISTRIBUTION,
url=XsUri('https://pypi.org/project/cyclonedx-python-lib/')
),
),
# to be extended...
)


def this_tool() -> Tool:
"""Representation of this very python library as a :class:`Tool`."""
return Tool.from_component(this_component())
Loading
Loading