versioneer: deriving your package version from the VCS tag instead of editing it
version-string management for VCS-controlled trees
At a glance
- What is it?
- Versioneer removes the manual step of updating an embedded version string from a Python release process, by computing the version at build time from the VCS tag and, for unreleased trees, from the nearest tag plus the commit distance. It installs a generated _version.py into your project and it is public domain.
- Who is it for?
- Adopt versioneer if you release Python packages from a tagged VCS tree and you are tired of the release checklist item that says update the version string. Do not adopt it if your project already handles versioning through a build backend that derives it, and check the setup.py sdist path if you publish wheels only, since the tool's sdist rewriting is a stated behaviour.
- Can I use it commercially?
- Yes. Unlicense is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 144 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem it removes from a release checklist
Versioneer is a tool for managing a recorded version number in setuptools-based Python projects. The stated goal is to remove the tedious and error-prone update the embedded version string step from the release process. Making a new release should be as easy as recording a new tag in the version control system, and maybe making new tarballs.
That is a precise description of a specific kind of friction. In a typical Python project, the version lives in more than one place: in setup.py or pyproject.toml, in the package's __init__.py as a __version__ attribute, and in the documentation and the changelog. Every release means editing them in step, and forgetting one produces a package whose metadata says 1.2.0 and whose __version__ says 1.1.0, which is the kind of bug that reaches users as a support question rather than as a test failure.
The tool's name is a joke and its author says so in one line, like a rocketeer, but for versions. The author is Brian Warner, the maintainers listed in pyproject.toml are Justin Wood and Nathan Buckner, and the licence is Public Domain (Unlicense), which is an unusual and very convenient choice. The tool is compatible with Python 3.9 and later and with pypy3, and the primary language is Python.
The audience is anyone publishing Python packages to PyPI, and the appeal is not that it is clever but that it removes a step. The README's framing of a release as a tag plus maybe some tarballs is the whole pitch, and the rest of the documentation is the machinery for making that true.
The release history is slow but steady: 0.29 on 2023-07-07, 0.28 on 2022-10-27 and 0.27 on 2022-10-19. The last push was on 2026-06-01 and the repository is not archived, so the project is maintained rather than finished, and it is worth checking the UPGRADING.md file in the repository before changing versions.
Vendored mode versus build-time dependency mode
The README describes two installation modes, and the difference is a real architectural choice rather than a packaging detail.
In vendored mode, you pip install versioneer, add a configuration section, run versioneer install --vendor in your source tree, and commit the results. A copy of versioneer ends up inside your repository, and the generated file is under your version control from then on.
In build-time dependency mode, you add versioneer to the requires key of the build-system table in pyproject.toml:
[build-system]
requires = ["setuptools", "versioneer[toml]"]
build-backend = "setuptools.build_meta"and then run versioneer install --no-vendor, committing the results. The README calls this mode experimental and says it is intended to allow you to skip the vendoring step and simplify the process of upgrading.
The trade is real. Vendoring means the version-computing code is in your tree, so your build does not depend on versioneer being installable at build time, which is what you want for a package that must build reproducibly on someone else's machine years from now. Build-time dependency mode means an upgrade is a change to one line rather than a diff of a vendored copy, and it means your build fetches a package at build time, which is a dependency on the index.
Both modes share the first two steps. You pip install versioneer to somewhere in your $PATH, and a conda-forge recipe is available so you can also use conda install -c conda-forge versioneer. Then you add a [tool.versioneer] section to your pyproject.toml or a [versioneer] section to your setup.cfg, and the README notes that if you use pyproject.toml you will need to add tomli; python_version < "3.11" to your build-time dependencies.
That tomli note is not incidental. Versioneer has to read the configuration during the build, and on Python below 3.11 there is no tomllib in the standard library, so the toml extra exists to pull in a parser. The optional-dependencies table in pyproject.toml declares exactly that, toml = ["tomli; python_version < '3.11'"].
Both modes then end with the same verification step, python setup.py version, which prints the version it computed.
How the version is actually computed
The theory of operation section describes the mechanism, and it is a good one to understand before you decide the tool is too clever.
Versioneer works by adding a special _version.py file into your source tree, where your __init__.py can import it. This _version.py knows how to dynamically ask the VCS tool for version information at import time.
There is a second layer. The _version.py file also contains $Revision$ markers, and the installation process marks _version.py to have this marker rewritten with a tag name during the git archive command. As a result, generated tarballs will contain enough information to get the proper version.
And a third: to allow setup.py to compute a version too, a versioneer.py is added to the top level of the source tree, next to setup.py and the setup.cfg that configures it. This overrides several distutils/setuptools commands to compute the version when invoked, and changes setup.py build and setup.py sdist to replace _version.py with a small static file that contains just the generated version data.
So there are three answers to three questions. At runtime, the answer comes from asking git. In a git archive export, the answer comes from the expanded keyword. In a built sdist, the answer is frozen into a static file at build time, which is what makes the tarball self-contained.
The version identifier itself can come from several places depending on where the source tree came from. A version-control system checkout, which is mostly used by developers, can ask the VCS tool itself, for example git describe, which knows about recent tags and an absolute revision id. A nightly tarball produced by build automation takes the identifier from the name of the directory into which the tarball was unpacked. A snapshot tarball from a web-based VCS browser, like GitHub's tarball from tag feature, uses an expanded VCS keyword such as $Id$. And a release tarball produced by setup.py sdist and distributed through PyPI uses a _version.py created by some earlier build step.
The result for released software is closely related to a VCS tag. Some projects use tag names that include more than just the version string, for example myproject-1.2 instead of just 1.2, in which case the tool needs to strip the tag prefix to extract the version identifier, and that is what parentdir_prefix is for.
What get_versions returns, and the four ways to use it
Code which uses Versioneer can learn about its version string at runtime by importing _version from the main __init__.py file and running the get_versions() function. From the outside, for example in setup.py, you can import the top-level versioneer.py and run get_versions(). Both return the same dictionary, with several flavours of version information.
The condensed version key holds a version string rendered using the selected style, and it is the most commonly used value. The default pep440 style yields strings like 0.11, or 0.11+2.g1076c97, or 0.11+2.g1076c97.dirty.
The full-revisionid key holds the detailed revision identifier, which for Git is the full SHA1 commit id, for example 1076c978a8d3cfc70f408fe5974aa6c092c949ac.
The date key holds the date and time of the latest HEAD commit, in ISO 8601 format, and it will be None if the date is not available.
The dirty key is a boolean, True if the tree has uncommitted changes, with the note that this is only accurate if run in a VCS checkout, otherwise it is likely to be False or None.
The error key is the one to build your release check around. If the version string could not be computed, this will be set to a string describing the problem, otherwise it will be None, and the README says it may be useful to throw an exception in setup.py if this is set, to avoid creating tarballs with a version string of unknown.
That last one is the important design point. A version of unknown is a real hazard, because it produces an artefact that installs and imports successfully and reports the wrong version to every user who has it. Versioneer gives you a way to detect that condition and refuse to build, and the fact that the tool is opinionated about it rather than silently degrading is the reason to prefer it over a hand-rolled substitute.
The style is configurable, and the pyproject.toml in the repository shows a typical configuration with VCS set to git, style set to pep440, versionfile_source set to _version.py and parentdir_prefix set to an empty string, which is the setting for a project whose tags are bare version numbers.
Editorial conclusion
Adopt versioneer if you release Python packages from a tagged VCS tree and you are tired of the release checklist item that says update the version string. Do not adopt it if your project already handles versioning through a build backend that derives it, and check the setup.py sdist path if you publish wheels only, since the tool's sdist rewriting is a stated behaviour. Verify first that your tag naming scheme is either a bare version or one with a prefix versioneer can strip, that the versionfile_source path in your configuration points at a file you actually keep in the tree, and that the build backend you use is one versioneer recognises before you add it to your build-system requires.
Frequently asked questions
How do I install and configure versioneer for a Python project?
Run `pip install versioneer` to somewhere in your $PATH, or `conda install -c conda-forge versioneer`, then add a [tool.versioneer] section to your pyproject.toml or a [versioneer] section to your setup.cfg. Run `versioneer install --vendor` for vendored mode or `versioneer install --no-vendor` for build-time dependency mode, commit the results, and verify with `python setup.py version`.
What is the difference between versioneer's vendored and build-time dependency modes?
Vendored mode copies versioneer into your repository so the build does not depend on installing it at build time. Build-time dependency mode adds versioneer to the requires key of the build-system table in pyproject.toml and is described in the README as experimental and intended to simplify upgrades.
How do I make sure a release does not get built with an unknown version?
The get_versions() dictionary has an error key that is set to a string describing the problem when the version string could not be computed, and the README suggests throwing an exception in setup.py when it is set, to avoid creating tarballs with a version string of unknown.
What licence does versioneer use?
It is released into the Public Domain under the Unlicense, and the README and pyproject.toml both state that. The classifier in pyproject.toml is License :: OSI Approved :: The Unlicense (Unlicense), and the package requires Python 3.9 or newer.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/python-versioneer-python-versioneer)Was this useful?