1 | #
|
---|
2 | # distutils/version.py
|
---|
3 | #
|
---|
4 | # Implements multiple version numbering conventions for the
|
---|
5 | # Python Module Distribution Utilities.
|
---|
6 | #
|
---|
7 | # $Id: version.py 71270 2009-04-05 21:11:43Z georg.brandl $
|
---|
8 | #
|
---|
9 |
|
---|
10 | """Provides classes to represent module version numbers (one class for
|
---|
11 | each style of version numbering). There are currently two such classes
|
---|
12 | implemented: StrictVersion and LooseVersion.
|
---|
13 |
|
---|
14 | Every version number class implements the following interface:
|
---|
15 | * the 'parse' method takes a string and parses it to some internal
|
---|
16 | representation; if the string is an invalid version number,
|
---|
17 | 'parse' raises a ValueError exception
|
---|
18 | * the class constructor takes an optional string argument which,
|
---|
19 | if supplied, is passed to 'parse'
|
---|
20 | * __str__ reconstructs the string that was passed to 'parse' (or
|
---|
21 | an equivalent string -- ie. one that will generate an equivalent
|
---|
22 | version number instance)
|
---|
23 | * __repr__ generates Python code to recreate the version number instance
|
---|
24 | * __cmp__ compares the current instance with either another instance
|
---|
25 | of the same class or a string (which will be parsed to an instance
|
---|
26 | of the same class, thus must follow the same rules)
|
---|
27 | """
|
---|
28 |
|
---|
29 | import string, re
|
---|
30 | from types import StringType
|
---|
31 |
|
---|
32 | class Version:
|
---|
33 | """Abstract base class for version numbering classes. Just provides
|
---|
34 | constructor (__init__) and reproducer (__repr__), because those
|
---|
35 | seem to be the same for all version numbering classes.
|
---|
36 | """
|
---|
37 |
|
---|
38 | def __init__ (self, vstring=None):
|
---|
39 | if vstring:
|
---|
40 | self.parse(vstring)
|
---|
41 |
|
---|
42 | def __repr__ (self):
|
---|
43 | return "%s ('%s')" % (self.__class__.__name__, str(self))
|
---|
44 |
|
---|
45 |
|
---|
46 | # Interface for version-number classes -- must be implemented
|
---|
47 | # by the following classes (the concrete ones -- Version should
|
---|
48 | # be treated as an abstract class).
|
---|
49 | # __init__ (string) - create and take same action as 'parse'
|
---|
50 | # (string parameter is optional)
|
---|
51 | # parse (string) - convert a string representation to whatever
|
---|
52 | # internal representation is appropriate for
|
---|
53 | # this style of version numbering
|
---|
54 | # __str__ (self) - convert back to a string; should be very similar
|
---|
55 | # (if not identical to) the string supplied to parse
|
---|
56 | # __repr__ (self) - generate Python code to recreate
|
---|
57 | # the instance
|
---|
58 | # __cmp__ (self, other) - compare two version numbers ('other' may
|
---|
59 | # be an unparsed version string, or another
|
---|
60 | # instance of your version class)
|
---|
61 |
|
---|
62 |
|
---|
63 | class StrictVersion (Version):
|
---|
64 |
|
---|
65 | """Version numbering for anal retentives and software idealists.
|
---|
66 | Implements the standard interface for version number classes as
|
---|
67 | described above. A version number consists of two or three
|
---|
68 | dot-separated numeric components, with an optional "pre-release" tag
|
---|
69 | on the end. The pre-release tag consists of the letter 'a' or 'b'
|
---|
70 | followed by a number. If the numeric components of two version
|
---|
71 | numbers are equal, then one with a pre-release tag will always
|
---|
72 | be deemed earlier (lesser) than one without.
|
---|
73 |
|
---|
74 | The following are valid version numbers (shown in the order that
|
---|
75 | would be obtained by sorting according to the supplied cmp function):
|
---|
76 |
|
---|
77 | 0.4 0.4.0 (these two are equivalent)
|
---|
78 | 0.4.1
|
---|
79 | 0.5a1
|
---|
80 | 0.5b3
|
---|
81 | 0.5
|
---|
82 | 0.9.6
|
---|
83 | 1.0
|
---|
84 | 1.0.4a3
|
---|
85 | 1.0.4b1
|
---|
86 | 1.0.4
|
---|
87 |
|
---|
88 | The following are examples of invalid version numbers:
|
---|
89 |
|
---|
90 | 1
|
---|
91 | 2.7.2.2
|
---|
92 | 1.3.a4
|
---|
93 | 1.3pl1
|
---|
94 | 1.3c4
|
---|
95 |
|
---|
96 | The rationale for this version numbering system will be explained
|
---|
97 | in the distutils documentation.
|
---|
98 | """
|
---|
99 |
|
---|
100 | version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',
|
---|
101 | re.VERBOSE)
|
---|
102 |
|
---|
103 |
|
---|
104 | def parse (self, vstring):
|
---|
105 | match = self.version_re.match(vstring)
|
---|
106 | if not match:
|
---|
107 | raise ValueError, "invalid version number '%s'" % vstring
|
---|
108 |
|
---|
109 | (major, minor, patch, prerelease, prerelease_num) = \
|
---|
110 | match.group(1, 2, 4, 5, 6)
|
---|
111 |
|
---|
112 | if patch:
|
---|
113 | self.version = tuple(map(string.atoi, [major, minor, patch]))
|
---|
114 | else:
|
---|
115 | self.version = tuple(map(string.atoi, [major, minor]) + [0])
|
---|
116 |
|
---|
117 | if prerelease:
|
---|
118 | self.prerelease = (prerelease[0], string.atoi(prerelease_num))
|
---|
119 | else:
|
---|
120 | self.prerelease = None
|
---|
121 |
|
---|
122 |
|
---|
123 | def __str__ (self):
|
---|
124 |
|
---|
125 | if self.version[2] == 0:
|
---|
126 | vstring = string.join(map(str, self.version[0:2]), '.')
|
---|
127 | else:
|
---|
128 | vstring = string.join(map(str, self.version), '.')
|
---|
129 |
|
---|
130 | if self.prerelease:
|
---|
131 | vstring = vstring + self.prerelease[0] + str(self.prerelease[1])
|
---|
132 |
|
---|
133 | return vstring
|
---|
134 |
|
---|
135 |
|
---|
136 | def __cmp__ (self, other):
|
---|
137 | if isinstance(other, StringType):
|
---|
138 | other = StrictVersion(other)
|
---|
139 |
|
---|
140 | compare = cmp(self.version, other.version)
|
---|
141 | if (compare == 0): # have to compare prerelease
|
---|
142 |
|
---|
143 | # case 1: neither has prerelease; they're equal
|
---|
144 | # case 2: self has prerelease, other doesn't; other is greater
|
---|
145 | # case 3: self doesn't have prerelease, other does: self is greater
|
---|
146 | # case 4: both have prerelease: must compare them!
|
---|
147 |
|
---|
148 | if (not self.prerelease and not other.prerelease):
|
---|
149 | return 0
|
---|
150 | elif (self.prerelease and not other.prerelease):
|
---|
151 | return -1
|
---|
152 | elif (not self.prerelease and other.prerelease):
|
---|
153 | return 1
|
---|
154 | elif (self.prerelease and other.prerelease):
|
---|
155 | return cmp(self.prerelease, other.prerelease)
|
---|
156 |
|
---|
157 | else: # numeric versions don't match --
|
---|
158 | return compare # prerelease stuff doesn't matter
|
---|
159 |
|
---|
160 |
|
---|
161 | # end class StrictVersion
|
---|
162 |
|
---|
163 |
|
---|
164 | # The rules according to Greg Stein:
|
---|
165 | # 1) a version number has 1 or more numbers separated by a period or by
|
---|
166 | # sequences of letters. If only periods, then these are compared
|
---|
167 | # left-to-right to determine an ordering.
|
---|
168 | # 2) sequences of letters are part of the tuple for comparison and are
|
---|
169 | # compared lexicographically
|
---|
170 | # 3) recognize the numeric components may have leading zeroes
|
---|
171 | #
|
---|
172 | # The LooseVersion class below implements these rules: a version number
|
---|
173 | # string is split up into a tuple of integer and string components, and
|
---|
174 | # comparison is a simple tuple comparison. This means that version
|
---|
175 | # numbers behave in a predictable and obvious way, but a way that might
|
---|
176 | # not necessarily be how people *want* version numbers to behave. There
|
---|
177 | # wouldn't be a problem if people could stick to purely numeric version
|
---|
178 | # numbers: just split on period and compare the numbers as tuples.
|
---|
179 | # However, people insist on putting letters into their version numbers;
|
---|
180 | # the most common purpose seems to be:
|
---|
181 | # - indicating a "pre-release" version
|
---|
182 | # ('alpha', 'beta', 'a', 'b', 'pre', 'p')
|
---|
183 | # - indicating a post-release patch ('p', 'pl', 'patch')
|
---|
184 | # but of course this can't cover all version number schemes, and there's
|
---|
185 | # no way to know what a programmer means without asking him.
|
---|
186 | #
|
---|
187 | # The problem is what to do with letters (and other non-numeric
|
---|
188 | # characters) in a version number. The current implementation does the
|
---|
189 | # obvious and predictable thing: keep them as strings and compare
|
---|
190 | # lexically within a tuple comparison. This has the desired effect if
|
---|
191 | # an appended letter sequence implies something "post-release":
|
---|
192 | # eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".
|
---|
193 | #
|
---|
194 | # However, if letters in a version number imply a pre-release version,
|
---|
195 | # the "obvious" thing isn't correct. Eg. you would expect that
|
---|
196 | # "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison
|
---|
197 | # implemented here, this just isn't so.
|
---|
198 | #
|
---|
199 | # Two possible solutions come to mind. The first is to tie the
|
---|
200 | # comparison algorithm to a particular set of semantic rules, as has
|
---|
201 | # been done in the StrictVersion class above. This works great as long
|
---|
202 | # as everyone can go along with bondage and discipline. Hopefully a
|
---|
203 | # (large) subset of Python module programmers will agree that the
|
---|
204 | # particular flavour of bondage and discipline provided by StrictVersion
|
---|
205 | # provides enough benefit to be worth using, and will submit their
|
---|
206 | # version numbering scheme to its domination. The free-thinking
|
---|
207 | # anarchists in the lot will never give in, though, and something needs
|
---|
208 | # to be done to accommodate them.
|
---|
209 | #
|
---|
210 | # Perhaps a "moderately strict" version class could be implemented that
|
---|
211 | # lets almost anything slide (syntactically), and makes some heuristic
|
---|
212 | # assumptions about non-digits in version number strings. This could
|
---|
213 | # sink into special-case-hell, though; if I was as talented and
|
---|
214 | # idiosyncratic as Larry Wall, I'd go ahead and implement a class that
|
---|
215 | # somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is
|
---|
216 | # just as happy dealing with things like "2g6" and "1.13++". I don't
|
---|
217 | # think I'm smart enough to do it right though.
|
---|
218 | #
|
---|
219 | # In any case, I've coded the test suite for this module (see
|
---|
220 | # ../test/test_version.py) specifically to fail on things like comparing
|
---|
221 | # "1.2a2" and "1.2". That's not because the *code* is doing anything
|
---|
222 | # wrong, it's because the simple, obvious design doesn't match my
|
---|
223 | # complicated, hairy expectations for real-world version numbers. It
|
---|
224 | # would be a snap to fix the test suite to say, "Yep, LooseVersion does
|
---|
225 | # the Right Thing" (ie. the code matches the conception). But I'd rather
|
---|
226 | # have a conception that matches common notions about version numbers.
|
---|
227 |
|
---|
228 | class LooseVersion (Version):
|
---|
229 |
|
---|
230 | """Version numbering for anarchists and software realists.
|
---|
231 | Implements the standard interface for version number classes as
|
---|
232 | described above. A version number consists of a series of numbers,
|
---|
233 | separated by either periods or strings of letters. When comparing
|
---|
234 | version numbers, the numeric components will be compared
|
---|
235 | numerically, and the alphabetic components lexically. The following
|
---|
236 | are all valid version numbers, in no particular order:
|
---|
237 |
|
---|
238 | 1.5.1
|
---|
239 | 1.5.2b2
|
---|
240 | 161
|
---|
241 | 3.10a
|
---|
242 | 8.02
|
---|
243 | 3.4j
|
---|
244 | 1996.07.12
|
---|
245 | 3.2.pl0
|
---|
246 | 3.1.1.6
|
---|
247 | 2g6
|
---|
248 | 11g
|
---|
249 | 0.960923
|
---|
250 | 2.2beta29
|
---|
251 | 1.13++
|
---|
252 | 5.5.kw
|
---|
253 | 2.0b1pl0
|
---|
254 |
|
---|
255 | In fact, there is no such thing as an invalid version number under
|
---|
256 | this scheme; the rules for comparison are simple and predictable,
|
---|
257 | but may not always give the results you want (for some definition
|
---|
258 | of "want").
|
---|
259 | """
|
---|
260 |
|
---|
261 | component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)
|
---|
262 |
|
---|
263 | def __init__ (self, vstring=None):
|
---|
264 | if vstring:
|
---|
265 | self.parse(vstring)
|
---|
266 |
|
---|
267 |
|
---|
268 | def parse (self, vstring):
|
---|
269 | # I've given up on thinking I can reconstruct the version string
|
---|
270 | # from the parsed tuple -- so I just store the string here for
|
---|
271 | # use by __str__
|
---|
272 | self.vstring = vstring
|
---|
273 | components = filter(lambda x: x and x != '.',
|
---|
274 | self.component_re.split(vstring))
|
---|
275 | for i in range(len(components)):
|
---|
276 | try:
|
---|
277 | components[i] = int(components[i])
|
---|
278 | except ValueError:
|
---|
279 | pass
|
---|
280 |
|
---|
281 | self.version = components
|
---|
282 |
|
---|
283 |
|
---|
284 | def __str__ (self):
|
---|
285 | return self.vstring
|
---|
286 |
|
---|
287 |
|
---|
288 | def __repr__ (self):
|
---|
289 | return "LooseVersion ('%s')" % str(self)
|
---|
290 |
|
---|
291 |
|
---|
292 | def __cmp__ (self, other):
|
---|
293 | if isinstance(other, StringType):
|
---|
294 | other = LooseVersion(other)
|
---|
295 |
|
---|
296 | return cmp(self.version, other.version)
|
---|
297 |
|
---|
298 |
|
---|
299 | # end class LooseVersion
|
---|