A library implementing the 'SemVer' scheme.
—
Comprehensive functionality for creating, manipulating, comparing, and validating semantic version objects. The Version class provides complete SemVer 2.0.0 compliance with parsing, validation, comparison, and version bumping capabilities.
Create Version objects from strings or individual components with full SemVer 2.0.0 validation.
class Version:
def __init__(
self,
version_string=None,
major=None,
minor=None,
patch=None,
prerelease=None,
build=None,
partial=False
):
"""
Create a Version object from string or components.
Args:
version_string: SemVer string like '1.2.3-alpha.1+build.456'
major: Major version number (required if using components)
minor: Minor version number (required if using components)
patch: Patch version number (required if using components)
prerelease: Tuple of prerelease identifiers
build: Tuple of build metadata identifiers
partial: Allow partial versions (deprecated)
Raises:
ValueError: If version string is invalid or components are invalid
"""Usage Examples:
# From version string
v1 = Version('1.2.3')
v2 = Version('1.2.3-alpha.1')
v3 = Version('1.2.3-alpha.1+build.456')
# From components
v4 = Version(major=1, minor=2, patch=3)
v5 = Version(major=1, minor=2, patch=3, prerelease=('alpha', '1'))
v6 = Version(major=1, minor=2, patch=3, prerelease=('alpha', '1'), build=('build', '456'))
# Invalid versions raise ValueError
try:
invalid = Version('1.2') # Missing patch version
except ValueError as e:
print(f"Invalid version: {e}")Parse version strings with optional coercion for non-standard formats.
@classmethod
def parse(cls, version_string, partial=False, coerce=False):
"""
Parse a version string into components.
Args:
version_string: Version string to parse
partial: Allow partial versions (deprecated)
coerce: Attempt to coerce invalid formats
Returns:
Tuple of (major, minor, patch, prerelease, build)
Raises:
ValueError: If version string cannot be parsed
"""
@classmethod
def coerce(cls, version_string, partial=False):
"""
Coerce a version-like string into a valid SemVer version.
Args:
version_string: Version-like string to coerce
partial: Allow partial versions (deprecated)
Returns:
Valid Version object
Examples:
Version.coerce('1') -> Version('1.0.0')
Version.coerce('1.2.3.4.5') -> Version('1.2.3+4.5')
Version.coerce('1.2.3a4') -> Version('1.2.3-a4')
"""Usage Examples:
# Parse version string
components = Version.parse('1.2.3-alpha.1+build.456')
print(components) # (1, 2, 3, ('alpha', '1'), ('build', '456'))
# Coerce non-standard formats
v1 = Version.coerce('1') # Version('1.0.0')
v2 = Version.coerce('1.2') # Version('1.2.0')
v3 = Version.coerce('1.2.3.4.5') # Version('1.2.3+4.5')
v4 = Version.coerce('1.2.3a4') # Version('1.2.3-a4')Access individual components of version objects.
@property
def major(self):
"""Major version number."""
@property
def minor(self):
"""Minor version number (None only for partial versions - deprecated)."""
@property
def patch(self):
"""Patch version number (None only for partial versions - deprecated)."""
@property
def prerelease(self):
"""Tuple of prerelease identifiers."""
@property
def build(self):
"""Tuple of build metadata identifiers."""
@property
def partial(self):
"""Whether this is a partial version (deprecated)."""
@property
def precedence_key(self):
"""Key used for version sorting and comparison."""
def __iter__(self):
"""Iterate over version components: (major, minor, patch, prerelease, build)."""
def __str__(self):
"""String representation in SemVer format."""
def __repr__(self):
"""Debug representation of the Version object."""
def __hash__(self):
"""Hash for use in sets and dictionaries."""Usage Examples:
version = Version('1.2.3-alpha.1+build.456')
print(version.major) # 1
print(version.minor) # 2
print(version.patch) # 3
print(version.prerelease) # ('alpha', '1')
print(version.build) # ('build', '456')
print(version.partial) # False
# Iterate over components
for component in version:
print(component)
# Output: 1, 2, 3, ('alpha', '1'), ('build', '456')Compare versions using standard Python comparison operators following SemVer precedence rules.
def __lt__(self, other: 'Version') -> bool: ...
def __le__(self, other: 'Version') -> bool: ...
def __eq__(self, other: 'Version') -> bool: ...
def __ne__(self, other: 'Version') -> bool: ...
def __gt__(self, other: 'Version') -> bool: ...
def __ge__(self, other: 'Version') -> bool: ...Usage Examples:
v1 = Version('1.0.0')
v2 = Version('1.0.1')
v3 = Version('1.0.0-alpha')
v4 = Version('1.0.0+build.1')
# Basic comparison
print(v1 < v2) # True
print(v1 > v3) # True (release > prerelease)
print(v1 == v4) # True (build metadata ignored in comparison)
# Sorting versions
versions = [Version('2.0.0'), Version('1.0.0'), Version('1.1.0')]
sorted_versions = sorted(versions)
print([str(v) for v in sorted_versions]) # ['1.0.0', '1.1.0', '2.0.0']Generate new versions by incrementing major, minor, or patch components.
def next_major(self) -> 'Version':
"""
Return the next major version.
Returns:
New Version with major incremented, minor/patch reset to 0,
prerelease and build metadata removed.
"""
def next_minor(self) -> 'Version':
"""
Return the next minor version.
Returns:
New Version with minor incremented, patch reset to 0,
prerelease and build metadata removed.
"""
def next_patch(self) -> 'Version':
"""
Return the next patch version.
Returns:
New Version with patch incremented,
prerelease and build metadata removed.
"""Usage Examples:
version = Version('1.2.3-alpha.1+build.456')
major = version.next_major()
print(major) # Version('2.0.0')
minor = version.next_minor()
print(minor) # Version('1.3.0')
patch = version.next_patch()
print(patch) # Version('1.2.4')Truncate versions to specific precision levels.
def truncate(self, level: str = 'patch') -> 'Version':
"""
Truncate version to specified level.
Args:
level: Truncation level ('major', 'minor', 'patch', 'prerelease', 'build')
Returns:
New Version truncated to specified level
"""Usage Examples:
version = Version('1.2.3-alpha.1+build.456')
major_only = version.truncate('major')
print(major_only) # Version('1.0.0')
minor_only = version.truncate('minor')
print(minor_only) # Version('1.2.0')
no_build = version.truncate('prerelease')
print(no_build) # Version('1.2.3-alpha.1')Standalone functions for common version operations.
def compare(v1: str, v2: str) -> int:
"""
Compare two version strings.
Args:
v1: First version string
v2: Second version string
Returns:
-1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
"""
def validate(version_string: str) -> bool:
"""
Check if a version string is valid SemVer.
Args:
version_string: Version string to validate
Returns:
True if valid, False otherwise
"""Usage Examples:
# Compare version strings directly
result = compare('1.0.0', '1.0.1')
print(result) # -1
result = compare('2.0.0', '1.9.9')
print(result) # 1
result = compare('1.0.0', '1.0.0+build')
print(result) # 0 (build metadata ignored)
# Validate version strings
print(validate('1.2.3')) # True
print(validate('1.2.3-alpha.1')) # True
print(validate('1.2')) # False
print(validate('invalid')) # FalseThe semantic_version library enforces strict SemVer 2.0.0 validation:
# Internal regex patterns used for validation
Version.version_re # Strict SemVer pattern for complete versions
Version.partial_version_re # Pattern allowing partial versions (deprecated)Examples of validation:
# Valid versions
Version('1.2.3')
Version('1.2.3-alpha.1')
Version('1.2.3+build.456')
Version('1.2.3-alpha.1+build.456')
# Invalid versions (raise ValueError)
Version('1.2') # Missing patch
Version('01.2.3') # Leading zero in major
Version('1.02.3') # Leading zero in minor
Version('1.2.03') # Leading zero in patch
Version('1.2.3-') # Empty prerelease identifier
Version('1.2.3+') # Empty build identifierAll version operations follow consistent error handling patterns:
# Handle invalid versions
try:
invalid_version = Version('not.a.version')
except ValueError as e:
print(f"Invalid version: {e}")
# Handle type errors in comparison
try:
result = Version('1.0.0') < "1.0.1" # Should use Version object
except TypeError as e:
print(f"Type error: {e}")
# Handle leading zero errors
try:
leading_zero = Version('01.2.3')
except ValueError as e:
print(f"Leading zero error: {e}")Install with Tessl CLI
npx tessl i tessl/pypi-semantic-version