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:
vcs
step: The version control system specified in the project’sversioningit
configuration 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.tag2version
step: A version is extracted from the tag returned by thevcs
stepnext-version
step: 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
tag2version
step is used as the project version. Otherwise:format
step: The results of the preceding steps are combined to produce a final project version.
template-fields
step: Values from the preceding steps are used to calculate a set of template fields for use by thewrite
and/oronbuild
steps
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, 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 “The [tool.versioningit.onbuild] Subtable” 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:
match
list of stringsA set of fileglob patterns to pass to the
--match
option ofgit describe
to 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.
exclude
list of stringsA set of fileglob patterns to pass to the
--exclude
option ofgit describe
to 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-tag
stringIf
git describe
cannot find a tag,versioningit
will raise aversioningit.errors.NoTagError
unlessdefault-tag
is 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-subst
string(required) Set this to
"$Format:%(describe)$"
and add the linepyproject.toml export-subst
to your repository’s.gitattributes
file. This will cause any Git archive made from your repository from this point forward to contain the minimum necessary information to determine a version.match
andexclude
options 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
versioningit
and will result in an error.default-tag
string(optional) If
git describe
cannot find a tag,versioningit
will raise aversioningit.errors.NoTagError
unlessdefault-tag
is 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-subst
to be set to the empty string),versioningit
will 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:
pattern
stringA 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-tag
stringIf there is no latest tag,
versioningit
will raise aversioningit.errors.NoTagError
unlessdefault-tag
is 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
rmprefix
parameter is set to a string and the tag begins with that string, the given string is removed from the tag.If the
rmsuffix
parameter is set to a string and the tag ends with that string, the given string is removed from the tag.If the
regex
parameter 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-match
parameter: 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-release
Like
minor
, except that if the input version is a prerelease or development release, the base version is returned; e.g.,1.2.3a0
becomes1.2.3
. This method requires the input version to be PEP 440-compliant.smallest
Like
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-release
Like
smallest
, except that if the input version is a prerelease or development release, the base version is returned; e.g.,1.2.3a0
becomes1.2.3
. This method requires the input version to be PEP 440-compliant.null
Returns 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
format
step with the “basic”format
method (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-on
string(optional) A Python regex that will be used to split apart the project version with
re.split
. AnyNone
or 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
pep440
is true.pep440
boolean(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
epoch
is true or ifepoch
is 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
epoch
boolean(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
pep440
is true.double-quote
boolean(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:
file
string(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
.gitignore
or.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"]
encoding
string(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
versioningit
adds automatically), as a string containing some number of{fieldname}
placeholders. The available placeholders are determined by thetemplate-fields
step (see above).If this parameter is omitted, the default is determined based on the
file
parameter’s file extension. For.txt
files and files without an extension, the default is:{version}
while for
.py
files, the default is:__version__ = "{version}"
If
template
is omitted andfile
has 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.
The [tool.versioningit.onbuild]
Subtable
Attention
Currently, the onbuild
step is not supported when using
versioningit
with Hatch. See issue #54 for more information.
New in version 1.1.0.
New in version 2.2.0: sdist
and build_py
classes added for use in setup.cfg
and
pyproject.toml
The onbuild
subtable configures an optional feature, inserting the project
version and/or other fields into built project trees when building an sdist or
wheel. Specifically, this feature allows you to create sdists & wheels in
which some file has been modified to contain the line __version__ = "<project
version>"
or similar while leaving your repository alone.
Enabling onbuild
In order to use this feature, in addition to filling out the subtable, you must
configure 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 thecmdclass
argument 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.
Configuring onbuild
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-file
string(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-file
string(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-file
is"src/mypackage/__init__.py"
, wheresrc/
is your project dir, setbuild-file
to"mypackage/__init__.py"
. If you do not use asrc/
-layout or other remapping of package files, setbuild-file
to the same value assource-file
.This path should use forward slashes (
/
) as the path separator, even on Windows.encoding
string(optional) The encoding with which to read & write the file. Defaults to UTF-8.
regex
string(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). Ifversion
did 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-match
boolean(optional) If
regex
does not match any lines in the file andappend-line
is not set, an error will be raised ifrequire-match
is true (default: false).replacement
string(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-fields
step (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-line
string(optional) If
regex
does not match any lines in the file andappend-line
is set, any occurrences of{fieldname}
inappend-line
are replaced with the values provided by thetemplate-fields
step, 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 inimportlib.metadata
to get your package’s version, like so:from importlib.metadata import version __version__ = version("mypackage")
Note that
importlib.metadata
was 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.toml
so 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 namedmypackage
and is stored in asrc/
directory, you can write the version to a Python filesrc/mypackage/_version.py
like 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) Fill out the
[tool.versioningit.onbuild]
subtable inpyproject.toml
and configure setuptools to useversioningit
’s custom build commands. This will allow you to create sdists & wheels in which some file has been modified to contain the line__version__ = "<project version>"
or similar while leaving your repository alone. See “The [tool.versioningit.onbuild] Subtable” for more information.
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 |
Requires configuration in |
No |
Yes |
Run when installing in editable mode? |
Yes |
No |
Usable with Hatch? |
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
versioningit
in 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.dynamic
key:[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
versioningit
as the version source:[tool.hatch.version] source = "versioningit"
Configure
versioningit
as normal. However, with Hatch, you have two possible locations to putversioningit
’s configuration in: either a[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
versioningit
via[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
write
step to create a file containing your project version, and this file is listed in your.gitignore
or.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"]
Attention
Currently, the onbuild step is not supported when using
versioningit
with Hatch. See issue #54 for more information.
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-version
step
- --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_LEVEL
environment variable. If both-v
andVERSIONINGIT_LOG_LEVEL
are specified, the more verbose log level of the two will be used, where one-v
corresponds toINFO
level and two or more correspond toDEBUG
level. (If neither are specified, the default level ofWARNING
is 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
config
isNone
, thenproject_dir
must contain apyproject.toml
file containing either a[tool.versioningit]
table or a[tool.hatch.version]
table with thesource
key set to"versioningit"
; if it does not, aNotVersioningitError
is raised. Ifconfig
is notNone
, then anypyproject.toml
file inproject_dir
will be ignored, and the configuration will be taken fromconfig
instead. See “Passing Explicit Configuration”.If
write
is true, then the file specified in thewrite
subtable of the versioningit configuration, if any, will be updated.If
fallback
is true, then ifproject_dir
is not under version control (or if the VCS executable is not installed),versioningit
will assume that the directory is an unpacked sdist and will read the version from thePKG-INFO
file.- Raises:
NotVCSError – if
fallback
is false andproject_dir
is not under version controlNotSdistError – if
fallback
is true,project_dir
is not under version control, and there is noPKG-INFO
file inproject_dir
ConfigError – if any of the values in
config
are 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
config
isNone
, thenproject_dir
must contain apyproject.toml
file containing either a[tool.versioningit]
table or a[tool.hatch.version]
table with thesource
key set to"versioningit"
; if it does not, aNotVersioningitError
is raised. Ifconfig
is notNone
, then anypyproject.toml
file inproject_dir
will be ignored, and the configuration will be taken fromconfig
instead. See “Passing Explicit Configuration”.- Raises:
NotVCSError – if
project_dir
is not under version controlConfigError – if any of the values in
config
are 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
dict
of custom setuptoolsCommand
classes, suitable for passing to thecmdclass
argument ofsetuptools.setup()
, that run theonbuild
step for the project when building an sdist or wheel. Specifically, thedict
contains a subclass ofsetuptools.command.sdist.sdist
at the"sdist"
key and a subclass ofsetuptools.command.build_py.build_py
at the"build_py"
key.A
dict
of alternative base classes can optionally be supplied; if thedict
contains an"sdist"
entry, that entry will be used as the base class for the customizedsdist
command, and likewise for"build_py"
. All other classes in the inputdict
are passed through unchanged.
- class versioningit.cmdclass.sdist
New in version 2.2.0.
A custom subclass of
setuptools.command.sdist.sdist
that runs theonbuild
step when building an sdist. This class is equivalent toget_cmdclasses()["sdist"]
, except that it can also be used in the[options]cmdclass
field insetup.cfg
.
- class versioningit.cmdclass.build_py
New in version 2.2.0.
A custom subclass of
setuptools.command.build_py.build_py
that runs theonbuild
step when building a wheel. This class is equivalent toget_cmdclasses()["build_py"]
, except that it can also be used in the[options]cmdclass
field 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
Versioningit
object for the project rooted atproject_dir
(default: the current directory).If
config
isNone
, thenproject_dir
must contain apyproject.toml
file containing either a[tool.versioningit]
table or a[tool.hatch.version]
table with thesource
key set to"versioningit"
; if it does not, aNotVersioningitError
is raised. Ifconfig
is notNone
, then anypyproject.toml
file inproject_dir
will be ignored, and the configuration will be taken fromconfig
instead. 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
write
is true, then the file specified in thewrite
subtable of the versioningit configuration, if any, will be updated.If
fallback
is true, then ifproject_dir
is not under version control (or if the VCS executable is not installed),versioningit
will assume that the directory is an unpacked sdist and will read the version from thePKG-INFO
file.Changed in version 2.0.0:
write
andfallback
arguments added- Raises:
NotVCSError – if
fallback
is false andproject_dir
is not under version controlNotSdistError – if
fallback
is true,project_dir
is not under version control, and there is noPKG-INFO
file inproject_dir
ConfigError – if any of the values in
config
are 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
write
is true, then the file specified in thewrite
subtable of the versioningit configuration, if any, will be updated.If
fallback
is true, then ifproject_dir
is not under version control (or if the VCS executable is not installed),versioningit
will assume that the directory is an unpacked sdist and will read the version from thePKG-INFO
file, returning aFallbackReport
instance instead of aReport
.- Raises:
NotVCSError – if
fallback
is false andproject_dir
is not under version controlNotSdistError – if
fallback
is true,project_dir
is not under version control, and there is noPKG-INFO
file inproject_dir
ConfigError – if any of the values in
config
are not of the correct typeMethodError – if a method returns a value of the wrong type
- do_vcs() VCSDescription [source]
Run the
vcs
step- Raises:
MethodError – if the method does not return a
VCSDescription
- do_tag2version(tag: str) str [source]
Run the
tag2version
step- Raises:
MethodError – if the method does not return a
str
- do_next_version(version: str, branch: str | None) str [source]
Run the
next-version
step- Raises:
MethodError – if the method does not return a
str
- do_format(description: VCSDescription, base_version: str, next_version: str) str [source]
Run the
format
stepChanged in version 2.0.0: The
version
argument 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_fields
step- Raises:
MethodError – if the method does not return a
dict
Exceptions
- exception versioningit.ConfigError[source]
Bases:
Error
,ValueError
Raised when the
versioningit
configuration contain invalid settings
- exception versioningit.InvalidTagError[source]
Bases:
Error
,ValueError
Raised by
tag2version
methods when passed a tag that they cannot work with
- exception versioningit.InvalidVersionError[source]
Bases:
Error
,ValueError
Raised by
next-version
andtemplate-fields
methods when passed a version that they cannot work with
- exception versioningit.MethodError[source]
Bases:
Error
Raised when a method is invalid or returns an invalid value
- exception versioningit.NoTagError[source]
Bases:
Error
Raised when a tag cannot be found in version control
- exception versioningit.NotSdistError[source]
Bases:
Error
Raised when attempting to read a
PKG-INFO
file 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
None
if it cannot be determined or does not apply
- fields: dict[str, Any]
A
dict
of additional information about the repository state to make available to theformat
method. Customvcs
methods 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 theformat
subtable 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
versioningit
run- base_version: str | None
A version string extracted from the VCS tag;
None
if the “tag2version” step or a previous step failed
- description: VCSDescription | None
A description of the state of the version control repository;
None
if the “vcs” step failed
- next_version: str | None
A “next version” calculated by the “next-version” step;
None
if the step or a previous one failed
- template_fields: dict[str, Any]
A
dict
of 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-INFO
file in an sdist
- versioningit.get_version_from_pkg_info(project_dir: str | Path) str [source]
Return the Version field from the
PKG-INFO
file inproject_dir
- Raises:
NotSdistError – if there is no
PKG-INFO
fileValueError – if the
PKG-INFO
file 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
onbuild
step for the given project.This function is intended to be used by custom setuptools commands that are used in place of
versioningit
’s custom commands but still need to be able to run theonbuild
step. Thetemplate_fields
value can be obtained by passing the command’sdistribution
attribute toget_template_fields_from_distribution()
; if this returnsNone
, then we are building from an sdist, andrun_onbuild()
should not be called.If
config
isNone
, thenproject_dir
must contain apyproject.toml
file containing a[tool.versioningit]
table; if it does not, aNotVersioningitError
is raised. Ifconfig
is notNone
, then anypyproject.toml
file inproject_dir
will be ignored, and the configuration will be taken fromconfig
instead; see “Passing Explicit Configuration”.Changed in version 2.0.0:
version
argument replaced withtemplate_fields
- Parameters:
- Raises:
if
config
isNone
andproject_dir
does not contain apyproject.toml
fileif the
pyproject.toml
file does not contain a[tool.versioningit]
table
ConfigError – if any of the values in
config
are 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.Distribution
byversioningit
’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, andNone
will 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_dir
is 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.VCSDescription
returned by avcs
methodbase_version (str) – a version string extracted from the VCS tag
next_version (str) – a “next version” calculated by the
next-version
stepparams (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.VCSDescription
returned by avcs
method;None
if thevcs
method failedbase_version (Optional[str]) – a version string extracted from the VCS tag;
None
if thetag2version
step or a previous step failednext_version (Optional[str]) – a “next version” calculated by the
next-version
step;None
if 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(*, build_dir: str | pathlib.Path, is_source: bool, template_fields: dict[str, Any], params: dict[str, Any]) None
Modifies one or more files in
build_dir
- Parameters:
build_dir (path) – the path to the directory where the project is being built
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-fields
stepparams (dict) – a collection of user-supplied parameters
Changed in version 2.0.0: version
argument replaced with template_fields
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 in
setup.cfg
as follows:
[options.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"
vcs
method and havedefault-tag
set 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 develop
orpip 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 becauseversioningit
loads 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
versioningit
as ahost
dependency in your condameta.yaml
file. 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
v2.3.0 (2023-11-19)
Always read
.hg_archival.txt
files using UTF-8 encodingAdded support for using versioningit with Hatch
v2.2.1 (2023-09-22)
Raise a
ConfigError
if the selectedtool.versioningit.format
field 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.cmdclass
module as an alternative to callingget_cmdclasses()
v2.1.0 (2022-10-25)
Drop support for Python 3.6
Support Python 3.11
Use
tomllib
on Python 3.11
v2.0.1 (2022-08-01)
Don’t run the
onbuild
step under setuptools’ upcoming PEP 660 editable mode (contributed by @abravalheri)
v2.0.0 (2022-06-12)
The
{version}
placeholder in the “basic”format
step has been renamed to{base_version}
. The old name remains usable, but is deprecated.Breaking: The
version
argument passed toVersioningit.do_format()
andformat
method callables has been renamed tobase_version
.
A
{version_tuple}
field, along with the fields available in theformat
step, is now available for use in templates in thewrite
andonbuild
steps.New step and subtable: “template-fields”
Breaking: The
version
arguments passed toVersioningit.do_write()
,Versioningit.do_onbuild()
,run_onbuild()
, andwrite
&onbuild
method callables have been replaced withtemplate_fields
argumentsAdded a
get_template_fields_from_distribution()
function for use by callers ofrun_onbuild()
Versioningit.get_version()
now takes optionalwrite
andfallback
argumentsThe
onbuild
step 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-match
parameters of thetag2version
andonbuild
steps 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_EPOCH
spec, and was even applied whenSOURCE_DATE_EPOCH
was 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
onbuild
step 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
versioningit
command now honors theVERSIONINGIT_LOG_LEVEL
environment 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-subst
parameter, which is now required, and the oldmatch
andexclude
parameters 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
file
parameter 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:
Config
is 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-version
option 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
Versioningit
to 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”
vcs
method:default-tag
will 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”
tag2version
method:If
regex
is 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-match
parameter for erroring if the regex does not match
“basic”
write
method:encoding
now defaults to UTF-8New
next-version
methods:"minor-release"
,"smallest-release"
, and"null"
Replaced
entrypoints
dependency withimportlib-metadata
Added
tool.versioningit.default-version
for 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.toml
Supports 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 setuptools commands 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.py
or the like, in case you don’t want to or can’t configure it viapyproject.toml
The 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