version.py 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. """
  2. 语义版本解析与校验,用于客户端分发的 version_code(如 1.2.3)。
  3. """
  4. import re
  5. from typing import Tuple
  6. # 允许:x / x.y / x.y.z,可选 -prerelease 后缀;前导 v 可选
  7. VERSION_CODE_PATTERN = re.compile(
  8. r"^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:[-.]([a-zA-Z0-9.-]+))?$",
  9. re.IGNORECASE,
  10. )
  11. def parse_version_code(version_code: str) -> Tuple[int, int, int, str]:
  12. """
  13. 将 version_code 解析为可比较元组 (major, minor, patch, prerelease)。
  14. 不符合格式的返回 (0, 0, 0, raw),保证可排序。
  15. """
  16. if not version_code or not isinstance(version_code, str):
  17. return (0, 0, 0, "")
  18. s = version_code.strip()
  19. m = VERSION_CODE_PATTERN.match(s)
  20. if not m:
  21. return (0, 0, 0, s)
  22. major = int(m.group(1))
  23. minor = int(m.group(2)) if m.group(2) else 0
  24. patch = int(m.group(3)) if m.group(3) else 0
  25. prerelease = (m.group(4) or "").strip()
  26. return (major, minor, patch, prerelease)
  27. def version_sort_key(version_code: str) -> Tuple[int, int, int, str]:
  28. """用于 sorted(key=...) 的排序键。"""
  29. return parse_version_code(version_code)
  30. def validate_version_code(version_code: str) -> None:
  31. """校验 version_code 格式,不符合则抛出 ValueError。"""
  32. if not version_code or not isinstance(version_code, str):
  33. raise ValueError("版本号不能为空")
  34. s = version_code.strip()
  35. if len(s) > 32:
  36. raise ValueError("版本号长度不能超过 32 字符")
  37. if not VERSION_CODE_PATTERN.match(s):
  38. raise ValueError(
  39. "版本号格式应为数字版本,如 1、1.2、1.2.3,可选 -prerelease 后缀(如 1.0.0-beta)"
  40. )