Dynamic version generation from version control system tags supporting multiple VCS and versioning standards.
—
Standalone helper functions for version manipulation, validation, and dynamic version detection.
Increment numerical components of version strings without requiring Version objects.
def bump_version(base: str, index: int = -1, increment: int = 1) -> strParameters:
base: Version core like "1.2.3" (without pre-release identifiers)index: Position to increment using Python indexing (0+ from left, -1 from right)increment: Amount to increment by (default: 1)Returns: Bumped version string
Usage Examples:
from dunamai import bump_version
# Bump patch version (rightmost, default)
print(bump_version("1.2.3"))
# "1.2.4"
# Bump minor version
print(bump_version("1.2.3", index=1))
# "1.3.0"
# Bump major version
print(bump_version("1.2.3", index=0))
# "2.0.0"
# Bump by custom amount
print(bump_version("1.2.3", increment=5))
# "1.2.8"
# Bump minor by 2
print(bump_version("1.2.3", index=1, increment=2))
# "1.4.0"
# Working with longer version strings
print(bump_version("1.2.3.4", index=2))
# "1.2.4.0"Validate version strings against specific versioning standards.
def check_version(version: str, style: Style = Style.Pep440) -> NoneParameters:
version: Version string to validatestyle: Versioning style to check against (default: PEP 440)Raises: ValueError if version is invalid for the specified style
Usage Examples:
from dunamai import check_version, Style
# Valid PEP 440 versions
check_version("1.2.3") # OK
check_version("1.2.3rc1") # OK
check_version("1.2.3.post7.dev0") # OK
check_version("2!1.2.3+local.version") # OK
# Invalid PEP 440 version
try:
check_version("v1.2.3") # 'v' prefix not allowed in PEP 440
except ValueError as e:
print(f"Invalid: {e}")
# Semantic Versioning validation
check_version("1.2.3", Style.SemVer) # OK
check_version("1.2.3-alpha.1", Style.SemVer) # OK
check_version("1.2.3+build.metadata", Style.SemVer) # OK
try:
check_version("1.2", Style.SemVer) # Missing patch version
except ValueError as e:
print(f"Invalid SemVer: {e}")
# Haskell PVP validation
check_version("1.2.3", Style.Pvp) # OK
check_version("1.2.3-alpha", Style.Pvp) # OK
try:
check_version("1.2.3+build", Style.Pvp) # '+' not allowed in PVP
except ValueError as e:
print(f"Invalid PVP: {e}")Determine version for installed packages with fallback strategies.
def get_version(
name: str,
first_choice: Optional[Callable[[], Optional[Version]]] = None,
third_choice: Optional[Callable[[], Optional[Version]]] = None,
fallback: Version = Version("0.0.0"),
ignore: Optional[Sequence[Version]] = None,
parser: Callable[[str], Version] = Version,
) -> VersionParameters:
name: Installed package namefirst_choice: Primary version detection callbackthird_choice: Tertiary fallback if package not found in metadatafallback: Ultimate fallback version (default: "0.0.0")ignore: List of versions to ignoreparser: Function to convert strings to Version objectsReturns: First available valid version
Usage Examples:
from dunamai import get_version, Version
# Basic usage - get version from package metadata
version = get_version("dunamai")
print(version.serialize())
# With VCS fallback
def vcs_fallback():
try:
return Version.from_any_vcs()
except:
return None
version = get_version(
"my-package",
first_choice=vcs_fallback,
fallback=Version("0.0.1")
)
# Complex fallback strategy
def git_version():
try:
return Version.from_git(strict=True)
except:
return None
def any_vcs_version():
try:
return Version.from_any_vcs(strict=True)
except:
return None
version = get_version(
"my-package",
first_choice=git_version,
third_choice=any_vcs_version,
fallback=Version("0.0.0", distance=1),
ignore=[Version("0.0.0")]
)
# Custom parser
def custom_parser(version_str):
# Custom logic to parse version strings
return Version.parse(version_str, pattern="custom-pattern")
version = get_version("my-package", parser=custom_parser)Common pattern for setting __version__ in Python packages.
Usage Examples:
# In your package's __init__.py
from dunamai import get_version, Version
def _get_version():
try:
return Version.from_any_vcs()
except:
return None
__version__ = get_version(
"my-package",
first_choice=_get_version,
fallback=Version("0.0.0")
).serialize()
# For development versions
def _get_dev_version():
try:
version = Version.from_git()
return version if version.distance > 0 else None
except:
return None
__version__ = get_version(
"my-package",
first_choice=_get_dev_version,
fallback=Version("0.0.0", distance=1)
).serialize()from dunamai import check_version, Style
versions_to_check = [
("1.2.3", Style.Pep440),
("1.2.3-alpha.1", Style.SemVer),
("1.2.3-test", Style.Pvp),
]
for version, style in versions_to_check:
try:
check_version(version, style)
print(f"✓ {version} is valid {style.value}")
except ValueError as e:
print(f"✗ {version} is invalid {style.value}: {e}")from dunamai import bump_version
base_versions = ["1.2.3", "2.0.0", "0.1.0"]
# Bump all patch versions
patch_bumped = [bump_version(v) for v in base_versions]
print(patch_bumped) # ["1.2.4", "2.0.1", "0.1.1"]
# Bump all minor versions
minor_bumped = [bump_version(v, index=1) for v in base_versions]
print(minor_bumped) # ["1.3.0", "2.1.0", "0.2.0"]from dunamai import get_version, Version
def create_fallback_chain(*fallback_funcs):
"""Create a fallback chain of version detection functions"""
def chain():
for func in fallback_funcs:
try:
result = func()
if result:
return result
except:
continue
return None
return chain
# Define fallback functions
def git_version():
return Version.from_git(strict=True)
def any_vcs_version():
return Version.from_any_vcs(strict=True)
def tagged_version():
return Version("1.0.0") # From some configuration
# Create chain
fallback_chain = create_fallback_chain(git_version, any_vcs_version, tagged_version)
# Use in get_version
version = get_version(
"my-package",
first_choice=fallback_chain,
fallback=Version("0.0.0")
)Install with Tessl CLI
npx tessl i tessl/pypi-dunamai