ยปCore Development>Code coverage>Lib/distutils/version.py

Python code coverage for Lib/distutils/version.py

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