SBOM Generation Guide for Python - UV, Poetry, Pipenv

Learn how to generate Software Bill of Materials for Python projects. Complete guide with uv.lock, poetry.lock, and Pipfile.lock examples.

Source vs Build SBOMs

Before we dive into the actual guide, let’s start by talking about SBOM generation. Generally speaking, an SBOM is either built from source (also known as ‘pre-build’ in CycloneDX terminology) or built after installation. When we talk about source SBOMs, we normally refer to generating an SBOM from a lockfile, and for built SBOMs, we look at what has been installed by the package manager. These might seem interchangeable, and the output might be identical. However, this really boils down to the quality of the lockfile, so let’s dive into this with some examples.

Lockfile Example

Modern Python package managers like UV and Poetry automatically generate complete lockfiles with all transitive dependencies and cryptographic hashes. This makes them ideal for SBOM generation.

Why Migrate Away from requirements.txt?

If you’re still using requirements.txt with pip, consider migrating to UV or Poetry. The traditional requirements.txt approach has several limitations that affect SBOM quality:

  • Missing transitive dependencies: A requirements.txt often only lists direct dependencies. Flask might be listed, but its dependencies (Werkzeug, Jinja2, etc.) are missing unless you manually run pip freeze.
  • No hashes by default: Adding cryptographic hashes requires extra tooling like pip-tools, and even then, SBOM tools have inconsistent support for this format.
  • Version ranges cause ambiguity: Expressions like flask>=3.0.0 make it impossible to generate accurate source/pre-build SBOMs since the exact version is unknown.

UV and Poetry solve all of these issues automatically.

For a Flask-based Python server, your pyproject.toml would declare:

[project]
dependencies = ["flask>=3.0.3"]

Running uv lock generates a uv.lock file that captures Flask and all its dependencies with exact versions and hashes—ready for accurate SBOM generation.

Similarly, with Poetry, running poetry lock generates a poetry.lock file with the same completeness.

The key point is that the SBOM is directly correlated to the quality of the lockfile. UV and Poetry solve this by automatically:

  • Capturing all transitive dependencies
  • Including cryptographic hashes
  • Using precise version pinning

Hashes Are Included Automatically

UV and Poetry automatically add cryptographic hashes to all packages in their lockfiles. This is required for NTIA Minimum Elements compliance—no extra tooling needed.

Generating an SBOM

SBOM generation is the first step in the SBOM lifecycle. After generation, you typically need to enrich your SBOM with package metadata and augment it with your organization’s details.

Lifecycle

The sbomify GitHub Action is a swiss army knife for SBOMs that automatically selects the best generation tool for your ecosystem, enriches the output with package metadata, and optionally augments it with your business information—all in one step.

For Python, sbomify uses cyclonedx-py under the hood as it provides the most accurate results for Python lockfiles, including proper hash support.

Standalone (no account needed):

- uses: sbomify/github-action@master
  env:
    LOCK_FILE: uv.lock
    OUTPUT_FILE: sbom.cdx.json
    COMPONENT_NAME: my-python-app
    COMPONENT_VERSION: ${{ github.ref_name }}
    ENRICH: true
    UPLOAD: false

Using github.ref_name automatically captures your git tag (e.g., v1.2.3) as the SBOM version. For rolling releases without tags, use github.sha instead. See our SBOM versioning guide for best practices.

With sbomify platform (adds augmentation and upload):

- uses: sbomify/github-action@master
  env:
    TOKEN: ${{ secrets.SBOMIFY_TOKEN }}
    COMPONENT_ID: my-component-id
    LOCK_FILE: uv.lock
    OUTPUT_FILE: sbom.cdx.json
    AUGMENT: true
    ENRICH: true

The action supports all Python lockfiles: uv.lock, poetry.lock, Pipfile.lock, and pyproject.toml.

Alternative Tools

If you prefer to run SBOM generation tools manually:

cyclonedx-py (Python-native, recommended for manual use):

pip install cyclonedx-bom
cyclonedx-py environment --output-format json -o sbom.cdx.json

This is the same tool sbomify uses under the hood. It reads from your installed environment and properly handles hashes.

Trivy:

trivy fs --format cyclonedx --output sbom.cdx.json .

cdxgen:

cdxgen -o sbom.cdx.json

When using these tools directly, you’ll need to handle enrichment and augmentation separately. See our Resources page for more Python SBOM tools.

GitLab CI

generate-sbom:
  image: sbomifyhub/sbomify-action
  variables:
    LOCK_FILE: uv.lock
    OUTPUT_FILE: sbom.cdx.json
    UPLOAD: "false"
    ENRICH: "true"
  script:
    - /sbomify.sh

Further Reading

Related blog posts:

Further Resources

For more SBOM tools and resources, see our SBOM Resources page, which includes additional Python-specific tools like CycloneDX Python, sbom4python, and SPDX Python libraries.