PyXR

c:\python24\lib \ distutils \ version.py



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
SourceForge.net Logo