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