Gentoo Archives: gentoo-portage-dev

From: Ali Polatel <hawking@g.o>
To: gentoo-portage-dev@l.g.o
Subject: [gentoo-portage-dev] [RFC/PATCH] New objects cpv, pv and version to be used instead of raw strings.
Date: Fri, 18 Jul 2008 10:39:20
Message-Id: 1216374112-1972-1-git-send-email-hawking@gentoo.org
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

Replies