versioningit — Versioning It with your Version In Git
GitHub | PyPI | Documentation | Issues | Changelog
How it Works
versioningit divides its operation into seven steps: vcs,
tag2version, next-version, format, template-fields, write,
and onbuild. The first four steps make up the actual version calculation,
while the rest normally only happen while building with setuptools or Hatch.
Version Calculation
The version for a given project is determined as follows:
vcsstep: The version control system specified in the project’sversioningitconfiguration is queried for information about the project’s working directory: the most recent tag, the number of commits since that tag, whether there are any uncommitted changes, and other data points.tag2versionstep: A version is extracted from the tag returned by thevcsstepnext-versionstep: The next version after the most recent version is calculatedIf there have been no commits or uncommitted changes since the most recent tag, the version returned by the
tag2versionstep is used as the project version. Otherwise:formatstep: The results of the preceding steps are combined to produce a final project version.
template-fieldsstep: Values from the preceding steps are used to calculate a set of template fields for use by thewriteand/oronbuildsteps
Build Integration
Setting the Version
versioningit registers plugins with both setuptools and Hatch that cause it
to be run whenever one of those backends computes the metadata for a project in
an environment in which versioningit is installed. If the project in
question has a pyproject.toml file with a [tool.versioningit] table
(or, for Hatch only, a [tool.hatch.version] table containing more than just
a source key), then versioningit performs the version calculations
described above and sets the project’s version to the final value. (If a
version cannot be determined because the project is not in a repository or
repository archive, then versioningit will assume the project is an
unpacked sdist and will look for a PKG-INFO file to fetch the version
from instead.) If the configuration table contains a write subtable, then
the write step will also be run at this time; the default write method
creates a file at a specified path containing the project’s version.
onbuild Step
When a project is built that uses versioningit’s custom setuptools commands
or Hatch build hook, the onbuild step becomes added to the build process.
The default onbuild method updates one of the files in the built
distribution to contain the project version while leaving the source files in
the actual project alone. See “Enabling & Configuring the onbuild Step” for more information.
Configuration
The [tool.versioningit] table in pyproject.toml is divided into
seven subtables, each describing how one of the seven steps of the version
extraction & calculation should be carried out. Each subtable consists of an
optional method key specifying the method (entry point or function)
that should be used to carry out that step, plus zero or more extra keys that
will be passed as parameters to the method when it’s called. If the method
key is omitted, the default method for the step is used.
Specifying the Method
A method can be specified in two different ways, depending on where it’s
implemented. A method that is built in to versioningit or provided by an
installed third-party extension is specified by giving its name as a string,
e.g.:
[tool.versioningit.vcs]
# The method key:
method = "git" # <- The method name
# Parameters to pass to the method:
match = ["v*"]
default-tag = "1.0.0"
Alternatively, a method can be implemented as a function in a Python source
file in your project directory (either part of the main Python package or in an
auxiliary file); see “Writing Your Own Methods” for more information. To tell
versioningit to use such a method, set the method key to a table with a
module key giving the dotted name of the module in which the method is
defined and a value key giving the name of the callable object in the
module that implements the method. For example, if you created a custom
next-version method that’s named my_next_version() and is located in
mypackage/mymodule.py, you would write:
[tool.versioningit.next-version]
method = { module = "mypackage.module", value = "my_next_version" }
# Put any parameters here
Note that this assumes that mypackage/ is located at the root of the
project directory (i.e., the directory containing the pyproject.toml
file); if is located inside another directory, like src/, you will need
to add a module-dir key to the method table giving the path to that
directory relative to the project root, like so:
[tool.versioningit.next-version]
method = { module = "mypackage.module", value = "my_next_version", module-dir = "src" }
# Put any parameters here
As a special case, if there are no parameters for a given step, the respective subtable can be replaced by the method specification, e.g.:
[tool.versioningit]
# Use the "git" method for the vcs step with no parameters:
vcs = "git"
# Use a custom function for the next-version step with no parameters:
next-version = { module = "mypackage.module", value = "my_next_version" }
The [tool.versioningit.vcs] Subtable
The vcs subtable specifies the version control system used by the project
and how to extract the tag and related information from it. versioningit
provides three vcs methods: "git" (the default), "git-archive", and
"hg".
"git"
The "git" method relies on the project directory being located inside a Git
repository with one or more commits. Git 1.8.0 or higher must be installed,
though some optional features require more recent Git versions.
The "git" method takes the following parameters, all optional:
matchlist of stringsA set of fileglob patterns to pass to the
--matchoption ofgit describeto make Git only consider tags matching the given pattern(s). Defaults to an empty list.Note
Specifying more than one match pattern requires Git 2.13.0 or higher.
excludelist of stringsA set of fileglob patterns to pass to the
--excludeoption ofgit describeto make Git not consider tags matching the given pattern(s). Defaults to an empty list.Note
This option requires Git 2.13.0 or higher.
default-tagstringIf
git describecannot find a tag,versioningitwill raise aversioningit.errors.NoTagErrorunlessdefault-tagis set, in which case it will act as though the initial commit is tagged with the value ofdefault-tag
"git-archive"
The "git-archive" method is a variation of the "git" method that also
supports determining the version when installing from a properly-prepared Git
archive. The method takes the following parameters:
describe-subststring(required) Set this to
"$Format:%(describe)$"and add the linepyproject.toml export-substto your repository’s.gitattributesfile. This will cause any Git archive made from your repository from this point forward to contain the minimum necessary information to determine a version.matchandexcludeoptions are set by including them in the format placeholder like so:# Match 'v*' tags: describe-subst = "$Format:%(describe:match=v*)$" # Match 'v*' and 'r*' tags: describe-subst = "$Format:%(describe:match=v*,match=r*)$" # Match 'v*' tags, exclude '*-final' tags: describe-subst = "$Format:%(describe:match=v*,exclude=*-final)$"
By default, only annotated tags are considered, and lightweight tags are ignored; this can be changed by including the “tags” option in the placeholder like so:
# Honor all tags: describe-subst = "$Format:%(describe:tags)$" # Honor all tags, exclude '*rc' tags: describe-subst = "$Format:%(describe:tags,exclude=*rc)$"
Options other than “match”, “exclude”, and “tags” are not supported by
versioningitand will result in an error.default-tagstring(optional) If
git describecannot find a tag,versioningitwill raise aversioningit.errors.NoTagErrorunlessdefault-tagis set, in which case it will act as though the initial commit is tagged with the value ofdefault-tag.Note that this parameter has no effect when installing from a Git archive; if the repository that the archive was produced from had no relevant tags for the archived commit (causing the value of
describe-substto be set to the empty string),versioningitwill raise an error when trying to install the archive.
Note that, in order to provide a consistent set of information regardless of
whether installing from a repository or an archive, the "git-archive"
method provides the format step with only a subset of the fields that the
"git" method does; see below for more information.
Changed in version 1.0.0: The “match” and “exclude” settings are now parsed from the
describe-subst parameter, which is now required, and the old match
and exclude parameters are now ignored. Also, support for the “tags”
option was added.
Note
In order to avoid DOS attacks, Git will not expand more than one
%(describe)s placeholder per archive, and so you should not have any
other $Format:%(describe)$ placeholders in your repository.
Note
This method will not work correctly if you have a tag that resembles git
describe output, i.e., that is of the form
<anything>-<number>-g<hex-chars>. So don’t do that.
"hg"
The "hg" method supports installing from a Mercurial repository or archive.
When installing from a repository, Mercurial 5.2 or higher must be installed.
The "hg" method takes the following parameters, all optional:
patternstringA revision pattern (See
hg help revisions.patterns) to pass to thelatesttag()template function. Note that this parameter has no effect when installing from a Mercurial archive.default-tagstringIf there is no latest tag,
versioningitwill raise aversioningit.errors.NoTagErrorunlessdefault-tagis set, in which case it will act as though the initial commit is tagged with the value ofdefault-tag
The [tool.versioningit.tag2version] Subtable
The tag2version subtable specifies how to extract the version from the tag
found by the vcs step. versioningit provides one tag2version
method, "basic" (the default), which proceeds as follows:
If the
rmprefixparameter is set to a string and the tag begins with that string, the given string is removed from the tag.If the
rmsuffixparameter is set to a string and the tag ends with that string, the given string is removed from the tag.If the
regexparameter is set to a string (a Python regex) and the regex matches the tag (usingre.search), the tag is replaced with the contents of the capturing group named “version”, or the entire matched text if there is no group by that name. If the regex does not match the tag, the behavior depends on therequire-matchparameter: if true, an error is raised; if false or unset, the tag is left as-is.Finally, any remaining leading
v’s are removed from the tag.
A warning is emitted if the resulting version is not PEP 440-compliant.
The [tool.versioningit.next-version] Subtable
The next-version subtable specifies how to calculate the next release
version from the version extracted from the VCS tag. versioningit provides
the following next-version methods; none of them take any parameters.
minor(default) Strips the input version down to just the epoch segment (if any) and release segment (i.e., the
N(.N)*bit), increments the second component of the release segment, and replaces the following components with a single zero. For example, if the version extracted from the VCS tag is1.2.3.4, the"minor"method will calculate a new version of1.3.0.minor-releaseLike
minor, except that if the input version is a prerelease or development release, the base version is returned; e.g.,1.2.3a0becomes1.2.3. This method requires the input version to be PEP 440-compliant.smallestLike
minor, except that it increments the last component of the release segment. For example, if the version extracted from the VCS tag is1.2.3.4, the"smallest"method will calculate a new version of1.2.3.5.smallest-releaseLike
smallest, except that if the input version is a prerelease or development release, the base version is returned; e.g.,1.2.3a0becomes1.2.3. This method requires the input version to be PEP 440-compliant.nullReturns the input version unchanged. Useful if your repo version is something horrible and unparsable.
A warning is emitted if the resulting version is not PEP 440-compliant.
The [tool.versioningit.format] Subtable
The format subtable specifies how to format the project’s final version
based on the information calculated in previous steps. (Note that, if the
repository’s current state is an exact tag match, this step will be skipped and
the version returned by the tag2version step will be used as the final
version.) versioningit provides one format method, "basic" (the
default).
The data returned by the vcs step includes a repository state (describing
the relationship of the repository’s current contents to the most recent tag)
and a collection of format fields. The "basic" format method takes
the name of that state, looks up the format parameter with the same name
(falling back to a default, given below) to get a format template string,
and formats the template using the given format fields plus {base_version},
{next_version}, and {branch} fields. A warning is emitted if the
resulting version is not PEP 440-compliant.
For the built-in vcs methods, the repository states are:
|
One or more commits have been made on the current branch since the latest tag |
|
No commits have been made on the branch since the latest tag, but the repository has uncommitted changes |
|
One or more commits have been made on the branch since the latest tag, and the repository has uncommitted changes |
For the built-in vcs methods, the available format fields are:
|
The author date of the HEAD commit [1] ( |
|
The version extracted from the most recent tag |
|
The name of the current branch (with non-alphanumeric
characters converted to periods), or |
|
The current date & time, or the date & time specified by
the environment variable |
|
The committer date of the HEAD commit [1] ( |
|
The number of commits since the most recent tag |
|
The next release version, calculated by the
|
|
The abbreviated hash of the HEAD commit |
|
The full hash of the HEAD commit ( |
|
The first letter of the name of the VCS (i.e., “ |
|
The name of the VCS (i.e., “ |
Changed in version 2.0.0: The {version} field was renamed to {base_version}. The old name
remains usable but is deprecated.
The default parameters for the format step are:
[tool.versioningit.format]
distance = "{base_version}.post{distance}+{vcs}{rev}"
dirty = "{base_version}+d{build_date:%Y%m%d}"
distance-dirty = "{base_version}.post{distance}+{vcs}{rev}.d{build_date:%Y%m%d}"
The [tool.versioningit.template-fields] Subtable
New in version 2.0.0.
The template-fields subtable controls the fields available for the
templates of the write and onbuild steps. versioningit provides
one template-fields method, "basic" (the default), which provides the
following template fields:
{version}— the project’s final version{version_tuple}— a string representation of a tuple of{version}’s components; see below for how to configure how the version is split upAll of the same fields available in the
formatstep with the “basic”formatmethod (see above), but with{branch}not sanitized and without the{version}alias of{base_version}
Important
If tool.versioningit.default-version (see below)
is set and an error occurs during version calculation, leading to
versioningit recovering by using the given default version, there may
not be enough information afterwards to populate all of the template
fields, and you will get an error if you try to use an unpopulated field in
a write or onbuild template.
If default-version is set, the only fields you can rely on to always be
present are {version} and {version_tuple}.
The "basic" method takes one optional parameter, a version-tuple
subtable (i.e., [tool.versioningit.template-fields.version-tuple]), used to
control how the project’s version is converted into the {version_tuple}
field. This subtable can contain the following fields:
split-onstring(optional) A Python regex that will be used to split apart the project version with
re.split. AnyNoneor empty items returned by the split are discarded. Any items that consist entirely of digits are converted to integers (i.e., they will not be enclosed in quotes in{version_tuple}). Defaults to[-_.+!].This field is ignored when
pep440is true.pep440boolean(optional) If true (default: false), the project version will be parsed & normalized as a PEP 440 version (If is not valid, an error will occur), and
{version_tuple}will consist of the following items, in order:The version’s epoch (as an integer), if
epochis true or ifepochis unspecified and the epoch is nonzeroThe individual components of the release version as integers, including trailing zero components
If the version is a prerelease, the phase identifier and prerelease number (e.g.,
"a0"or"rc1")If the version is a postrelease, “
post” and the postrelease numberIf the version is a dev release, “
dev” and the dev release numberIf the version has a local version label, “
+” and the label
epochboolean(optional) Whether to include the version’s epoch in
{version_tuple}. If unspecified, the epoch is included iff it is nonzero.This option only has an effect when
pep440is true.double-quoteboolean(optional) Whether to enclose string components in double quotes (
True, the default) or single quotes (False)
Here are some examples of how a version can be converted to a
{version_tuple}:
|
|
|
|
|
|---|---|---|---|---|
1.2.3 |
(default) |
Any |
— |
|
1.2.3a0 |
(default) |
|
— |
|
1.2.3a0 |
(default) |
|
— |
|
1.2.3.post1 |
(default) |
Any |
— |
|
1.2.3-1 |
(default) |
|
— |
|
1.2.3-1 |
(default) |
|
— |
|
1.2.3+local.2022 |
(default) |
|
— |
|
1.2.3+local.2022 |
|
|
— |
|
1.2.3+local.2022 |
(default) |
|
— |
|
1.2.3b1.dev3 |
(default) |
|
— |
|
1.2.3 |
(default) |
|
|
|
1!2.3.4 |
(default) |
|
— |
|
1!2.3.4 |
(default) |
|
|
|
0.1.0.0.0 |
(default) |
Any |
— |
|
1.2.3j |
(default) |
|
— |
|
1.2.3j |
(default) |
|
— |
ERROR — Not a PEP 440 version |
1.2.3~local.2022 |
|
|
— |
|
1.2.3~local.2022 |
|
|
— |
ERROR — Not a PEP 440 version |
The [tool.versioningit.write] Subtable
The write subtable enables an optional feature, writing the final version
and/or other fields to a file. Unlike the other subtables, if the write
subtable is omitted, the corresponding step will not be carried out.
versioningit provides one write method, "basic" (the default),
which takes the following parameters:
filestring(required) The path to the file to which to write the version, relative to the root of your project directory. This path should use forward slashes (
/) as the path separator, even on Windows.Note
This file should not be committed to version control, but it should be included in your project’s built sdists and wheels.
Note
If you’re using Hatch and you followed the advice above by adding the file to your
.gitignoreor.hgignore, then you will need to tell Hatch to include the file in sdists & wheels like so:[tool.hatch.build] # Replace the path below with the value you gave to the "file" key: artifacts = ["src/mypackage/_version.py"]
encodingstring(optional) The encoding with which to write the file. Defaults to UTF-8.
template: string(optional) The content to write to the file (minus the final newline, which
versioningitadds automatically), as a string containing some number of{fieldname}placeholders. The available placeholders are determined by thetemplate-fieldsstep (see above).If this parameter is omitted, the default is determined based on the
fileparameter’s file extension. For.txtfiles and files without an extension, the default is:{version}
while for
.pyfiles, the default is:__version__ = "{version}"
If
templateis omitted andfilehas any other extension, an error is raised.
Note
When testing out your configuration with the versioningit command (See
Command), you will need to pass the --write option if you want
the [tool.versioningit.write] subtable to take effect.
Enabling & Configuring the onbuild Step
New in version 1.1.0.
versioningit provides custom setuptools and Hatch hooks for enabling an
optional feature (called the “onbuild step”) in which your project’s
version and/or other fields are inserted into a file in sdists & wheels at
build time while leaving your local project directory alone.
The steps for enabling the onbuild step differ depending on whether you’re
using setuptools or Hatch as your build backend. The configuration options for
the step are the same between the backends, but where you put the configuration
and how you tell the backend to enable the hooks differs.
Using onbuild with setuptools
There are two steps to enabling the onbuild step with setuptools. First,
add a [tool.versioningit.onbuild] table to your pyproject.toml
containing your desired configuration for the step (see below). Second, you need to tell setuptools to use
versioningit’s custom command classes. How to do this depends on what file
you’ve placed your project’s setuptools configuration in.
If you’re configuring setuptools via
setup.cfg, you can simply add the following field to its[options]table:[options] cmdclass = sdist = versioningit.cmdclass.sdist build_py = versioningit.cmdclass.build_py
If you’ve instead placed all your setuptools configuration in
pyproject.toml, then add the following table to it:[tool.setuptools.cmdclass] build_py = "versioningit.cmdclass.build_py" sdist = "versioningit.cmdclass.sdist"
If you’re still configuring setuptools through
setup.py, you’ll need to passversioningit.get_cmdclasses()as thecmdclassargument tosetup(), like so:from setuptools import setup from versioningit import get_cmdclasses setup( cmdclass=get_cmdclasses(), # Other arguments go here )
If you’re already using other custom build_py and/or sdist command
classes, you’ll need to combine them with versioningit’s command classes.
One option is to pass your custom classes to get_cmdclasses() in
setup.py so that versioningit will use them as parent classes; see
the function’s documentation for more information. If that doesn’t work, you
may have to manually modify or subclass your command classes and add a call to
run_onbuild() at the appropriate location; see the function’s documentation
for more information, but you’ll largely be on your own at this point.
New in version 2.2.0: sdist and build_py classes added for use in setup.cfg and
pyproject.toml
Using onbuild with Hatch
New in version 3.0.0: Support for using the onbuild step with Hatch
In order to enable & configure the onbuild step when using versioningit
with Hatch, simply place all of your desired configuration for the step under a
[tool.hatch.build.hooks.versioningit-onbuild] table. Do not use the
[tool.versioningit.onbuild] table with Hatch; it will be ignored, and its
presence will generate a warning.
Note
The versioningit-onbuild build hook is only usable when also using
versioningit as a Hatch version source. Trying to use the build hook
with a different version source will result in an error.
Note
The versioningit-onbuild build hook is only supported when building an
sdist or wheel. Using other Hatch builders (such as the application
builder) with versioningit-onbuild is not supported or endorsed in
any way.
onbuild Configuration Options
versioningit provides one onbuild method, "replace-version" (the
default). It scans a given file for a line matching a given regex and inserts
the project version (or other templated string) into the first line that
matches. The method takes the following parameters:
source-filestring(required) The path to the file to modify, relative to the root of your project directory. This path should use forward slashes (
/) as the path separator, even on Windows.build-filestring(required) The path to the file to modify when building a wheel. This path should be the location of the file when your project is installed, relative to the root of the installation directory. For example, if
source-fileis"src/mypackage/__init__.py", wheresrc/is your project dir, setbuild-fileto"mypackage/__init__.py". If you do not use asrc/-layout or other remapping of package files, setbuild-fileto the same value assource-file.This path should use forward slashes (
/) as the path separator, even on Windows.encodingstring(optional) The encoding with which to read & write the file. Defaults to UTF-8.
regexstring(optional) A Python regex that is tested against each line of the file using
re.search. The first line that matches is updated as follows:If the regex contains a capturing group named “
version”, the substring matched by the group is replaced with the expansion ofreplacement(see below). Ifversiondid not participate in the match, an error is raised.Otherwise, the entire substring of the line matched by the regex is replaced by the expansion of
replacement.
The default regex is:
^\s*__version__\s*=\s*(?P<version>.*)
require-matchboolean(optional) If
regexdoes not match any lines in the file andappend-lineis not set, an error will be raised ifrequire-matchis true (default: false).replacementstring(optional) The string used to replace the relevant portion of the matched line. The string is first expanded by replacing any occurrences of
{fieldname}placeholders with the values provided by thetemplate-fieldsstep (see above), and then any backreferences to capturing groups in the regex are expanded.The default value is
"{version}"(that is, the version enclosed in double quotes).append-linestring(optional) If
regexdoes not match any lines in the file andappend-lineis set, any occurrences of{fieldname}inappend-lineare replaced with the values provided by thetemplate-fieldsstep, and the resulting line is appended to the end of the file.
Thus, with the default settings, "replace-version" finds the first line in
the given file of the form “__version__ = ...” and replaces the part after
the = with the project version in double quotes; if there is no such line,
the file is left unmodified.
Note
If you use this feature and run python setup.py directly (as
opposed to building with build or similar), you must invoke
setup.py from the root project directory (the one containing your
setup.py).
Tip
You are encouraged to test your onbuild configuration by building an
sdist and wheel for your project and examining the files within to ensure
that they look how you want. An sdist can be expanded by running
tar zxf filename, and a wheel can be expanded by running
unzip filename.
tool.versioningit.default-version
The final key in the [tool.versioningit] table is default-version,
which is a string rather than a subtable. When this key is set and an error
occurs during version calculation, versioningit will set your package’s
version to the given default version. When this key is not set, any errors
that occur inside versioningit will cause the build/install process to
fail.
Note that default-version is not applied if an error occurs while parsing
the [tool.versioningit] table; however, such errors can be caught ahead of
time by running the versioningit command (See “Command”).
Log Level Environment Variable
When versioningit is invoked via the setuptools or Hatch plugin interface,
it logs various information to stderr. By default, only messages at
WARNING level or higher are displayed, but this can be changed by setting
the VERSIONINGIT_LOG_LEVEL environment variable to the name of a
Python logging level (case insensitive) or the equivalent integer value.
Getting Package Version at Runtime
Automatically setting your project’s version is all well and good, but you
usually also want to expose that version at runtime, usually via a
__version__ variable. There are three options for doing this:
Use the
version()function fromimportlib.metadatato get your package’s version, like so:from importlib.metadata import version __version__ = version("mypackage")
Note that
importlib.metadatawas only added to Python in version 3.8. If you wish to support older Python versions, use the importlib-metadata backport available on PyPI for those versions, e.g.:import sys if sys.version_info >= (3, 8): from importlib.metadata import version else: from importlib_metadata import version __version__ = version("mypackage")
If relying on the backport, don’t forget to include
importlib-metadata; python_version < "3.8"in your project’sinstall_requires!Fill out the
[tool.versioningit.write]subtable inpyproject.tomlso that the project version will be written to a file in your Python package which you can then import or read. For example, if your package is namedmypackageand is stored in asrc/directory, you can write the version to a Python filesrc/mypackage/_version.pylike so:[tool.versioningit.write] file = "src/mypackage/_version.py"
Then, within
mypackage/__init__.py, you can import the version like so:from ._version import __version__
Alternatively, you can write the version to a text file, say,
src/mypackage/VERSION:[tool.versioningit.write] file = "src/mypackage/VERSION"
and then read the version in at runtime with:
from pathlib import Path __version__ = Path(__file__).with_name("VERSION").read_text().strip()
(New in version 1.1.0) Use the onbuild step and its custom hooks to create sdists & wheels in which some file has been modified to contain the line
__version__ = "<project version>"or similar while leaving your repository’s contents alone.
Tip
Wondering which of write and onbuild is right for your project?
See this table for a comparison:
|
|
|
|---|---|---|
Should affected file be under version control? |
No |
Yes |
Affected file must already exist? |
No |
Yes |
Modifies working tree? [1] |
Yes |
No |
Run when installing in editable mode? |
Yes |
No |
That is, the write method causes a file to be present (though
likely ignored) in your repository after running, while the onbuild
method only modifies a file inside sdists & wheels and leaves the
original copy in your repository unchanged.
Hatch Integration
New in version 2.3.0.
If you’re not a setuptools user, versioningit can also be used as a version
source plugin for the Hatch build backend. You use it in pretty much the same
way as for setuptools:
Include
versioningitin your build requirements like so:[build-system] requires = ["hatchling", "versioningit"] build-backend = "hatchling.build"
Tell Hatch that you’re using a dynamic version source by including
"version"in theproject.dynamickey:[project] name = "your-project-name" dynamic = ["version"] # The rest of your project metadata follows after. # Do not set the `version` key in [project]. If it's currently set, # remove it.
Tell Hatch to use
versioningitas the version source:[tool.hatch.version] source = "versioningit"
Configure
versioningitas normal (mostly; see the note aboutonbuildbelow). However, with Hatch, you have two possible locations to putversioningit’s configuration in: either the[tool.versioningit]table as used with setuptools or under the[tool.hatch.version]table. Moreover, unlike when using setuptools, you don’t even need the[tool.versioningit]table if it’s just going to be empty.For example, the following configurations are equivalent:
Using
[tool.versioningit]:[tool.hatch.version] source = "versioningit" [tool.versioningit] default-version = "0.0.0+unknown" [tool.versioningit.format] distance = "{next_version}.dev{distance}+{vcs}{rev}" dirty = "{version}+dirty" distance-dirty = "{next_version}.dev{distance}+{vcs}{rev}.dirty"
Using
[tool.hatch.version]:[tool.hatch.version] source = "versioningit" default-version = "0.0.0+unknown" [tool.hatch.version.format] distance = "{next_version}.dev{distance}+{vcs}{rev}" dirty = "{version}+dirty" distance-dirty = "{next_version}.dev{distance}+{vcs}{rev}.dirty"
If you configure
versioningitvia[tool.hatch.version]and also define a[tool.versioningit]table (even if it’s empty), a warning will be emitted, and only the[tool.hatch.version]configuration will be used.If you use the
writestep to create a file containing your project version, and this file is listed in your.gitignoreor.hgignore, you will need to tell Hatch to include the file in sdists & wheels like so:[tool.hatch.build] # Replace the path below with the path to the file created by the # `write` step: artifacts = ["src/mypackage/_version.py"]
The configuration for the
onbuildstep is placed in the[tool.hatch.build.hooks.versioningit-onbuild]table (not in[tool.versioningit.onbuild]or[tool.hatch.version.onbuild]). In addition, filling out this table is all you need to do to enable theonbuildstep — no fiddling with command classes necessary!
Note
If you use versioningit with Hatch, you will not be able to set your
project’s version by running hatch version x.y.z. Just create a tag
instead!
Command
versioningit [<options>] [<project-dir>]
When versioningit is installed in the current Python environment, a command
of the same name will be available that prints out the version for a given
versioningit-enabled project (by default, the project rooted in the current
directory). This can be used to test out your versioningit setup before
publishing.
Options
- -n, --next-version
Instead of printing the current version of the project, print the value of the next release version as computed by the
next-versionstep
- --traceback
Normally, any library errors are shown as just the error message. Specify this option to show the complete error traceback.
- -v, --verbose
Increase the amount of log messages displayed. Specify twice for maximum information.
The logging level can also be set via the
VERSIONINGIT_LOG_LEVELenvironment variable. If both-vandVERSIONINGIT_LOG_LEVELare specified, the more verbose log level of the two will be used, where one-vcorresponds toINFOlevel and two or more correspond toDEBUGlevel. (If neither are specified, the default level ofWARNINGis used.)
- -w, --write
Write the version to the file specified in the
[tool.versioningit.write]subtable, if so configured
Library API
High-Level Functions and Classes
- versioningit.get_version(project_dir: str | Path = '.', config: dict | None = None, write: bool = False, fallback: bool = True) str[source]
Determine the version for the project at
project_dir.If
configisNone, thenproject_dirmust contain apyproject.tomlfile containing either a[tool.versioningit]table or a[tool.hatch.version]table with thesourcekey set to"versioningit"; if it does not, aNotVersioningitErroris raised. Ifconfigis notNone, then anypyproject.tomlfile inproject_dirwill be ignored, and the configuration will be taken fromconfiginstead. See “Passing Explicit Configuration”.If
writeis true, then the file specified in thewritesubtable of the versioningit configuration, if any, will be updated.If
fallbackis true, then ifproject_diris not under version control (or if the VCS executable is not installed),versioningitwill assume that the directory is an unpacked sdist and will read the version from thePKG-INFOfile.- Raises:
NotVCSError – if
fallbackis false andproject_diris not under version controlNotSdistError – if
fallbackis true,project_diris not under version control, and there is noPKG-INFOfile inproject_dirConfigError – if any of the values in
configare not of the correct typeMethodError – if a method returns a value of the wrong type
- versioningit.get_next_version(project_dir: str | Path = '.', config: dict | None = None) str[source]
New in version 0.3.0.
Determine the next version after the current VCS-tagged version for
project_dir.If
configisNone, thenproject_dirmust contain apyproject.tomlfile containing either a[tool.versioningit]table or a[tool.hatch.version]table with thesourcekey set to"versioningit"; if it does not, aNotVersioningitErroris raised. Ifconfigis notNone, then anypyproject.tomlfile inproject_dirwill be ignored, and the configuration will be taken fromconfiginstead. See “Passing Explicit Configuration”.- Raises:
NotVCSError – if
project_diris not under version controlConfigError – if any of the values in
configare not of the correct typeMethodError – if a method returns a value of the wrong type
- versioningit.get_cmdclasses(bases: dict[str, type[Command]] | None = None) dict[str, type[Command]][source]
New in version 1.1.0.
Return a
dictof custom setuptoolsCommandclasses, suitable for passing to thecmdclassargument ofsetuptools.setup(), that run theonbuildstep for the project when building an sdist or wheel. Specifically, thedictcontains a subclass ofsetuptools.command.sdist.sdistat the"sdist"key and a subclass ofsetuptools.command.build_py.build_pyat the"build_py"key.A
dictof alternative base classes can optionally be supplied; if thedictcontains an"sdist"entry, that entry will be used as the base class for the customizedsdistcommand, and likewise for"build_py". All other classes in the inputdictare passed through unchanged.
- class versioningit.cmdclass.sdist
New in version 2.2.0.
A custom subclass of
setuptools.command.sdist.sdistthat runs theonbuildstep when building an sdist. This class is equivalent toget_cmdclasses()["sdist"], except that it can also be used in the[options]cmdclassfield insetup.cfg.
- class versioningit.cmdclass.build_py
New in version 2.2.0.
A custom subclass of
setuptools.command.build_py.build_pythat runs theonbuildstep when building a wheel. This class is equivalent toget_cmdclasses()["build_py"], except that it can also be used in the[options]cmdclassfield insetup.cfg.
Note
When importing or referring to the sdist and build_py command
classes, the .cmdclass submodule needs to be specified; unlike the rest
of the library API, they are not importable directly from versioningit.
[options]
cmdclass =
# Right!
sdist = versioningit.cmdclass.sdist
build_py = versioningit.cmdclass.build_py
[options]
cmdclass =
# Wrong!
sdist = versioningit.sdist
build_py = versioningit.build_py
Low-Level Class
- class versioningit.Versioningit[source]
A class for getting a version-controlled project’s current version based on its most recent tag and the difference therefrom
- classmethod from_project_dir(project_dir: str | Path = '.', config: dict | None = None) Versioningit[source]
Construct a
Versioningitobject for the project rooted atproject_dir(default: the current directory).If
configisNone, thenproject_dirmust contain apyproject.tomlfile containing either a[tool.versioningit]table or a[tool.hatch.version]table with thesourcekey set to"versioningit"; if it does not, aNotVersioningitErroris raised. Ifconfigis notNone, then anypyproject.tomlfile inproject_dirwill be ignored, and the configuration will be taken fromconfiginstead. See “Passing Explicit Configuration”.- Raises:
ConfigError – if the configuration object/table or any of its subfields are not of the correct type
- get_version(write: bool = False, fallback: bool = True) str[source]
Determine the version for the project.
If
writeis true, then the file specified in thewritesubtable of the versioningit configuration, if any, will be updated.If
fallbackis true, then ifproject_diris not under version control (or if the VCS executable is not installed),versioningitwill assume that the directory is an unpacked sdist and will read the version from thePKG-INFOfile.Changed in version 2.0.0:
writeandfallbackarguments added- Raises:
NotVCSError – if
fallbackis false andproject_diris not under version controlNotSdistError – if
fallbackis true,project_diris not under version control, and there is noPKG-INFOfile inproject_dirConfigError – if any of the values in
configare not of the correct typeMethodError – if a method returns a value of the wrong type
- run(write: bool = False, fallback: bool = True) Report | FallbackReport[source]
New in version 2.0.0.
Run all of the steps for the project — aside from “onbuild” and, optionally, “write” — and return an object containing the final version and intermediate values.
If
writeis true, then the file specified in thewritesubtable of the versioningit configuration, if any, will be updated.If
fallbackis true, then ifproject_diris not under version control (or if the VCS executable is not installed),versioningitwill assume that the directory is an unpacked sdist and will read the version from thePKG-INFOfile, returning aFallbackReportinstance instead of aReport.- Raises:
NotVCSError – if
fallbackis false andproject_diris not under version controlNotSdistError – if
fallbackis true,project_diris not under version control, and there is noPKG-INFOfile inproject_dirConfigError – if any of the values in
configare not of the correct typeMethodError – if a method returns a value of the wrong type
- do_vcs() VCSDescription[source]
Run the
vcsstep- Raises:
MethodError – if the method does not return a
VCSDescription
- do_tag2version(tag: str) str[source]
Run the
tag2versionstep- Raises:
MethodError – if the method does not return a
str
- do_next_version(version: str, branch: str | None) str[source]
Run the
next-versionstep- Raises:
MethodError – if the method does not return a
str
- do_format(description: VCSDescription, base_version: str, next_version: str) str[source]
Run the
formatstepChanged in version 2.0.0: The
versionargument was renamed tobase_version.- Raises:
MethodError – if the method does not return a
str
- do_template_fields(version: str, description: VCSDescription | None, base_version: str | None, next_version: str | None) dict[source]
New in version 2.0.0.
Run the
template_fieldsstep- Raises:
MethodError – if the method does not return a
dict
Exceptions
- exception versioningit.ConfigError[source]
Bases:
Error,ValueErrorRaised when the
versioningitconfiguration contain invalid settings
- exception versioningit.InvalidTagError[source]
Bases:
Error,ValueErrorRaised by
tag2versionmethods when passed a tag that they cannot work with
- exception versioningit.InvalidVersionError[source]
Bases:
Error,ValueErrorRaised by
next-versionandtemplate-fieldsmethods when passed a version that they cannot work with
- exception versioningit.MethodError[source]
Bases:
ErrorRaised when a method is invalid or returns an invalid value
- exception versioningit.NoTagError[source]
Bases:
ErrorRaised when a tag cannot be found in version control
- exception versioningit.NotSdistError[source]
Bases:
ErrorRaised when attempting to read a
PKG-INFOfile from a directory that doesn’t have one
Utilities
- class versioningit.VCSDescription(tag: str, state: str, branch: str | None, fields: dict[str, Any])[source]
A description of the state of a version control repository
- branch: str | None
The name of the repository’s current branch, or
Noneif it cannot be determined or does not apply
- fields: dict[str, Any]
A
dictof additional information about the repository state to make available to theformatmethod. Customvcsmethods are advised to adhere closely to the set of fields used by the built-in methods.
- state: str
The relationship of the repository’s current state to the tag. If the repository state is exactly the tagged state, this field should equal
"exact"; otherwise, it will be a string that will be used as a key in theformatsubtable of the versioningit configuration. Recommended values are"distance","dirty", and"distance-dirty".
- class versioningit.Report(version: str, description: VCSDescription | None, base_version: str | None, next_version: str | None, template_fields: dict[str, Any], using_default_version: bool)[source]
New in version 2.0.0.
A report of the intermediate & final values calculated during a
versioningitrun- base_version: str | None
A version string extracted from the VCS tag;
Noneif the “tag2version” step or a previous step failed
- description: VCSDescription | None
A description of the state of the version control repository;
Noneif the “vcs” step failed
- next_version: str | None
A “next version” calculated by the “next-version” step;
Noneif the step or a previous one failed
- template_fields: dict[str, Any]
A
dictof fields for use in templating by the “write” and “onbuild” steps
- class versioningit.FallbackReport(version: str)[source]
New in version 2.0.0.
A report of the version extracted from a
PKG-INFOfile in an sdist
- versioningit.get_version_from_pkg_info(project_dir: str | Path) str[source]
Return the Version field from the
PKG-INFOfile inproject_dir- Raises:
NotSdistError – if there is no
PKG-INFOfileValueError – if the
PKG-INFOfile does not contain a Version field
- versioningit.run_onbuild(*, build_dir: str | Path, is_source: bool, template_fields: dict[str, Any], project_dir: str | Path = '.', config: dict | None = None) None[source]
New in version 1.1.0.
Run the
onbuildstep for the given setuptools project.This function is intended to be used by custom setuptools command classes that are used in place of
versioningit’s command classes but still need to be able to run theonbuildstep. Thetemplate_fieldsvalue can be obtained by passing a command class’sdistributionattribute toget_template_fields_from_distribution(); if this returnsNone, then we are building from an sdist, andrun_onbuild()should not be called.If
configisNone, thenproject_dirmust contain apyproject.tomlfile containing a[tool.versioningit]table; if it does not, aNotVersioningitErroris raised. Ifconfigis notNone, then anypyproject.tomlfile inproject_dirwill be ignored, and the configuration will be taken fromconfiginstead; see “Passing Explicit Configuration”.Changed in version 2.0.0:
versionargument replaced withtemplate_fields- Parameters:
- Raises:
if
configisNoneandproject_dirdoes not contain apyproject.tomlfileif the
pyproject.tomlfile does not contain a[tool.versioningit]table
ConfigError – if any of the values in
configare not of the correct typeMethodError – if a method returns a value of the wrong type
- versioningit.get_template_fields_from_distribution(dist: Distribution) dict[str, Any] | None[source]
Extract the template fields (calculated by the “template-fields” step) that were stashed on the
setuptools.Distributionbyversioningit’s setuptools hook, for passing to the “onbuild” step. If setuptools is building from an sdist instead of a repository, no template fields will have been calculated, andNonewill be returned, indicating that the “onbuild” step should not be run.
Passing Explicit Configuration
The functions & methods that take a path to a project directory normally read
the project’s configuration from the pyproject.toml file therein, but
they can also be passed a config argument to take the configuration from
instead, in which case pyproject.toml will be ignored and need not even
exist.
A config argument must be a dict whose structure mirrors the structure of
the [tool.versioningit] table in pyproject.toml. For example, the
following TOML configuration:
[tool.versioningit.vcs]
method = "git"
match = ["v*"]
[tool.versioningit.next-version]
method = { module = "setup", value = "my_next_version" }
[tool.versioningit.format]
distance = "{next_version}.dev{distance}+{vcs}{rev}"
dirty = "{base_version}+dirty"
distance-dirty = "{next_version}.dev{distance}+{vcs}{rev}.dirty"
corresponds to the following Python config value:
{
"vcs": {
"method": "git",
"match": ["v*"],
},
"next-version": {
"method": {
"module": "setup",
"value": "my_next_version",
},
},
"format": {
"distance": "{next_version}.dev{distance}+{vcs}{rev}",
"dirty": "{base_version}+dirty",
"distance-dirty": "{next_version}.dev{distance}+{vcs}{rev}.dirty",
},
}
This is the same structure that you would get by reading from the
pyproject.toml file like so:
import tomli
with open("pyproject.toml", "rb") as fp:
config = tomli.load(fp)["tool"]["versioningit"]
When passing versioningit configuration as a config argument, an
alternative way to specify methods becomes available: in place of a method
specification, one can pass a callable object directly.
Writing Your Own Methods
Changed in version 1.0.0: User parameters, previously passed as keyword arguments, are now passed as
a single params argument.
If you need to customize how a versioningit step is carried out, you can
write a custom function in a Python module in your project directory and point
versioningit to that function as described under
“Specifying the Method”.
When a custom function is called, it will be passed a step-specific set of
arguments, as documented below, plus all of the parameters specified in the
step’s subtable in pyproject.toml. (The arguments are passed as
keyword arguments, so custom methods need to give them the same names as
documented here.) For example, given the below configuration:
[tool.versioningit.vcs]
method = { module = "ving_methods", value = "my_vcs", module-dir = "tools" }
tag-dir = "tags"
annotated-only = true
versioningit will carry out the vcs step by calling my_vcs() in
ving_methods.py in the tools/ directory with the arguments
project_dir (set to the directory in which the pyproject.toml file
is located) and params={"tag-dir": "tags", "annotated-only": True}.
If a user-supplied parameter to a method is invalid, the method should raise a
versioningit.errors.ConfigError. If a method is passed a parameter that it
does not recognize, it should ignore it (though it may log a warning).
If you choose to store your custom methods in your setup.py, be sure to
place the call to setup() under an if __name__ == "__main__": guard so
that the module can be imported without executing setup().
If you store your custom methods in a module other than setup.py that
is not part of the project’s Python package (e.g., if the module is stored in a
tools/ directory), you need to ensure that the module is included in
your project’s sdists but not in wheels.
If your custom method depends on any third-party libraries, they must be listed
in your project’s build-system.requires.
vcs
A custom vcs method is a callable with the following synopsis:
- funcname(*, project_dir: str | pathlib.Path, params: dict[str, Any]) versioningit.VCSDescription
- Parameters:
project_dir (path) – the path to a project directory
params (dict) – a collection of user-supplied parameters
- Returns:
a description of the state of the version control repository at the directory
- Return type:
- Raises:
versioningit.errors.NoTagError – If a tag cannot be determined for the repository
versioningit.errors.NotVCSError – if
project_diris not under the expected type of version control
tag2version
A custom tag2version method is a callable with the following synopsis:
- funcname(*, tag: str, params: dict[str, Any]) str
- Parameters:
- Returns:
a version string extracted from
tag- Return type:
- Raises:
versioningit.errors.InvalidTagError – if the tag cannot be parsed
next-version
A custom next-version method is a callable with the following synopsis:
format
A custom format method is a callable with the following synopsis:
- funcname(*, description: versioningit.VCSDescription, base_version: str, next_version: str, params: dict[str, Any]) str
- Parameters:
description – a
versioningit.VCSDescriptionreturned by avcsmethodbase_version (str) – a version string extracted from the VCS tag
next_version (str) – a “next version” calculated by the
next-versionstepparams (dict) – a collection of user-supplied parameters
- Returns:
the project’s final version string
- Return type:
Changed in version 2.0.0: The version argument was renamed to base_version.
Note that the format method is not called if description.state is
"exact", in which case the version returned by the tag2version step is
used as the final version.
template-fields
A custom template-fields method is a callable with the following synopsis:
- funcname(*, version: str, description: VCSDescription | None, base_version: str | None, next_version: str | None, params: dict[str, Any]) dict[str, Any]
- Parameters:
version (str) – the project’s final version
description (Optional[VCSDescription]) – a
versioningit.VCSDescriptionreturned by avcsmethod;Noneif thevcsmethod failedbase_version (Optional[str]) – a version string extracted from the VCS tag;
Noneif thetag2versionstep or a previous step failednext_version (Optional[str]) – a “next version” calculated by the
next-versionstep;Noneif the step or a previous one failedparams (dict) – a collection of user-supplied parameters
- Return type:
write
A custom write method is a callable with the following synopsis:
- funcname(*, project_dir: str | pathlib.Path, template_fields: dict[str, Any], params: dict[str, Any]) None
Changed in version 2.0.0: version argument replaced with template_fields
onbuild
New in version 1.1.0.
A custom onbuild method is a callable with the following synopsis:
- funcname(*, file_provider: OnbuildFileProvider, is_source: bool, template_fields: dict[str, Any], params: dict[str, Any]) None
Modifies the files about to be included in an sdist or wheel
- Parameters:
file_provider – an object for accessing files being built into an sdist or wheel
is_source (bool) – true if an sdist or other artifact that preserves source paths is being built, false if a wheel or other artifact that uses installation paths is being built
template_fields (dict) – a collection of variables to use in filling out templates, as calculated by the
template-fieldsstepparams (dict) – a collection of user-supplied parameters
Changed in version 2.0.0: version argument replaced with template_fields
Changed in version 3.0.0: build_dir argument replaced with file_provider
onbuild methods are provided with instances of the following abstract base
classes for operating on:
- class versioningit.OnbuildFileProvider[source]
New in version 3.0.0.
An abstract base class for accessing files that are about to be included in an sdist or wheel currently being built
- abstract get_file(source_path: str | PurePath, install_path: str | PurePath, is_source: bool) OnbuildFile[source]
Get an object for reading & writing a file in the project being built.
- Parameters:
source_path – the path to the file relative to the root of the project’s source
install_path – the path to the same file when it’s in a wheel, relative to the root of the wheel (or, equivalently, the path to the file when it’s installed in a site-packages directory, relative to that directory)
is_source –
Trueif building an sdist or other artifact that preserves source paths,Falseif building a wheel or other artifact that uses install paths
- class versioningit.OnbuildFile[source]
New in version 3.0.0.
An abstract base class for opening a file in a project currently being built
- abstract open(mode: TextMode = 'r', encoding: str | None = None, errors: str | None = None, newline: str | None = None) TextIO[source]
- abstract open(mode: BinaryMode, encoding: None = None, errors: None = None, newline: None = None) IO[bytes]
Open the associated file.
modemust be"r","w","a","rb","br","wb","bw","ab", or"ba".When opening a file for writing or appending, if the file does not already exist, any parent directories are created automatically.
Distributing Your Methods in an Extension Package
If you want to make your custom versioningit methods available for others
to use, you can package them in a Python package and distribute it on PyPI.
Simply create a Python package as normal that contains the method function, and
specify the method function as an entry point of the project. The name of the
entry point group is versioningit.STEP (though, for next-version and
template-fields, the group is spelled with an underscore instead of a
hyphen). For example, if you have a custom vcs method implemented as a
foobar_vcs() function in mypackage/vcs.py, you would declare it as
follows:
[options.entry_points]
versioningit.vcs =
foobar = mypackage.vcs:foobar_vcs
[project.entry-points."versioningit.vcs"]
foobar = "mypackage.vcs:foobar_vcs"
Once your package is on PyPI, package developers can use it by including it in
their build-system.requires and specifying the name of the entry point (For
the entry point above, this would be foobar) as the method name in the
appropriate subtable. For example, a user of the foobar method for the
vcs step would specify it as:
[tool.versioningit.vcs]
method = "foobar"
# Parameters go here
Notes
Restrictions & Caveats
When building or installing a project that uses
versioningit, the entire repository history (or at least everything back through the most recent tag) must be available. This means that installing from a shallow clone (the default on most CI systems) will not work. If you are using the"git"or"git-archive"vcsmethod and havedefault-tagset in[tool.versioningit.vcs], then shallow clones will end up assigned the default tag, which may or may not be what you want.If using the
[tool.versioningit.write]subtable to write the version to a file, this file will only be updated whenever the project is built or installed. If using editable installs, this means that you must re-runpython setup.py developorpip install -e .after each commit if you want the version to be up-to-date.If you define & use a custom method inside your Python project’s package, you will not be able to retrieve your project version by calling
importlib.metadata.version()inside__init__.py— at least, not without atry: ... except ...wrapper. This is becauseversioningitloads the package containing the custom method before the package is installed, butimportlib.metadata.version()only works after the package is installed.If you generate a conda package from your sdist (e.g., for a conda-forge feedstock), you will likely want to include
versioningitas ahostdependency in your condameta.yamlfile. This is needed for the package produced from your sdist to contain the correct version number in itsdist-info.
Backwards Compatibility Policy
versioningit follows Semantic Versioning, in which the major version
component is incremented whenever a breaking change is made. Moreover, the
basic pyproject.toml interface to versioningit can be considered very
stable; the only changes to expect to it will be the addition of new features
and the occasional patching over of corner-case bugs. Nearly all breaking
changes will be to the library or custom method API; if you’ve written any code
that uses this part of the API, you are advised to declare the next major
version of versioningit as an upper bound on your versioningit
dependency.
Bug fixes may, on occasion, be backported to previous major versions of
versioningit, but this is in general unlikely to happen unless a user
specifically requests it.
Changelog
v3.1.0 (2024-03-16)
When git describe fails to retrieve a tag, the resulting log/error message now includes all options passed to the command (based on contribution by @jenshnielsen)
When hg log fails to retrieve a tag, the resulting log/error message now includes the tag pattern passed to
latesttag(), if any
v3.0.0 (2023-12-13)
Migrated from setuptools to hatch
Support using the
onbuildstep with HatchBreaking: The
build_dirargument passed toVersioningit.do_onbuild()andonbuildmethod callables has been changed to anOnbuildFileProviderABC
v2.3.0 (2023-11-19)
Always read
.hg_archival.txtfiles using UTF-8 encodingAdded support for using versioningit with Hatch
v2.2.1 (2023-09-22)
Raise a
ConfigErrorif the selectedtool.versioningit.formatfield is not a stringUpdate tests for pydantic 2.0
Update tests for Python 3.12
Support Python 3.12
v2.2.0 (2023-02-11)
The custom setuptools command classes can now be imported directly from the
versioningit.cmdclassmodule as an alternative to callingget_cmdclasses()
v2.1.0 (2022-10-25)
Drop support for Python 3.6
Support Python 3.11
Use
tomllibon Python 3.11
v2.0.1 (2022-08-01)
Don’t run the
onbuildstep under setuptools’ upcoming PEP 660 editable mode (contributed by @abravalheri)
v2.0.0 (2022-06-12)
The
{version}placeholder in the “basic”formatstep has been renamed to{base_version}. The old name remains usable, but is deprecated.Breaking: The
versionargument passed toVersioningit.do_format()andformatmethod callables has been renamed tobase_version.
A
{version_tuple}field, along with the fields available in theformatstep, is now available for use in templates in thewriteandonbuildsteps.New step and subtable: “template-fields”
Breaking: The
versionarguments passed toVersioningit.do_write(),Versioningit.do_onbuild(),run_onbuild(), andwrite&onbuildmethod callables have been replaced withtemplate_fieldsargumentsAdded a
get_template_fields_from_distribution()function for use by callers ofrun_onbuild()
Versioningit.get_version()now takes optionalwriteandfallbackargumentsThe
onbuildstep is no longer run when building from an sdist; the configuration therefore no longer needs to be idempotentDrop setuptools runtime dependency
setuptools is only needed for
get_cmdclasses(), which should only be called in an environment where setuptools is already installed.
Prevent log messages from being printed twice under recent versions of setuptools
Values supplied for the
require-matchparameters of thetag2versionandonbuildsteps must now actually be booleans; previously, values of any type were accepted and were converted to booleans.Added a
Versioningit.run()method that returns a structure containing all intermediate & final values“git” method:
{author_date}and{committer_date}are no longer “clamped” to less than or equal to{build_date}. This undocumented behavior was based on a misinterpretation of theSOURCE_DATE_EPOCHspec, and was even applied whenSOURCE_DATE_EPOCHwas not set.When resolving entry points, if multiple entry points with the given group & name are found, error instead of using the first one returned
v1.1.2 (2022-08-12)
Backport “Don’t run the
onbuildstep under setuptools’ upcoming PEP 660 editable mode” from v2.0.1 (contributed by @abravalheri)
v1.1.1 (2022-04-08)
Do not import setuptools unless needed (contributed by @jenshnielsen)
v1.1.0 (2022-03-03)
Added custom setuptools commands for inserting the project version into a source file at build time
New step and subtable: “onbuild”
New public
get_cmdclasses()andrun_onbuild()functions
Moved documentation from the README to a Read the Docs site
Established external documentation for the public library API
When falling back to using
tool.versioningit.default-version, emit a warning if the version is not PEP 440-compliant.The
versioningitcommand now honors theVERSIONINGIT_LOG_LEVELenvironment variable
v1.0.0 (2022-02-06)
Changes to custom methods:
The signatures of the method functions have changed; user-supplied parameters are now passed as a single
params: Dict[str, Any]argument instead of as keyword arguments.User-supplied parameters with the same names as step-specific method arguments are no longer discarded.
Changes to the “git-archive” method:
Lightweight tags are now ignored (by default, but see below) when installing from a repository in order to match the behavior of the
%(describe)format placeholder.The “match” and “exclude” settings are now parsed from the
describe-substparameter, which is now required, and the oldmatchandexcludeparameters are now ignored.Git 2.35’s “tags” option for honoring lightweight tags is now recognized.
Added a dedicated error message when an invalid
%(describe)placeholder is “expanded” into itself in an archive
The
fileparameter to the “basic” write method is now required when the[tool.versioningit.write]table is present. If you don’t want to write the version to a file, omit the table entirely.Library API:
Configis no longer exported; it should now be considered private.Merged
Versioningit.from_config()functionality intoVersioningit.from_project_dir()Renamed
Versioningit.from_config_obj()toVersioningit.from_config(); it should now be considered private
v0.3.3 (2022-02-04)
Git 1.8.0 is now the minimum required version for the git methods, and this is documented. (Previously, the undocumented minimum version was Git 1.8.5.)
Document the minimum supported Mercurial version as 5.2.
v0.3.2 (2022-01-16)
Call
importlib.metadata.entry_points()only once and reuse the result for a speedup (contributed by @jenshnielsen)
v0.3.1 (2022-01-02)
Support Python 3.10
Support tomli 2.0
v0.3.0 (2021-09-27)
Gave the CLI interface an
-n/--next-versionoption for showing a project’s next release versionAdded a
get_next_version()functionAdded a mention to the README of the existence of exported functionality other than
get_version()Renamed the individual step-calling methods of
Versioningitto have names of the formdo_$STEP()
v0.2.1 (2021-08-02)
Update for tomli 1.2.0
v0.2.0 (2021-07-13)
The log messages displayed for unknown parameters are now at WARNING level instead of INFO and include suggestions for what you might have meant
“git”
vcsmethod:default-tagwill now be honored if the git describe command fails (which generally only happens in a repository without any commits)Added an experimental “git-archive” method for determining a version when installing from a Git archive
Project directories under
.git/are no longer considered to be under version controlProject directories inside Git working directories that are not themselves tracked by Git are no longer considered to be under version control
Support added for installing from Mercurial repositories & archives
v0.1.0 (2021-07-08)
Add more logging messages
Changed default version formats to something that doesn’t use
{next_version}“basic”
tag2versionmethod:If
regexis given and it does not contain a group named “version,” the entire text matched by the regex will be used as the versionAdded a
require-matchparameter for erroring if the regex does not match
“basic”
writemethod:encodingnow defaults to UTF-8New
next-versionmethods:"minor-release","smallest-release", and"null"Replaced
entrypointsdependency withimportlib-metadataAdded
tool.versioningit.default-versionfor setting the version to use if an error occursWhen building a project from a shallow clone or in a non-sdist directory without VCS information, display an informative error message.
v0.1.0a1 (2021-07-05)
Alpha release
versioningit is yet another Python packaging plugin for automatically
determining your package’s version based on your version control repository’s
tags. Unlike others, it allows easy customization of the version format and
even lets you easily override the separate functions used for version
extraction & calculation.
Features:
Works with both setuptools and Hatch
Installed & configured through PEP 518’s
pyproject.tomlSupports Git, modern Git archives, and Mercurial
Formatting of the final version uses format template strings, with fields for basic VCS information and separate template strings for distanced vs. dirty vs. distanced-and-dirty repository states
Can optionally write the final version and other details to a file for loading at runtime
Provides custom hooks for inserting the final version and other details into a source file at build time
The individual methods for VCS querying, tag-to-version calculation, version bumping, version formatting, and writing the version to a file can all be customized using either functions defined alongside one’s project code or via publicly-distributed entry points
Can alternatively be used as a library for use in
setup.pyor the like, in case you don’t want to or can’t configure it viapyproject.tomlThe only thing it does is calculate your version and optionally write it to a file; there’s no overriding of your sdist contents based on what’s in your Git repository, especially not without a way to turn it off, because that would just be rude.
Installation & Setup
versioningit requires Python 3.7 or higher. Just use pip for Python 3 (You have pip, right?) to install
versioningit and its dependencies:
python3 -m pip install versioningit
However, usually you won’t need to install versioningit in your environment
directly. Instead, you specify it in your project’s pyproject.toml
file in the build-system.requires key, like so:
[build-system]
requires = [
"setuptools",
"versioningit",
]
build-backend = "setuptools.build_meta"
[build-system]
requires = [
"hatchling",
"versioningit",
]
build-backend = "hatchling.build"
[tool.hatch.version]
source = "versioningit"
Then, you configure versioningit by adding a [tool.versioningit] table
to your pyproject.toml. See “Configuration” for details, but
you can get up & running with just the minimal configuration, an empty table:
[tool.versioningit]
versioningit eliminates the need to list an explicit version in
setup.py, setup.cfg, or pyproject.toml (and any
explicit version you do list will be ignored when using versioningit), so
you should remove any such settings in order to reduce confusion.
Note
If you’re specifying your project metadata via a [project] table in
pyproject.toml`, you need to set project.dynamic = ["version"]
in order for versioningit to work.
Once you have a [tool.versioningit] table in your pyproject.toml —
and once your repository has at least one tag — building your project with
build or similar will result in your project’s version automatically being set
based on the latest tag in your Git repository. You can test your
configuration and see what the resulting version will be using the
versioningit command (see “Command”).
Example Configurations
One of versioningit’s biggest strengths is its ability to configure the
version format using placeholder strings. The default format configuration
looks like this:
[tool.versioningit.format]
# Format used when there have been commits since the most recent tag:
distance = "{base_version}.post{distance}+{vcs}{rev}"
# Example formatted version: 1.2.3.post42+ge174a1f
# Format used when there are uncommitted changes:
dirty = "{base_version}+d{build_date:%Y%m%d}"
# Example formatted version: 1.2.3+d20230922
# Format used when there are both commits and uncommitted changes:
distance-dirty = "{base_version}.post{distance}+{vcs}{rev}.d{build_date:%Y%m%d}"
# Example formatted version: 1.2.3.post42+ge174a1f.d20230922
Other format configurations of interest include:
The default format used by setuptools_scm:
[tool.versioningit.next-version] method = "smallest" [tool.versioningit.format] distance = "{next_version}.dev{distance}+{vcs}{rev}" # Example formatted version: 1.2.4.dev42+ge174a1f dirty = "{base_version}+d{build_date:%Y%m%d}" # Example formatted version: 1.2.3+d20230922 distance-dirty = "{next_version}.dev{distance}+{vcs}{rev}.d{build_date:%Y%m%d}" # Example formatted version: 1.2.4.dev42+ge174a1f.d20230922
The format used by versioneer:
[tool.versioningit.format] distance = "{base_version}+{distance}.{vcs}{rev}" # Example formatted version: 1.2.3+42.ge174a1f dirty = "{base_version}+{distance}.{vcs}{rev}.dirty" # Example formatted version: 1.2.3+42.ge174a1f.dirty distance-dirty = "{base_version}+{distance}.{vcs}{rev}.dirty" # Example formatted version: 1.2.3+42.ge174a1f.dirty
The format used by vcversioner:
[tool.versioningit.format] distance = "{base_version}.post{distance}" # Example formatted version: 1.2.3.post42 dirty = "{base_version}" # Example formatted version: 1.2.3 distance-dirty = "{base_version}.post{distance}" # Example formatted version: 1.2.3.post42