From c21c594833473b75808c4e7f7d65d6c67f639db9 Mon Sep 17 00:00:00 2001 From: Fabian Groffen Date: Sun, 25 May 2008 17:54:21 +0000 Subject: [PATCH] Merged from trunk 10393:10415 | 10394 | For dep_expand() backward compatibility, handle atoms with | | zmedico | missing '=' prefix which did not raise an error here before | | | the portage.dep.Atom class was introduced. | | 10395 | Fix broken dep_getusedeps() cache behavior so that it | | zmedico | returns the correct type. | | 10396 | Add support for conditionals inside USE dependencies. This | | zmedico | is useful for cases in which the package satisfying a | | | dependency should have identical or opposite flag state | | | relative to the parent package. Conditionals are evaluated | | | as follows: parent state conditional result x [x?] [x] -x | | | [x?] [-x] x [-x?] [-x] -x [-x?] [x] | | 10398 | * Fix dep_getusedeps() test to work with tuples. * Add test | | zmedico | cases for conditional USE deps. | | 10399 | Fix isvalidatom() so tests pass, and add a USE deps case to | | zmedico | the tests. | | 10400 | Remove category validation cruft from catpkgsplit(). | | zmedico | | | 10410 | Remove redundant SLOT dep code in portdbapi.xmatch() since | | zmedico | it's now handled by dbapi._iter_match(). | | 10413 | Facilitate creation of Atom instances in a few obvious | | zmedico | places: * PackageSet._setAtoms() * AtomArg and PackageArg | | | constructors. | | 10415 | * Fix PackageSet.findAtomForPackage() to take a Package | | zmedico | instance as an argument. * Simplify | | | PackageSet.findAtomForPackage() by implementing it on top of | | | iterAtomsForPackage(). | svn path=/main/branches/prefix/; revision=10418 --- pym/_emerge/__init__.py | 31 +++++----- pym/portage/__init__.py | 33 ++++++++-- pym/portage/dbapi/porttree.py | 13 +--- pym/portage/dep.py | 63 ++++++++++++++++---- pym/portage/sets/base.py | 43 ++++--------- pym/portage/tests/__init__.py | 3 +- pym/portage/tests/dep/test_dep_getusedeps.py | 6 +- pym/portage/tests/dep/test_isvalidatom.py | 8 +-- pym/portage/versions.py | 6 +- 9 files changed, 121 insertions(+), 85 deletions(-) diff --git a/pym/_emerge/__init__.py b/pym/_emerge/__init__.py index e652026b6..17482b6a3 100644 --- a/pym/_emerge/__init__.py +++ b/pym/_emerge/__init__.py @@ -756,7 +756,8 @@ def create_world_atom(pkg_key, metadata, args_set, root_config): in world since system atoms can only match one slot while world atoms can be greedy with respect to slots. Unslotted system packages will not be stored in world.""" - arg_atom = args_set.findAtomForPackage(pkg_key, metadata) + pkg = Package(cpv=pkg_key, metadata=metadata) + arg_atom = args_set.findAtomForPackage(pkg) if not arg_atom: return None cp = portage.dep_getkey(arg_atom) @@ -812,13 +813,13 @@ def create_world_atom(pkg_key, metadata, args_set, root_config): if len(matched_slots) == 1: new_world_atom = slot_atom - if new_world_atom == sets["world"].findAtomForPackage(pkg_key, metadata): + if new_world_atom == sets["world"].findAtomForPackage(pkg): # Both atoms would be identical, so there's nothing to add. return None if not slotted: # Unlike world atoms, system atoms are not greedy for slots, so they # can't be safely excluded from world if they are slotted. - system_atom = sets["system"].findAtomForPackage(pkg_key, metadata) + system_atom = sets["system"].findAtomForPackage(pkg) if system_atom: if not portage.dep_getkey(system_atom).startswith("virtual/"): return None @@ -1360,13 +1361,15 @@ class AtomArg(DependencyArg): def __init__(self, atom=None, **kwargs): DependencyArg.__init__(self, **kwargs) self.atom = atom + if not isinstance(self.atom, portage.dep.Atom): + self.atom = portage.dep.Atom(self.atom) self.set = (self.atom, ) class PackageArg(DependencyArg): def __init__(self, package=None, **kwargs): DependencyArg.__init__(self, **kwargs) self.package = package - self.atom = "=" + package.cpv + self.atom = portage.dep.Atom("=" + package.cpv) self.set = (self.atom, ) class SetArg(DependencyArg): @@ -4822,11 +4825,11 @@ class depgraph(object): pkg_system = False pkg_world = False try: - pkg_system = system_set.findAtomForPackage(pkg_key, metadata) - pkg_world = world_set.findAtomForPackage(pkg_key, metadata) + pkg_system = system_set.findAtomForPackage(pkg) + pkg_world = world_set.findAtomForPackage(pkg) if not (oneshot or pkg_world) and \ myroot == self.target_root and \ - favorites_set.findAtomForPackage(pkg_key, metadata): + favorites_set.findAtomForPackage(pkg): # Maybe it will be added to world now. if create_world_atom(pkg_key, metadata, favorites_set, root_config): @@ -5882,7 +5885,7 @@ class MergeTask(object): #buildsyspkg: Check if we need to _force_ binary package creation issyspkg = ("buildsyspkg" in myfeat) \ and x[0] != "blocks" \ - and system_set.findAtomForPackage(pkg_key, metadata) \ + and system_set.findAtomForPackage(pkg) \ and "--buildpkg" not in self.myopts if x[0] in ["ebuild","blocks"]: if x[0] == "blocks" and "--fetchonly" not in self.myopts: @@ -6087,7 +6090,7 @@ class MergeTask(object): #need to check for errors if not buildpkgonly: if not (fetchonly or oneshot or pretend) and \ - args_set.findAtomForPackage(pkg_key, metadata): + args_set.findAtomForPackage(pkg): world_set.lock() world_set.load() # maybe it's changed on disk myfavkey = create_world_atom(pkg_key, metadata, @@ -7952,11 +7955,10 @@ def action_depclean(settings, trees, ldpath_mtimes, # to remove those. filtered_pkgs = [] for pkg in pkgs: - metadata = dict(izip(metadata_keys, - vardb.aux_get(pkg, metadata_keys))) arg_atom = None try: - arg_atom = args_set.findAtomForPackage(pkg, metadata) + arg_atom = args_set.findAtomForPackage( + pkg_cache[("installed", myroot, pkg, "nomerge")]) except portage.exception.InvalidDependString, e: file_path = os.path.join( myroot, portage.VDB_PATH, pkg, "PROVIDE") @@ -8065,11 +8067,10 @@ def action_depclean(settings, trees, ldpath_mtimes, if action == "depclean": if myfiles: for pkg in vardb.cpv_all(): - metadata = dict(izip(metadata_keys, - vardb.aux_get(pkg, metadata_keys))) arg_atom = None try: - arg_atom = args_set.findAtomForPackage(pkg, metadata) + arg_atom = args_set.findAtomForPackage( + pkg_cache[("installed", myroot, pkg, "nomerge")]) except portage.exception.InvalidDependString: # this error has already been displayed by now continue diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py index 50e47f0dc..e75c84274 100644 --- a/pym/portage/__init__.py +++ b/pym/portage/__init__.py @@ -5451,6 +5451,7 @@ def _expand_new_virtuals(mysplit, edebug, mydbapi, mysettings, myroot="/", if kwargs["use_binaries"]: portdb = trees[myroot]["bintree"].dbapi myvirtuals = mysettings.getvirtuals() + myuse = kwargs["myuse"] for x in mysplit: if x == "||": newsplit.append(x) @@ -5459,10 +5460,23 @@ def _expand_new_virtuals(mysplit, edebug, mydbapi, mysettings, myroot="/", newsplit.append(_expand_new_virtuals(x, edebug, mydbapi, mysettings, myroot=myroot, trees=trees, **kwargs)) continue - if portage.dep._dep_check_strict and \ - not isvalidatom(x, allow_blockers=True): - raise portage.exception.ParseError( - "invalid atom: '%s'" % x) + + if not isinstance(x, portage.dep.Atom): + try: + x = portage.dep.Atom(x) + except portage.exception.InvalidAtom: + if portage.dep._dep_check_strict: + raise portage.exception.ParseError( + "invalid atom: '%s'" % x) + + if isinstance(x, portage.dep.Atom) and x.use: + if x.use.conditional: + evaluated_atom = portage.dep.remove_slot(x) + if x.slot: + evaluated_atom += ":%s" % x.slot + evaluated_atom += str(x.use.evaluate_conditionals(myuse)) + x = portage.dep.Atom(evaluated_atom) + mykey = dep_getkey(x) if not mykey.startswith("virtual/"): newsplit.append(x) @@ -5768,8 +5782,15 @@ def dep_expand(mydep, mydb=None, use_cache=1, settings=None): myindex = orig_dep.index(mydep) prefix = orig_dep[:myindex] postfix = orig_dep[myindex+len(mydep):] - return portage.dep.Atom(prefix + cpv_expand( - mydep, mydb=mydb, use_cache=use_cache, settings=settings) + postfix) + expanded = cpv_expand(mydep, mydb=mydb, + use_cache=use_cache, settings=settings) + try: + return portage.dep.Atom(prefix + expanded + postfix) + except portage.exception.InvalidAtom: + # Missing '=' prefix is allowed for backward compatibility. + if not isvalidatom("=" + prefix + expanded + postfix): + raise + return portage.dep.Atom("=" + prefix + expanded + postfix) def dep_check(depstring, mydbapi, mysettings, use="yes", mode=None, myuse=None, use_cache=1, use_binaries=0, myroot="/", trees=None): diff --git a/pym/portage/dbapi/porttree.py b/pym/portage/dbapi/porttree.py index 7fe361570..685fd18c5 100644 --- a/pym/portage/dbapi/porttree.py +++ b/pym/portage/dbapi/porttree.py @@ -607,7 +607,6 @@ class portdbapi(dbapi): mydep = dep_expand(origdep, mydb=self, settings=self.mysettings) mykey = dep_getkey(mydep) - myslot = dep_getslot(mydep) if level == "list-visible": #a list of all visible packages, not called directly (just by xmatch()) #myval = self.visible(self.cp_list(mykey)) @@ -650,7 +649,7 @@ class portdbapi(dbapi): continue if not eapi_is_supported(metadata["EAPI"]): continue - if myslot and myslot != metadata["SLOT"]: + if mydep.slot and mydep.slot != metadata["SLOT"]: continue if settings.getMissingKeywords(cpv, metadata): continue @@ -700,15 +699,7 @@ class portdbapi(dbapi): else: print "ERROR: xmatch doesn't handle", level, "query!" raise KeyError - if myslot is not None and isinstance(myval, list): - slotmatches = [] - for cpv in myval: - try: - if self.aux_get(cpv, ["SLOT"])[0] == myslot: - slotmatches.append(cpv) - except KeyError: - pass # ebuild masked by corruption - myval = slotmatches + if self.frozen and (level not in ["match-list", "bestmatch-list"]): self.xcache[level][mydep] = myval if origdep and origdep != mydep: diff --git a/pym/portage/dep.py b/pym/portage/dep.py index fa87a7719..75d4f4c47 100644 --- a/pym/portage/dep.py +++ b/pym/portage/dep.py @@ -19,6 +19,7 @@ # import re, sys, types +from itertools import chain import portage.exception from portage.exception import InvalidData, InvalidAtom from portage.versions import catpkgsplit, catsplit, pkgcmp, pkgsplit, ververify @@ -340,14 +341,52 @@ class _use_dep(object): def __init__(self, use): enabled_flags = [] disabled_flags = [] + conditional_enabled = [] + conditional_disabled = [] for x in use: if "-" == x[:1]: - disabled_flags.append(x[1:]) + if "?" == x[-1:]: + conditional_disabled.append(x[1:-1]) + else: + disabled_flags.append(x[1:]) else: - enabled_flags.append(x) + if "?" == x[-1:]: + conditional_enabled.append(x[:-1]) + else: + enabled_flags.append(x) + self.tokens = use + if not isinstance(self.tokens, tuple): + self.tokens = tuple(self.tokens) self.enabled = frozenset(enabled_flags) self.disabled = frozenset(disabled_flags) - self.required = self.enabled.union(self.disabled) + self.conditional_enabled = frozenset(conditional_enabled) + self.conditional_disabled = frozenset(conditional_disabled) + self.conditional = self.conditional_enabled.union( + self.conditional_disabled) + self.required = frozenset(chain(self.enabled, self.disabled, + self.conditional_enabled, self.conditional_disabled)) + + def __str__(self): + return "".join("[%s]" % x for x in self.tokens) + + def evaluate_conditionals(self, use): + """ + Create a new instance with conditionals evaluated as follows: + + parent state conditional result + x x? x + -x x? -x + x -x? -x + -x -x? x + """ + tokens = [] + tokens.extend(self.enabled) + tokens.extend("-" + x for x in self.disabled) + tokens.extend(self.conditional_enabled.intersection(use)) + tokens.extend("-" + x for x in self.conditional_enabled.difference(use)) + tokens.extend("-" + x for x in self.conditional_disabled.intersection(use)) + tokens.extend(self.conditional_disabled.difference(use)) + return _use_dep(tokens) class Atom(str): @@ -499,8 +538,8 @@ def dep_getusedeps( depend ): @return: List of use flags ( or [] if no flags exist ) """ use = getattr(depend, "use", None) - if use is not None: - return use + if use is not None and hasattr(use, "tokens"): + return use.tokens use_list = [] open_bracket = depend.find('[') # -1 = failure (think c++ string::npos) @@ -518,7 +557,8 @@ def dep_getusedeps( depend ): open_bracket = depend.find( '[', open_bracket+1 ) return tuple(use_list) -_invalid_atom_chars_regexp = re.compile("[()|?@]") +_valid_category = re.compile("^\w[\w-]*") +_invalid_atom_chars_regexp = re.compile("[()|@]") def isvalidatom(atom, allow_blockers=False): """ @@ -546,10 +586,13 @@ def isvalidatom(atom, allow_blockers=False): return 0 if allow_blockers and atom.startswith("!"): atom = atom[1:] - try: - mycpv_cps = catpkgsplit(dep_getcpv(atom)) - except InvalidData: - return 0 + cpv = dep_getcpv(atom) + if cpv: + if _valid_category.match(catsplit(cpv)[0]) is None: + return 0 + mycpv_cps = catpkgsplit(cpv) + else: + mycpv_cps = None operator = get_operator(atom) if operator: if operator[0] in "<>" and remove_slot(atom).endswith("*"): diff --git a/pym/portage/sets/base.py b/pym/portage/sets/base.py index 71547813e..ac77e95c3 100644 --- a/pym/portage/sets/base.py +++ b/pym/portage/sets/base.py @@ -3,7 +3,7 @@ # $Id$ from portage import cpv_getkey, flatten -from portage.dep import isvalidatom, match_from_list, \ +from portage.dep import Atom, isvalidatom, match_from_list, \ best_match_to_list, dep_getkey, use_reduce, paren_reduce from portage.exception import InvalidAtom @@ -58,15 +58,16 @@ class PackageSet(object): return self._nonatoms.copy() def _setAtoms(self, atoms): - atoms = map(str.strip, atoms) + self._atoms.clear() self._nonatoms.clear() - for a in atoms[:]: - if a == "": - atoms.remove(a) - elif not isvalidatom(a): - atoms.remove(a) + for a in atoms: + a = a.strip() + if not a: + continue + try: + self._atoms.add(Atom(a)) + except InvalidAtom: self._nonatoms.add(a) - self._atoms = set(atoms) self._updateAtomMap() def load(self): @@ -99,33 +100,15 @@ class PackageSet(object): self._atommap[cp].add(a) # Not sure if this one should really be in PackageSet - def findAtomForPackage(self, cpv, metadata): + def findAtomForPackage(self, pkg): """Return the best match for a given package from the arguments, or None if there are no matches. This matches virtual arguments against the PROVIDE metadata. This can raise an InvalidDependString exception if an error occurs while parsing PROVIDE.""" - cpv_slot = "%s:%s" % (cpv, metadata["SLOT"]) - cp = dep_getkey(cpv) - self._load() # make sure the atoms are loaded - atoms = self._atommap.get(cp) - if atoms: - best_match = best_match_to_list(cpv_slot, atoms) - if best_match: - return best_match - if not metadata["PROVIDE"]: + atoms = list(self.iterAtomsForPackage(pkg)) + if not atoms: return None - provides = flatten(use_reduce(paren_reduce(metadata["PROVIDE"]), - uselist=metadata["USE"].split())) - for provide in provides: - provided_cp = dep_getkey(provide) - atoms = self._atommap.get(provided_cp) - if atoms: - atoms = list(atoms) - transformed_atoms = [atom.replace(provided_cp, cp) for atom in atoms] - best_match = best_match_to_list(cpv_slot, transformed_atoms) - if best_match: - return atoms[transformed_atoms.index(best_match)] - return None + return best_match_to_list(pkg.cpv_slot, atoms) def iterAtomsForPackage(self, pkg): """ diff --git a/pym/portage/tests/__init__.py b/pym/portage/tests/__init__.py index 8380b6c31..7ca048b9a 100644 --- a/pym/portage/tests/__init__.py +++ b/pym/portage/tests/__init__.py @@ -177,4 +177,5 @@ class TextTestRunner(unittest.TextTestRunner): test_cps = ['sys-apps/portage','virtual/portage'] test_versions = ['1.0', '1.0-r1','2.3_p4','1.0_alpha57'] test_slots = [ None, '1','gentoo-sources-2.6.17','spankywashere'] -test_usedeps = ['foo','-bar', ['foo','bar'],['foo','-bar'] ] +test_usedeps = ['foo','-bar', ('foo','bar'), + ('foo','-bar'), ('foo?', '-bar?') ] diff --git a/pym/portage/tests/dep/test_dep_getusedeps.py b/pym/portage/tests/dep/test_dep_getusedeps.py index 30fa7772f..71b0fe290 100644 --- a/pym/portage/tests/dep/test_dep_getusedeps.py +++ b/pym/portage/tests/dep/test_dep_getusedeps.py @@ -24,7 +24,7 @@ class DepGetUseDeps(TestCase): cpv += version if slot: cpv += ":" + slot - if isinstance( use, list ): + if isinstance(use, tuple): for u in use: cpv = cpv + "[" + u + "]" self.assertEqual( dep_getusedeps( @@ -32,7 +32,7 @@ class DepGetUseDeps(TestCase): else: if len(use): self.assertEqual( dep_getusedeps( - cpv + "[" + use + "]" ), [use] ) + cpv + "[" + use + "]" ), (use,) ) else: self.assertEqual( dep_getusedeps( - cpv + "[" + use + "]" ), [] ) + cpv + "[" + use + "]" ), () ) diff --git a/pym/portage/tests/dep/test_isvalidatom.py b/pym/portage/tests/dep/test_isvalidatom.py index 512d9b465..2b99b3dcb 100644 --- a/pym/portage/tests/dep/test_isvalidatom.py +++ b/pym/portage/tests/dep/test_isvalidatom.py @@ -13,8 +13,7 @@ class IsValidAtom(TestCase): """ def testIsValidAtom(self): - - self.todo = True + tests = [ ( "sys-apps/portage", True ), ( "=sys-apps/portage-2.1", True ), ( "=sys-apps/portage-2.1*", True ), @@ -26,9 +25,10 @@ class IsValidAtom(TestCase): ( "sys-apps/portage:foo", True ), ( "sys-apps/portage-2.1:foo", False ), ( "sys-apps/portage-2.1:", False ), + ( "=sys-apps/portage-2.2*:foo[bar][-baz][doc?][-build?]", True ), ( ">~cate-gory/foo-1.0", False ), - ( ">~category/foo-1.0", True ), - ( "<~category/foo-1.0", True ), + ( ">~category/foo-1.0", False ), + ( "<~category/foo-1.0", False ), ( "###cat/foo-1.0", False ), ( "~sys-apps/portage", False ), ( "portage", False ) ] diff --git a/pym/portage/versions.py b/pym/portage/versions.py index 42c700cc7..4b27a213a 100644 --- a/pym/portage/versions.py +++ b/pym/portage/versions.py @@ -293,12 +293,8 @@ def catpkgsplit(mydata,silent=1): 1. If each exists, it returns [cat, pkgname, version, rev] 2. If cat is not specificed in mydata, cat will be "null" 3. if rev does not exist it will be '-r0' - 4. If cat is invalid (specified but has incorrect syntax) - an InvalidData Exception will be thrown """ - - # Categories may contain a-zA-z0-9+_- but cannot start with - - import portage.dep + try: if not catcache[mydata]: return None -- 2.26.2