Setuptools Integration Guide¶
gitversioned provides seamless, native integration with Setuptools by automatically registering as a plugin via standard Python packaging entry points. This means once it is added to your project's build requirements, Setuptools will automatically invoke it during packaging operations to resolve your project's version.
1. Setup Configurations¶
Depending on your project's layout and preference, you can configure Setuptools using modern pyproject.toml, declarative setup.cfg, or legacy setup.py.
Declare `gitversioned` under `build-system` requirements and specify `version` as dynamic in your `pyproject.toml`. It runs zero-config out of the box, or you can configure custom settings under `[tool.gitversioned]`:
```toml
# pyproject.toml
[build-system]
requires = ["setuptools>=64.0", "gitversioned"]
build-backend = "setuptools.build_meta"
[project]
name = "my-package"
dynamic = ["version"]
[tool.gitversioned]
source_type = ["tag"]
output = "src/my_package/version.py"
[tool.gitversioned.auto_increment]
pre = "minor"
dev = "patch"
```
If you define your package metadata in `setup.cfg`, you can enable dynamic versioning with zero-config defaults by setting `gitversioned = True` under `[options]`. Alternatively, customize your settings under `[tool:gitversioned]`:
```ini
# setup.cfg
[metadata]
name = my-package
[options]
setup_requires =
gitversioned
# Set to True to enable zero-config defaults
gitversioned = True
# Optional: Declare custom configuration overrides
[tool:gitversioned]
source_type = tag
output = src/my_package/version.py
[tool:gitversioned:auto_increment]
pre = minor
dev = patch
```
If your project uses an imperative `setup.py` file, pass the `gitversioned` parameter. Set it to `True` for zero-config defaults, or pass a configuration dictionary for custom behavior:
```python
# setup.py
from setuptools import setup
setup(
name="my-package",
setup_requires=["gitversioned"],
# Set to True for zero-config, or pass a dict for custom overrides:
gitversioned={
"source_type": ["tag"],
"output": "src/my_package/version.py",
"auto_increment": {
"pre": "minor",
"dev": "patch",
},
},
)
```
2. Common User Stories & Patterns¶
Generating Stable vs. Dev Builds¶
By default, gitversioned is smart about determining what type of build you are creating based on the state of your Git repository.
Scenario A: Clean Tagged Release
- State: You check out
v1.5.0. The working directory is clean. - Action: You run
python -m build. - Result:
gitversioneddetects the clean tag and assigns thereleaseversion type. The generated version is1.5.0.
Scenario B: Nightly Build
- State: You are on the
mainbranch, 3 commits ahead ofv1.5.0in a clean CI environment. - Action: You configure
pre = "minor"under[tool.gitversioned.auto_increment]and run the build. - Result:
gitversionedresolves thepreversion type (or you force it viaversion_type = "pre"). It auto-increments the minor segment, resulting in a pre-release like1.6.0a20260507+<sha>.
Scenario C: Local Dirty Build
- State: You have uncommitted changes.
- Result: Automatically resolved as
devand increments patch (if configureddev = "patch"), resulting in1.5.1.devYYYYMMDD+<sha>.
Ensuring Sdist Portability¶
When you run python -m build, Setuptools creates a source distribution (sdist) containing your project files. However, the .git directory is not included in the sdist!
To ensure that downstream users (or pip) can still resolve the version when installing from the sdist, configure gitversioned to fall back to the generated version file:
[tool.gitversioned]
# Look at tags first. If the repo is missing, fall back to the generated file!
source_type = ["tag", "file"]
version_source_file = "src/my_package/version.py"
output = "src/my_package/version.py"
regex_file = [
# Regex to extract the version string from the generated file
'(?i)__version__\s*=\s*[\'"](?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)[\'"]'
]
With this configuration:
python -m buildlocally uses Git to build1.6.0a...and writesversion.py.- The sdist packages
version.py(provided it's tracked by Git or added to your manifest). - When a user runs
pip install my-package.tar.gz,gitversionedfails to find.git, falls back tofile, parsesversion.py, and successfully sets the version.
[!TIP] GitHub ZIP Downloads vs. sdists If a user downloads your repository as a ZIP directly from GitHub, there is no
.gitdirectory and no pre-generatedversion.pyfile! For this scenario,gitversionedprovides an Archive Fallback mechanism that parses a substituted.git_archival.txtfile. See the Quick Start for setup instructions.
Excluding the Version File from Git¶
If you add your generated version.py file to .gitignore so it isn't committed to your repository, Setuptools will natively ignore it during the build process if you are using setuptools-scm or native git file discovery, meaning it won't be included in your final wheel or sdist.
To ensure the ignored version file is correctly packaged, you must instruct Setuptools to explicitly include it by adding it to your MANIFEST.in:
You can then cleanly expose this version in your package's __init__.py:
# src/my_package/__init__.py
# Copyright 2026 Mark Kurtz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
try:
from .version import __version__
except ImportError:
# Fallback if the build hasn't run yet or failed
__version__ = "0.0.0"
__all__ = ["__version__"]