Skip to content

Commit 9c52576

Browse files
committed
Custom Version implementation
1 parent 5166809 commit 9c52576

File tree

2 files changed

+146
-0
lines changed

2 files changed

+146
-0
lines changed

libtmux/_compat.py

+103
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# -*- coding: utf8 -*-
22
# flake8: NOQA
3+
import functools
34
import sys
45
from collections.abc import MutableMapping
56

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

tests/test_version.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import operator
2+
from contextlib import nullcontext as does_not_raise
3+
4+
import pytest
5+
6+
from libtmux._compat import LooseVersion
7+
8+
9+
@pytest.mark.parametrize(
10+
"version",
11+
[
12+
"1",
13+
"1.0",
14+
"1.0.0",
15+
"1.0.0b",
16+
"1.0.0b1",
17+
"1.0.0b-openbsd",
18+
"1.0.0-next",
19+
"1.0.0-next.1",
20+
],
21+
)
22+
def test_version(version):
23+
assert LooseVersion(version)
24+
25+
26+
@pytest.mark.parametrize(
27+
"version,op,versionb,raises",
28+
[
29+
["1", operator.eq, "1", False],
30+
["1", operator.eq, "1.0", False],
31+
["1", operator.eq, "1.0.0", False],
32+
["1", operator.gt, "1.0.0a", False],
33+
["1", operator.gt, "1.0.0b", False],
34+
["1", operator.lt, "1.0.0p1", False],
35+
["1", operator.lt, "1.0.0-openbsd", False],
36+
["1", operator.lt, "1", AssertionError],
37+
["1", operator.lt, "1", AssertionError],
38+
],
39+
)
40+
def test_version_compare(version, op, versionb, raises):
41+
raises_ctx = pytest.raises(raises) if raises else does_not_raise()
42+
with raises_ctx:
43+
assert op(LooseVersion(version), LooseVersion(versionb))

0 commit comments

Comments
 (0)