refactor: drop packaging dependency, inline version parsing
Lint & Test / test (push) Successful in 3m9s
Lint & Test / test (push) Successful in 3m9s
The only user of 'packaging' was version_check.py — two small functions (normalize_version, is_newer) that just need to parse "1.2.3-alpha.1" and compare PEP 440-style versions. That's well within stdlib reach. - Inline a NamedTuple-based Version with kind/pre_num ordering (dev < alpha < beta < rc < release), same regex-normalized format - Define a local InvalidVersion exception - Remove packaging>=23.0 from pyproject.toml dependencies Why now: the Windows cross-build uses a hard-coded DEPS array in build-dist-windows.sh, which was never updated when 'packaging' was added on March 25. Result: importable from pip-installed dev envs, missing from the portable installer — tray icon appeared but uvicorn died with ModuleNotFoundError: No module named 'packaging'. Removing the dep entirely is cleaner than adding one more hard-coded entry to the Windows DEPS list. Tests (678 passing) and a manual test matrix covering dev/alpha/beta/rc/release ordering all pass.
This commit is contained in:
@@ -26,7 +26,6 @@ dependencies = [
|
|||||||
"fastapi>=0.115.0",
|
"fastapi>=0.115.0",
|
||||||
"uvicorn[standard]>=0.32.0",
|
"uvicorn[standard]>=0.32.0",
|
||||||
"httpx>=0.27.2",
|
"httpx>=0.27.2",
|
||||||
"packaging>=23.0",
|
|
||||||
"mss>=9.0.2",
|
"mss>=9.0.2",
|
||||||
"numpy>=2.1.3",
|
"numpy>=2.1.3",
|
||||||
"pydantic>=2.9.2",
|
"pydantic>=2.9.2",
|
||||||
|
|||||||
@@ -1,40 +1,99 @@
|
|||||||
"""Version comparison utilities.
|
"""Version comparison utilities.
|
||||||
|
|
||||||
Normalizes Gitea-style tags (v0.3.0-alpha.1) to PEP 440 (0.3.0a1)
|
Normalizes Gitea-style tags (v0.3.0-alpha.1) to PEP 440 (0.3.0a1)
|
||||||
so that ``packaging.version.Version`` can compare them correctly.
|
and compares them using a tuple-based ordering. Deliberately does
|
||||||
|
not depend on the external ``packaging`` library — it's just one
|
||||||
|
more dependency to ship in the portable installer for a handful
|
||||||
|
of simple comparisons we can do with stdlib.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
from typing import NamedTuple
|
||||||
from packaging.version import InvalidVersion, Version
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidVersion(ValueError):
|
||||||
|
"""Raised when a version string cannot be parsed."""
|
||||||
|
|
||||||
|
|
||||||
|
# Release-kind ordering — smaller = earlier.
|
||||||
|
# dev < alpha < beta < rc < release
|
||||||
|
_KIND_DEV = 0
|
||||||
|
_KIND_ALPHA = 1
|
||||||
|
_KIND_BETA = 2
|
||||||
|
_KIND_RC = 3
|
||||||
|
_KIND_RELEASE = 4
|
||||||
|
|
||||||
|
|
||||||
|
class Version(NamedTuple):
|
||||||
|
"""Comparable version tuple. Larger tuples are newer versions."""
|
||||||
|
|
||||||
|
major: int
|
||||||
|
minor: int
|
||||||
|
patch: int
|
||||||
|
kind: int
|
||||||
|
pre_num: int
|
||||||
|
|
||||||
|
|
||||||
_PRE_MAP = {
|
_PRE_MAP = {
|
||||||
"alpha": "a",
|
"alpha": _KIND_ALPHA,
|
||||||
"beta": "b",
|
"a": _KIND_ALPHA,
|
||||||
"rc": "rc",
|
"beta": _KIND_BETA,
|
||||||
|
"b": _KIND_BETA,
|
||||||
|
"rc": _KIND_RC,
|
||||||
}
|
}
|
||||||
|
|
||||||
_PRE_PATTERN = re.compile(
|
# Matches "1.2.3" optionally followed by a pre-release segment.
|
||||||
r"^(\d+\.\d+\.\d+)[-.]?(alpha|beta|rc)[.-]?(\d+)$", re.IGNORECASE
|
# Accepts Gitea-style ("1.2.3-alpha.1") and PEP 440 ("1.2.3a1", "1.2.3.dev0").
|
||||||
|
_VERSION_PATTERN = re.compile(
|
||||||
|
r"""
|
||||||
|
^
|
||||||
|
(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
|
||||||
|
(?:
|
||||||
|
[-.]? # optional separator
|
||||||
|
(?P<pre_label>alpha|beta|rc|a|b|dev) # pre-release label
|
||||||
|
[.-]? # optional separator
|
||||||
|
(?P<pre_num>\d+) # pre-release number
|
||||||
|
)?
|
||||||
|
$
|
||||||
|
""",
|
||||||
|
re.IGNORECASE | re.VERBOSE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def normalize_version(raw: str) -> Version:
|
def normalize_version(raw: str) -> Version:
|
||||||
"""Convert a tag like ``v0.3.0-alpha.1`` to a PEP 440 ``Version``.
|
"""Parse a version string into a comparable ``Version`` tuple.
|
||||||
|
|
||||||
Raises ``InvalidVersion`` if the string cannot be parsed.
|
Accepts Gitea-style tags (``v0.3.0-alpha.1``) and PEP 440
|
||||||
|
(``0.3.0a1``, ``0.0.0.dev0``). Raises ``InvalidVersion`` if
|
||||||
|
the string cannot be parsed.
|
||||||
"""
|
"""
|
||||||
cleaned = raw.lstrip("v").strip()
|
cleaned = raw.lstrip("vV").strip()
|
||||||
m = _PRE_PATTERN.match(cleaned)
|
m = _VERSION_PATTERN.match(cleaned)
|
||||||
if m:
|
if not m:
|
||||||
base, pre_label, pre_num = m.group(1), m.group(2).lower(), m.group(3)
|
raise InvalidVersion(f"Unparseable version: {raw!r}")
|
||||||
pep_label = _PRE_MAP.get(pre_label, pre_label)
|
|
||||||
cleaned = f"{base}{pep_label}{pre_num}"
|
major = int(m.group("major"))
|
||||||
return Version(cleaned)
|
minor = int(m.group("minor"))
|
||||||
|
patch = int(m.group("patch"))
|
||||||
|
|
||||||
|
pre_label = m.group("pre_label")
|
||||||
|
pre_num_str = m.group("pre_num")
|
||||||
|
|
||||||
|
if pre_label is None:
|
||||||
|
kind = _KIND_RELEASE
|
||||||
|
pre_num = 0
|
||||||
|
else:
|
||||||
|
label = pre_label.lower()
|
||||||
|
if label == "dev":
|
||||||
|
kind = _KIND_DEV
|
||||||
|
else:
|
||||||
|
kind = _PRE_MAP[label]
|
||||||
|
pre_num = int(pre_num_str) if pre_num_str is not None else 0
|
||||||
|
|
||||||
|
return Version(major, minor, patch, kind, pre_num)
|
||||||
|
|
||||||
|
|
||||||
def is_newer(candidate: str, current: str) -> bool:
|
def is_newer(candidate: str, current: str) -> bool:
|
||||||
|
|||||||
Reference in New Issue
Block a user