From: Sebastian Luther Date: Sat, 24 Jul 2010 18:19:56 +0000 (+0200) Subject: extended atom syntax: Allow wildcards in all places X-Git-Tag: v2.2_rc68~464 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=03d2b5c37ac483153c3eeba859c649276ad6bbab;p=portage.git extended atom syntax: Allow wildcards in all places --- diff --git a/pym/portage/dep/__init__.py b/pym/portage/dep/__init__.py index e6f2362b1..c276edbea 100644 --- a/pym/portage/dep/__init__.py +++ b/pym/portage/dep/__init__.py @@ -33,7 +33,7 @@ import portage.exception from portage.exception import InvalidData, InvalidAtom from portage.localization import _ from portage.versions import catpkgsplit, catsplit, \ - pkgcmp, pkgsplit, ververify, _cat, _pkg, _cp, _cpv + pkgcmp, pkgsplit, ververify, _cp, _cpv import portage.cache.mappings if sys.hexversion >= 0x3000000: @@ -602,15 +602,20 @@ class Atom(_atom_base): blocker = False self.__dict__['blocker'] = blocker m = _atom_re.match(s) + extended_syntax = False if m is None: if allow_wildcard: m = _atom_wildcard_re.match(s) if m is None: raise InvalidAtom(self) op = None - cpv = cp = m.groupdict()['simple'] - slot = None + gdict = m.groupdict() + cpv = cp = gdict['simple'] + if cpv.find("**") != -1: + raise InvalidAtom(self) + slot = gdict['slot'] use_str = None + extended_syntax = True else: raise InvalidAtom(self) elif m.group('op') is not None: @@ -644,6 +649,7 @@ class Atom(_atom_base): self.__dict__['cpv'] = cpv self.__dict__['slot'] = slot self.__dict__['operator'] = op + self.__dict__['extended_syntax'] = extended_syntax if use_str is not None: use = _use_dep(dep_getusedeps(s)) @@ -748,6 +754,107 @@ class Atom(_atom_base): memo[id(self)] = self return self +def extended_cp_match(extended_atom, other): + """ + Checks if an extended syntax atom matches the other, non extended atom + """ + my_slot = dep_getslot(extended_atom) + if my_slot is not None: + extended_atom = extended_atom[:-(len(my_slot)+1)] + mysplit = catsplit(extended_atom) + my_cat = mysplit[0] + my_pkg = mysplit[1] + + other_slot = dep_getslot(other) + if other_slot is not None: + other = other[:-(len(other_slot)+1)] + othersplit = catsplit(other) + other_cat = othersplit[0] + other_pkg = othersplit[1] + + if my_slot is not None and other_slot is not None and \ + my_slot != other_slot: + return False + + for my_val, other_val in ((my_cat, other_cat), (my_pkg, other_pkg)): + if my_val == "*": + continue + + start = 0 + parts = my_val.split("*") + for id, part in enumerate(parts): + if not part: + if id == len(parts)-1: + start = len(other_val) + continue + start = other_val.find(part, start) + if start == -1: + return False + + start += len(part) + + if start != len(other_val): + return False + + return True + +class ExtendedAtomDict(object): + """ + dict() wrapper that supports extended atoms as keys and allows lookup + of a normal cp against other normal cp and extended cp. + The value type has to be given to __init__ and is assumed to be the same + for all values. + """ + def __init__(self, value_class): + self._extended = {} + self._normal = {} + self._value_class = value_class + + def setdefault(self, cp, default=None): + if "*" in cp: + return self._extended.setdefault(cp, default) + else: + return self._normal.setdefault(cp, default) + + def get(self, cp): + ret = self._value_class() + normal_match = self._normal.get(cp) + if normal_match is not None: + if hasattr(ret, "update"): + ret.update(normal_match) + elif hasattr(ret, "extend"): + ret.extend(normal_match) + else: + raise NotImplementedError() + + for extended_cp in self._extended: + if extended_cp_match(extended_cp, cp): + if hasattr(ret, "update"): + ret.update(self._extended[extended_cp]) + elif hasattr(ret, "extend"): + ret.extend(self._extended[extended_cp]) + else: + raise NotImplementedError() + + return ret + + def __setitem__(self, cp, val): + if "*" in cp: + self._extended[cp] = val + else: + self._normal[cp] = val + + def __getitem__(self, cp): + if "*" in cp: + return self._extended[cp] + else: + return self._normal[cp] + + def clear(self): + self._extended.clear() + self._normal.clear() + + def get_operator(mydep): """ Return the operator used in a depstring. @@ -945,7 +1052,11 @@ _atom_re = re.compile('^(?P(?:' + '(?P' + _op + _cpv + ')|' + '(?P=' + _cpv + r'\*)|' + '(?P' + _cp + '))(:' + _slot + ')?)(' + _use + ')?$', re.VERBOSE) -_atom_wildcard_re = re.compile('(?P((' + _cat + '|\*)/(' + _pkg + '|\*)))$') + +_extended_cat = r'[\w+*][\w+.*-]*' +_extended_pkg = r'[\w+*][\w+*-]*?' + +_atom_wildcard_re = re.compile('(?P(' + _extended_cat + ')/(' + _extended_pkg + '))(:(?P' + _slot + '))?$') def isvalidatom(atom, allow_blockers=False, allow_wildcard=False): """ @@ -1103,30 +1214,29 @@ def best_match_to_list(mypkg, mylist): - >=cpv 2 - <=cpv 2 - cp 1 - - */p 0 - - c/* 0 - - */* -1 + - cp:slot with extended syntax 0 + - cp with extended syntax -1 """ operator_values = {'=':6, '~':5, '=*':4, '>':2, '<':2, '>=':2, '<=':2, None:1} maxvalue = -2 bestm = None for x in match_to_list(mypkg, mylist): + if x.extended_syntax: + if dep_getslot(x) is not None: + if maxvalue < 0: + maxvalue = 0 + bestm = x + else: + if maxvalue < -1: + maxvalue = -1 + bestm = x + continue if dep_getslot(x) is not None: if maxvalue < 3: maxvalue = 3 bestm = x op_val = operator_values[x.operator] - if x.operator is None: - c, p = catsplit(x) - if c == "*": - if p == "*": - op_val = -1 - else: - op_val = 0 - elif p == "*": - op_val = 0 - if op_val > maxvalue: maxvalue = op_val bestm = x @@ -1183,18 +1293,14 @@ def match_from_list(mydep, candidate_list): if cp is None: mysplit = catpkgsplit(remove_slot(x)) if mysplit is not None: - c = mysplit[0] - p = mysplit[1] - else: - continue - else: - mysplit = catsplit(cp) - c = mysplit[0] - p = mysplit[1] + cp = mysplit[0] + '/' + mysplit[1] + + if cp is None: + continue - if cat in (c, "*") and pkg in (p, "*"): + if cp == mycpv or (mydep.extended_syntax and \ + extended_cp_match(mycpv, cp)): mylist.append(x) - elif operator == "=": # Exact match for x in candidate_list: @@ -1270,7 +1376,7 @@ def match_from_list(mydep, candidate_list): else: raise KeyError(_("Unknown operator: %s") % mydep) - if slot is not None: + if slot is not None and not mydep.extended_syntax: candidate_list = mylist mylist = [] for x in candidate_list: diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py index c7aa42d20..a58cd0c3b 100644 --- a/pym/portage/package/ebuild/config.py +++ b/pym/portage/package/ebuild/config.py @@ -771,11 +771,11 @@ class config(object): self["EROOT"] = target_root self.backup_changes("EROOT") - self.pusedict = {} - self.pkeywordsdict = {} - self._plicensedict = {} - self._ppropertiesdict = {} - self.punmaskdict = {} + self.pusedict = portage.dep.ExtendedAtomDict(dict) + self.pkeywordsdict = portage.dep.ExtendedAtomDict(dict) + self._plicensedict = portage.dep.ExtendedAtomDict(dict) + self._ppropertiesdict = portage.dep.ExtendedAtomDict(dict) + self.punmaskdict = portage.dep.ExtendedAtomDict(list) abs_user_config = os.path.join(config_root, USER_CONFIG_PATH) # locations for "categories" and "arch.list" files @@ -922,7 +922,7 @@ class config(object): pkgmasklines = stack_lists(pkgmasklines, incremental=1) pkgunmasklines = stack_lists(pkgunmasklines, incremental=1) - self.pmaskdict = {} + self.pmaskdict = portage.dep.ExtendedAtomDict(list) for x in pkgmasklines: self.pmaskdict.setdefault(x.cp, []).append(x) @@ -1497,11 +1497,7 @@ class config(object): has_changed = True oldpuse = self.puse self.puse = "" - cpdict = {} - cpdict.update(self.pusedict.get("*/*", {})) - cpdict.update(self.pusedict.get(cat+"/*", {})) - cpdict.update(self.pusedict.get("*/"+cp.split("/")[1], {})) - cpdict.update(self.pusedict.get(cp, {})) + cpdict = self.pusedict.get(cp) if cpdict: keys = list(cpdict) while keys: @@ -1714,19 +1710,10 @@ class config(object): """ cp = cpv_getkey(cpv) - c, p = catsplit(cp) - mask_atoms = [] - mask_atoms.extend(self.pmaskdict.get("*/*", [])) - mask_atoms.extend(self.pmaskdict.get(c+"/*", [])) - mask_atoms.extend(self.pmaskdict.get("*/"+p, [])) - mask_atoms.extend(self.pmaskdict.get(cp, [])) + mask_atoms = self.pmaskdict.get(cp) if mask_atoms: pkg_list = ["%s:%s" % (cpv, metadata["SLOT"])] - unmask_atoms = [] - unmask_atoms.extend(self.punmaskdict.get("*/*", [])) - unmask_atoms.extend(self.punmaskdict.get(c+"/*", [])) - unmask_atoms.extend(self.punmaskdict.get("*/"+p, [])) - unmask_atoms.extend(self.punmaskdict.get(cp, [])) + unmask_atoms = self.punmaskdict.get(cp) for x in mask_atoms: if not match_from_list(x, pkg_list): continue @@ -1874,12 +1861,7 @@ class config(object): """ accept_license = self._accept_license cp = cpv_getkey(cpv) - c, p = catsplit(cp) - cpdict = {} - cpdict.update(self._plicensedict.get("*/*", {})) - cpdict.update(self._plicensedict.get(c+"/*", {})) - cpdict.update(self._plicensedict.get("*/"+p, {})) - cpdict.update(self._plicensedict.get(cp, {})) + cpdict = self._plicensedict.get(cp) if cpdict: accept_license = list(self._accept_license) cpv_slot = "%s:%s" % (cpv, metadata["SLOT"]) @@ -1959,12 +1941,7 @@ class config(object): """ accept_properties = self._accept_properties cp = cpv_getkey(cpv) - c, p = catsplit(cp) - cpdict = {} - cpdict.update(self._ppropertiesdict.get("*/*", {})) - cpdict.update(self._ppropertiesdict.get(c+"/*", {})) - cpdict.update(self._ppropertiesdict.get("*/"+p, {})) - cpdict.update(self._ppropertiesdict.get(cp, {})) + cpdict = self._ppropertiesdict.get(cp) if cpdict: accept_properties = list(self._accept_properties) cpv_slot = "%s:%s" % (cpv, metadata["SLOT"]) diff --git a/pym/portage/sets/base.py b/pym/portage/sets/base.py index 108dda3ec..a143b8272 100644 --- a/pym/portage/sets/base.py +++ b/pym/portage/sets/base.py @@ -2,7 +2,7 @@ # Distributed under the terms of the GNU General Public License v2 import sys -from portage.dep import Atom, best_match_to_list, match_from_list +from portage.dep import Atom, ExtendedAtomDict, best_match_to_list, match_from_list from portage.exception import InvalidAtom from portage.versions import catsplit, cpv_getkey @@ -21,7 +21,7 @@ class PackageSet(object): def __init__(self): self._atoms = set() - self._atommap = {} + self._atommap = ExtendedAtomDict(set) self._loaded = False self._loading = False self.errors = [] @@ -140,15 +140,9 @@ class PackageSet(object): """ cpv_slot_list = [pkg] cp = cpv_getkey(pkg.cpv) - c, p = catsplit(cp) self._load() # make sure the atoms are loaded - atoms = set() - atoms.update(self._atommap.get("*/*", set())) - atoms.update(self._atommap.get(c+"/*", set())) - atoms.update(self._atommap.get("*/"+p, set())) - atoms.update(self._atommap.get(cp, set())) - + atoms = self._atommap.get(cp) if atoms: for atom in atoms: if match_from_list(atom, cpv_slot_list): diff --git a/pym/portage/tests/dep/testAtom.py b/pym/portage/tests/dep/testAtom.py index 091cef3a2..a4972905c 100644 --- a/pym/portage/tests/dep/testAtom.py +++ b/pym/portage/tests/dep/testAtom.py @@ -27,7 +27,11 @@ class TestAtom(TestCase): ( "sys-apps/*", (None, 'sys-apps/*', None, None, None), True ), ( "*/portage", - (None, '*/portage', None, None, None), True ) + (None, '*/portage', None, None, None), True ), + ( "s*s-*/portage:1", + (None, 's*s-*/portage', None, '1', None), True ), + ( "*/po*ge:2", + (None, '*/po*ge', None, '2', None), True ), ] tests_xfail = [ diff --git a/pym/portage/tests/dep/testExtendedAtomDict.py b/pym/portage/tests/dep/testExtendedAtomDict.py new file mode 100644 index 000000000..702bec196 --- /dev/null +++ b/pym/portage/tests/dep/testExtendedAtomDict.py @@ -0,0 +1,18 @@ +# test_isvalidatom.py -- Portage Unit Testing Functionality +# Copyright 2006 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +from portage.tests import TestCase +from portage.dep import ExtendedAtomDict + +class TestExtendedAtomDict(TestCase): + + def testExtendedAtomDict(self): + d = ExtendedAtomDict(dict) + d["*/*"] = { "test1": "x" } + d["dev-libs/*"] = { "test2": "y" } + d.setdefault("sys-apps/portage", {})["test3"] = "z" + self.assertEqual(d.get("dev-libs/A"), { "test1": "x", "test2": "y" }) + self.assertEqual(d.get("sys-apps/portage"), { "test1": "x", "test3": "z" }) + self.assertEqual(d["dev-libs/*"], { "test2": "y" }) + self.assertEqual(d["sys-apps/portage"], { "test3": "z" }) diff --git a/pym/portage/tests/dep/test_best_match_to_list.py b/pym/portage/tests/dep/test_best_match_to_list.py index c5f4fbbc0..d050adc51 100644 --- a/pym/portage/tests/dep/test_best_match_to_list.py +++ b/pym/portage/tests/dep/test_best_match_to_list.py @@ -31,12 +31,12 @@ class Test_best_match_to_list(TestCase): [Atom("=dev-libs/A-1:0")]), ("dev-libs/A-1", [Atom("dev-libs/*", allow_wildcard=True), Atom("=dev-libs/A-1:0")], \ [Atom("=dev-libs/A-1:0"), Atom("dev-libs/*", allow_wildcard=True)]), - ("dev-libs/A-1", [Atom("*/*", allow_wildcard=True), Atom("dev-libs/*", allow_wildcard=True), \ + ("dev-libs/A-1:0", [Atom("dev-*/*", allow_wildcard=True), Atom("dev-*/*:0", allow_wildcard=True),\ Atom("dev-libs/A"), Atom("<=dev-libs/A-2"), Atom("dev-libs/A:0"), \ Atom("=dev-libs/A-1*"), Atom("~dev-libs/A-1"), Atom("=dev-libs/A-1")], \ [Atom("=dev-libs/A-1"), Atom("~dev-libs/A-1"), Atom("=dev-libs/A-1*"), \ Atom("dev-libs/A:0"), Atom("<=dev-libs/A-2"), Atom("dev-libs/A"), \ - Atom("dev-libs/*", allow_wildcard=True), Atom("*/*", allow_wildcard=True)]) + Atom("dev-*/*:0", allow_wildcard=True), Atom("dev-*/*", allow_wildcard=True)]) ] for pkg, atom_list, result in tests: