164 lines
5.8 KiB
Python
164 lines
5.8 KiB
Python
# 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
|