No project description provided
Project description
Hatch VCS Footgun Example
A somewhat hacky usage of the Hatch VCS plugin to ensure that the __version__ variable stays up-to-date, even when the project is installed in editable mode.
Quick summary
- Ensure that Hatch VCS is configured in
pyproject.toml. - Copy the contents of
version.pyand adjust to your project. - Recommended: import
__version__from that module into your top-level__init__.pyfile. - Set the
MYPROJECT_HATCH_VCS_RUNTIME_VERSIONenvironment variable to anything (e.g.1) to enable updating the version number at runtime.
Background
For consistency's sake, it's good to have a single source of truth for the version number of your project. However, there are at least four common places where the version number commonly appears in modern Python projects:
- The
versionfield of the[project]section ofpyproject.toml. - The dist-info
METADATAfile from when the project was installed. - The
__version__variable in the__init__.pyfile 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. For example, a CLI tool might print its version.
Standard solutions
-
Dynamically read the version number from the package metadata with
importlib.metadata# __init__.py from importlib.metadata import version __version__ = version("myproject")
This works well in most cases, and does not require the Hatch VCS plugin. If your project is properly installed, you can even replace
"myproject"with__package__.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
METADATAfile can be relatively slow. If performance is crucial and every millisecond of startup time counts (e.g. if one is writing a CLI tool), then this is not an ideal solution.
-
-
Use a static
_version.pyfileIf using the Hatch VCS build hook option of the
hatch-vcsplugin, a_version.pyfile will be generated when either building a distribution or installing the project from source.Since
_version.pyis generated dynamically, it should be added to.gitignore.As with the
importlib.metadataapproach, if the project is installed in editable mode then the_version.pyfile will not be updated unless the package is reinstalled (or locally rebuilt).For more details, see the
_version.pybuild hook section. -
Use
hatch-vcsto dynamically compute the version number at runtimeThis strategy has several requirements:
- The
pyproject.tomlfile must be present. (This is usually not a viable option because this file is typically absent when a project is installed from a wheel!) - The
hatch-vcsplugin must be installed. (This is usually only true in the build environment.) gitmust be available, and the tags must be accessible and up-to-date.
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.
This method should always be used with a fallback to one of the other two methods to avoid failure when the requirements are not met. For example, a production deployment will typically not have
git,hatchling, orhatch-vcsinstalled. - The
We recommend a default of using importlib.metadata to compute the version number. When more up-to-date version numbers are needed, the hatch-vcs method can be enabled by setting the MYPROJECT_HATCH_VCS_RUNTIME_VERSION environment variable.
Optional: Using the _version.py build hook
Enabling the _version.py build hook has no advantage over importlib.metadata in terms of version updates, but it is a viable alternative.
To enable this method, add the following to your pyproject.toml file:
[tool.hatch.build.hooks.vcs]
version-file = "myproject/_version.py"
Then in version.py, remove _get_importlib_metadata_version and replace its invocation with
from myproject._version import __version__
Conclusion
In most cases, using importlib.metadata.version is the best solution. 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 implemented in version.py may be desirable:
- Default to using
importlib.metadata.versionto compute the version number. - Use
hatch-vcsto update the version number at runtime ifMYPROJECT_HATCH_VCS_RUNTIME_VERSIONis set.
Why "Footgun"?
Such a hybrid approach to determine the version number is somewhat of a footgun: it involves distinct version detection mechanisms between development and deployment. Ideally you should always remember to reinstall the package whenever checking out a new commit so that you can simply use the standard importlib.metadata.version mechanism. In contrast, the hybrid approach is unsupported, so it must be used at your own risk.
Earlier versions of this project were significantly more fragile because they tried to guess whether or not the project was being run in a development environment. Thanks to community feedback, the current version is much less of a footgun.
Usage
After cloning this repository,
# Fix an initial version number
git commit --allow-empty -m "For v100.2.3"
git tag v100.2.3
# Try to run the package without installing it
python -m hatch_vcs_footgun_example.main # Fails with PackageNotFoundError
# Install the package
pip install --editable .
# Run the package
python -m hatch_vcs_footgun_example.main # Prints "My version is '100.2.3'."
Without setting the environment variable, the version number is reported incorrectly after a new tag.
git commit --allow-empty -m "For v100.2.4"
git tag v100.2.4
unset MYPROJECT_HATCH_VCS_RUNTIME_VERSION # Just in case it was previously set
python -m hatch_vcs_footgun_example.main # My version is '100.2.3'.
After setting the environment variable, the version number is correctly reported:
export MYPROJECT_HATCH_VCS_RUNTIME_VERSION=1
python -m hatch_vcs_footgun_example.main # My version is '100.2.4'.
Setting the environment variable
There are several ways to set MYPROJECT_HATCH_VCS_RUNTIME_VERSION in your development environment:
-
Shell configuration (
.bashrc,.zshrc, etc.):export MYPROJECT_HATCH_VCS_RUNTIME_VERSION=1
-
direnv (
.envrcin your project root):export MYPROJECT_HATCH_VCS_RUNTIME_VERSION=1
-
Hatch (
pyproject.tomlorhatch.toml):[tool.hatch.envs.default.env-vars] MYPROJECT_HATCH_VCS_RUNTIME_VERSION = "1"
-
conda env config vars set MYPROJECT_HATCH_VCS_RUNTIME_VERSION=1
-
pixi (
pixi.toml):[activation.env] MYPROJECT_HATCH_VCS_RUNTIME_VERSION = "1"
-
Dev Containers (
.devcontainer/devcontainer.json):{ "containerEnv": { "MYPROJECT_HATCH_VCS_RUNTIME_VERSION": "1" } }
Troubleshooting
There are many potential pitfalls to this approach. Please open an issue if you encounter one not covered here, or if the solution is insufficient.
-
The version number computed by
hatch-vcsis incorrectEnsure that your clone of the repository has the latest tags. You may need to run
git pull --tags
-
PackageNotFoundError/ModuleNotFoundError: No module named ...This occurs when the package is not installed. With a
src/layout, you may seeModuleNotFoundErrorinstead. Install the package first:pip install --editable .
-
ModuleNotFoundError: No module named '..._version'This occurs when using the
_version.pybuild hook but running from source without installing. The_version.pyfile is generated during install/build.Install the package (editable or otherwise) to generate it.
-
Unknown version source: vcsThis occurs when
MYPROJECT_HATCH_VCS_RUNTIME_VERSIONis set buthatch-vcsis not installed.Either install
hatch-vcsin your environment, or unset the environment variable if you don't need runtime version updates. -
RuntimeError: __package__ not set in '...'This occurs when running the script directly instead of as a module.
Correct:
python -m mypackage.main
Incorrect:
python mypackage/main.py(The latter should only be used for running scripts that are not part of a package!)
-
LookupError: Error getting the version from sourcevcs: setuptools-scm was unable to detect versionThis occurs when
MYPROJECT_HATCH_VCS_RUNTIME_VERSIONis set butgitis not correctly installed.Either ensure
gitis available, or unset the environment variable. -
ImportError: cannot import name '__version__' from partially initialized module '...' (most likely due to a circular import)This can occur when importing
__version__from the top-level__init__.pyfile.Instead, import
__version__fromversion.py.For example, the following is a classical circular import:
# __init__.py import myproject.initialize from myproject.version import __version__
# initialize.py from myproject import __version__ print(f"{__version__=}")
while the following is not:
# __init__.py import myproject.initialize from myproject.version import __version__
# initialize.py from myproject.version import __version__ # Always import from version.py! print(f"{__version__=}")
-
ModuleNotFoundError: No module named 'importlib.metadata'For end-of-life versions of Python below 3.8, the
importlib.metadatamodule is not available. In this case, you need to install theimportlib-metadatabackport and fall back toimportlib_metadatain place ofimportlib.metadata.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hatch_vcs_footgun_example-2.0.1.tar.gz.
File metadata
- Download URL: hatch_vcs_footgun_example-2.0.1.tar.gz
- Upload date:
- Size: 18.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
70b14d6226527418a4411c096abc24e1cee46bd4de086921b327df9e0bb9e51b
|
|
| MD5 |
7a9d7421e38d604a0d517050d3f891f3
|
|
| BLAKE2b-256 |
dcc1e32ae9c598faaaaec87f5d074c636cf3c09a49cdd622392db66df56c73f8
|
Provenance
The following attestation bundles were made for hatch_vcs_footgun_example-2.0.1.tar.gz:
Publisher:
release.yaml on maresb/hatch-vcs-footgun-example
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hatch_vcs_footgun_example-2.0.1.tar.gz -
Subject digest:
70b14d6226527418a4411c096abc24e1cee46bd4de086921b327df9e0bb9e51b - Sigstore transparency entry: 844765853
- Sigstore integration time:
-
Permalink:
maresb/hatch-vcs-footgun-example@2cdccbfdb7a470f0e53b2105ac1d8e9cc8cb96bc -
Branch / Tag:
refs/tags/v2.0.1 - Owner: https://github.com/maresb
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@2cdccbfdb7a470f0e53b2105ac1d8e9cc8cb96bc -
Trigger Event:
release
-
Statement type:
File details
Details for the file hatch_vcs_footgun_example-2.0.1-py2.py3-none-any.whl.
File metadata
- Download URL: hatch_vcs_footgun_example-2.0.1-py2.py3-none-any.whl
- Upload date:
- Size: 8.4 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
363dc676f42a844eb8ece7289ac1f3766f0afde54002c836b69cab0912575b43
|
|
| MD5 |
ddac4cab1a05fd7a16505656003ed6db
|
|
| BLAKE2b-256 |
b7d42d9d9c3f3ea9ee0a378278545900ba2b75e8a4cb6f0de2d6ef9008022b68
|
Provenance
The following attestation bundles were made for hatch_vcs_footgun_example-2.0.1-py2.py3-none-any.whl:
Publisher:
release.yaml on maresb/hatch-vcs-footgun-example
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hatch_vcs_footgun_example-2.0.1-py2.py3-none-any.whl -
Subject digest:
363dc676f42a844eb8ece7289ac1f3766f0afde54002c836b69cab0912575b43 - Sigstore transparency entry: 844765858
- Sigstore integration time:
-
Permalink:
maresb/hatch-vcs-footgun-example@2cdccbfdb7a470f0e53b2105ac1d8e9cc8cb96bc -
Branch / Tag:
refs/tags/v2.0.1 - Owner: https://github.com/maresb
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@2cdccbfdb7a470f0e53b2105ac1d8e9cc8cb96bc -
Trigger Event:
release
-
Statement type: