Path: blob/main/test/lib/python3.9/site-packages/setuptools/_distutils/version.py
4799 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 re29import warnings30import contextlib313233@contextlib.contextmanager34def suppress_known_deprecation():35with warnings.catch_warnings(record=True) as ctx:36warnings.filterwarnings(37action='default',38category=DeprecationWarning,39message="distutils Version classes are deprecated.",40)41yield ctx424344class Version:45"""Abstract base class for version numbering classes. Just provides46constructor (__init__) and reproducer (__repr__), because those47seem to be the same for all version numbering classes; and route48rich comparisons to _cmp.49"""5051def __init__ (self, vstring=None):52if vstring:53self.parse(vstring)54warnings.warn(55"distutils Version classes are deprecated. "56"Use packaging.version instead.",57DeprecationWarning,58stacklevel=2,59)6061def __repr__ (self):62return "%s ('%s')" % (self.__class__.__name__, str(self))6364def __eq__(self, other):65c = self._cmp(other)66if c is NotImplemented:67return c68return c == 06970def __lt__(self, other):71c = self._cmp(other)72if c is NotImplemented:73return c74return c < 07576def __le__(self, other):77c = self._cmp(other)78if c is NotImplemented:79return c80return c <= 08182def __gt__(self, other):83c = self._cmp(other)84if c is NotImplemented:85return c86return c > 08788def __ge__(self, other):89c = self._cmp(other)90if c is NotImplemented:91return c92return c >= 0939495# Interface for version-number classes -- must be implemented96# by the following classes (the concrete ones -- Version should97# be treated as an abstract class).98# __init__ (string) - create and take same action as 'parse'99# (string parameter is optional)100# parse (string) - convert a string representation to whatever101# internal representation is appropriate for102# this style of version numbering103# __str__ (self) - convert back to a string; should be very similar104# (if not identical to) the string supplied to parse105# __repr__ (self) - generate Python code to recreate106# the instance107# _cmp (self, other) - compare two version numbers ('other' may108# be an unparsed version string, or another109# instance of your version class)110111112class StrictVersion (Version):113114"""Version numbering for anal retentives and software idealists.115Implements the standard interface for version number classes as116described above. A version number consists of two or three117dot-separated numeric components, with an optional "pre-release" tag118on the end. The pre-release tag consists of the letter 'a' or 'b'119followed by a number. If the numeric components of two version120numbers are equal, then one with a pre-release tag will always121be deemed earlier (lesser) than one without.122123The following are valid version numbers (shown in the order that124would be obtained by sorting according to the supplied cmp function):1251260.4 0.4.0 (these two are equivalent)1270.4.11280.5a11290.5b31300.51310.9.61321.01331.0.4a31341.0.4b11351.0.4136137The following are examples of invalid version numbers:13813911402.7.2.21411.3.a41421.3pl11431.3c4144145The rationale for this version numbering system will be explained146in the distutils documentation.147"""148149version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',150re.VERBOSE | re.ASCII)151152153def parse (self, vstring):154match = self.version_re.match(vstring)155if not match:156raise ValueError("invalid version number '%s'" % vstring)157158(major, minor, patch, prerelease, prerelease_num) = \159match.group(1, 2, 4, 5, 6)160161if patch:162self.version = tuple(map(int, [major, minor, patch]))163else:164self.version = tuple(map(int, [major, minor])) + (0,)165166if prerelease:167self.prerelease = (prerelease[0], int(prerelease_num))168else:169self.prerelease = None170171172def __str__ (self):173174if self.version[2] == 0:175vstring = '.'.join(map(str, self.version[0:2]))176else:177vstring = '.'.join(map(str, self.version))178179if self.prerelease:180vstring = vstring + self.prerelease[0] + str(self.prerelease[1])181182return vstring183184185def _cmp (self, other):186if isinstance(other, str):187with suppress_known_deprecation():188other = StrictVersion(other)189elif not isinstance(other, StrictVersion):190return NotImplemented191192if self.version != other.version:193# numeric versions don't match194# prerelease stuff doesn't matter195if self.version < other.version:196return -1197else:198return 1199200# have to compare prerelease201# case 1: neither has prerelease; they're equal202# case 2: self has prerelease, other doesn't; other is greater203# case 3: self doesn't have prerelease, other does: self is greater204# case 4: both have prerelease: must compare them!205206if (not self.prerelease and not other.prerelease):207return 0208elif (self.prerelease and not other.prerelease):209return -1210elif (not self.prerelease and other.prerelease):211return 1212elif (self.prerelease and other.prerelease):213if self.prerelease == other.prerelease:214return 0215elif self.prerelease < other.prerelease:216return -1217else:218return 1219else:220assert False, "never get here"221222# end class StrictVersion223224225# The rules according to Greg Stein:226# 1) a version number has 1 or more numbers separated by a period or by227# sequences of letters. If only periods, then these are compared228# left-to-right to determine an ordering.229# 2) sequences of letters are part of the tuple for comparison and are230# compared lexicographically231# 3) recognize the numeric components may have leading zeroes232#233# The LooseVersion class below implements these rules: a version number234# string is split up into a tuple of integer and string components, and235# comparison is a simple tuple comparison. This means that version236# numbers behave in a predictable and obvious way, but a way that might237# not necessarily be how people *want* version numbers to behave. There238# wouldn't be a problem if people could stick to purely numeric version239# numbers: just split on period and compare the numbers as tuples.240# However, people insist on putting letters into their version numbers;241# the most common purpose seems to be:242# - indicating a "pre-release" version243# ('alpha', 'beta', 'a', 'b', 'pre', 'p')244# - indicating a post-release patch ('p', 'pl', 'patch')245# but of course this can't cover all version number schemes, and there's246# no way to know what a programmer means without asking him.247#248# The problem is what to do with letters (and other non-numeric249# characters) in a version number. The current implementation does the250# obvious and predictable thing: keep them as strings and compare251# lexically within a tuple comparison. This has the desired effect if252# an appended letter sequence implies something "post-release":253# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".254#255# However, if letters in a version number imply a pre-release version,256# the "obvious" thing isn't correct. Eg. you would expect that257# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison258# implemented here, this just isn't so.259#260# Two possible solutions come to mind. The first is to tie the261# comparison algorithm to a particular set of semantic rules, as has262# been done in the StrictVersion class above. This works great as long263# as everyone can go along with bondage and discipline. Hopefully a264# (large) subset of Python module programmers will agree that the265# particular flavour of bondage and discipline provided by StrictVersion266# provides enough benefit to be worth using, and will submit their267# version numbering scheme to its domination. The free-thinking268# anarchists in the lot will never give in, though, and something needs269# to be done to accommodate them.270#271# Perhaps a "moderately strict" version class could be implemented that272# lets almost anything slide (syntactically), and makes some heuristic273# assumptions about non-digits in version number strings. This could274# sink into special-case-hell, though; if I was as talented and275# idiosyncratic as Larry Wall, I'd go ahead and implement a class that276# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is277# just as happy dealing with things like "2g6" and "1.13++". I don't278# think I'm smart enough to do it right though.279#280# In any case, I've coded the test suite for this module (see281# ../test/test_version.py) specifically to fail on things like comparing282# "1.2a2" and "1.2". That's not because the *code* is doing anything283# wrong, it's because the simple, obvious design doesn't match my284# complicated, hairy expectations for real-world version numbers. It285# would be a snap to fix the test suite to say, "Yep, LooseVersion does286# the Right Thing" (ie. the code matches the conception). But I'd rather287# have a conception that matches common notions about version numbers.288289class LooseVersion (Version):290291"""Version numbering for anarchists and software realists.292Implements the standard interface for version number classes as293described above. A version number consists of a series of numbers,294separated by either periods or strings of letters. When comparing295version numbers, the numeric components will be compared296numerically, and the alphabetic components lexically. The following297are all valid version numbers, in no particular order:2982991.5.13001.5.2b23011613023.10a3038.023043.4j3051996.07.123063.2.pl03073.1.1.63082g630911g3100.9609233112.2beta293121.13++3135.5.kw3142.0b1pl0315316In fact, there is no such thing as an invalid version number under317this scheme; the rules for comparison are simple and predictable,318but may not always give the results you want (for some definition319of "want").320"""321322component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)323324def parse (self, vstring):325# I've given up on thinking I can reconstruct the version string326# from the parsed tuple -- so I just store the string here for327# use by __str__328self.vstring = vstring329components = [x for x in self.component_re.split(vstring)330if x and x != '.']331for i, obj in enumerate(components):332try:333components[i] = int(obj)334except ValueError:335pass336337self.version = components338339340def __str__ (self):341return self.vstring342343344def __repr__ (self):345return "LooseVersion ('%s')" % str(self)346347348def _cmp (self, other):349if isinstance(other, str):350other = LooseVersion(other)351elif not isinstance(other, LooseVersion):352return NotImplemented353354if self.version == other.version:355return 0356if self.version < other.version:357return -1358if self.version > other.version:359return 1360361362# end class LooseVersion363364365