"""Tolerant version comparator for real-world pipelines.""" import re from functools import total_ordering @total_ordering class Version: """ Tolerant version comparator for real-world pipelines. Handles: v1.2 3.19 3.19.0-260127 3.19.0-rc1 3.19.0-rc1-260127 3.19_final build_42 anything with numbers in it """ _PRERELEASE_RE = re.compile(r"(rc|alpha|beta|a|b)", re.I) def __init__(self, raw: str): self.raw = raw self.parts = tuple(int(n) for n in re.findall(r"\d+", raw)) self.is_prerelease = bool(self._PRERELEASE_RE.search(raw)) def __eq__(self, other): if not isinstance(other, Version): return NotImplemented return self.parts == other.parts and self.is_prerelease == other.is_prerelease def __lt__(self, other): if not isinstance(other, Version): return NotImplemented # compare numeric parts first if self.parts != other.parts: return self.parts < other.parts # stable > prerelease return self.is_prerelease and not other.is_prerelease def __repr__(self): return f"Version({self.raw!r})" # ------------------------------- # Micro helpers # ------------------------------- def version_gt(v1, v2): """Greater than""" return Version(v1) > Version(v2) def version_lt(v1, v2): """Less than""" return Version(v1) < Version(v2) def version_eq(v1, v2): """Equal""" return Version(v1) == Version(v2) def compare_versions(v1, v2): """Compare two version strings. Returns: -1 if v1 < v2 0 if v1 == v2 1 if v1 > v2 """ ver1 = Version(v1) ver2 = Version(v2) if ver1 < ver2: return -1 elif ver1 > ver2: return 1 else: return 0