17db96d56Sopenharmony_ci#
27db96d56Sopenharmony_ci# distutils/version.py
37db96d56Sopenharmony_ci#
47db96d56Sopenharmony_ci# Implements multiple version numbering conventions for the
57db96d56Sopenharmony_ci# Python Module Distribution Utilities.
67db96d56Sopenharmony_ci#
77db96d56Sopenharmony_ci# $Id$
87db96d56Sopenharmony_ci#
97db96d56Sopenharmony_ci
107db96d56Sopenharmony_ci"""Provides classes to represent module version numbers (one class for
117db96d56Sopenharmony_cieach style of version numbering).  There are currently two such classes
127db96d56Sopenharmony_ciimplemented: StrictVersion and LooseVersion.
137db96d56Sopenharmony_ci
147db96d56Sopenharmony_ciEvery version number class implements the following interface:
157db96d56Sopenharmony_ci  * the 'parse' method takes a string and parses it to some internal
167db96d56Sopenharmony_ci    representation; if the string is an invalid version number,
177db96d56Sopenharmony_ci    'parse' raises a ValueError exception
187db96d56Sopenharmony_ci  * the class constructor takes an optional string argument which,
197db96d56Sopenharmony_ci    if supplied, is passed to 'parse'
207db96d56Sopenharmony_ci  * __str__ reconstructs the string that was passed to 'parse' (or
217db96d56Sopenharmony_ci    an equivalent string -- ie. one that will generate an equivalent
227db96d56Sopenharmony_ci    version number instance)
237db96d56Sopenharmony_ci  * __repr__ generates Python code to recreate the version number instance
247db96d56Sopenharmony_ci  * _cmp compares the current instance with either another instance
257db96d56Sopenharmony_ci    of the same class or a string (which will be parsed to an instance
267db96d56Sopenharmony_ci    of the same class, thus must follow the same rules)
277db96d56Sopenharmony_ci"""
287db96d56Sopenharmony_ci
297db96d56Sopenharmony_ciimport re
307db96d56Sopenharmony_ci
317db96d56Sopenharmony_ciclass Version:
327db96d56Sopenharmony_ci    """Abstract base class for version numbering classes.  Just provides
337db96d56Sopenharmony_ci    constructor (__init__) and reproducer (__repr__), because those
347db96d56Sopenharmony_ci    seem to be the same for all version numbering classes; and route
357db96d56Sopenharmony_ci    rich comparisons to _cmp.
367db96d56Sopenharmony_ci    """
377db96d56Sopenharmony_ci
387db96d56Sopenharmony_ci    def __init__ (self, vstring=None):
397db96d56Sopenharmony_ci        if vstring:
407db96d56Sopenharmony_ci            self.parse(vstring)
417db96d56Sopenharmony_ci
427db96d56Sopenharmony_ci    def __repr__ (self):
437db96d56Sopenharmony_ci        return "%s ('%s')" % (self.__class__.__name__, str(self))
447db96d56Sopenharmony_ci
457db96d56Sopenharmony_ci    def __eq__(self, other):
467db96d56Sopenharmony_ci        c = self._cmp(other)
477db96d56Sopenharmony_ci        if c is NotImplemented:
487db96d56Sopenharmony_ci            return c
497db96d56Sopenharmony_ci        return c == 0
507db96d56Sopenharmony_ci
517db96d56Sopenharmony_ci    def __lt__(self, other):
527db96d56Sopenharmony_ci        c = self._cmp(other)
537db96d56Sopenharmony_ci        if c is NotImplemented:
547db96d56Sopenharmony_ci            return c
557db96d56Sopenharmony_ci        return c < 0
567db96d56Sopenharmony_ci
577db96d56Sopenharmony_ci    def __le__(self, other):
587db96d56Sopenharmony_ci        c = self._cmp(other)
597db96d56Sopenharmony_ci        if c is NotImplemented:
607db96d56Sopenharmony_ci            return c
617db96d56Sopenharmony_ci        return c <= 0
627db96d56Sopenharmony_ci
637db96d56Sopenharmony_ci    def __gt__(self, other):
647db96d56Sopenharmony_ci        c = self._cmp(other)
657db96d56Sopenharmony_ci        if c is NotImplemented:
667db96d56Sopenharmony_ci            return c
677db96d56Sopenharmony_ci        return c > 0
687db96d56Sopenharmony_ci
697db96d56Sopenharmony_ci    def __ge__(self, other):
707db96d56Sopenharmony_ci        c = self._cmp(other)
717db96d56Sopenharmony_ci        if c is NotImplemented:
727db96d56Sopenharmony_ci            return c
737db96d56Sopenharmony_ci        return c >= 0
747db96d56Sopenharmony_ci
757db96d56Sopenharmony_ci
767db96d56Sopenharmony_ci# Interface for version-number classes -- must be implemented
777db96d56Sopenharmony_ci# by the following classes (the concrete ones -- Version should
787db96d56Sopenharmony_ci# be treated as an abstract class).
797db96d56Sopenharmony_ci#    __init__ (string) - create and take same action as 'parse'
807db96d56Sopenharmony_ci#                        (string parameter is optional)
817db96d56Sopenharmony_ci#    parse (string)    - convert a string representation to whatever
827db96d56Sopenharmony_ci#                        internal representation is appropriate for
837db96d56Sopenharmony_ci#                        this style of version numbering
847db96d56Sopenharmony_ci#    __str__ (self)    - convert back to a string; should be very similar
857db96d56Sopenharmony_ci#                        (if not identical to) the string supplied to parse
867db96d56Sopenharmony_ci#    __repr__ (self)   - generate Python code to recreate
877db96d56Sopenharmony_ci#                        the instance
887db96d56Sopenharmony_ci#    _cmp (self, other) - compare two version numbers ('other' may
897db96d56Sopenharmony_ci#                        be an unparsed version string, or another
907db96d56Sopenharmony_ci#                        instance of your version class)
917db96d56Sopenharmony_ci
927db96d56Sopenharmony_ci
937db96d56Sopenharmony_ciclass StrictVersion (Version):
947db96d56Sopenharmony_ci
957db96d56Sopenharmony_ci    """Version numbering for anal retentives and software idealists.
967db96d56Sopenharmony_ci    Implements the standard interface for version number classes as
977db96d56Sopenharmony_ci    described above.  A version number consists of two or three
987db96d56Sopenharmony_ci    dot-separated numeric components, with an optional "pre-release" tag
997db96d56Sopenharmony_ci    on the end.  The pre-release tag consists of the letter 'a' or 'b'
1007db96d56Sopenharmony_ci    followed by a number.  If the numeric components of two version
1017db96d56Sopenharmony_ci    numbers are equal, then one with a pre-release tag will always
1027db96d56Sopenharmony_ci    be deemed earlier (lesser) than one without.
1037db96d56Sopenharmony_ci
1047db96d56Sopenharmony_ci    The following are valid version numbers (shown in the order that
1057db96d56Sopenharmony_ci    would be obtained by sorting according to the supplied cmp function):
1067db96d56Sopenharmony_ci
1077db96d56Sopenharmony_ci        0.4       0.4.0  (these two are equivalent)
1087db96d56Sopenharmony_ci        0.4.1
1097db96d56Sopenharmony_ci        0.5a1
1107db96d56Sopenharmony_ci        0.5b3
1117db96d56Sopenharmony_ci        0.5
1127db96d56Sopenharmony_ci        0.9.6
1137db96d56Sopenharmony_ci        1.0
1147db96d56Sopenharmony_ci        1.0.4a3
1157db96d56Sopenharmony_ci        1.0.4b1
1167db96d56Sopenharmony_ci        1.0.4
1177db96d56Sopenharmony_ci
1187db96d56Sopenharmony_ci    The following are examples of invalid version numbers:
1197db96d56Sopenharmony_ci
1207db96d56Sopenharmony_ci        1
1217db96d56Sopenharmony_ci        2.7.2.2
1227db96d56Sopenharmony_ci        1.3.a4
1237db96d56Sopenharmony_ci        1.3pl1
1247db96d56Sopenharmony_ci        1.3c4
1257db96d56Sopenharmony_ci
1267db96d56Sopenharmony_ci    The rationale for this version numbering system will be explained
1277db96d56Sopenharmony_ci    in the distutils documentation.
1287db96d56Sopenharmony_ci    """
1297db96d56Sopenharmony_ci
1307db96d56Sopenharmony_ci    version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',
1317db96d56Sopenharmony_ci                            re.VERBOSE | re.ASCII)
1327db96d56Sopenharmony_ci
1337db96d56Sopenharmony_ci
1347db96d56Sopenharmony_ci    def parse (self, vstring):
1357db96d56Sopenharmony_ci        match = self.version_re.match(vstring)
1367db96d56Sopenharmony_ci        if not match:
1377db96d56Sopenharmony_ci            raise ValueError("invalid version number '%s'" % vstring)
1387db96d56Sopenharmony_ci
1397db96d56Sopenharmony_ci        (major, minor, patch, prerelease, prerelease_num) = \
1407db96d56Sopenharmony_ci            match.group(1, 2, 4, 5, 6)
1417db96d56Sopenharmony_ci
1427db96d56Sopenharmony_ci        if patch:
1437db96d56Sopenharmony_ci            self.version = tuple(map(int, [major, minor, patch]))
1447db96d56Sopenharmony_ci        else:
1457db96d56Sopenharmony_ci            self.version = tuple(map(int, [major, minor])) + (0,)
1467db96d56Sopenharmony_ci
1477db96d56Sopenharmony_ci        if prerelease:
1487db96d56Sopenharmony_ci            self.prerelease = (prerelease[0], int(prerelease_num))
1497db96d56Sopenharmony_ci        else:
1507db96d56Sopenharmony_ci            self.prerelease = None
1517db96d56Sopenharmony_ci
1527db96d56Sopenharmony_ci
1537db96d56Sopenharmony_ci    def __str__ (self):
1547db96d56Sopenharmony_ci
1557db96d56Sopenharmony_ci        if self.version[2] == 0:
1567db96d56Sopenharmony_ci            vstring = '.'.join(map(str, self.version[0:2]))
1577db96d56Sopenharmony_ci        else:
1587db96d56Sopenharmony_ci            vstring = '.'.join(map(str, self.version))
1597db96d56Sopenharmony_ci
1607db96d56Sopenharmony_ci        if self.prerelease:
1617db96d56Sopenharmony_ci            vstring = vstring + self.prerelease[0] + str(self.prerelease[1])
1627db96d56Sopenharmony_ci
1637db96d56Sopenharmony_ci        return vstring
1647db96d56Sopenharmony_ci
1657db96d56Sopenharmony_ci
1667db96d56Sopenharmony_ci    def _cmp (self, other):
1677db96d56Sopenharmony_ci        if isinstance(other, str):
1687db96d56Sopenharmony_ci            other = StrictVersion(other)
1697db96d56Sopenharmony_ci        elif not isinstance(other, StrictVersion):
1707db96d56Sopenharmony_ci            return NotImplemented
1717db96d56Sopenharmony_ci
1727db96d56Sopenharmony_ci        if self.version != other.version:
1737db96d56Sopenharmony_ci            # numeric versions don't match
1747db96d56Sopenharmony_ci            # prerelease stuff doesn't matter
1757db96d56Sopenharmony_ci            if self.version < other.version:
1767db96d56Sopenharmony_ci                return -1
1777db96d56Sopenharmony_ci            else:
1787db96d56Sopenharmony_ci                return 1
1797db96d56Sopenharmony_ci
1807db96d56Sopenharmony_ci        # have to compare prerelease
1817db96d56Sopenharmony_ci        # case 1: neither has prerelease; they're equal
1827db96d56Sopenharmony_ci        # case 2: self has prerelease, other doesn't; other is greater
1837db96d56Sopenharmony_ci        # case 3: self doesn't have prerelease, other does: self is greater
1847db96d56Sopenharmony_ci        # case 4: both have prerelease: must compare them!
1857db96d56Sopenharmony_ci
1867db96d56Sopenharmony_ci        if (not self.prerelease and not other.prerelease):
1877db96d56Sopenharmony_ci            return 0
1887db96d56Sopenharmony_ci        elif (self.prerelease and not other.prerelease):
1897db96d56Sopenharmony_ci            return -1
1907db96d56Sopenharmony_ci        elif (not self.prerelease and other.prerelease):
1917db96d56Sopenharmony_ci            return 1
1927db96d56Sopenharmony_ci        elif (self.prerelease and other.prerelease):
1937db96d56Sopenharmony_ci            if self.prerelease == other.prerelease:
1947db96d56Sopenharmony_ci                return 0
1957db96d56Sopenharmony_ci            elif self.prerelease < other.prerelease:
1967db96d56Sopenharmony_ci                return -1
1977db96d56Sopenharmony_ci            else:
1987db96d56Sopenharmony_ci                return 1
1997db96d56Sopenharmony_ci        else:
2007db96d56Sopenharmony_ci            assert False, "never get here"
2017db96d56Sopenharmony_ci
2027db96d56Sopenharmony_ci# end class StrictVersion
2037db96d56Sopenharmony_ci
2047db96d56Sopenharmony_ci
2057db96d56Sopenharmony_ci# The rules according to Greg Stein:
2067db96d56Sopenharmony_ci# 1) a version number has 1 or more numbers separated by a period or by
2077db96d56Sopenharmony_ci#    sequences of letters. If only periods, then these are compared
2087db96d56Sopenharmony_ci#    left-to-right to determine an ordering.
2097db96d56Sopenharmony_ci# 2) sequences of letters are part of the tuple for comparison and are
2107db96d56Sopenharmony_ci#    compared lexicographically
2117db96d56Sopenharmony_ci# 3) recognize the numeric components may have leading zeroes
2127db96d56Sopenharmony_ci#
2137db96d56Sopenharmony_ci# The LooseVersion class below implements these rules: a version number
2147db96d56Sopenharmony_ci# string is split up into a tuple of integer and string components, and
2157db96d56Sopenharmony_ci# comparison is a simple tuple comparison.  This means that version
2167db96d56Sopenharmony_ci# numbers behave in a predictable and obvious way, but a way that might
2177db96d56Sopenharmony_ci# not necessarily be how people *want* version numbers to behave.  There
2187db96d56Sopenharmony_ci# wouldn't be a problem if people could stick to purely numeric version
2197db96d56Sopenharmony_ci# numbers: just split on period and compare the numbers as tuples.
2207db96d56Sopenharmony_ci# However, people insist on putting letters into their version numbers;
2217db96d56Sopenharmony_ci# the most common purpose seems to be:
2227db96d56Sopenharmony_ci#   - indicating a "pre-release" version
2237db96d56Sopenharmony_ci#     ('alpha', 'beta', 'a', 'b', 'pre', 'p')
2247db96d56Sopenharmony_ci#   - indicating a post-release patch ('p', 'pl', 'patch')
2257db96d56Sopenharmony_ci# but of course this can't cover all version number schemes, and there's
2267db96d56Sopenharmony_ci# no way to know what a programmer means without asking him.
2277db96d56Sopenharmony_ci#
2287db96d56Sopenharmony_ci# The problem is what to do with letters (and other non-numeric
2297db96d56Sopenharmony_ci# characters) in a version number.  The current implementation does the
2307db96d56Sopenharmony_ci# obvious and predictable thing: keep them as strings and compare
2317db96d56Sopenharmony_ci# lexically within a tuple comparison.  This has the desired effect if
2327db96d56Sopenharmony_ci# an appended letter sequence implies something "post-release":
2337db96d56Sopenharmony_ci# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".
2347db96d56Sopenharmony_ci#
2357db96d56Sopenharmony_ci# However, if letters in a version number imply a pre-release version,
2367db96d56Sopenharmony_ci# the "obvious" thing isn't correct.  Eg. you would expect that
2377db96d56Sopenharmony_ci# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison
2387db96d56Sopenharmony_ci# implemented here, this just isn't so.
2397db96d56Sopenharmony_ci#
2407db96d56Sopenharmony_ci# Two possible solutions come to mind.  The first is to tie the
2417db96d56Sopenharmony_ci# comparison algorithm to a particular set of semantic rules, as has
2427db96d56Sopenharmony_ci# been done in the StrictVersion class above.  This works great as long
2437db96d56Sopenharmony_ci# as everyone can go along with bondage and discipline.  Hopefully a
2447db96d56Sopenharmony_ci# (large) subset of Python module programmers will agree that the
2457db96d56Sopenharmony_ci# particular flavour of bondage and discipline provided by StrictVersion
2467db96d56Sopenharmony_ci# provides enough benefit to be worth using, and will submit their
2477db96d56Sopenharmony_ci# version numbering scheme to its domination.  The free-thinking
2487db96d56Sopenharmony_ci# anarchists in the lot will never give in, though, and something needs
2497db96d56Sopenharmony_ci# to be done to accommodate them.
2507db96d56Sopenharmony_ci#
2517db96d56Sopenharmony_ci# Perhaps a "moderately strict" version class could be implemented that
2527db96d56Sopenharmony_ci# lets almost anything slide (syntactically), and makes some heuristic
2537db96d56Sopenharmony_ci# assumptions about non-digits in version number strings.  This could
2547db96d56Sopenharmony_ci# sink into special-case-hell, though; if I was as talented and
2557db96d56Sopenharmony_ci# idiosyncratic as Larry Wall, I'd go ahead and implement a class that
2567db96d56Sopenharmony_ci# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is
2577db96d56Sopenharmony_ci# just as happy dealing with things like "2g6" and "1.13++".  I don't
2587db96d56Sopenharmony_ci# think I'm smart enough to do it right though.
2597db96d56Sopenharmony_ci#
2607db96d56Sopenharmony_ci# In any case, I've coded the test suite for this module (see
2617db96d56Sopenharmony_ci# ../test/test_version.py) specifically to fail on things like comparing
2627db96d56Sopenharmony_ci# "1.2a2" and "1.2".  That's not because the *code* is doing anything
2637db96d56Sopenharmony_ci# wrong, it's because the simple, obvious design doesn't match my
2647db96d56Sopenharmony_ci# complicated, hairy expectations for real-world version numbers.  It
2657db96d56Sopenharmony_ci# would be a snap to fix the test suite to say, "Yep, LooseVersion does
2667db96d56Sopenharmony_ci# the Right Thing" (ie. the code matches the conception).  But I'd rather
2677db96d56Sopenharmony_ci# have a conception that matches common notions about version numbers.
2687db96d56Sopenharmony_ci
2697db96d56Sopenharmony_ciclass LooseVersion (Version):
2707db96d56Sopenharmony_ci
2717db96d56Sopenharmony_ci    """Version numbering for anarchists and software realists.
2727db96d56Sopenharmony_ci    Implements the standard interface for version number classes as
2737db96d56Sopenharmony_ci    described above.  A version number consists of a series of numbers,
2747db96d56Sopenharmony_ci    separated by either periods or strings of letters.  When comparing
2757db96d56Sopenharmony_ci    version numbers, the numeric components will be compared
2767db96d56Sopenharmony_ci    numerically, and the alphabetic components lexically.  The following
2777db96d56Sopenharmony_ci    are all valid version numbers, in no particular order:
2787db96d56Sopenharmony_ci
2797db96d56Sopenharmony_ci        1.5.1
2807db96d56Sopenharmony_ci        1.5.2b2
2817db96d56Sopenharmony_ci        161
2827db96d56Sopenharmony_ci        3.10a
2837db96d56Sopenharmony_ci        8.02
2847db96d56Sopenharmony_ci        3.4j
2857db96d56Sopenharmony_ci        1996.07.12
2867db96d56Sopenharmony_ci        3.2.pl0
2877db96d56Sopenharmony_ci        3.1.1.6
2887db96d56Sopenharmony_ci        2g6
2897db96d56Sopenharmony_ci        11g
2907db96d56Sopenharmony_ci        0.960923
2917db96d56Sopenharmony_ci        2.2beta29
2927db96d56Sopenharmony_ci        1.13++
2937db96d56Sopenharmony_ci        5.5.kw
2947db96d56Sopenharmony_ci        2.0b1pl0
2957db96d56Sopenharmony_ci
2967db96d56Sopenharmony_ci    In fact, there is no such thing as an invalid version number under
2977db96d56Sopenharmony_ci    this scheme; the rules for comparison are simple and predictable,
2987db96d56Sopenharmony_ci    but may not always give the results you want (for some definition
2997db96d56Sopenharmony_ci    of "want").
3007db96d56Sopenharmony_ci    """
3017db96d56Sopenharmony_ci
3027db96d56Sopenharmony_ci    component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)
3037db96d56Sopenharmony_ci
3047db96d56Sopenharmony_ci    def __init__ (self, vstring=None):
3057db96d56Sopenharmony_ci        if vstring:
3067db96d56Sopenharmony_ci            self.parse(vstring)
3077db96d56Sopenharmony_ci
3087db96d56Sopenharmony_ci
3097db96d56Sopenharmony_ci    def parse (self, vstring):
3107db96d56Sopenharmony_ci        # I've given up on thinking I can reconstruct the version string
3117db96d56Sopenharmony_ci        # from the parsed tuple -- so I just store the string here for
3127db96d56Sopenharmony_ci        # use by __str__
3137db96d56Sopenharmony_ci        self.vstring = vstring
3147db96d56Sopenharmony_ci        components = [x for x in self.component_re.split(vstring)
3157db96d56Sopenharmony_ci                              if x and x != '.']
3167db96d56Sopenharmony_ci        for i, obj in enumerate(components):
3177db96d56Sopenharmony_ci            try:
3187db96d56Sopenharmony_ci                components[i] = int(obj)
3197db96d56Sopenharmony_ci            except ValueError:
3207db96d56Sopenharmony_ci                pass
3217db96d56Sopenharmony_ci
3227db96d56Sopenharmony_ci        self.version = components
3237db96d56Sopenharmony_ci
3247db96d56Sopenharmony_ci
3257db96d56Sopenharmony_ci    def __str__ (self):
3267db96d56Sopenharmony_ci        return self.vstring
3277db96d56Sopenharmony_ci
3287db96d56Sopenharmony_ci
3297db96d56Sopenharmony_ci    def __repr__ (self):
3307db96d56Sopenharmony_ci        return "LooseVersion ('%s')" % str(self)
3317db96d56Sopenharmony_ci
3327db96d56Sopenharmony_ci
3337db96d56Sopenharmony_ci    def _cmp (self, other):
3347db96d56Sopenharmony_ci        if isinstance(other, str):
3357db96d56Sopenharmony_ci            other = LooseVersion(other)
3367db96d56Sopenharmony_ci        elif not isinstance(other, LooseVersion):
3377db96d56Sopenharmony_ci            return NotImplemented
3387db96d56Sopenharmony_ci
3397db96d56Sopenharmony_ci        if self.version == other.version:
3407db96d56Sopenharmony_ci            return 0
3417db96d56Sopenharmony_ci        if self.version < other.version:
3427db96d56Sopenharmony_ci            return -1
3437db96d56Sopenharmony_ci        if self.version > other.version:
3447db96d56Sopenharmony_ci            return 1
3457db96d56Sopenharmony_ci
3467db96d56Sopenharmony_ci
3477db96d56Sopenharmony_ci# end class LooseVersion
348