mrkeyoor.com_
Tue 22 Sept 00:44 UTC
PyPIUtilsupdated 20 Sept 2026

cyclonedx-python-lib review

cyclonedx-python-lib 11.12.0 gives Python programs an object model for CycloneDX bills of materials, plus readers, JSON and XML writers, and optional schema validation. Our Python 3.12 sandbox loaded the typed, pure-Python package in 0.01 seconds. You populate components, services, licenses, vulnerabilities, metadata, and dependency edges, then choose an explicit CycloneDX schema for output. Release 11.12.0 adds the `isExternal` field for components written as CycloneDX 1.7 and adds Python 3.14 to the test matrix. It is a construction library, so installing it alone does not scan an environment or create an SBOM.

Verdict

Our cyclonedx-python-lib 11.12.0 install took 0.4 seconds, used 6 MB across 8 packages, and had no pip-audit findings, so it is a low-friction model layer for software that reads or writes CycloneDX. Install cyclonedx-bom instead when the desired outcome is a generated Python SBOM rather than an API.

We installed it

Lab card: what happened when we installed cyclonedx-python-libScreenshot of cyclonedx-python-lib documentation
Install✓ · 0.4s8 packages on disk · 6 MB
Importimport cyclonedx in 0.01s · pure Python · py.typed · requires Python >=3.9,<4.0
Known vulns0(pip-audit)

Answers from our run

Does cyclonedx-python-lib install cleanly?

Yes. In a fresh container with an empty cache, pip install cyclonedx-python-lib finished in 0.4s, leaving 8 packages and 6 MB on disk. pip-audit reported no known vulnerabilities.

What does cyclonedx-python-lib need to run?

Python >=3.9,<4.0, and nothing compiled: it is pure Python. In our run import cyclonedx succeeded in 0.01s, and the package ships py.typed for type checkers.

cyclonedx-python-lib or cyclonedx-bom: which should you use?

cyclonedx-bom: Pick it to generate a CycloneDX document from a Python environment, requirements file, or project. Our cyclonedx-python-lib 11.12.0 install took 0.4 seconds, used 6 MB across 8 packages, and had no pip-audit findings, so it is a low-friction model layer for software that reads or writes CycloneDX.

When should you not use cyclonedx-python-lib?

The README directs one-command Python SBOM generation to cyclonedx-bom because this library expects your code to supply inventory data

API stability3/5Version 11.12.0 makes the target CycloneDX schema explicit in outputters and validators, which prevents a caller from silently assuming the newest format. Component, Bom, and dependency registration remain the main abstractions. A major version of 11 signals meaningful prior API movement, and the new isExternal property works only with CycloneDX 1.7 output, so production integrations should pin the library and test migrations against their chosen schema.
Docs4/5The 11.12.0 Read the Docs site covers responsibilities, installation, API reference, integration guidance, and examples. Repository examples show serialization, deserialization, and validation, including defusedxml and MissingOptionalDependencyException. Those examples answer concrete Python questions, but the CycloneDX specification is still required to understand when a field exists and what a bom-ref means to downstream consumers.
Maintenance5/5Release 11.12.0 was published on 2026-08-13 and the repository was pushed that day. It added CycloneDX 1.7 isExternal support and Python 3.14 to the test matrix. GitHub reports that the repository is not archived and has 59 open issues and pull requests. The release is tied to both a specification feature and interpreter testing, giving stronger maintenance evidence than a version bump alone.
Ecosystem4/5PyPI Stats recorded 8,795,005 downloads in the current weekly window, while GitHub showed 115 stars. The package supplies the shared Python model beneath higher-level CycloneDX tools and can emit both JSON and XML across named schema versions. Its README explicitly sends standalone SBOM users to cyclonedx-bom, which explains why direct popularity signals understate the number of workflows that receive it transitively.

Use it if

  • make_outputter can render one Python BOM model as CycloneDX 1.7 JSON or XML for different consumers
  • Bom.from_json and Bom.from_xml let an inventory service load a document, alter its graph, and write it again
  • A compliance pipeline needs stable bom-ref values and explicit dependency edges instead of assembling raw dictionaries
  • Schema validators should reject malformed CycloneDX output before it reaches a signing or ingestion step
Skip it if

Setup reality

We installed cyclonedx-python-lib 11.12.0 in 0.4 seconds inside a fresh, unprivileged Python 3.12 Bookworm sandbox with 3 CPUs and 8 GB of RAM. Eight packages occupied 6 MB afterward. pip-audit returned zero known vulnerabilities, and import cyclonedx took 0.01 seconds. The wheel is pure Python, declares 8 direct dependencies, includes py.typed, and supports Python 3.9 through the 3.x line. Our run required no compiler or system package.

Creating and serializing a BOM works from the base install, but schema checks use extras named validation, json-validation, and xml-validation. Missing support raises MissingOptionalDependencyException. Treat that as a failed validation stage rather than letting a build continue. The project's own 11.12.0 example feeds untrusted XML through defusedxml before Bom.from_xml. No credentials are involved unless your surrounding inventory process calls a registry or vulnerability service.

