0001 # 0002 # distutils/version.py 0003 # 0004 # Implements multiple version numbering conventions for the 0005 # Python Module Distribution Utilities. 0006 # 0007 # $Id: version.py,v 1.7 2002/11/14 02:25:42 akuchling Exp $ 0008 # 0009 0010 """Provides classes to represent module version numbers (one class for 0011 each style of version numbering). There are currently two such classes 0012 implemented: StrictVersion and LooseVersion. 0013 0014 Every version number class implements the following interface: 0015 * the 'parse' method takes a string and parses it to some internal 0016 representation; if the string is an invalid version number, 0017 'parse' raises a ValueError exception 0018 * the class constructor takes an optional string argument which, 0019 if supplied, is passed to 'parse' 0020 * __str__ reconstructs the string that was passed to 'parse' (or 0021 an equivalent string -- ie. one that will generate an equivalent 0022 version number instance) 0023 * __repr__ generates Python code to recreate the version number instance 0024 * __cmp__ compares the current instance with either another instance 0025 of the same class or a string (which will be parsed to an instance 0026 of the same class, thus must follow the same rules) 0027 """ 0028 0029 import string, re 0030 from types import StringType 0031 0032 class Version: 0033 """Abstract base class for version numbering classes. Just provides 0034 constructor (__init__) and reproducer (__repr__), because those 0035 seem to be the same for all version numbering classes. 0036 """ 0037 0038 def __init__ (self, vstring=None): 0039 if vstring: 0040 self.parse(vstring) 0041 0042 def __repr__ (self): 0043 return "%s ('%s')" % (self.__class__.__name__, str(self)) 0044 0045 0046 # Interface for version-number classes -- must be implemented 0047 # by the following classes (the concrete ones -- Version should 0048 # be treated as an abstract class). 0049 # __init__ (string) - create and take same action as 'parse' 0050 # (string parameter is optional) 0051 # parse (string) - convert a string representation to whatever 0052 # internal representation is appropriate for 0053 # this style of version numbering 0054 # __str__ (self) - convert back to a string; should be very similar 0055 # (if not identical to) the string supplied to parse 0056 # __repr__ (self) - generate Python code to recreate 0057 # the instance 0058 # __cmp__ (self, other) - compare two version numbers ('other' may 0059 # be an unparsed version string, or another 0060 # instance of your version class) 0061 0062 0063 class StrictVersion (Version): 0064 0065 """Version numbering for anal retentives and software idealists. 0066 Implements the standard interface for version number classes as 0067 described above. A version number consists of two or three 0068 dot-separated numeric components, with an optional "pre-release" tag 0069 on the end. The pre-release tag consists of the letter 'a' or 'b' 0070 followed by a number. If the numeric components of two version 0071 numbers are equal, then one with a pre-release tag will always 0072 be deemed earlier (lesser) than one without. 0073 0074 The following are valid version numbers (shown in the order that 0075 would be obtained by sorting according to the supplied cmp function): 0076 0077 0.4 0.4.0 (these two are equivalent) 0078 0.4.1 0079 0.5a1 0080 0.5b3 0081 0.5 0082 0.9.6 0083 1.0 0084 1.0.4a3 0085 1.0.4b1 0086 1.0.4 0087 0088 The following are examples of invalid version numbers: 0089 0090 1 0091 2.7.2.2 0092 1.3.a4 0093 1.3pl1 0094 1.3c4 0095 0096 The rationale for this version numbering system will be explained 0097 in the distutils documentation. 0098 """ 0099 0100 version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$', 0101 re.VERBOSE) 0102 0103 0104 def parse (self, vstring): 0105 match = self.version_re.match(vstring) 0106 if not match: 0107 raise ValueError, "invalid version number '%s'" % vstring 0108 0109 (major, minor, patch, prerelease, prerelease_num) = \ 0110 match.group(1, 2, 4, 5, 6) 0111 0112 if patch: 0113 self.version = tuple(map(string.atoi, [major, minor, patch])) 0114 else: 0115 self.version = tuple(map(string.atoi, [major, minor]) + [0]) 0116 0117 if prerelease: 0118 self.prerelease = (prerelease[0], string.atoi(prerelease_num)) 0119 else: 0120 self.prerelease = None 0121 0122 0123 def __str__ (self): 0124 0125 if self.version[2] == 0: 0126 vstring = string.join(map(str, self.version[0:2]), '.') 0127 else: 0128 vstring = string.join(map(str, self.version), '.') 0129 0130 if self.prerelease: 0131 vstring = vstring + self.prerelease[0] + str(self.prerelease[1]) 0132 0133 return vstring 0134 0135 0136 def __cmp__ (self, other): 0137 if isinstance(other, StringType): 0138 other = StrictVersion(other) 0139 0140 compare = cmp(self.version, other.version) 0141 if (compare == 0): # have to compare prerelease 0142 0143 # case 1: neither has prerelease; they're equal 0144 # case 2: self has prerelease, other doesn't; other is greater 0145 # case 3: self doesn't have prerelease, other does: self is greater 0146 # case 4: both have prerelease: must compare them! 0147 0148 if (not self.prerelease and not other.prerelease): 0149 return 0 0150 elif (self.prerelease and not other.prerelease): 0151 return -1 0152 elif (not self.prerelease and other.prerelease): 0153 return 1 0154 elif (self.prerelease and other.prerelease): 0155 return cmp(self.prerelease, other.prerelease) 0156 0157 else: # numeric versions don't match -- 0158 return compare # prerelease stuff doesn't matter 0159 0160 0161 # end class StrictVersion 0162 0163 0164 # The rules according to Greg Stein: 0165 # 1) a version number has 1 or more numbers separate by a period or by 0166 # sequences of letters. If only periods, then these are compared 0167 # left-to-right to determine an ordering. 0168 # 2) sequences of letters are part of the tuple for comparison and are 0169 # compared lexicographically 0170 # 3) recognize the numeric components may have leading zeroes 0171 # 0172 # The LooseVersion class below implements these rules: a version number 0173 # string is split up into a tuple of integer and string components, and 0174 # comparison is a simple tuple comparison. This means that version 0175 # numbers behave in a predictable and obvious way, but a way that might 0176 # not necessarily be how people *want* version numbers to behave. There 0177 # wouldn't be a problem if people could stick to purely numeric version 0178 # numbers: just split on period and compare the numbers as tuples. 0179 # However, people insist on putting letters into their version numbers; 0180 # the most common purpose seems to be: 0181 # - indicating a "pre-release" version 0182 # ('alpha', 'beta', 'a', 'b', 'pre', 'p') 0183 # - indicating a post-release patch ('p', 'pl', 'patch') 0184 # but of course this can't cover all version number schemes, and there's 0185 # no way to know what a programmer means without asking him. 0186 # 0187 # The problem is what to do with letters (and other non-numeric 0188 # characters) in a version number. The current implementation does the 0189 # obvious and predictable thing: keep them as strings and compare 0190 # lexically within a tuple comparison. This has the desired effect if 0191 # an appended letter sequence implies something "post-release": 0192 # eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002". 0193 # 0194 # However, if letters in a version number imply a pre-release version, 0195 # the "obvious" thing isn't correct. Eg. you would expect that 0196 # "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison 0197 # implemented here, this just isn't so. 0198 # 0199 # Two possible solutions come to mind. The first is to tie the 0200 # comparison algorithm to a particular set of semantic rules, as has 0201 # been done in the StrictVersion class above. This works great as long 0202 # as everyone can go along with bondage and discipline. Hopefully a 0203 # (large) subset of Python module programmers will agree that the 0204 # particular flavour of bondage and discipline provided by StrictVersion 0205 # provides enough benefit to be worth using, and will submit their 0206 # version numbering scheme to its domination. The free-thinking 0207 # anarchists in the lot will never give in, though, and something needs 0208 # to be done to accommodate them. 0209 # 0210 # Perhaps a "moderately strict" version class could be implemented that 0211 # lets almost anything slide (syntactically), and makes some heuristic 0212 # assumptions about non-digits in version number strings. This could 0213 # sink into special-case-hell, though; if I was as talented and 0214 # idiosyncratic as Larry Wall, I'd go ahead and implement a class that 0215 # somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is 0216 # just as happy dealing with things like "2g6" and "1.13++". I don't 0217 # think I'm smart enough to do it right though. 0218 # 0219 # In any case, I've coded the test suite for this module (see 0220 # ../test/test_version.py) specifically to fail on things like comparing 0221 # "1.2a2" and "1.2". That's not because the *code* is doing anything 0222 # wrong, it's because the simple, obvious design doesn't match my 0223 # complicated, hairy expectations for real-world version numbers. It 0224 # would be a snap to fix the test suite to say, "Yep, LooseVersion does 0225 # the Right Thing" (ie. the code matches the conception). But I'd rather 0226 # have a conception that matches common notions about version numbers. 0227 0228 class LooseVersion (Version): 0229 0230 """Version numbering for anarchists and software realists. 0231 Implements the standard interface for version number classes as 0232 described above. A version number consists of a series of numbers, 0233 separated by either periods or strings of letters. When comparing 0234 version numbers, the numeric components will be compared 0235 numerically, and the alphabetic components lexically. The following 0236 are all valid version numbers, in no particular order: 0237 0238 1.5.1 0239 1.5.2b2 0240 161 0241 3.10a 0242 8.02 0243 3.4j 0244 1996.07.12 0245 3.2.pl0 0246 3.1.1.6 0247 2g6 0248 11g 0249 0.960923 0250 2.2beta29 0251 1.13++ 0252 5.5.kw 0253 2.0b1pl0 0254 0255 In fact, there is no such thing as an invalid version number under 0256 this scheme; the rules for comparison are simple and predictable, 0257 but may not always give the results you want (for some definition 0258 of "want"). 0259 """ 0260 0261 component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE) 0262 0263 def __init__ (self, vstring=None): 0264 if vstring: 0265 self.parse(vstring) 0266 0267 0268 def parse (self, vstring): 0269 # I've given up on thinking I can reconstruct the version string 0270 # from the parsed tuple -- so I just store the string here for 0271 # use by __str__ 0272 self.vstring = vstring 0273 components = filter(lambda x: x and x != '.', 0274 self.component_re.split(vstring)) 0275 for i in range(len(components)): 0276 try: 0277 components[i] = int(components[i]) 0278 except ValueError: 0279 pass 0280 0281 self.version = components 0282 0283 0284 def __str__ (self): 0285 return self.vstring 0286 0287 0288 def __repr__ (self): 0289 return "LooseVersion ('%s')" % str(self) 0290 0291 0292 def __cmp__ (self, other): 0293 if isinstance(other, StringType): 0294 other = LooseVersion(other) 0295 0296 return cmp(self.version, other.version) 0297 0298 0299 # end class LooseVersion 0300
Generated by PyXR 0.9.4