Source code for pivpy.update

from __future__ import annotations

import json
import re
import urllib.request
from dataclasses import dataclass
from importlib.metadata import PackageNotFoundError, version as _dist_version
from typing import Optional


[docs] @dataclass(frozen=True) class UpdateCheckResult: """Result of a package update check against PyPI.""" status: int installed: str latest: str
def _get_installed_version_str(dist_name: str = "pivpy") -> str: try: return _dist_version(dist_name) except PackageNotFoundError: return "0.0.0" def _get_pypi_version_str(package: str = "pivpy", timeout: float = 3.0) -> str: url = f"https://pypi.org/pypi/{package}/json" req = urllib.request.Request( url, headers={ "Accept": "application/json", "User-Agent": "pivpy-update-check/1.0 (+https://pypi.org/project/pivpy/)", }, ) with urllib.request.urlopen(req, timeout=timeout) as resp: payload = resp.read() data = json.loads(payload.decode("utf-8")) return str(data["info"]["version"]) def _compare_versions(installed: str, latest: str) -> int: """Compare version strings. Returns: -1 if installed < latest 0 if installed == latest 1 if installed > latest Prefers packaging.version.Version when available; falls back to a simple numeric comparison for dotted versions. """ try: from packaging.version import Version # type: ignore a = Version(installed) b = Version(latest) return (a > b) - (a < b) except Exception: # Fallback: compare numeric components only (best-effort). def parts(v: str) -> tuple[int, ...]: nums = re.findall(r"\d+", v) if not nums: return (0,) return tuple(int(n) for n in nums) a = parts(installed) b = parts(latest) return (a > b) - (a < b)
[docs] def check_update( package: str = "pivpy", *, dist_name: Optional[str] = None, timeout: float = 3.0, verbose: bool = False, ) -> UpdateCheckResult: """Check whether a newer version of pivpy exists on PyPI. Status codes (mirrors the PIVMat convention): 0: server unavailable / request failed 1: no new version available (installed == latest) 2: a new version is available online (installed < latest) 3: the online version is older than installed (installed > latest) Args: package: PyPI package name to query (defaults to "pivpy"). dist_name: Installed distribution name (defaults to package). timeout: Network timeout (seconds). verbose: If True, prints a short human-readable message. Returns: UpdateCheckResult with status + installed/latest strings. """ dist = dist_name or package installed = _get_installed_version_str(dist) try: latest = _get_pypi_version_str(package, timeout=timeout) except Exception: if verbose: print("Update check failed: server unavailable.") return UpdateCheckResult(status=0, installed=installed, latest="") cmp = _compare_versions(installed, latest) if cmp < 0: status = 2 if verbose: print(f"New version available: {package} {latest} (installed: {installed}).") elif cmp > 0: status = 3 if verbose: print(f"Installed version ({installed}) is newer than PyPI ({latest}).") else: status = 1 if verbose: print(f"{package} is up to date ({installed}).") return UpdateCheckResult(status=status, installed=installed, latest=latest)