A somewhat hacky usage of the Hatch VCS build hook which ensures that the __version__
variable stays up-to-date, even when the project is installed in editable mode.
- Ensure that Hatch VCS is configured in
pyproject.toml
. - Copy the contents of
__init__.py
and adjust to your project. - Add
_version.py
to your.gitignore
file. - Install
setuptools-scm
as a development dependency. - Enjoy up-to-date version numbers, even in editable mode.
For consistency's sake, it's good to have a single source of truth for the version number of your project. However, there are four common places where the version number appears in most modern Python projects:
- The
version
field of the[project]
section ofpyproject.toml
. - The dist-info
METADATA
file from when the project was installed. - The
__version__
variable in the__init__.py
file of the project's main package. - The Git tag of the release commit.
With Hatch VCS, the definitive source of truth is the Git tag. One often still needs a technique to access this version number programmatically. The quasi-standard is to store it in the __version__
variable, so that the following works:
import myproject
print(myproject.__version__)
-
For Python 3.8 and higher, one can do:
# __init__.py from importlib.metadata import version __version__ = version("myproject")
This works well in most cases, and does not require the Hatch VCS build hook.
There are two important caveats to this approach.
-
The version number comes from the last time the project was installed. In case you are developing your project in editable mode, the reported version may be outdated unless you remember to reinstall each time the version number changes.
-
Parsing the
METADATA
file can be relatively slow. If performance is crucial and every millisecond counts (e.g. if one is writing a CLI tool), then this is not an ideal solution.
(For compatibility with Python 3.7 and lower, see the examples here regarding
importlib_metadata
.) -
-
Using the Hatch VCS build hook, a
_version.py
file is generated when either building a distribution or installing the project from source.# __init__.py from myproject._version import __version__
Since
_version.py
is generated dynamically, it should be added to.gitignore
.As with the
importlib.metadata
approach, if the project is installed in editable mode then the_version.py
file will not be updated unless the package is reinstalled (or locally rebuilt). -
Using
setuptools_scm
as follows only succeeds in particular cases:from setuptools_scm import get_version __version__ = get_version(root="..", relative_to=__file__)
It requires that
setuptools_scm
is installed in the runtime environment alongside the VCS tool (git
orhg
), and in order to read the tags, the project must be installed from a source repository.This is very fragile, but has the advantage that when it works, the version number is always up-to-date, even for an editable installation.
Note that parsing the version from pyproject.toml
is usually not a viable option because this file is typically absent when a project is installed from a wheel!
In most cases, using importlib.metadata.version
or a _version.py
are the best solutions. In the second case, the Hatch VCS build hook is a good way to generate the _version.py
file. However, this data can become outdated during development with an editable install. If reporting the correct version during development is important, then the hybrid approach as follows may be desirable.
In case you are developing in editable mode, and it is important that the version number be kept up-to-date automatically, then it is possible to use a solution similar to that illustrated in this example. Namely:
- Default to using
setuptools_scm
to set__version__
. - When that fails, fall back to
_version.py
.
However, it is somewhat of a footgun: it involves distinct version detection mechanisms between development and deployment. Furthermore, this technique is unsupported, so it must be used at your own risk.
After cloning this repository,
python -m hatch_vcs_footgun_example.main # Prints an error because it's not installed
pip install --editable . # Installs and creates the "_version.py" file
python -m hatch_vcs_footgun_example.main # Prints "My version is '1.0.2'."
Without setuptools-scm
installed, the version number is reported incorrectly after a new tag.
git commit --allow-empty -m "For v1.2.3"
git tag v1.2.3
python -m hatch_vcs_footgun_example.main # My version is '1.0.2'.
With setuptools-scm
installed the version is correctly reported:
pip install setuptools-scm
python -m hatch_vcs_footgun_example.main # My version is '1.2.3'.