| OLD | NEW |
| (Empty) | |
| 1 # versions.py -- core Portage functionality |
| 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 |