Merged from trunk 10393:10415
authorFabian Groffen <grobian@gentoo.org>
Sun, 25 May 2008 17:54:21 +0000 (17:54 -0000)
committerFabian Groffen <grobian@gentoo.org>
Sun, 25 May 2008 17:54:21 +0000 (17:54 -0000)
   | 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
pym/portage/__init__.py
pym/portage/dbapi/porttree.py
pym/portage/dep.py
pym/portage/sets/base.py
pym/portage/tests/__init__.py
pym/portage/tests/dep/test_dep_getusedeps.py
pym/portage/tests/dep/test_isvalidatom.py
pym/portage/versions.py

index e652026b639b6e85455159cdcb03a3855f34b9c0..17482b6a3784ad4e4adf67a8cd4986b184d7c031 100644 (file)
@@ -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
index 50e47f0dce2c1047e8a1b9fb5ec7008d1045d8b6..e75c842749b975cfaf9e977b8d24eda226352dc3 100644 (file)
@@ -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):
index 7fe36157067a5c9f8dc5392c6d5c8801ee77fc8a..685fd18c5adec34f824d46dd1fc912723fb55e0f 100644 (file)
@@ -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:
index fa87a7719d1c8462f22a5c47adc53e883b8aa9d6..75d4f4c471dfad96c61c898a1276a9d0828b22dd 100644 (file)
@@ -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("*"):
index 71547813e37bb135a3d19cb11fdc0d3d7b87764a..ac77e95c3597bd7bd863f11e796c3a3f653d5f72 100644 (file)
@@ -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):
                """
index 8380b6c31f167b56e26abb3d0a0f8d15f6ecbee3..7ca048b9ac6b0f974374b37447d3080338b56dfb 100644 (file)
@@ -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?') ]
index 30fa7772fae843fbf19986ce3b1960ea6493d639..71b0fe290100809a39bf24138fc9d5db3948ae12 100644 (file)
@@ -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 + "]" ), () )
index 512d9b4656005588d102589197c6e5991540ab8a..2b99b3dcb0b3f30375b799afa9458cbff2dbe608 100644 (file)
@@ -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 ) ]
index 42c700cc73ed41d3413765d56fd0d8b7b885dadb..4b27a213aa3be27d32b2b6240e73840d4066a881 100644 (file)
@@ -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