Source code for aioqbt.version

import re
from functools import total_ordering
from typing import Any, NamedTuple, Optional, Tuple, Union

from typing_extensions import Protocol, Self

from aioqbt.exc import VersionError

__all__ = (
    "ClientVersion",
    "APIVersion",
)

_VERSION_PATTERN = re.compile(
    r"^v?(\d+)\.(\d+)(?:\.(\d+)(?:\.(\d+))?)?((?:alpha|beta|rc)\d*)?$",
    re.IGNORECASE,
)

_API_VERSION_PATTERN = re.compile(
    r"^(\d+)\.(\d+)(?:\.(\d+))?$",
    re.IGNORECASE,
)


[docs] @total_ordering class ClientVersion: """ Represent client version. """ _key: Tuple[int, int, int, int, str, int] _status: str def __init__(self, major: int, minor: int, patch: int, build: int = 0, status: str = ""): st0, st1 = self._parse_status(status) self._key = (major, minor, patch, build, st0, st1) self._status = status @property def major(self) -> int: """Major number.""" return self._key[0] @property def minor(self) -> int: """Minor number.""" return self._key[1] @property def patch(self) -> int: """Patch number.""" return self._key[2] @property def build(self) -> int: """Build number.""" return self._key[3] @property def status(self) -> str: """Status string.""" return self._status def __eq__(self, other: object) -> bool: if isinstance(other, ClientVersion): return self._key == other._key return NotImplemented def __hash__(self) -> int: return hash(self._key) def __lt__(self, other: Any) -> bool: if isinstance(other, ClientVersion): return self._key < other._key return NotImplemented def __str__(self) -> str: result = f"{self.major:d}.{self.minor:d}.{self.patch:d}" if self.build != 0: result += f".{self.build:d}" result += self.status return result @classmethod def _parse_status(cls, status: str) -> Tuple[str, int]: if status == "": return "release", 0 match = re.match(r"(alpha|beta|rc)(\d*)", status, re.IGNORECASE) if match is None: raise ValueError(f"Bad status: {status!r}") a, b = match.groups() return a.lower(), int(b or 0)
[docs] @classmethod def parse(cls, version: str) -> "ClientVersion": """ Parse client version. Format:: major.minor.patch[.build][status] Examples:: 4.2.5 4.4.0beta2 4.4.3.1 """ match = _VERSION_PATTERN.match(version) if match is None: raise ValueError(f"Bad version: {version!r}") major = int(match[1]) minor = int(match[2]) patch = int(match[3] or 0) build = int(match[4] or 0) status = match[5] or "" return cls(major, minor, patch, build, status)
[docs] class APIVersion(NamedTuple): """ Represent API version. Instances can also be compared with 3-tuple of :class:`int`. """ major: int """Major number.""" minor: int """Minor number.""" release: int = 0 """Release number."""
[docs] @classmethod def parse(cls, version: str) -> "APIVersion": """ Parse API version. Format:: major.minor[.release] where ``major``, ``minor`` and ``release`` are all digits. """ match = _API_VERSION_PATTERN.match(version) if match is None: raise ValueError(f"Bad API version: {version!r}") s1, s2, s3 = match.groups() return cls(int(s1), int(s2), int(s3 or 0))
def __str__(self) -> str: return f"{self.major}.{self.minor}.{self.release}"
[docs] @classmethod def compare( cls, a: Optional[Union[Self, Tuple[int, int, int]]], b: Optional[Union[Self, Tuple[int, int, int]]], ) -> int: """ Compare two API versions. Return zero if ``a == b``; a negative value if ``a < b``; or a positive value if ``a > b``. ``None`` is a special value treated as the latest version. :return: integer value indicating version relationship. """ if a is None: if b is None: return 0 else: return 1 elif b is None: return -1 elif a == b: return 0 elif a < b: return -1 else: return 1
class Comparable(Protocol): def __eq__(self, other: Any) -> bool: pass def __ne__(self, other: Any) -> bool: pass def __lt__(self, other: Any) -> bool: pass def __le__(self, other: Any) -> bool: pass def __gt__(self, other: Any) -> bool: pass def __ge__(self, other: Any) -> bool: pass def version_satisfy(version: Optional[Comparable], minimum: Comparable) -> bool: """ Compare version with minimum requirement and return boolean :param version: current version, or ``None`` as the latest :param minimum: minimum version """ return version is None or version >= minimum def version_check(version: Optional[Comparable], minimum: Comparable) -> None: """ Compare version with minimum requirement and raise if violated. :param version: current version, or ``None`` as the latest :param minimum: minimum version """ if version is not None and version < minimum: raise VersionError(f"Version {minimum} is required but {version} is found") def param_version_check(param: str, version: Optional[Comparable], minimum: Comparable) -> None: if not version_satisfy(version, minimum): if type(minimum) is tuple: minimum = type(version)(*minimum) # type: ignore raise VersionError(f"{param!r} requires version {version!r} but {minimum!r} found")