Select SchemaVersion on every outputter. The new isExternal property is representable in CycloneDX 1.7, while an older target cannot carry it. Give important components a repeatable bom_ref because Bom.register_dependency links objects through those references; random references make two otherwise identical builds difficult to diff. output_to_file protects an existing artifact and raises FileExistsError until allow_overwrite=True is passed.

Bom.from_json expects an already parsed mapping, and Bom.from_xml expects an XML element. In the repository's deserialization example, both methods carry a targeted type-ignore because serialization attaches them dynamically even though the distribution ships py.typed. This 6 MB library also does no package discovery. Use cyclonedx-bom for an environment scan, or supply your own component and dependency inventory before constructing the model.

Patterns

Define the application at the BOM root build-bom

from cyclonedx.model.bom import Bom
from cyclonedx.model.component import Component, ComponentType

app = Component(name='billing-api', type=ComponentType.APPLICATION, version='2.4.0', bom_ref='billing-api@2.4.0')
bom = Bom()
bom.metadata.component = app

A repeatable bom_ref keeps dependency links and generated diffs stable between builds.

Record a PyPI component add-component

from packageurl import PackageURL
from cyclonedx.model.component import Component, ComponentType

requests = Component(
    type=ComponentType.LIBRARY, name='requests', version='2.32.5',
    purl=PackageURL(type='pypi', name='requests', version='2.32.5'),
    bom_ref='pkg:pypi/requests@2.32.5',
)
bom.components.add(requests)

Bom.components is a sorted set, so component identity controls whether repeated additions collapse.

Flag an externally managed component mark-external

external_parser = Component(
    type=ComponentType.LIBRARY,
    name='vendor-parser',
    bom_ref='vendor-parser@4',
    is_external=True,
)
bom.components.add(external_parser)

Package 11.12.0 can serialize isExternal in CycloneDX 1.7; earlier target schemas omit that field.

Turn license text into a model value add-license

from cyclonedx.contrib.license.factories import LicenseFactory

license_factory = LicenseFactory()
requests.licenses.add(license_factory.make_from_string('Apache-2.0'))

LicenseFactory identifies SPDX IDs and expressions before falling back to a named license.

Connect the application to a dependency dependency-graph

bom.register_dependency(app, [requests])
bom.register_dependency(requests, [])

register_dependency uses each object's bom_ref and also registers referenced child components in the graph.

Render CycloneDX 1.7 as JSON output-json

from cyclonedx.output import make_outputter
from cyclonedx.schema import OutputFormat, SchemaVersion

outputter = make_outputter(bom, OutputFormat.JSON, SchemaVersion.V1_7)
json_text = outputter.output_as_string(indent=2)

SchemaVersion.V1_7 makes the consumer contract explicit instead of following a future default.

Render the BOM as XML output-xml

xml_outputter = make_outputter(bom, OutputFormat.XML, SchemaVersion.V1_7)
xml_text = xml_outputter.output_as_string(indent=2)

The same Bom feeds both formats, while XML and JSON validation require their respective extras.

Allow replacement of a build artifact write-file

outputter.output_to_file(
    'build/bom.json',
    allow_overwrite=True,
    indent=2,
)

output_to_file raises FileExistsError for an existing path unless allow_overwrite is true.

Fail a build on invalid JSON validate-json

from cyclonedx.schema import SchemaVersion
from cyclonedx.validation.json import JsonStrictValidator

errors = JsonStrictValidator(SchemaVersion.V1_7).validate_str(json_text)
if errors:
    raise ValueError(f'invalid CycloneDX document: {errors!r}')

JsonStrictValidator needs the JSON validation extra; missing support raises MissingOptionalDependencyException.

Load an existing JSON BOM deserialize-json

import json
from cyclonedx.model.bom import Bom

with open('bom.json', encoding='utf-8') as handle:
    bom = Bom.from_json(json.load(handle))  # type: ignore[attr-defined]

Bom.from_json receives a parsed mapping. The official 11.12.0 example uses a type-ignore for the dynamically attached method.

Parse an outside XML BOM deserialize-xml

from defusedxml import ElementTree
from cyclonedx.model.bom import Bom

root = ElementTree.parse('bom.xml').getroot()
bom = Bom.from_xml(root)  # type: ignore[attr-defined]

The repository example uses defusedxml before passing an element to Bom.from_xml.

Validate against the output schema select-validator

from cyclonedx.validation import make_schemabased_validator

validator = make_schemabased_validator(outputter.output_format, outputter.schema_version)
errors = validator.validate_str(outputter.output_as_string())

Taking format and version from the outputter prevents a CycloneDX 1.7 document from being checked as another schema.

Alternatives

PackageRegistryPick it when
cyclonedx-bomPyPIPick it to generate a CycloneDX document from a Python environment, requirements file, or project
spdx-toolsPyPIPick it when the required exchange format and policy language are SPDX
pip-auditPyPIPick it when dependency vulnerability findings matter more than editing a BOM model

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.