Python dependencies can be missing from an SBOM when they are declared solely inside a setup.py file, because static tools cannot always evaluate that code. GitHub's documentation warns about this. The fix is to commit a requirements file or a lock file with resolved versions, or generate the SBOM from an installed environment.
Why setup.py is hard to read
A setup.py file is a program. Dependency lists in it can be built from variables, conditions or function calls. A tool that merely reads the file text cannot always work out what the final list would be. GitHub's documentation on supported ecosystems says that if you list your Python dependencies within a setup.py file, it may not be able to parse and list every dependency in your project.
It lists several Python formats that it reads more reliably, including requirements.txt, pipfile and pipfile.lock, and poetry.lock with pyproject.toml.
Which file gives the most complete inventory
| File | What it records | Reliability for an SBOM |
|---|---|---|
| setup.py with install_requires | Version ranges, sometimes computed | Low to medium |
| requirements.txt with loose ranges | Ranges | Medium |
| requirements.txt with pinned versions | Exact versions for direct packages | Good |
| Lock file such as poetry.lock or pipfile.lock | Exact versions including transitive packages | Strongest |
| Installed environment | What is actually present | Strongest for a built artifact |
Steps to fix it
- Decide how your project is installed in production: from a lock file, a requirements file or a built container.
- Generate a lock file with your package manager and commit it. For pip based projects, a compiled requirements file with pinned versions and hashes serves a similar role.
- Check the dependency graph or your SBOM tool again and compare the package count to the installed environment.
- Install the project in a clean virtual environment and list installed packages. Differences point to missing entries.
- If you ship a container, scan the image too, since that includes system packages that no Python file lists.
- Regenerate the SBOM in CI on every release so it stays in step.
Libraries versus applications
A library published for others usually leaves ranges open on purpose, so its users can resolve versions. An application should pin. If your repository holds both, keep a pinned file for the deployed application and say clearly in the data room which one the SBOM describes.
How to check for gaps quickly
Count the lines in your pinned requirements file and compare with the component count in the SBOM. They should be close for direct packages. Spot check three transitive packages, such as a library that your framework pulls in, and confirm they appear.
What to tell the reader
"The SBOM for the analytics service was generated on 19 October from a clean install of the lock file. It lists 142 Python packages. Build time tools are excluded." Replace the numbers with your own. State exclusions so nobody thinks the file is claiming more.
Try it yourself
Click through it at your own pace, or open it full screen.
Look for how transitive packages appear beside direct ones, the part a loosely declared setup.py hides.
Safeguard generates SBOMs and scans dependencies for Python and other ecosystems. A free scan shows what one repository produces.
Quick check
Is pip freeze good enough?
It records what is installed in one environment, which is useful, but make sure it was run in a clean environment that matches production.
Do I need to rewrite setup.py?
No. Keep it for packaging and add a lock or pinned file for deployment.
Sources
Last reviewed: 19 October 2026.