Path: blob/master/venv/Lib/site-packages/setuptools/_distutils/version.py
811 views
#1# distutils/version.py2#3# Implements multiple version numbering conventions for the4# Python Module Distribution Utilities.5#6# $Id$7#89"""Provides classes to represent module version numbers (one class for10each style of version numbering). There are currently two such classes11implemented: StrictVersion and LooseVersion.1213Every version number class implements the following interface:14* the 'parse' method takes a string and parses it to some internal15representation; if the string is an invalid version number,16'parse' raises a ValueError exception17* the class constructor takes an optional string argument which,18if supplied, is passed to 'parse'19* __str__ reconstructs the string that was passed to 'parse' (or20an equivalent string -- ie. one that will generate an equivalent21version number instance)22* __repr__ generates Python code to recreate the version number instance23* _cmp compares the current instance with either another instance24of the same class or a string (which will be parsed to an instance25of the same class, thus must follow the same rules)26"""2728import re2930class Version:31"""Abstract base class for version numbering classes. Just provides32constructor (__init__) and reproducer (__repr__), because those33seem to be the same for all version numbering classes; and route34rich comparisons to _cmp.35"""3637def __init__ (self, vstring=None):38if vstring:39self.parse(vstring)4041def __repr__ (self):42return "%s ('%s')" % (self.__class__.__name__, str(self))4344def __eq__(self, other):45c = self._cmp(other)46if c is NotImplemented:47return c48return c == 04950def __lt__(self, other):51c = self._cmp(other)52if c is NotImplemented:53return c54return c < 05556def __le__(self, other):57c = self._cmp(other)58if c is NotImplemented:59return c60return c <= 06162def __gt__(self, other):63c = self._cmp(other)64if c is NotImplemented:65return c66return c > 06768def __ge__(self, other):69c = self._cmp(other)70if c is NotImplemented:71return c72return c >= 0737475# Interface for version-number classes -- must be implemented76# by the following classes (the concrete ones -- Version should77# be treated as an abstract class).78# __init__ (string) - create and take same action as 'parse'79# (string parameter is optional)80# parse (string) - convert a string representation to whatever81# internal representation is appropriate for82# this style of version numbering83# __str__ (self) - convert back to a string; should be very similar84# (if not identical to) the string supplied to parse85# __repr__ (self) - generate Python code to recreate86# the instance87# _cmp (self, other) - compare two version numbers ('other' may88# be an unparsed version string, or another89# instance of your version class)909192class StrictVersion (Version):9394"""Version numbering for anal retentives and software idealists.95Implements the standard interface for version number classes as96described above. A version number consists of two or three97dot-separated numeric components, with an optional "pre-release" tag98on the end. The pre-release tag consists of the letter 'a' or 'b'99followed by a number. If the numeric components of two version100numbers are equal, then one with a pre-release tag will always101be deemed earlier (lesser) than one without.102103The following are valid version numbers (shown in the order that104would be obtained by sorting according to the supplied cmp function):1051060.4 0.4.0 (these two are equivalent)1070.4.11080.5a11090.5b31100.51110.9.61121.01131.0.4a31141.0.4b11151.0.4116117The following are examples of invalid version numbers:11811911202.7.2.21211.3.a41221.3pl11231.3c4124125The rationale for this version numbering system will be explained126in the distutils documentation.127"""128129version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',130re.VERBOSE | re.ASCII)131132133def parse (self, vstring):134match = self.version_re.match(vstring)135if not match:136raise ValueError("invalid version number '%s'" % vstring)137138(major, minor, patch, prerelease, prerelease_num) = \139match.group(1, 2, 4, 5, 6)140141if patch:142self.version = tuple(map(int, [major, minor, patch]))143else:144self.version = tuple(map(int, [major, minor])) + (0,)145146if prerelease:147self.prerelease = (prerelease[0], int(prerelease_num))148else:149self.prerelease = None150151152def __str__ (self):153154if self.version[2] == 0:155vstring = '.'.join(map(str, self.version[0:2]))156else:157vstring = '.'.join(map(str, self.version))158159if self.prerelease:160vstring = vstring + self.prerelease[0] + str(self.prerelease[1])161162return vstring163164165def _cmp (self, other):166if isinstance(other, str):167other = StrictVersion(other)168elif not isinstance(other, StrictVersion):169return NotImplemented170171if self.version != other.version:172# numeric versions don't match173# prerelease stuff doesn't matter174if self.version < other.version:175return -1176else:177return 1178179# have to compare prerelease180# case 1: neither has prerelease; they're equal181# case 2: self has prerelease, other doesn't; other is greater182# case 3: self doesn't have prerelease, other does: self is greater183# case 4: both have prerelease: must compare them!184185if (not self.prerelease and not other.prerelease):186return 0187elif (self.prerelease and not other.prerelease):188return -1189elif (not self.prerelease and other.prerelease):190return 1191elif (self.prerelease and other.prerelease):192if self.prerelease == other.prerelease:193return 0194elif self.prerelease < other.prerelease:195return -1196else:197return 1198else:199assert False, "never get here"200201# end class StrictVersion202203204# The rules according to Greg Stein:205# 1) a version number has 1 or more numbers separated by a period or by206# sequences of letters. If only periods, then these are compared207# left-to-right to determine an ordering.208# 2) sequences of letters are part of the tuple for comparison and are209# compared lexicographically210# 3) recognize the numeric components may have leading zeroes211#212# The LooseVersion class below implements these rules: a version number213# string is split up into a tuple of integer and string components, and214# comparison is a simple tuple comparison. This means that version215# numbers behave in a predictable and obvious way, but a way that might216# not necessarily be how people *want* version numbers to behave. There217# wouldn't be a problem if people could stick to purely numeric version218# numbers: just split on period and compare the numbers as tuples.219# However, people insist on putting letters into their version numbers;220# the most common purpose seems to be:221# - indicating a "pre-release" version222# ('alpha', 'beta', 'a', 'b', 'pre', 'p')223# - indicating a post-release patch ('p', 'pl', 'patch')224# but of course this can't cover all version number schemes, and there's225# no way to know what a programmer means without asking him.226#227# The problem is what to do with letters (and other non-numeric228# characters) in a version number. The current implementation does the229# obvious and predictable thing: keep them as strings and compare230# lexically within a tuple comparison. This has the desired effect if231# an appended letter sequence implies something "post-release":232# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".233#234# However, if letters in a version number imply a pre-release version,235# the "obvious" thing isn't correct. Eg. you would expect that236# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison237# implemented here, this just isn't so.238#239# Two possible solutions come to mind. The first is to tie the240# comparison algorithm to a particular set of semantic rules, as has241# been done in the StrictVersion class above. This works great as long242# as everyone can go along with bondage and discipline. Hopefully a243# (large) subset of Python module programmers will agree that the244# particular flavour of bondage and discipline provided by StrictVersion245# provides enough benefit to be worth using, and will submit their246# version numbering scheme to its domination. The free-thinking247# anarchists in the lot will never give in, though, and something needs248# to be done to accommodate them.249#250# Perhaps a "moderately strict" version class could be implemented that251# lets almost anything slide (syntactically), and makes some heuristic252# assumptions about non-digits in version number strings. This could253# sink into special-case-hell, though; if I was as talented and254# idiosyncratic as Larry Wall, I'd go ahead and implement a class that255# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is256# just as happy dealing with things like "2g6" and "1.13++". I don't257# think I'm smart enough to do it right though.258#259# In any case, I've coded the test suite for this module (see260# ../test/test_version.py) specifically to fail on things like comparing261# "1.2a2" and "1.2". That's not because the *code* is doing anything262# wrong, it's because the simple, obvious design doesn't match my263# complicated, hairy expectations for real-world version numbers. It264# would be a snap to fix the test suite to say, "Yep, LooseVersion does265# the Right Thing" (ie. the code matches the conception). But I'd rather266# have a conception that matches common notions about version numbers.267268class LooseVersion (Version):269270"""Version numbering for anarchists and software realists.271Implements the standard interface for version number classes as272described above. A version number consists of a series of numbers,273separated by either periods or strings of letters. When comparing274version numbers, the numeric components will be compared275numerically, and the alphabetic components lexically. The following276are all valid version numbers, in no particular order:2772781.5.12791.5.2b22801612813.10a2828.022833.4j2841996.07.122853.2.pl02863.1.1.62872g628811g2890.9609232902.2beta292911.13++2925.5.kw2932.0b1pl0294295In fact, there is no such thing as an invalid version number under296this scheme; the rules for comparison are simple and predictable,297but may not always give the results you want (for some definition298of "want").299"""300301component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)302303def __init__ (self, vstring=None):304if vstring:305self.parse(vstring)306307308def parse (self, vstring):309# I've given up on thinking I can reconstruct the version string310# from the parsed tuple -- so I just store the string here for311# use by __str__312self.vstring = vstring313components = [x for x in self.component_re.split(vstring)314if x and x != '.']315for i, obj in enumerate(components):316try:317components[i] = int(obj)318except ValueError:319pass320321self.version = components322323324def __str__ (self):325return self.vstring326327328def __repr__ (self):329return "LooseVersion ('%s')" % str(self)330331332def _cmp (self, other):333if isinstance(other, str):334other = LooseVersion(other)335elif not isinstance(other, LooseVersion):336return NotImplemented337338if self.version == other.version:339return 0340if self.version < other.version:341return -1342if self.version > other.version:343return 1344345346# end class LooseVersion347348349