mrkeyoor.com_
Sun 20 Sept 04:57 UTC
PyPICLI & Toolingupdated 20 Sept 2026

setuptools-scm review

setuptools-scm 10.2.1 calculates a Python distribution version from repository tags while setuptools builds the artifact. A release tag becomes the clean version; commits after it can become a PEP 440 development version containing commit distance and revision data. Installing the tool also registers a file finder that feeds version-controlled files into source distributions. The current patch removes `scm_version.json` and `scm_file_list.json` from wheel `.dist-info`, while retaining them in sdists where metadata-free fallback discovery needs them. This is build tooling, not a runtime version service, and it depends on trustworthy Git, Mercurial, or supported VCS metadata being present during the build.

Verdict

setuptools-scm 10.2.1 installed in 0.2 seconds and occupied 5 MB across 4 packages, with zero audit findings in our sandbox. Use it for tagged setuptools releases only when CI supplies trustworthy history and inspects sdists; choose the build backend's native plugin or an external version when `.git` is routinely absent.

We installed it

Lab card: what happened when we installed setuptools-scmScreenshot of setuptools-scm documentation
Install✓ · 0.2s4 packages on disk · 5 MB
Importimport setuptools_scm in 0.31s · pure Python · py.typed · requires Python >=3.8
Known vulns0(pip-audit)

Answers from our run

Does setuptools-scm install cleanly?

Yes. In a fresh container with an empty cache, pip install setuptools-scm finished in 0.2s, leaving 4 packages and 5 MB on disk. pip-audit reported no known vulnerabilities.

What does setuptools-scm need to run?

Python >=3.8, and nothing compiled: it is pure Python. In our run import setuptools_scm succeeded in 0.31s, and the package ships py.typed for type checkers.

setuptools-scm or hatch-vcs: which should you use?

hatch-vcs: Hatchling owns the build and VCS versioning should remain in that backend. setuptools-scm 10.2.1 installed in 0.2 seconds and occupied 5 MB across 4 packages, with zero audit findings in our sandbox.

When should you not use setuptools-scm?

Builds routinely receive shallow history, exported trees, or Docker contexts without .git. Those inputs need an external version override or archive metadata.

API stability3/5Tag-driven versions and the `pyproject.toml` integration are established, but activation and implementation ownership have changed recently. Release 9.2 removed ordinary implicit activation, while 10 moved shared logic into `vcs-versioning` and revised version-file behavior. Tag matching is also moving toward a stricter default. The 10.2.1 change is narrow, yet build pipelines should read both projects' release notes and compare artifact contents on upgrades.
Docs5/5The official manual has concrete recipes for explicit and simple activation, generated files, runtime metadata, Docker builds, pretend-version variables, Git archives, custom schemes, debugging, and sdist file listing. Warnings explain that merely installing the tool activates file discovery and that runtime calls to `get_version()` are discouraged. Examples also name supported setuptools floors, making it possible to build a complete configuration without guessing hidden defaults.
Maintenance5/5The PyPA repository was pushed on 2026-08-21, and GitHub reports 23 open issues and pull requests. After setuptools-scm 10.2.1, companion `vcs-versioning` releases repaired missing submodule working trees, `export-ignore` handling, strict-tag diagnostics, reproducibility, and Jujutsu performance. Two release streams now matter to upgrade review, but shared backend fixes are landing promptly rather than waiting for another setuptools-scm major.
Ecosystem5/5GitHub shows 952 stars, and setuptools-scm participates directly in the PyPA build ecosystem. It supports Git, Mercurial, and Jujutsu workflows, while related tooling such as hatch-vcs shares the extracted versioning machinery. Its reach goes beyond version strings because the setuptools file-finder hook changes sdist contents whenever the build dependency is present, giving it wide compatibility and a larger-than-expected packaging surface.

Use it if

  • A setuptools project publishes from tags and wants wheel metadata to use the same version source.
  • Artifacts built between releases need distinct, sortable PEP 440 development versions.
  • The sdist should follow tracked files, with a short exclusion list instead of a full inclusion manifest.
  • Each package in a monorepo has a tag prefix that must be selected and stripped before parsing.
Skip it if

Setup reality

We installed setuptools-scm 10.2.1 in a fresh Python 3.12 Bookworm container in 0.2 seconds. Four packages used 5 MB, import setuptools_scm took 0.31 seconds, and pip-audit found zero known vulnerabilities. The package declares 6 direct dependencies and supports Python 3.8 or newer. It is pure Python, ships py.typed, and declares MIT. These numbers describe the build helper; a normal installed application should not need it at runtime.

Put the package under [build-system].requires, declare the project version dynamic, and add [tool.setuptools_scm]. Sectionless activation only works with the documented simple extra because plain dependency-triggered activation was removed in 9.2. Runtime code should read importlib.metadata.version() or import a generated version module. Calling setuptools_scm.get_version() from application code makes production depend on repository state that is often absent.

