1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import re
from looseversion import LooseVersion
from packaging.version import InvalidVersion
class StrictVersion:
def __init__(self, vstring):
self.parse(vstring)
def __repr__(self):
return "%s ('%s')" % (self.__class__.__name__, str(self))
def __eq__(self, other):
return self._cmp(other) == 0
def __lt__(self, other):
return self._cmp(other) < 0
def parse(self, vstring):
match = self.version_re.match(vstring)
if not match:
raise InvalidVersion("invalid version number '%s'" % vstring)
major, minor, patch, pre, pre_num = match.group(1, 2, 4, 5, 6)
self.version = int(major), int(minor), int(patch or 0)
self.pre = (pre[0], int(pre_num)) if pre else ()
def __str__(self):
return ".".join(map(str, self.version)) + (
"".join(map(str, self.pre)) if self.pre else ""
)
def _cmp(self, other):
if isinstance(other, str):
other = StrictVersion(other)
elif not isinstance(other, StrictVersion):
raise NotImplementedError
if self.version < other.version:
return -1
elif self.version == other.version:
if self.pre == other.pre:
return 0
elif not self.pre:
return 1
elif not other.pre:
return -1
elif self.pre < other.pre:
return -1
else:
return 1
else:
return 1
class MozillaVersionCompareMixin:
def __cmp__(self, other):
# We expect this function to never be called.
raise AssertionError()
def _cmp(self, other):
has_esr = set()
if isinstance(other, LooseModernMozillaVersion) and str(other).endswith("esr"):
# If other version ends with esr, coerce through MozillaVersion ending up with
# a StrictVersion if possible
has_esr.add("other")
other = MozillaVersion(str(other)[:-3]) # strip ESR from end of string
if isinstance(self, LooseModernMozillaVersion) and str(self).endswith("esr"):
# If our version ends with esr, coerce through MozillaVersion ending up with
# a StrictVersion if possible
has_esr.add("self")
self = MozillaVersion(str(self)[:-3]) # strip ESR from end of string
if isinstance(other, LooseModernMozillaVersion) or isinstance(
self, LooseModernMozillaVersion
):
# If we're still LooseVersion for self or other, run LooseVersion compare
# Being sure to pass through Loose Version type first
val = LooseVersion._cmp(
LooseModernMozillaVersion(str(self)),
LooseModernMozillaVersion(str(other)),
)
else:
# No versions are loose, therefore we can use StrictVersion
val = StrictVersion._cmp(self, other)
if has_esr.isdisjoint(set(["other", "self"])) or has_esr.issuperset(
set(["other", "self"])
):
# If both had esr string or neither, then _cmp() was accurate
return val
elif val != 0:
# cmp is accurate here even if esr is present in only 1 compare, since
# versions are not equal
return val
elif "other" in has_esr:
return -1 # esr is not greater than non esr
return 1 # non esr is greater than esr
class ModernMozillaVersion(MozillaVersionCompareMixin, StrictVersion):
"""A version class that is slightly less restrictive than StrictVersion.
Instead of just allowing "a" or "b" as prerelease tags, it allows any
alpha. This allows us to support the once-shipped "3.6.3plugin1" and
similar versions."""
version_re = re.compile(
r"""^(\d+) \. (\d+) (\. (\d+))?
([a-zA-Z]+(\d+))?$""",
re.VERBOSE,
)
class AncientMozillaVersion(MozillaVersionCompareMixin, StrictVersion):
"""A version class that is slightly less restrictive than StrictVersion.
Instead of just allowing "a" or "b" as prerelease tags, it allows any
alpha. This allows us to support the once-shipped "3.6.3plugin1" and
similar versions.
It also supports versions w.x.y.z by transmuting to w.x.z, which
is useful for versions like 1.5.0.x and 2.0.0.y"""
version_re = re.compile(
r"""^(\d+) \. (\d+) \. \d (\. (\d+))
([a-zA-Z]+(\d+))?$""",
re.VERBOSE,
)
class LooseModernMozillaVersion(MozillaVersionCompareMixin, LooseVersion):
"""A version class that is more restrictive than LooseVersion.
This class reduces the valid strings to "esr", "a", "b" and "rc" in order
to support esr. StrictVersion requires a trailing number after all strings."""
component_re = re.compile(r"(\d+ | a | b | rc | esr | \.)", re.VERBOSE)
def __repr__(self):
return "LooseModernMozillaVersion ('%s')" % str(self)
def MozillaVersion(version):
try:
return ModernMozillaVersion(version)
except InvalidVersion:
pass
try:
if version.count(".") == 3:
return AncientMozillaVersion(version)
except InvalidVersion:
pass
try:
return LooseModernMozillaVersion(version)
except ValueError:
pass
raise ValueError("Version number %s is invalid." % version)
def getPrettyVersion(version):
version = re.sub(r"a([0-9]+)$", r" Alpha \1", version)
version = re.sub(r"b([0-9]+)$", r" Beta \1", version)
version = re.sub(r"rc([0-9]+)$", r" RC \1", version)
return version
|