Extending setuptools-scm
setuptools-scm uses entry-point based hooks to extend its default capabilities.
Adding a new SCM
setuptools-scm provides two entrypoints for adding new SCMs:
setuptools_scm.parse_scm-
A function used to parse the metadata of the current workdir using the name of the control directory/file of your SCM as the entrypoint's name. E.g. for the built-in entrypoint for Git the entrypoint is named
.gitand referencessetuptools_scm.git:parseThe return value MUST be a
vcs_versioning.ScmVersioninstance created by the functionvcs_versioning._version_schemes.meta. setuptools_scm.files_command- Either a string containing a shell command that prints all SCM-managed files in its current working directory or a callable, that given a pathname will return that list.
Also uses the name of your SCM control directory as the name of the entrypoint.
api reference for scm version objects
vcs_versioning.ScmVersion
dataclass
represents a parsed version from scm
Source code in vcs-versioning/src/vcs_versioning/_scm_version.py
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 | |
branch
class-attribute
instance-attribute
branch: str | None = None
the branch name if any
config
instance-attribute
config: Configuration
the configuration used to parse the version
dirty
class-attribute
instance-attribute
dirty: bool = False
whether the working copy had uncommitted changes
distance
class-attribute
instance-attribute
distance: int = 0
the number of commits since the tag
exact
property
exact: bool
returns true checked out exactly on a tag and no local changes apply
node
class-attribute
instance-attribute
node: str | None = None
the shortened node id
node_date
class-attribute
instance-attribute
node_date: date | None = None
the date of the commit if available
preformatted
class-attribute
instance-attribute
preformatted: bool = False
whether the version string was preformatted
short_node
property
short_node: str | None
Return the node formatted for output.
tag
instance-attribute
tag: Version | NonNormalizedVersion
the related tag or preformatted version
time
class-attribute
instance-attribute
time: datetime = field(default_factory=_source_epoch_or_utc_now)
the current time or source epoch time
only set for unit-testing version schemes
for real usage it must be now(utc) or SOURCE_EPOCH
format
format() -> str
Format this version using the configured version and local schemes.
This is the final step in the chain::
env -> config -> workdir -> scm_version -> scm_version.format()
Source code in vcs-versioning/src/vcs_versioning/_scm_version.py
288 289 290 291 292 293 294 295 296 297 | |
format_choice
format_choice(clean_format: str, dirty_format: str, **kw: object) -> str
given clean_format and dirty_format
choose one based on self.dirty and format it using self.format_with
Source code in vcs-versioning/src/vcs_versioning/_scm_version.py
271 272 273 274 275 276 | |
format_with
format_with(fmt: str, **kw: object) -> str
format a given format string with attributes of this object
Source code in vcs-versioning/src/vcs_versioning/_scm_version.py
258 259 260 261 262 263 264 265 266 267 268 269 | |
matches
matches(**expectations: Unpack[VersionExpectations]) -> bool | mismatches
Check if this ScmVersion matches the given expectations.
Returns True if all specified properties match, or a mismatches object (which is falsy) containing details of what didn't match.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**expectations
|
Unpack[VersionExpectations]
|
Properties to check, using VersionExpectations TypedDict |
{}
|
Source code in vcs-versioning/src/vcs_versioning/_scm_version.py
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 | |
vcs_versioning._version_schemes.meta
meta(tag: str | _Version, *, distance: int = 0, dirty: bool = False, node: str | None = None, preformatted: bool = False, branch: str | None = None, config: Configuration, node_date: date | None = None, time: datetime | None = None) -> ScmVersion
Source code in vcs-versioning/src/vcs_versioning/_scm_version.py
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 | |
vcs_versioning._version_schemes.DirtyWorkingTreeError
Bases: ValueError
Raised when the fail-on-uncommitted-changes local scheme sees a dirty tree.
Source code in vcs-versioning/src/vcs_versioning/_exceptions.py
4 5 | |
Adding a workdir discovery factory
The vcs_versioning.discover_workdir entry-point group controls how
setuptools-scm finds VCS checkouts and fallback metadata. Each factory
receives a directory path and the current Configuration, and returns
a ScmWorkdir, FallbackWorkdir, or None.
# mypackage/discovery.py
from pathlib import Path
from vcs_versioning._backends._scm_workdir import ScmWorkdir
from vcs_versioning._fallback_workdir import FallbackWorkdir
from vcs_versioning._config import Configuration
def discover_my_scm(
path: Path, *, config: Configuration
) -> ScmWorkdir | FallbackWorkdir | None:
if (path / ".myscm").is_dir():
return MyScmWorkdir(path=path, _config=config)
return None
Register it in your pyproject.toml:
[project.entry-points."vcs_versioning.discover_workdir"]
myscm = "mypackage.discovery:discover_my_scm"
Discovery runs in two phases:
- SCM phase — probes the configured root (and parent directories when
search_parent_directoriesis enabled) for live VCS checkouts. AScmWorkdirresult is returned immediately. - Fallback phase — if no SCM workdir was found, probes the project directory for fallback metadata (archival files, egg-info, etc.).
SCM results always take priority over fallback results.
Built-in factories
| Entry point | Package | Type |
|---|---|---|
hg-git |
vcs-versioning | SCM (Git + Mercurial) |
archival |
vcs-versioning | Fallback (.git_archival.txt) |
pkginfo |
setuptools-scm | Fallback (PKG-INFO) |
egg-info |
setuptools-scm | Fallback (*.egg-info/scm_version.json) |
Version number construction
setuptools_scm.version_scheme
Configures how the version number is constructed given a ScmVersion instance and should return a string representing the version.
Available implementations
guess-next-dev (default)-
Automatically guesses the next development version (default). Guesses the upcoming release by incrementing the pre-release segment if present, otherwise by incrementing the micro segment. Then appends
.devN. In case the tag ends with.dev0the version is not bumped and custom.devNversions will trigger an error.Examples:
- Tag
v1.0.0→ version1.0.1.dev0(if dirty or distance > 0) - Tag
v1.0.0→ version1.0.0(if exact match)
- Tag
calver-by-date-
Calendar versioning scheme that generates versions based on dates. Uses the format
YY.MM.DD.patchorYYYY.MM.DD.patchdepending on the existing tag format. If the commit is on the same date as the latest tag, increments the patch number. Otherwise, uses the current date with patch 0. Supports branch-specific versioning for release branches.Examples:
- Tag
v23.01.15.0on same day → version23.01.15.1.devN - Tag
v23.01.15.0on different day (e.g., 2023-01-16) → version23.01.16.0.devN - Tag
v2023.01.15.0→ uses 4-digit year format for new versions
- Tag
no-guess-dev-
Does no next version guessing, just adds
.post1.devN. This is the recommended replacement for the deprecatedpost-releasescheme.Examples:
- Tag
v1.0.0→ version1.0.0.post1.devN(if distance > 0) - Tag
v1.0.0→ version1.0.0(if exact match)
- Tag
only-version-
Only use the version from the tag, as given.
This means version is no longer pseudo unique per commit
Examples:
- Tag
v1.0.0→ version1.0.0(always, regardless of distance or dirty state)
- Tag
post-release (deprecated)-
Generates post release versions (adds
.postN) after review of the version number pep this is considered a bad idea as post releases are intended to be chosen not autogenerated.the recommended replacement is
no-guess-devExamples:
- Tag
1.0.0→ version1.0.0.postN(where N is the distance)
- Tag
semver-pep440-
Basic semantic versioning with PEP 440-compliant version numbers.
Guesses the upcoming release by incrementing the minor segment and setting the micro segment to zero if the current branch contains the string
feature, otherwise by incrementing the micro version. Then appending.devN.This scheme is not compatible with pre-releases.
Renamed in setuptools-scm 10
Previously called
python-simplified-semver. The old name still works but is deprecated.Examples:
- Tag
1.0.0on non-feature branch → version1.0.1.devN - Tag
1.0.0on feature branch → version1.1.0.devN
- Tag
semver-pep440-release-branch-
Semantic versioning with PEP 440-compliant version numbers for projects with release branches. The same as
guess-next-dev(incrementing the pre-release or micro segment) however when on a release branch: a branch whose name (ignoring namespace) parses as a version that matches the most recent tag up to the minor segment. Otherwise if on a non-release branch, increments the minor segment and sets the micro segment to zero, then appends.devNNamespaces are unix pathname separated parts of a branch/tag name.
Renamed in setuptools-scm 10
Previously called
release-branch-semver. The old name still works but is deprecated.Examples:
-
Tag
1.0.0on release branchrelease-1.0→ version1.0.1.devN -
Tag
1.0.0on release branchrelease/v1.0→ version1.0.1.devN -
Tag
1.0.0on development branch → version1.1.0.devN
-
setuptools_scm.local_scheme
Configures how the local part of a version is rendered given a
ScmVersion instance. A callable should return a string
for the local segment, or you may pass a list of scheme names—each is tried in order
until one returns a non-None value (an empty string still stops the chain; return
None to defer to the next scheme—see fail-on-uncommitted-changes below).
Dates and times are in Coordinated Universal Time (UTC), because as part
of the version, they should be location independent.
Available implementations
node-and-date (default)- Adds the node on dev versions and the date on dirty workdir
node-and-timestamp- Like
node-and-datebut with a timestamp of the form%Y%m%d%H%M%Sinstead dirty-tag- Adds
+dirtyif the current workdir has changes no-local-version- Omits local version, useful e.g. because PyPI does not support it
no-local-version-strict- Like
no-local-versionbut raisesDirtyWorkingTreeErrorwhen the working tree is dirty. Equivalent to["fail-on-uncommitted-changes", "no-local-version"]as a single scheme name. Recommended for release CI where you want PyPI-compatible versions and an explicit build failure on uncommitted changes. fail-on-uncommitted-changes-
When the working tree is dirty (
ScmVersion.dirtyis true), raisesDirtyWorkingTreeError. When clean, returnsNoneso the next scheme in alocal_schemelist runs—pair it with your usual local scheme (for examplenode-and-dateorno-local-version). This works with anyversion_scheme, so release CI can enforce a clean tree without a separate implementation per version scheme.Example:
local_scheme = ["fail-on-uncommitted-changes", "node-and-date"]See Configuration Overrides for enabling this via environment variables in CI without editing
pyproject.toml.