A correct version needs reachable tags and enough history to calculate distance. Shallow CI checkouts, release archives, and Docker contexts often omit one or both. Fetch complete tags for releases. When another build system already knows the version, pass the distribution-scoped SETUPTOOLS_SCM_PRETEND_VERSION_FOR_* variable. A fallback_version prevents failure but can silently stamp the wrong value, so use it only where downstream packaging owns that decision.

Presence in build requirements activates the SCM file finder, which includes tracked files in the sdist. Inspect the list with python -m setuptools_scm ls, prune unwanted paths in MANIFEST.in, then open the built archive before publishing. Ignore generated version modules in Git; rewriting a tracked module dirties the checkout and may alter its own calculated version. In 10.2.1, the 2 SCM JSON metadata files remain in sdists but are deliberately excluded from wheels.

Patterns

Configure dynamic versioning enable-explicit-versioning

[build-system]
requires = ["setuptools>=80", "setuptools-scm>=10"]
build-backend = "setuptools.build_meta"

[project]
name = "sample-package"
dynamic = ["version"]

[tool.setuptools_scm]

A dynamic version cannot coexist with a static `project.version`. Even an empty tool table explicitly activates setuptools-scm.

Use sectionless simple activation enable-simple-versioning

[build-system]
requires = ["setuptools>=80", "setuptools-scm[simple]>=10"]
build-backend = "setuptools.build_meta"

[project]
name = "sample-package"
dynamic = ["version"]

Sectionless setup depends on the `simple` extra. Since 9.2, merely listing the ordinary package no longer activates version inference.

Print the version before building inspect-computed-version

python -m setuptools_scm
SETUPTOOLS_SCM_DEBUG=1 python -m setuptools_scm

The debug flag prints how the repository and tag were selected. Compare this output in CI when the calculated version differs from a workstation.

Inspect automatic file discovery list-sdist-files

python -m setuptools_scm ls
python -m build --sdist
tar -tzf dist/sample_package-*.tar.gz

`ls` displays files found through source control. Setuptools applies `MANIFEST.in` pruning after that discovery step.

Exclude tracked development data prune-source-distribution

# MANIFEST.in
prune tests/fixtures/large-corpus
prune docs/_build
exclude .pre-commit-config.yaml
global-exclude *.pyc

The file finder activates with the build dependency. Exclude tracked paths through setuptools manifest rules because the tool table has no off switch.

Write a version module during builds generate-version-module

[tool.setuptools_scm]
version_file = "src/sample_package/_version.py"

# .gitignore
# src/sample_package/_version.py

Keep `_version.py` out of source control. Rewriting a tracked generated file dirties the checkout and can change the version being written.

Read distribution metadata at runtime read-installed-version

from importlib.metadata import PackageNotFoundError, version

try:
    __version__ = version('sample-package')
except PackageNotFoundError:
    __version__ = '0+uninstalled'

`version()` expects the installed distribution name, which may differ from the import name. It reads wheel metadata without needing `.git` at runtime.

Provide release history in GitHub Actions fetch-tags-in-actions

- uses: actions/checkout@v6
  with:
    fetch-depth: 0

- run: python -m build

`fetch-depth: 0` brings tags and ancestry into the runner. A shallow clone may lack either the release tag or the distance from it.

Override a history-free build supply-known-build-version

SETUPTOOLS_SCM_PRETEND_VERSION_FOR_SAMPLE_PACKAGE=2.4.0 \
  python -m build

The scoped variable uppercases the distribution name and replaces separators with underscores. It avoids giving every package in a monorepo one version.

Reject dirty release builds configure-release-scheme

[tool.setuptools_scm]
local_scheme = "no-local-version-strict"

`no-local-version-strict` rejects a dirty tree while omitting local metadata. Run it only in a release path that treats any uncommitted change as an error.

Select one package's tag namespace filter-monorepo-tags

[tool.setuptools_scm]
root = "../.."
relative_to = "pyproject.toml"

[tool.setuptools_scm.tag]
prefix = "api-v"
strict = true

The prefix selects this package's tags and is removed before version parsing. `root` is resolved relative to the configured project file.

Embed version hints in git archives support-git-archives

# .git_archival.txt
node: $Format:%H$
node-date: $Format:%cI$
describe-name: $Format:%(describe:tags=true,match=*[0-9]*)$

# .gitattributes
.git_archival.txt export-subst

Both archive files must exist in the tagged commit. Placeholder substitution happens during `git archive`, not in an ordinary working tree.

Alternatives

PackageRegistryPick it when
hatch-vcsPyPIPick it when Hatchling owns the build and VCS versioning should remain in that backend.
versioningitPyPIPick it for configurable formatting, file replacement, and source-archive steps.
dunamaiPyPIPick its library or CLI when version derivation must stay independent of the Python build backend.

More cli & tooling guides

commander · chalk · typescript · esbuild · yargs · click · 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.