Use it with hatchling

python-sbom ships a hatchling build hook. Add it to the build requirements, with the hatch extra, and enable it with an empty table:

pyproject.toml
[build-system]
build-backend = "hatchling.build"
requires = [ "hatchling>=1.28", "python-sbom[hatch]>=0.3" ]

[project]
name = "greet"
version = "1.0"
description = "says hello"
license = "MIT"
license-files = [ "LICENSE" ]
requires-python = ">=3.11"
classifiers = [
  "Programming Language :: Python :: 3 :: Only",
  "Programming Language :: Python :: 3.11",
  "Programming Language :: Python :: 3.12",
  "Programming Language :: Python :: 3.13",
  "Programming Language :: Python :: 3.14",
  "Programming Language :: Python :: 3.15",
]
dependencies = [ "packaging>=24" ]
urls.Source = "https://github.com/you/greet"

[tool.hatch]
build.hooks.python-sbom = {}

[tool.python-sbom]
supplier = { name = "You", url = [ "https://github.com/you" ] }

Every wheel you build now carries the SBOM. An editable wheel carries none, and an sdist carries the source revision instead, so a wheel built from it still names its commit. Build one and check it:

$ python -m build --wheel
$ python-sbom validate dist/
SBOM in greet-1.0-py3-none-any.whl is valid

docs/examples/hatchling holds the whole project.

Put python-sbom’s options in [tool.python-sbom], the table every integration reads, never in the hook’s table: the hook’s table holds only hatchling’s own options, such as enable-by-default, and a python-sbom option there fails the build with a pointer to [tool.python-sbom]. Configuration lists the options.

The hook writes the SBOM once hatchling has written the wheel, so the document describes the final file name and metadata, whatever dependencies or tag other hooks changed. The order of the hook tables does not matter.

Projects that still build on Python 3.10 or older

python-sbom needs Python 3.11 or later. A project that still builds on older interpreters guards the requirement with a marker. An enabled python-sbom hook table would then fail those builds, since hatchling refuses a hook it cannot find, so enable the hook from a custom hook script that falls back to a hook doing nothing:

[build-system]
requires = ['hatchling>=1.28', '''python-sbom[hatch]>=0.2; python_version>='3.11'''']

[tool.hatch.build.hooks.custom]
path = 'hatch_build.py'
# hatch_build.py
import sys

if sys.version_info >= (3, 11):
    from python_sbom.hatch import SbomBuildHook
else:  # no python-sbom here, so these builds carry no SBOM
    from hatchling.builders.hooks.plugin.interface import (
        BuildHookInterface as SbomBuildHook,
    )


def get_build_hook():
    return SbomBuildHook

Wheels built on Python 3.11 or later carry the SBOM; publish those.

From a custom hook

A project with its own hatchling custom hook can subclass SbomBuildHook instead of enabling the plugin, and override content_provider() to hand python-sbom what the hook knows. Add what your project knows walks through one.

Release builds that install from a lock

A release workflow that installs [build-system] requires from a lock with hashes, such as tox-dev/github-workflows does, needs python-sbom and its dependencies (installer, packageurl-python, packaging and zipremove) in the locked group, or the build stops on a missing hash.