1 |
Hi, |
2 |
Attached patch adds objects cpv, pv and version to portage.versions. This is |
3 |
meant as a thin layer over functions vercmp(), pkgcmp(), pkgsplit() and |
4 |
catpkgsplit(). |
5 |
Using these objects instead of the mentioned functions allows us to write |
6 |
cleaner code and remove deprecated stuff like: |
7 |
list.sort(pkgcmp) |
8 |
which won't exist in py3k. |
9 |
|
10 |
Please comment. |
11 |
|
12 |
--- |
13 |
pym/portage/versions.py | 292 +++++++++++++++++++++++++++++++++++++++++------ |
14 |
1 files changed, 256 insertions(+), 36 deletions(-) |
15 |
|
16 |
diff --git a/pym/portage/versions.py b/pym/portage/versions.py |
17 |
index 261fa9d..20b0d4f 100644 |
18 |
--- a/pym/portage/versions.py |
19 |
+++ b/pym/portage/versions.py |
20 |
@@ -4,6 +4,7 @@ |
21 |
# $Id$ |
22 |
|
23 |
import re |
24 |
+import warnings |
25 |
|
26 |
ver_regexp = re.compile("^(cvs\\.)?(\\d+)((\\.\\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\\d*)*)(-r(\\d+))?$") |
27 |
suffix_regexp = re.compile("^(alpha|beta|rc|pre|p)(\\d*)$") |
28 |
@@ -12,6 +13,205 @@ endversion_keys = ["pre", "p", "alpha", "beta", "rc"] |
29 |
|
30 |
from portage.exception import InvalidData |
31 |
|
32 |
+# builtin all() is new in Python-2.5 |
33 |
+# TODO Move compatibility stuff to a new module portage.compat |
34 |
+# and import from it like from portage.compat import all |
35 |
+from sys import hexversion |
36 |
+if hexversion < 0x02050000: |
37 |
+ def all(iterable): |
38 |
+ for i in iterable: |
39 |
+ if not bool(i): |
40 |
+ return False |
41 |
+ return True |
42 |
+del hexversion |
43 |
+ |
44 |
+def needs_version(func): |
45 |
+ """Decorator for functions that require non-keyword arguments of type version.""" |
46 |
+ def func_proxy(*args, **kwargs): |
47 |
+ if not all([isinstance(arg, version) for arg in args]): |
48 |
+ raise TypeError("Not all non-keyword arguments are of type version") |
49 |
+ return func(*args, **kwargs) |
50 |
+ func_proxy.__doc__ = func.__doc__ |
51 |
+ return func_proxy |
52 |
+ |
53 |
+def needs_pv(func): |
54 |
+ """Decorator for functions that require non-keyword arguments of type pv.""" |
55 |
+ def func_proxy(*args, **kwargs): |
56 |
+ if not all([isinstance(arg, pv) for arg in args]): |
57 |
+ raise TypeError("Not all non-keyword arguments are of type pv") |
58 |
+ return func(*args, **kwargs) |
59 |
+ func_proxy.__doc__ = func.__doc__ |
60 |
+ return func_proxy |
61 |
+ |
62 |
+def needs_cpv(func): |
63 |
+ """Decorator for functions that require non-keyword arguments of type cpv.""" |
64 |
+ def func_proxy(*args, **kwargs): |
65 |
+ if not all([isinstance(arg, cpv) for arg in args]): |
66 |
+ raise TypeError("Not all non-keyword arguments are of type cpv") |
67 |
+ return func(*args, **kwargs) |
68 |
+ func_proxy.__doc__ = func.__doc__ |
69 |
+ return func_proxy |
70 |
+ |
71 |
+class version(str): |
72 |
+ """Represents a package version""" |
73 |
+ |
74 |
+ __hash = None |
75 |
+ __parts = () |
76 |
+ __str = "" |
77 |
+ |
78 |
+ def __new__(cls, value): |
79 |
+ m = ver_regexp.match(value) |
80 |
+ if m is None: |
81 |
+ raise TypeError("Syntax error in version: %s" % value) |
82 |
+ else: |
83 |
+ new_ver = str.__new__(cls, m.groups()) |
84 |
+ new_ver.__hash = hash(m.groups()) + hash(value) |
85 |
+ new_ver.__parts = m.groups() |
86 |
+ new_ver.__str = value |
87 |
+ |
88 |
+ return new_ver |
89 |
+ |
90 |
+ def __str__(self): |
91 |
+ return self.__str |
92 |
+ |
93 |
+ def __repr__(self): |
94 |
+ return "<%s object at 0x%x: %s>" % (self.__class__.__name__, |
95 |
+ id(self), self.__str) |
96 |
+ |
97 |
+ def __hash__(self): |
98 |
+ return self.__hash |
99 |
+ |
100 |
+ def __getitem__(self, i): |
101 |
+ return self.__parts[i] |
102 |
+ |
103 |
+ def __getslice__(self, i, j): |
104 |
+ return self.__parts[i:j] |
105 |
+ |
106 |
+ def __len__(self): |
107 |
+ return len(self.__parts) |
108 |
+ |
109 |
+ @needs_version |
110 |
+ def __cmp__(self, y): |
111 |
+ return vercmp(self, y) |
112 |
+ |
113 |
+ @needs_version |
114 |
+ def __eq__(self, y): |
115 |
+ return vercmp(self, y) == 0 |
116 |
+ |
117 |
+ @needs_version |
118 |
+ def __ne__(self, y): |
119 |
+ return vercmp(self, y) != 0 |
120 |
+ |
121 |
+ @needs_version |
122 |
+ def __lt__(self, y): |
123 |
+ return vercmp(self, y) < 0 |
124 |
+ |
125 |
+ @needs_version |
126 |
+ def __le__(self, y): |
127 |
+ return vercmp(self, y) <= 0 |
128 |
+ |
129 |
+ @needs_version |
130 |
+ def __gt__(self, y): |
131 |
+ return vercmp(self, y) > 0 |
132 |
+ |
133 |
+ @needs_version |
134 |
+ def __ge__(self, y): |
135 |
+ return vercmp(self, y) >= 0 |
136 |
+ |
137 |
+class pv(str): |
138 |
+ """Represents a pv""" |
139 |
+ |
140 |
+ __hash = None |
141 |
+ __parts = () |
142 |
+ __str = "" |
143 |
+ |
144 |
+ def __new__(cls, value): |
145 |
+ parts = pkgsplit(value) |
146 |
+ if parts is None: |
147 |
+ # Ideally a TypeError should be raised here. |
148 |
+ # But to fit code using this easily, fail silently. |
149 |
+ return None |
150 |
+ else: |
151 |
+ new_pv = str.__new__(cls, parts) |
152 |
+ new_pv.__hash = hash(parts) + hash(value) |
153 |
+ new_pv.__parts = (parts[0], version("-".join(parts[1:]))) |
154 |
+ new_pv.__str = value |
155 |
+ |
156 |
+ return new_pv |
157 |
+ |
158 |
+ def __str__(self): |
159 |
+ return self.__str |
160 |
+ |
161 |
+ def __repr__(self): |
162 |
+ return "<%s object at 0x%x: %s>" % (self.__class__.__name__, |
163 |
+ id(self), self.__str) |
164 |
+ |
165 |
+ def __hash__(self): |
166 |
+ return self.__hash |
167 |
+ |
168 |
+ def __getitem__(self, i): |
169 |
+ return self.__parts[i] |
170 |
+ |
171 |
+ def __getslice__(self, i, j): |
172 |
+ return self.__parts[i:j] |
173 |
+ |
174 |
+ def __len__(self): |
175 |
+ return len(self.__parts) |
176 |
+ |
177 |
+ @needs_pv |
178 |
+ def __cmp__(self, y): |
179 |
+ if self.__parts[0] != y.__parts[0]: |
180 |
+ return None |
181 |
+ else: |
182 |
+ return cmp(self[1], y[1]) |
183 |
+ |
184 |
+class cpv(str): |
185 |
+ """Represents a cpv""" |
186 |
+ |
187 |
+ __hash = None |
188 |
+ __parts = () |
189 |
+ __str = "" |
190 |
+ |
191 |
+ def __new__(cls, value): |
192 |
+ parts = catpkgsplit(value) |
193 |
+ if parts is None: |
194 |
+ # Ideally a TypeError should be raised here. |
195 |
+ # But to fit code using this easily, fail silently. |
196 |
+ return None |
197 |
+ else: |
198 |
+ new_cpv = str.__new__(cls, parts) |
199 |
+ new_cpv.__hash = hash(parts) + hash(value) |
200 |
+ new_cpv.__parts = (parts[0], pv("-".join(parts[1:]))) |
201 |
+ new_cpv.__str = value |
202 |
+ |
203 |
+ return new_cpv |
204 |
+ |
205 |
+ def __str__(self): |
206 |
+ return self.__str |
207 |
+ |
208 |
+ def __repr__(self): |
209 |
+ return "<%s object at 0x%x: %s>" % (self.__class__.__name__, |
210 |
+ id(self), self.__str) |
211 |
+ |
212 |
+ def __hash__(self): |
213 |
+ return self.__hash |
214 |
+ |
215 |
+ def __getitem__(self, i): |
216 |
+ return self.__parts[i] |
217 |
+ |
218 |
+ def __getslice__(self, i, j): |
219 |
+ return self.__parts[i:j] |
220 |
+ |
221 |
+ def __len__(self): |
222 |
+ return len(self.__parts) |
223 |
+ |
224 |
+ @needs_cpv |
225 |
+ def __cmp__(self, y): |
226 |
+ if self[0] != y[0]: |
227 |
+ return None |
228 |
+ else: |
229 |
+ return cmp(self[1], y[1]) |
230 |
+ |
231 |
def ververify(myver, silent=1): |
232 |
if ver_regexp.match(myver): |
233 |
return 1 |
234 |
@@ -45,43 +245,63 @@ def vercmp(ver1, ver2, silent=1): |
235 |
4. None if ver1 or ver2 are invalid (see ver_regexp in portage.versions.py) |
236 |
""" |
237 |
|
238 |
- if ver1 == ver2: |
239 |
- return 0 |
240 |
- mykey=ver1+":"+ver2 |
241 |
- try: |
242 |
- return vercmp_cache[mykey] |
243 |
- except KeyError: |
244 |
- pass |
245 |
- match1 = ver_regexp.match(ver1) |
246 |
- match2 = ver_regexp.match(ver2) |
247 |
- |
248 |
- # checking that the versions are valid |
249 |
- if not match1 or not match1.groups(): |
250 |
- if not silent: |
251 |
- print "!!! syntax error in version: %s" % ver1 |
252 |
- return None |
253 |
- if not match2 or not match2.groups(): |
254 |
- if not silent: |
255 |
- print "!!! syntax error in version: %s" % ver2 |
256 |
- return None |
257 |
+ if isinstance(ver1, version) and isinstance(ver2, version): |
258 |
+ if ver1._str == ver2._str: |
259 |
+ return 0 |
260 |
+ mykey = ver1._str+":"+ver2._str |
261 |
+ if mykey in vercmp_cache: |
262 |
+ return vercmp_cache[mykey] |
263 |
+ |
264 |
+ group1 = ver1[:] |
265 |
+ group2 = ver2[:] |
266 |
+ elif isinstance(ver1, str) and isinstance(ver2, str): |
267 |
+ ## Backwards compatibility |
268 |
+ msg = "vercmp(str,str) is deprecated use portage.version object instead" |
269 |
+ warnings.warn(msg, DeprecationWarning) |
270 |
+ |
271 |
+ if ver1 == ver2: |
272 |
+ return 0 |
273 |
+ mykey=ver1+":"+ver2 |
274 |
+ try: |
275 |
+ return vercmp_cache[mykey] |
276 |
+ except KeyError: |
277 |
+ pass |
278 |
+ match1 = ver_regexp.match(ver1) |
279 |
+ match2 = ver_regexp.match(ver2) |
280 |
+ |
281 |
+ # checking that the versions are valid |
282 |
+ if not match1 or not match1.groups(): |
283 |
+ if not silent: |
284 |
+ print "!!! syntax error in version: %s" % ver1 |
285 |
+ return None |
286 |
+ if not match2 or not match2.groups(): |
287 |
+ if not silent: |
288 |
+ print "!!! syntax error in version: %s" % ver2 |
289 |
+ return None |
290 |
+ |
291 |
+ group1 = match1.groups() |
292 |
+ group2 = match2.groups() |
293 |
+ else: |
294 |
+ raise TypeError( |
295 |
+ "Arguments aren't of type str,str or version,version") |
296 |
|
297 |
# shortcut for cvs ebuilds (new style) |
298 |
- if match1.group(1) and not match2.group(1): |
299 |
+ if group1[0] and not group2[0]: |
300 |
vercmp_cache[mykey] = 1 |
301 |
return 1 |
302 |
- elif match2.group(1) and not match1.group(1): |
303 |
+ elif group2[0] and not group1[0]: |
304 |
vercmp_cache[mykey] = -1 |
305 |
return -1 |
306 |
|
307 |
# building lists of the version parts before the suffix |
308 |
# first part is simple |
309 |
- list1 = [int(match1.group(2))] |
310 |
- list2 = [int(match2.group(2))] |
311 |
+ list1 = [int(group1[1])] |
312 |
+ list2 = [int(group2[1])] |
313 |
|
314 |
# this part would greatly benefit from a fixed-length version pattern |
315 |
- if len(match1.group(3)) or len(match2.group(3)): |
316 |
- vlist1 = match1.group(3)[1:].split(".") |
317 |
- vlist2 = match2.group(3)[1:].split(".") |
318 |
+ if len(group1[2]) or len(group2[2]): |
319 |
+ vlist1 = group1[2][1:].split(".") |
320 |
+ vlist2 = group2[2][1:].split(".") |
321 |
for i in range(0, max(len(vlist1), len(vlist2))): |
322 |
# Implcit .0 is given a value of -1, so that 1.0.0 > 1.0, since it |
323 |
# would be ambiguous if two versions that aren't literally equal |
324 |
@@ -111,10 +331,10 @@ def vercmp(ver1, ver2, silent=1): |
325 |
list2.append(int(vlist2[i].ljust(max_len, "0"))) |
326 |
|
327 |
# and now the final letter |
328 |
- if len(match1.group(5)): |
329 |
- list1.append(ord(match1.group(5))) |
330 |
- if len(match2.group(5)): |
331 |
- list2.append(ord(match2.group(5))) |
332 |
+ if len(group1[4]): |
333 |
+ list1.append(ord(group1[4])) |
334 |
+ if len(group2[4]): |
335 |
+ list2.append(ord(group2[4])) |
336 |
|
337 |
for i in range(0, max(len(list1), len(list2))): |
338 |
if len(list1) <= i: |
339 |
@@ -128,8 +348,8 @@ def vercmp(ver1, ver2, silent=1): |
340 |
return list1[i] - list2[i] |
341 |
|
342 |
# main version is equal, so now compare the _suffix part |
343 |
- list1 = match1.group(6).split("_")[1:] |
344 |
- list2 = match2.group(6).split("_")[1:] |
345 |
+ list1 = group1[5].split("_")[1:] |
346 |
+ list2 = group2[5].split("_")[1:] |
347 |
|
348 |
for i in range(0, max(len(list1), len(list2))): |
349 |
# Implicit _p0 is given a value of -1, so that 1 < 1_p0 |
350 |
@@ -154,12 +374,12 @@ def vercmp(ver1, ver2, silent=1): |
351 |
return r1 - r2 |
352 |
|
353 |
# the suffix part is equal to, so finally check the revision |
354 |
- if match1.group(10): |
355 |
- r1 = int(match1.group(10)) |
356 |
+ if group1[9]: |
357 |
+ r1 = int(group1[9]) |
358 |
else: |
359 |
r1 = 0 |
360 |
- if match2.group(10): |
361 |
- r2 = int(match2.group(10)) |
362 |
+ if group2[9]: |
363 |
+ r2 = int(group2[9]) |
364 |
else: |
365 |
r2 = 0 |
366 |
vercmp_cache[mykey] = r1 - r2 |
367 |
-- |
368 |
1.5.6.2 |
369 |
|
370 |
-- |
371 |
gentoo-portage-dev@l.g.o mailing list |