# 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 distutils.version import StrictVersion from looseversion import LooseVersion 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 ValueError: pass try: if version.count(".") == 3: return AncientMozillaVersion(version) except ValueError: 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