| """ |
| Semantic version parsing and comparison utilities. |
| """ |
|
|
| import re |
|
|
|
|
| def parse_version(version_string: str) -> tuple[int, int, int, str | None]: |
| """ |
| Parse a semantic version string into major, minor, patch, and optional prerelease. |
| |
| Supports formats like: |
| - "1.0.0" |
| - "v1.0.0" |
| - "1.0.0-beta" |
| - "v1.0.0-rc.1" |
| |
| Args: |
| version_string: Version string to parse |
| |
| Returns: |
| Tuple of (major, minor, patch, prerelease) |
| """ |
| |
| version = version_string.strip() |
| if version.lower().startswith("v"): |
| version = version[1:] |
|
|
| |
| pattern = r"^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$" |
| match = re.match(pattern, version) |
|
|
| if not match: |
| |
| simple_pattern = r"^(\d+)(?:\.(\d+))?(?:\.(\d+))?$" |
| simple_match = re.match(simple_pattern, version) |
| if simple_match: |
| major = int(simple_match.group(1)) |
| minor = int(simple_match.group(2) or 0) |
| patch = int(simple_match.group(3) or 0) |
| return (major, minor, patch, None) |
| raise ValueError(f"Invalid version string: {version_string}") |
|
|
| major = int(match.group(1)) |
| minor = int(match.group(2)) |
| patch = int(match.group(3)) |
| prerelease = match.group(4) |
|
|
| return (major, minor, patch, prerelease) |
|
|
|
|
| def compare_versions(version1: str, version2: str) -> int: |
| """ |
| Compare two semantic version strings. |
| |
| Args: |
| version1: First version string |
| version2: Second version string |
| |
| Returns: |
| -1 if version1 < version2 |
| 0 if version1 == version2 |
| 1 if version1 > version2 |
| """ |
| v1 = parse_version(version1) |
| v2 = parse_version(version2) |
|
|
| |
| major1, minor1, patch1, pre1 = v1 |
| major2, minor2, patch2, pre2 = v2 |
|
|
| |
| pairs = [(major1, major2), (minor1, minor2), (patch1, patch2)] |
| for v1_val, v2_val in pairs: |
| if v1_val < v2_val: |
| return -1 |
| elif v1_val > v2_val: |
| return 1 |
|
|
| |
| |
| if pre1 is None and pre2 is None: |
| return 0 |
| elif pre1 is None: |
| return 1 |
| elif pre2 is None: |
| return -1 |
| else: |
| |
| if pre1 < pre2: |
| return -1 |
| elif pre1 > pre2: |
| return 1 |
| return 0 |
|
|
|
|
| def is_newer_version(current: str, latest: str) -> bool: |
| """ |
| Check if latest version is newer than current version. |
| |
| Args: |
| current: Current version string |
| latest: Latest version string to compare |
| |
| Returns: |
| True if latest > current, False otherwise |
| """ |
| try: |
| return compare_versions(latest, current) > 0 |
| except ValueError: |
| |
| return False |
|
|