OLD | NEW |
---|---|
(Empty) | |
1 # versions.py -- core Portage functionality | |
sosa
2010/11/12 21:28:40
This is no longer needed.
davidjames
2010/11/12 21:37:50
Done.
| |
2 # Copyright 1998-2006 Gentoo Foundation | |
3 # Distributed under the terms of the GNU General Public License v2 | |
4 # $Id: versions.py 15234 2010-01-29 18:45:23Z zmedico $ | |
5 | |
6 import re | |
7 | |
8 | |
9 # \w is [a-zA-Z0-9_] | |
10 | |
11 # 2.1.1 A category name may contain any of the characters [A-Za-z0-9+_.-]. | |
12 # It must not begin with a hyphen or a dot. | |
13 _cat = r'[\w+][\w+.-]*' | |
14 | |
15 # 2.1.2 A package name may contain any of the characters [A-Za-z0-9+_-]. | |
16 # It must not begin with a hyphen, | |
17 # and must not end in a hyphen followed by one or more digits. | |
18 _pkg = r'[\w+][\w+-]*?' | |
19 | |
20 _v = r'(cvs\.)?(\d+)((\.\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\d*)*)' | |
21 _rev = r'\d+' | |
22 _vr = _v + '(-r(' + _rev + '))?' | |
23 | |
24 _cp = '(' + _cat + '/' + _pkg + '(-' + _vr + ')?)' | |
25 _cpv = '(' + _cp + '-' + _vr + ')' | |
26 _pv = '(?P<pn>' + _pkg + '(?P<pn_inval>-' + _vr + ')?)' + '-(?P<ver>' + _v + ')( -r(?P<rev>' + _rev + '))?' | |
27 | |
28 ver_regexp = re.compile("^" + _vr + "$") | |
29 suffix_regexp = re.compile("^(alpha|beta|rc|pre|p)(\\d*)$") | |
30 suffix_value = {"pre": -2, "p": 0, "alpha": -4, "beta": -3, "rc": -1} | |
31 endversion_keys = ["pre", "p", "alpha", "beta", "rc"] | |
32 | |
33 def ververify(myver, silent=1): | |
34 if ver_regexp.match(myver): | |
35 return 1 | |
36 else: | |
37 if not silent: | |
38 print("!!! syntax error in version: %s" % myver) | |
39 return 0 | |
40 | |
41 vercmp_cache = {} | |
42 def vercmp(ver1, ver2, silent=1): | |
43 """ | |
44 Compare two versions | |
45 Example usage: | |
46 >>> from portage.versions import vercmp | |
47 >>> vercmp('1.0-r1','1.2-r3') | |
48 negative number | |
49 >>> vercmp('1.3','1.2-r3') | |
50 positive number | |
51 >>> vercmp('1.0_p3','1.0_p3') | |
52 0 | |
53 | |
54 @param pkg1: version to compare with (see ver_regexp in portage.versions .py) | |
55 @type pkg1: string (example: "2.1.2-r3") | |
56 @param pkg2: version to compare againts (see ver_regexp in portage.versi ons.py) | |
57 @type pkg2: string (example: "2.1.2_rc5") | |
58 @rtype: None or float | |
59 @return: | |
60 1. positive if ver1 is greater than ver2 | |
61 2. negative if ver1 is less than ver2 | |
62 3. 0 if ver1 equals ver2 | |
63 4. None if ver1 or ver2 are invalid (see ver_regexp in portage.versions. py) | |
64 """ | |
65 | |
66 if ver1 == ver2: | |
67 return 0 | |
68 mykey=ver1+":"+ver2 | |
69 try: | |
70 return vercmp_cache[mykey] | |
71 except KeyError: | |
72 pass | |
73 match1 = ver_regexp.match(ver1) | |
74 match2 = ver_regexp.match(ver2) | |
75 | |
76 # checking that the versions are valid | |
77 if not match1 or not match1.groups(): | |
78 if not silent: | |
79 print("!!! syntax error in version: %s" % ver1) | |
80 return None | |
81 if not match2 or not match2.groups(): | |
82 if not silent: | |
83 print("!!! syntax error in version: %s" % ver2) | |
84 return None | |
85 | |
86 # shortcut for cvs ebuilds (new style) | |
87 if match1.group(1) and not match2.group(1): | |
88 vercmp_cache[mykey] = 1 | |
89 return 1 | |
90 elif match2.group(1) and not match1.group(1): | |
91 vercmp_cache[mykey] = -1 | |
92 return -1 | |
93 | |
94 # building lists of the version parts before the suffix | |
95 # first part is simple | |
96 list1 = [int(match1.group(2))] | |
97 list2 = [int(match2.group(2))] | |
98 | |
99 # this part would greatly benefit from a fixed-length version pattern | |
100 if match1.group(3) or match2.group(3): | |
101 vlist1 = match1.group(3)[1:].split(".") | |
102 vlist2 = match2.group(3)[1:].split(".") | |
103 | |
104 for i in range(0, max(len(vlist1), len(vlist2))): | |
105 # Implcit .0 is given a value of -1, so that 1.0.0 > 1.0 , since it | |
106 # would be ambiguous if two versions that aren't literal ly equal | |
107 # are given the same value (in sorting, for example). | |
108 if len(vlist1) <= i or len(vlist1[i]) == 0: | |
109 list1.append(-1) | |
110 list2.append(int(vlist2[i])) | |
111 elif len(vlist2) <= i or len(vlist2[i]) == 0: | |
112 list1.append(int(vlist1[i])) | |
113 list2.append(-1) | |
114 # Let's make life easy and use integers unless we're for ced to use floats | |
115 elif (vlist1[i][0] != "0" and vlist2[i][0] != "0"): | |
116 list1.append(int(vlist1[i])) | |
117 list2.append(int(vlist2[i])) | |
118 # now we have to use floats so 1.02 compares correctly a gainst 1.1 | |
119 else: | |
120 # list1.append(float("0."+vlist1[i])) | |
121 # list2.append(float("0."+vlist2[i])) | |
122 # Since python floats have limited range, we mul tiply both | |
123 # floating point representations by a constant s o that they are | |
124 # transformed into whole numbers. This allows th e practically | |
125 # infinite range of a python int to be exploited . The | |
126 # multiplication is done by padding both literal strings with | |
127 # zeros as necessary to ensure equal length. | |
128 max_len = max(len(vlist1[i]), len(vlist2[i])) | |
129 list1.append(int(vlist1[i].ljust(max_len, "0"))) | |
130 list2.append(int(vlist2[i].ljust(max_len, "0"))) | |
131 | |
132 # and now the final letter | |
133 # NOTE: Behavior changed in r2309 (between portage-2.0.x and portage-2.1 ). | |
134 # The new behavior is 12.2.5 > 12.2b which, depending on how you look at , | |
135 # may seem counter-intuitive. However, if you really think about it, it | |
136 # seems like it's probably safe to assume that this is the behavior that | |
137 # is intended by anyone who would use versions such as these. | |
138 if len(match1.group(5)): | |
139 list1.append(ord(match1.group(5))) | |
140 if len(match2.group(5)): | |
141 list2.append(ord(match2.group(5))) | |
142 | |
143 for i in range(0, max(len(list1), len(list2))): | |
144 if len(list1) <= i: | |
145 vercmp_cache[mykey] = -1 | |
146 return -1 | |
147 elif len(list2) <= i: | |
148 vercmp_cache[mykey] = 1 | |
149 return 1 | |
150 elif list1[i] != list2[i]: | |
151 a = list1[i] | |
152 b = list2[i] | |
153 rval = (a > b) - (a < b) | |
154 vercmp_cache[mykey] = rval | |
155 return rval | |
156 | |
157 # main version is equal, so now compare the _suffix part | |
158 list1 = match1.group(6).split("_")[1:] | |
159 list2 = match2.group(6).split("_")[1:] | |
160 | |
161 for i in range(0, max(len(list1), len(list2))): | |
162 # Implicit _p0 is given a value of -1, so that 1 < 1_p0 | |
163 if len(list1) <= i: | |
164 s1 = ("p","-1") | |
165 else: | |
166 s1 = suffix_regexp.match(list1[i]).groups() | |
167 if len(list2) <= i: | |
168 s2 = ("p","-1") | |
169 else: | |
170 s2 = suffix_regexp.match(list2[i]).groups() | |
171 if s1[0] != s2[0]: | |
172 a = suffix_value[s1[0]] | |
173 b = suffix_value[s2[0]] | |
174 rval = (a > b) - (a < b) | |
175 vercmp_cache[mykey] = rval | |
176 return rval | |
177 if s1[1] != s2[1]: | |
178 # it's possible that the s(1|2)[1] == '' | |
179 # in such a case, fudge it. | |
180 try: | |
181 r1 = int(s1[1]) | |
182 except ValueError: | |
183 r1 = 0 | |
184 try: | |
185 r2 = int(s2[1]) | |
186 except ValueError: | |
187 r2 = 0 | |
188 rval = (r1 > r2) - (r1 < r2) | |
189 if rval: | |
190 vercmp_cache[mykey] = rval | |
191 return rval | |
192 | |
193 # the suffix part is equal to, so finally check the revision | |
194 if match1.group(10): | |
195 r1 = int(match1.group(10)) | |
196 else: | |
197 r1 = 0 | |
198 if match2.group(10): | |
199 r2 = int(match2.group(10)) | |
200 else: | |
201 r2 = 0 | |
202 rval = (r1 > r2) - (r1 < r2) | |
203 vercmp_cache[mykey] = rval | |
204 return rval | |
205 | |
206 def pkgcmp(pkg1, pkg2): | |
207 """ | |
208 Compare 2 package versions created in pkgsplit format. | |
209 | |
210 Example usage: | |
211 >>> from portage.versions import * | |
212 >>> pkgcmp(pkgsplit('test-1.0-r1'),pkgsplit('test-1.2-r3')) | |
213 -1 | |
214 >>> pkgcmp(pkgsplit('test-1.3'),pkgsplit('test-1.2-r3')) | |
215 1 | |
216 | |
217 @param pkg1: package to compare with | |
218 @type pkg1: list (example: ['test', '1.0', 'r1']) | |
219 @param pkg2: package to compare againts | |
220 @type pkg2: list (example: ['test', '1.0', 'r1']) | |
221 @rtype: None or integer | |
222 @return: | |
223 1. None if package names are not the same | |
224 2. 1 if pkg1 is greater than pkg2 | |
225 3. -1 if pkg1 is less than pkg2 | |
226 4. 0 if pkg1 equals pkg2 | |
227 """ | |
228 if pkg1[0] != pkg2[0]: | |
229 return None | |
230 return vercmp("-".join(pkg1[1:]), "-".join(pkg2[1:])) | |
231 | |
232 _pv_re = re.compile('^' + _pv + '$', re.VERBOSE) | |
233 | |
234 def _pkgsplit(mypkg): | |
235 """ | |
236 @param mypkg: pv | |
237 @return: | |
238 1. None if input is invalid. | |
239 2. (pn, ver, rev) if input is pv | |
240 """ | |
241 m = _pv_re.match(mypkg) | |
242 if m is None: | |
243 return None | |
244 | |
245 if m.group('pn_inval') is not None: | |
246 # package name appears to have a version-like suffix | |
247 return None | |
248 | |
249 rev = m.group('rev') | |
250 if rev is None: | |
251 rev = '0' | |
252 rev = 'r' + rev | |
253 | |
254 return (m.group('pn'), m.group('ver'), rev) | |
255 | |
256 _missing_cat = 'null' | |
257 catcache={} | |
258 def catpkgsplit(mydata,silent=1): | |
259 """ | |
260 Takes a Category/Package-Version-Rev and returns a list of each. | |
261 | |
262 @param mydata: Data to split | |
263 @type mydata: string | |
264 @param silent: suppress error messages | |
265 @type silent: Boolean (integer) | |
266 @rype: list | |
267 @return: | |
268 1. If each exists, it returns [cat, pkgname, version, rev] | |
269 2. If cat is not specificed in mydata, cat will be "null" | |
270 3. if rev does not exist it will be '-r0' | |
271 """ | |
272 | |
273 try: | |
274 return catcache[mydata] | |
275 except KeyError: | |
276 pass | |
277 mysplit = mydata.split('/', 1) | |
278 p_split=None | |
279 if len(mysplit)==1: | |
280 cat = _missing_cat | |
281 p_split = _pkgsplit(mydata) | |
282 elif len(mysplit)==2: | |
283 cat = mysplit[0] | |
284 p_split = _pkgsplit(mysplit[1]) | |
285 if not p_split: | |
286 catcache[mydata]=None | |
287 return None | |
288 retval = (cat, p_split[0], p_split[1], p_split[2]) | |
289 catcache[mydata]=retval | |
290 return retval | |
291 | |
292 def pkgsplit(mypkg, silent=1): | |
293 """ | |
294 @param mypkg: either a pv or cpv | |
295 @return: | |
296 1. None if input is invalid. | |
297 2. (pn, ver, rev) if input is pv | |
298 3. (cp, ver, rev) if input is a cpv | |
299 """ | |
300 catpsplit = catpkgsplit(mypkg) | |
301 if catpsplit is None: | |
302 return None | |
303 cat, pn, ver, rev = catpsplit | |
304 if cat is _missing_cat and '/' not in mypkg: | |
305 return (pn, ver, rev) | |
306 else: | |
307 return (cat + '/' + pn, ver, rev) | |
308 | |
309 def catsplit(mydep): | |
310 return mydep.split("/", 1) | |
311 | |
312 def best(mymatches): | |
313 """Accepts None arguments; assumes matches are valid.""" | |
314 if not mymatches: | |
315 return "" | |
316 if len(mymatches) == 1: | |
317 return mymatches[0] | |
318 bestmatch = mymatches[0] | |
319 p2 = catpkgsplit(bestmatch)[1:] | |
320 for x in mymatches[1:]: | |
321 p1 = catpkgsplit(x)[1:] | |
322 if pkgcmp(p1, p2) > 0: | |
323 bestmatch = x | |
324 p2 = catpkgsplit(bestmatch)[1:] | |
325 return bestmatch | |
OLD | NEW |