version.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. """Version comparison utilities for semantic versioning.
  2. This module provides utilities for parsing and comparing semantic version strings.
  3. Supports version strings in the format: major.minor (e.g., "1.0", "1.2")
  4. """
  5. from __future__ import annotations
  6. import re
  7. from typing import Tuple
  8. import logging
  9. logger = logging.getLogger(__name__)
  10. def parse_version(version_str: str) -> Tuple[int, int]:
  11. """Parse a semantic version string into a tuple of integers.
  12. Args:
  13. version_str: Version string in format "major.minor" (e.g., "1.0", "1.2")
  14. Returns:
  15. Tuple of (major, minor) as integers
  16. Raises:
  17. ValueError: If version string is not in valid semantic version format
  18. Examples:
  19. >>> parse_version("1.0")
  20. (1, 0)
  21. >>> parse_version("1.2")
  22. (1, 2)
  23. """
  24. if not version_str:
  25. raise ValueError("Version string cannot be empty")
  26. # Remove 'v' prefix if present
  27. version_str = version_str.lstrip("v")
  28. # Match semantic version pattern: major.minor
  29. pattern = r"^(\d+)\.(\d+)$"
  30. match = re.match(pattern, version_str)
  31. if not match:
  32. raise ValueError(
  33. f"Invalid version format '{version_str}'. "
  34. "Expected format: major.minor (e.g., '1.0', '1.2')"
  35. )
  36. major, minor = match.groups()
  37. return (int(major), int(minor))
  38. def compare_versions(version1: str, version2: str) -> int:
  39. """Compare two semantic version strings.
  40. Args:
  41. version1: First version string
  42. version2: Second version string
  43. Returns:
  44. -1 if version1 < version2
  45. 0 if version1 == version2
  46. 1 if version1 > version2
  47. Raises:
  48. ValueError: If either version string is invalid
  49. Examples:
  50. >>> compare_versions("1.0", "0.9")
  51. 1
  52. >>> compare_versions("1.0", "1.0")
  53. 0
  54. >>> compare_versions("1.0", "1.1")
  55. -1
  56. """
  57. v1 = parse_version(version1)
  58. v2 = parse_version(version2)
  59. if v1 < v2:
  60. return -1
  61. if v1 > v2:
  62. return 1
  63. return 0
  64. def is_compatible(current_version: str, required_version: str) -> bool:
  65. """Check if current version meets the minimum required version.
  66. Args:
  67. current_version: Current version
  68. required_version: Minimum required version
  69. Returns:
  70. True if current_version >= required_version, False otherwise
  71. Examples:
  72. >>> is_compatible("1.0", "0.9")
  73. True
  74. >>> is_compatible("1.0", "1.0")
  75. True
  76. >>> is_compatible("1.0", "1.1")
  77. False
  78. """
  79. try:
  80. return compare_versions(current_version, required_version) >= 0
  81. except ValueError as e:
  82. logger.warning("Version compatibility check failed: %s", e)
  83. # If we can't parse versions, assume incompatible for safety
  84. return False