]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/distutils/version.py
4 # Implements multiple version numbering conventions for the
5 # Python Module Distribution Utilities.
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.
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)
30 from types
import StringType
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.
38 def __init__ (self
, vstring
=None):
43 return "%s ('%s')" % (self
.__class
__.__name
__, str(self
))
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
58 # __cmp__ (self, other) - compare two version numbers ('other' may
59 # be an unparsed version string, or another
60 # instance of your version class)
63 class StrictVersion (Version
):
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.
74 The following are valid version numbers (shown in the order that
75 would be obtained by sorting according to the supplied cmp function):
77 0.4 0.4.0 (these two are equivalent)
88 The following are examples of invalid version numbers:
96 The rationale for this version numbering system will be explained
97 in the distutils documentation.
100 version_re
= re
.compile(r
'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',
104 def parse (self
, vstring
):
105 match
= self
.version_re
.match(vstring
)
107 raise ValueError, "invalid version number '%s'" % vstring
109 (major
, minor
, patch
, prerelease
, prerelease_num
) = \
110 match
.group(1, 2, 4, 5, 6)
113 self
.version
= tuple(map(string
.atoi
, [major
, minor
, patch
]))
115 self
.version
= tuple(map(string
.atoi
, [major
, minor
]) + [0])
118 self
.prerelease
= (prerelease
[0], string
.atoi(prerelease_num
))
120 self
.prerelease
= None
125 if self
.version
[2] == 0:
126 vstring
= string
.join(map(str, self
.version
[0:2]), '.')
128 vstring
= string
.join(map(str, self
.version
), '.')
131 vstring
= vstring
+ self
.prerelease
[0] + str(self
.prerelease
[1])
136 def __cmp__ (self
, other
):
137 if isinstance(other
, StringType
):
138 other
= StrictVersion(other
)
140 compare
= cmp(self
.version
, other
.version
)
141 if (compare
== 0): # have to compare prerelease
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!
148 if (not self
.prerelease
and not other
.prerelease
):
150 elif (self
.prerelease
and not other
.prerelease
):
152 elif (not self
.prerelease
and other
.prerelease
):
154 elif (self
.prerelease
and other
.prerelease
):
155 return cmp(self
.prerelease
, other
.prerelease
)
157 else: # numeric versions don't match --
158 return compare
# prerelease stuff doesn't matter
161 # end class StrictVersion
164 # The rules according to Greg Stein:
165 # 1) a version number has 1 or more numbers separate 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
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.
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".
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.
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.
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.
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.
228 class LooseVersion (Version
):
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:
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
261 component_re
= re
.compile(r
'(\d+ | [a-z]+ | \.)', re
.VERBOSE
)
263 def __init__ (self
, vstring
=None):
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
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
)):
277 components
[i
] = int(components
[i
])
281 self
.version
= components
289 return "LooseVersion ('%s')" % str(self
)
292 def __cmp__ (self
, other
):
293 if isinstance(other
, StringType
):
294 other
= LooseVersion(other
)
296 return cmp(self
.version
, other
.version
)
299 # end class LooseVersion