| title | Version Validation and Bumping |
| description | Validate version formats and control version progression. Covers PEP 440 compliance, valid formats, invalid formats, and version bumping validation rules. |
Version Validation and Bumping
Version validation ensures that version strings follow the correct format and that version changes follow logical progression. Hatchling provides comprehensive validation based on PEP 440 and customizable bumping rules.
PEP 440 Validation
All versions in Hatchling must comply with PEP 440:
Valid Version Formats
"1.2.3"
"2024.12.1"
"1.2"
"1.0a1"
"1.0b2"
"1.0rc3"
"1.0.dev0"
"1.0.post1"
"1.0a1.dev0"
"1.0rc1.post2"
"1!2.0.0"
"1.0+local"
"1.0+ubuntu1"
Invalid Formats
"v1.2.3"
"1.2.3-alpha"
"latest"
"1.x"
"1.2.3.4.5"
Validate-Bump Configuration
Control whether new versions must be higher than current:
Enabled (Default)
[tool.hatch.version.scheme.standard]
validate-bump = true
Behavior:
$ hatch version "1.9.0"
Error: Version '1.9.0' is not higher than current version '2.0.0'
$ hatch version "2.0.1"
Old: 2.0.0
New: 2.0.1
Disabled
[tool.hatch.version.scheme.standard]
validate-bump = false
Behavior:
$ hatch version "1.9.0"
Old: 2.0.0
New: 1.9.0
Version Comparison Rules
Standard Comparison
Versions are compared according to PEP 440:
from packaging.version import Version
versions = [
"1.0.dev0",
"1.0a1.dev0",
"1.0a1",
"1.0a2",
"1.0b1",
"1.0b2",
"1.0rc1",
"1.0rc2",
"1.0",
"1.0.post1",
"1!0.0",
]
sorted_versions = sorted(versions, key=Version)
Epoch Comparison
Epochs override all other version components:
"1!0.0" > "99.99.99"
"2!0.0" > "1!99.99"
"0!2.0" < "1!1.0"
Local Version Comparison
Local versions are higher than their base:
"1.0+local" > "1.0"
"1.0+a" < "1.0+b"
"1.0+1" < "1.0+2"
Bumping Validation
Valid Bump Sequences
Each bump type has validation rules:
Patch Bumps
"1.2.3" -> "1.2.4"
"1.2" -> "1.2.1"
"2024.1.1" -> "2024.1.2"
"1.2.3a1" -> ERROR
"1.2.3.dev0" -> ERROR
Minor Bumps
"1.2.3" -> "1.3.0"
"1.2" -> "1.3.0"
"1.2.3b1" -> ERROR
Major Bumps
"1.2.3" -> "2.0.0"
"0.9.9" -> "1.0.0"
"2.0.0a1" -> ERROR
Pre-release Progression
Pre-releases must follow order:
"1.0a1" -> "1.0a2"
"1.0a2" -> "1.0b0"
"1.0b1" -> "1.0rc0"
"1.0rc1" -> "1.0"
"1.0b1" -> "1.0a1"
"1.0rc1" -> "1.0b1"
"1.0" -> "1.0a1"
Validation in CI/CD
GitHub Actions
name: Validate Version
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: "3.11"
- name: Install dependencies
run: |
pip install hatch packaging
- name: Validate version format
run: |
python -c "
import subprocess
from packaging.version import Version, InvalidVersion
result = subprocess.run(['hatch', 'version'], capture_output=True, text=True)
version_str = result.stdout.strip()
try:
v = Version(version_str)
{} {}
{} {} {}
{} {} {} {}
Pre-commit Hook
repos:
- repo: local
hooks:
- id: validate-version
name: Validate version format
entry: python scripts/validate_version.py
language: script
files: pyproject.toml
import subprocess
import sys
from packaging.version import Version, InvalidVersion
def main():
result = subprocess.run(
["hatch", "version"],
capture_output=True,
text=True
)
version_str = result.stdout.strip()
try:
version = Version(version_str)
except InvalidVersion:
print(f"ERROR: Invalid version format: {version_str}")
return 1
if version_str.startswith("v"):
print("ERROR: Version should not start with 'v'")
return 1
if version.is_devrelease:
print(f"WARNING: Development version: {version}")
print(f"✓ Valid version: {version}")
return 0
if __name__ == "__main__":
sys.exit(main())
Custom Validation Rules
Organization-Specific Rules
from packaging.version import Version
def validate_version(version_str: str) -> bool:
"""Validate version against org policy."""
version = Version(version_str)
if version.local:
print("ERROR: Local versions not allowed")
return False
parts = str(version.base_version).split(".")
if len(parts) != 3:
print("ERROR: Version must be X.Y.Z format")
return False
if parts[0] == "0" and not version.is_prerelease:
print("ERROR: Version 0.x must be pre-release")
return False
if version.is_devrelease:
import subprocess
result = subprocess.run(
["git", "branch", "--show-current"],
capture_output=True,
text=True
)
if result.stdout.strip() == "main":
()
Calendar Versioning Validation
def validate_calver(version_str: str) -> bool:
"""Validate calendar versioning format."""
from datetime import datetime
parts = version_str.split(".")
if len(parts) != 3:
return False
try:
year = int(parts[0])
month = int(parts[1])
patch = int(parts[2])
except ValueError:
return False
current_year = datetime.now().year
if year < 2020 or year > current_year:
print(f"ERROR: Invalid year: {year}")
return False
if month < 1 or month > 12:
print(f"ERROR: Invalid month: {month}")
return False
return True
Version Normalization
Hatchling normalizes versions to canonical form:
Normalization Rules
"1.0" -> "1.0.0"
"1.0.0.0" -> "1.0.0"
"1.0.alpha" -> "1.0a0"
"1.0-beta" -> "1.0b0"
"1.0c1" -> "1.0rc1"
"01.02.03" -> "1.2.3"
"1.0.dev" -> "1.0.dev0"
Validation vs Normalization
from packaging.version import Version
assert Version("1.0") == Version("1.0.0")
assert Version("1.0a") == Version("1.0a0")
assert Version("1.0.dev") == Version("1.0.dev0")
assert str(Version("1.0")) == "1.0"
assert str(Version("1.0a")) == "1.0a0"
Troubleshooting
Common Validation Errors
| Error | Cause | Solution |
|---|
| "Invalid version" | Non-PEP 440 format | Remove prefixes, fix format |
| "Version not higher" | validate-bump enabled | Use higher version or disable |
| "Can't bump from pre-release" | Invalid bump type | Use release first |
| "Unknown bump command" | Typo in command | Check available: patch, minor, major, etc |
Debug Validation
$ hatch version -v
Looking for version...
Found: 1.2.3
$ python -c "from packaging.version import Version; print(Version('1.2.3'))"
1.2.3
$ python -c "
from packaging.version import Version
v1 = Version('1.2.3')
v2 = Version('1.2.4')
print(f'{v1} < {v2}: {v1 < v2}')
"
Best Practices
- Always Use PEP 440: Stick to standard formats for compatibility
- Enable validate-bump: Prevent accidental downgrades
- Automate Validation: Add CI checks for all version changes
- Document Policy: Create clear versioning guidelines
- Test Bumping: Verify bump commands work correctly
- No Manual Editing: Use
hatch version commands
- Pre-release for Testing: Use alpha/beta/rc for test releases
See Also