|
1 | 1 | # flake8: NOQA
|
| 2 | +import functools |
2 | 3 | import sys
|
3 | 4 | import types
|
4 | 5 | import typing as t
|
@@ -31,3 +32,106 @@ def str_from_console(s: t.Union[str, bytes]) -> str:
|
31 | 32 | return str(s)
|
32 | 33 | except UnicodeDecodeError:
|
33 | 34 | return str(s, encoding="utf_8") if isinstance(s, bytes) else s
|
| 35 | + |
| 36 | + |
| 37 | +import re |
| 38 | +from typing import Iterator, List, Tuple |
| 39 | + |
| 40 | +from packaging.version import Version |
| 41 | + |
| 42 | +### |
| 43 | +### Legacy support for LooseVersion / LegacyVersion, e.g. 2.4-openbsd |
| 44 | +### https://github.com/pypa/packaging/blob/21.3/packaging/version.py#L106-L115 |
| 45 | +### License: BSD, Accessed: Jan 14th, 2022 |
| 46 | +### |
| 47 | + |
| 48 | +LegacyCmpKey = Tuple[int, Tuple[str, ...]] |
| 49 | + |
| 50 | +_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE) |
| 51 | +_legacy_version_replacement_map = { |
| 52 | + "pre": "c", |
| 53 | + "preview": "c", |
| 54 | + "-": "final-", |
| 55 | + "rc": "c", |
| 56 | + "dev": "@", |
| 57 | +} |
| 58 | + |
| 59 | + |
| 60 | +def _parse_version_parts(s: str) -> Iterator[str]: |
| 61 | + for part in _legacy_version_component_re.split(s): |
| 62 | + part = _legacy_version_replacement_map.get(part, part) |
| 63 | + |
| 64 | + if not part or part == ".": |
| 65 | + continue |
| 66 | + |
| 67 | + if part[:1] in "0123456789": |
| 68 | + # pad for numeric comparison |
| 69 | + yield part.zfill(8) |
| 70 | + else: |
| 71 | + yield "*" + part |
| 72 | + |
| 73 | + # ensure that alpha/beta/candidate are before final |
| 74 | + yield "*final" |
| 75 | + |
| 76 | + |
| 77 | +def _legacy_cmpkey(version: str) -> LegacyCmpKey: |
| 78 | + # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch |
| 79 | + # greater than or equal to 0. This will effectively put the LegacyVersion, |
| 80 | + # which uses the defacto standard originally implemented by setuptools, |
| 81 | + # as before all PEP 440 versions. |
| 82 | + epoch = -1 |
| 83 | + |
| 84 | + # This scheme is taken from pkg_resources.parse_version setuptools prior to |
| 85 | + # it's adoption of the packaging library. |
| 86 | + parts: List[str] = [] |
| 87 | + for part in _parse_version_parts(version.lower()): |
| 88 | + if part.startswith("*"): |
| 89 | + # remove "-" before a prerelease tag |
| 90 | + if part < "*final": |
| 91 | + while parts and parts[-1] == "*final-": |
| 92 | + parts.pop() |
| 93 | + |
| 94 | + # remove trailing zeros from each series of numeric parts |
| 95 | + while parts and parts[-1] == "00000000": |
| 96 | + parts.pop() |
| 97 | + |
| 98 | + parts.append(part) |
| 99 | + |
| 100 | + return epoch, tuple(parts) |
| 101 | + |
| 102 | + |
| 103 | +@functools.total_ordering |
| 104 | +class LegacyVersion: |
| 105 | + _key: LegacyCmpKey |
| 106 | + |
| 107 | + def __hash__(self) -> int: |
| 108 | + return hash(self._key) |
| 109 | + |
| 110 | + def __init__(self, version: object) -> None: |
| 111 | + self._version = str(version) |
| 112 | + self._key = _legacy_cmpkey(self._version) |
| 113 | + |
| 114 | + def __str__(self) -> str: |
| 115 | + return self._version |
| 116 | + |
| 117 | + def __lt__(self, other: object) -> bool: |
| 118 | + if isinstance(other, str): |
| 119 | + other = LegacyVersion(other) |
| 120 | + if not isinstance(other, LegacyVersion): |
| 121 | + return NotImplemented |
| 122 | + |
| 123 | + return self._key < other._key |
| 124 | + |
| 125 | + def __eq__(self, other: object) -> bool: |
| 126 | + if isinstance(other, str): |
| 127 | + other = LegacyVersion(other) |
| 128 | + if not isinstance(other, LegacyVersion): |
| 129 | + return NotImplemented |
| 130 | + |
| 131 | + return self._key == other._key |
| 132 | + |
| 133 | + def __repr__(self) -> str: |
| 134 | + return "<LegacyVersion({0})>".format(repr(str(self))) |
| 135 | + |
| 136 | + |
| 137 | +LooseVersion = LegacyVersion |
0 commit comments