Merged from trunk 8141:8142 (Manually applied)
authorFabian Groffen <grobian@gentoo.org>
Sun, 21 Oct 2007 12:24:56 +0000 (12:24 -0000)
committerFabian Groffen <grobian@gentoo.org>
Sun, 21 Oct 2007 12:24:56 +0000 (12:24 -0000)
   | 8142    | Move the emerge module to _emerge to make it private since   |
   | zmedico | it's mostly unsuitable for api consumers at this time.       |

svn path=/main/branches/prefix/; revision=8209

pym/_emerge/__init__.py
pym/portage/news.py
pym/portage/sets/__init__.py
pym/portage/sets/dbapi.py
pym/portage/sets/files.py

index ef3a42dc8ecfa7743ab9962968559e5287f9c0e0..71bac634671d13658fbabc5c72582d3cf6bb916e 100644 (file)
@@ -362,6 +362,22 @@ def create_depgraph_params(myopts, myaction):
                myparams.add("deep")
        return myparams
 
+
+class EmergeConfig(portage.config):
+       def __init__(self, settings, trees=None, setconfig=None):
+               """ You have to specify one of trees or setconfig """
+               portage.config.__init__(self, clone=settings)
+               if not setconfig:
+                       setconfigpaths = [os.path.join(GLOBAL_CONFIG_PATH, "sets.conf")]
+                       setconfigpaths.append(os.path.join(settings["PORTDIR"], "sets.conf"))
+                       setconfigpaths += [os.path.join(x, "sets.conf") for x in settings["PORDIR_OVERLAY"].split()]
+                       setconfigpaths.append(os.path.join(settings["PORTAGE_CONFIGROOT"],
+                               USER_CONFIG_PATH.lstrip(os.path.sep), "sets.conf"))
+                       #setconfig = SetConfig(setconfigpaths, settings, trees)
+                       setconfig = make_default_config(settings, trees)
+               self.setconfig = setconfig
+               self.sets = self.setconfig.getSetsWithAliases()
+
 # search functionality
 class search(object):
 
@@ -375,7 +391,7 @@ class search(object):
        # public interface
        #
        def __init__(self, settings, portdb, vartree, spinner, searchdesc,
-               verbose, setconfig):
+               verbose):
                """Searches the available and installed packages for the supplied search key.
                The list of available and installed packages is created at object instantiation.
                This makes successive searches faster."""
@@ -385,7 +401,7 @@ class search(object):
                self.spinner = spinner
                self.verbose = verbose
                self.searchdesc = searchdesc
-               self.setconfig = setconfig
+               self.setconfig = settings.setconfig
 
        def execute(self,searchkey):
                """Performs the search for the supplied search key"""
@@ -443,7 +459,7 @@ class search(object):
                                if self.searchre.search(full_desc):
                                        self.matches["desc"].append([full_package,masked])
 
-               self.sdict = self.setconfig.getSets()[0]
+               self.sdict = self.setconfig.getSets()
                for setname in self.sdict:
                        self.spinner.update()
                        if match_category:
@@ -555,76 +571,13 @@ class search(object):
                return result
 
 
-#build our package digraph
-def getlist(settings, mode):
-       if mode=="system":
-               mylines = settings.packages
-       elif mode=="world":
-               try:
-                       file_path = os.path.join(settings["ROOT"], portage.WORLD_FILE)
-                       myfile = open(file_path, "r")
-                       mylines = myfile.readlines()
-                       myfile.close()
-               except (OSError, IOError), e:
-                       if e.errno == errno.ENOENT:
-                               portage.writemsg("\n!!! World file does not exist: '%s'\n" % file_path)
-                               mylines=[]
-                       else:
-                               raise
-       mynewlines=[]
-       for x in mylines:
-               myline=" ".join(x.split())
-               if not len(myline):
-                       continue
-               elif myline[0]=="#":
-                       continue
-               elif mode=="system":
-                       if myline[0]!="*":
-                               continue
-                       myline=myline[1:]
-               mynewlines.append(myline.strip())
-
-       return mynewlines
-
-def clean_world(vardb, cpv):
-       """Remove a package from the world file when unmerged."""
-       world_set = WorldSet(vardb.settings["ROOT"])
-       world_set.lock()
-       worldlist = list(world_set) # loads latest from disk
-       mykey = portage.cpv_getkey(cpv)
-       newworldlist = []
-       for x in worldlist:
-               if portage.dep_getkey(x) == mykey:
-                       matches = vardb.match(x, use_cache=0)
-                       if not matches:
-                               #zap our world entry
-                               pass
-                       elif len(matches) == 1 and matches[0] == cpv:
-                               #zap our world entry
-                               pass
-                       else:
-                               #others are around; keep it.
-                               newworldlist.append(x)
-               else:
-                       #this doesn't match the package we're unmerging; keep it.
-                       newworldlist.append(x)
-
-       world_set.replace(newworldlist)
-       world_set.unlock()
-
-
 class RootConfig(object):
        """This is used internally by depgraph to track information about a
        particular $ROOT."""
        def __init__(self, trees):
                self.trees = trees
-               self.settings = trees["vartree"].settings
+               self.settings = EmergeConfig(trees["vartree"].settings, trees=trees)
                self.root = self.settings["ROOT"]
-               self.sets = {}
-               world_set = WorldSet(self.root)
-               self.sets["world"] = world_set
-               system_set = SystemSet(self.settings.profiles)
-               self.sets["system"] = system_set
 
 def create_world_atom(pkg_key, metadata, args_set, root_config):
        """Create a new atom for the world file if one does not exist.  If the
@@ -636,7 +589,7 @@ def create_world_atom(pkg_key, metadata, args_set, root_config):
        arg_atom = args_set.findAtomForPackage(pkg_key, metadata)
        cp = portage.dep_getkey(arg_atom)
        new_world_atom = cp
-       sets = root_config.sets
+       sets = root_config.settings.sets
        portdb = root_config.trees["porttree"].dbapi
        vardb = root_config.trees["vartree"].dbapi
        available_slots = set(portdb.aux_get(cpv, ["SLOT"])[0] \
@@ -2769,8 +2722,8 @@ class depgraph(object):
                world_problems = False
 
                root_config = self.roots[self.target_root]
-               world_set = root_config.sets["world"]
-               system_set = root_config.sets["system"]
+               world_set = root_config.settings.sets["world"]
+               system_set = root_config.settings.sets["system"]
                mylist = list(system_set)
                self._sets["system"] = system_set
                if mode == "world":
@@ -3358,8 +3311,8 @@ class depgraph(object):
 
                                pkg_cp = xs[0]
                                root_config = self.roots[myroot]
-                               system_set = root_config.sets["system"]
-                               world_set  = root_config.sets["world"]
+                               system_set = root_config.settings.sets["system"]
+                               world_set  = root_config.settings.sets["world"]
 
                                pkg_system = False
                                pkg_world = False
@@ -3572,7 +3525,7 @@ class depgraph(object):
                        if x in self.myopts:
                                return
                root_config = self.roots[self.target_root]
-               world_set = root_config.sets["world"]
+               world_set = root_config.settings.sets["world"]
                world_set.lock()
                world_set.load() # maybe it's changed on disk
                args_set = self._sets["args"]
@@ -3765,10 +3718,10 @@ class MergeTask(object):
                if settings.get("PORTAGE_DEBUG", "") == "1":
                        self.edebug = 1
                self.pkgsettings = {}
-               self.pkgsettings[self.target_root] = portage.config(clone=settings)
+               self.pkgsettings[self.target_root] = EmergeConfig(settings, setconfig=settings.setconfig)
                if self.target_root != "/":
                        self.pkgsettings["/"] = \
-                               portage.config(clone=trees["/"]["vartree"].settings)
+                               EmergeConfig(trees["/"]["vartree"].settings, setconfig=settings.setconfig)
                self.curval = 0
 
        def merge(self, mylist, favorites, mtimedb):
@@ -3835,9 +3788,9 @@ class MergeTask(object):
                        del shown_verifying_msg, quiet_settings
 
                root_config = RootConfig(self.trees[self.target_root])
-               system_set = root_config.sets["system"]
+               system_set = root_config.settings.sets["system"]
                args_set = InternalPackageSet(favorites)
-               world_set = root_config.sets["world"]
+               world_set = root_config.settings.sets["world"]
                if "--resume" not in self.myopts:
                        mymergelist = mylist
                        mtimedb["resume"]["mergelist"]=mymergelist[:]
@@ -4254,7 +4207,7 @@ def unmerge(settings, myopts, vartree, unmerge_action, unmerge_files,
        try:
                if os.access(vdb_path, os.W_OK):
                        vdb_lock = portage.locks.lockdir(vdb_path)
-               realsyslist = getlist(settings, "system")
+               realsyslist = settings.sets["system"].getAtoms()
                syslist = []
                for x in realsyslist:
                        mycp = portage.dep_getkey(x)
@@ -4289,7 +4242,7 @@ def unmerge(settings, myopts, vartree, unmerge_action, unmerge_files,
                        if not unmerge_files or "world" in unmerge_files:
                                candidate_catpkgs.extend(vartree.dbapi.cp_all())
                        elif "system" in unmerge_files:
-                               candidate_catpkgs.extend(getlist(settings, "system"))
+                               candidate_catpkgs.extend(settings.sets["system"].getAtoms())
                else:
                        #we've got command-line arguments
                        if not unmerge_files:
@@ -4541,7 +4494,7 @@ def unmerge(settings, myopts, vartree, unmerge_action, unmerge_files,
                                show_unmerge_failure_message(y, ebuild, retval)
                                sys.exit(retval)
                        else:
-                               clean_world(vartree.dbapi, y)
+                               settings.sets["world"].cleanPackage(vartree.dbapi, y)
                                emergelog(xterm_titles, " >>> unmerge success: "+y)
        return 1
 
@@ -5730,13 +5683,13 @@ def action_info(settings, trees, myopts, myfiles):
                                mydbapi=trees[settings["ROOT"]]["vartree"].dbapi,
                                tree="vartree")
 
-def action_search(settings, portdb, vartree, myopts, myfiles, spinner, setconfig):
+def action_search(settings, portdb, vartree, myopts, myfiles, spinner):
        if not myfiles:
                print "emerge: no search terms provided."
        else:
                searchinstance = search(settings, portdb,
                        vartree, spinner, "--searchdesc" in myopts,
-                       "--quiet" not in myopts, setconfig)
+                       "--quiet" not in myopts)
                for mysearch in myfiles:
                        try:
                                searchinstance.execute(mysearch)
@@ -6475,9 +6428,12 @@ def load_emerge_config(trees=None):
                if myroot != "/":
                        settings = trees[myroot]["vartree"].settings
                        break
+       
+       settings = EmergeConfig(settings, trees=trees[settings["ROOT"]])
 
        mtimedbfile = os.path.join("/", portage.CACHE_PATH.lstrip(os.path.sep), "mtimedb")
        mtimedb = portage.MtimeDB(mtimedbfile)
+       
        return settings, trees, mtimedb
 
 def adjust_config(myopts, settings):
@@ -6660,28 +6616,20 @@ def emerge_main():
                        print colorize("BAD", "\n*** emerging by path is broken and may not always work!!!\n")
                        break
 
-       setconfigpaths = [os.path.join(GLOBAL_CONFIG_PATH, "sets.conf")]
-       setconfigpaths.append(os.path.join(settings["PORTDIR"], "sets.conf"))
-       setconfigpaths += [os.path.join(x, "sets.conf") for x in settings["PORDIR_OVERLAY"].split()]
-       setconfigpaths.append(os.path.join(settings["PORTAGE_CONFIGROOT"],
-               USER_CONFIG_PATH.lstrip(os.path.sep), "sets.conf"))
-       #setconfig = SetConfig(setconfigpaths, settings, trees[settings["ROOT"]])
-       setconfig = make_default_config(settings, trees[settings["ROOT"]])
-       del setconfigpaths
+       # only expand sets for actions taking package arguments
        if myaction not in ["search", "metadata", "sync"]:
                oldargs = myfiles[:]
-               packagesets, setconfig_errors = setconfig.getSetsWithAliases()
-               for s in packagesets:
+               for s in settings.sets:
                        if s in myfiles:
                                # TODO: check if the current setname also resolves to a package name
                                if myaction in ["unmerge", "prune", "clean", "depclean"] and not packagesets[s].supportsOperation("unmerge"):
                                        print "emerge: the given set %s does not support unmerge operations" % s
                                        sys.exit(1)
-                               if not packagesets[s].getAtoms():
+                               if not settings.sets[s].getAtoms():
                                        print "emerge: '%s' is an empty set" % s
                                else:
-                                       myfiles.extend(packagesets[s].getAtoms())
-                               for e in packagesets[s].errors:
+                                       myfiles.extend(settings.sets[s].getAtoms())
+                               for e in settings.sets[s].errors:
                                        print e
                                myfiles.remove(s)
                # Need to handle empty sets specially, otherwise emerge will react 
@@ -6879,7 +6827,7 @@ def emerge_main():
        elif "search"==myaction:
                validate_ebuild_environment(trees)
                action_search(settings, portdb, trees["/"]["vartree"],
-                       myopts, myfiles, spinner, setconfig)
+                       myopts, myfiles, spinner)
        elif myaction in ("clean", "unmerge") or \
                (myaction == "prune" and "--nodeps" in myopts):
                validate_ebuild_environment(trees)
index dff66e3da5daafbe842edaf2ce8a5ce44da42ea5..6f2eac2b2afa09d9a6af7d75315f5526fbfe6cfa 100644 (file)
@@ -6,7 +6,7 @@
 import os
 import re
 from portage.const import INCREMENTALS, PROFILE_PATH, NEWS_LIB_PATH
-from portage.util import ensure_dirs, apply_permissions, normalize_path
+from portage.util import ensure_dirs, apply_permissions, normalize_path, grabfile, write_atomic
 from portage.data import portage_gid
 from portage.locks import lockfile, unlockfile, lockdir, unlockdir
 from portage.exception import FileNotFound
@@ -19,20 +19,16 @@ class NewsManager(object):
        
        Creating a news manager requires:
        root - typically ${ROOT} see man make.conf and man emerge for details
-       NEWS_PATH - path to news items; usually $REPODIR/metadata/news
-       UNREAD_PATH - path to the news.repoid.unread file; this helps us track news items
+       news_path - path to news items; usually $REPODIR/metadata/news
+       unread_path - path to the news.repoid.unread file; this helps us track news items
        
        """
 
-       TIMESTAMP_FILE = 'news-timestamp'
-
-       def __init__(self, portdb, vardb, NEWS_PATH, UNREAD_PATH, LANGUAGE_ID='en'):
-               self.NEWS_PATH = NEWS_PATH
-               self.UNREAD_PATH = UNREAD_PATH
-               self.TIMESTAMP_PATH = os.path.join(vardb.root,
-                       NEWS_LIB_PATH, NewsManager.TIMESTAMP_FILE)
+       def __init__(self, portdb, vardb, news_path, unread_path, language_id='en'):
+               self.news_path = news_path
+               self.unread_path = unread_path
                self.target_root = vardb.root
-               self.LANGUAGE_ID = LANGUAGE_ID
+               self.language_id = language_id
                self.config = vardb.settings
                self.vdb = vardb
                self.portdb = portdb
@@ -48,7 +44,7 @@ class NewsManager(object):
                # Ensure that the unread path exists and is writable.
                dirmode  = 02070
                modemask =    02
-               ensure_dirs(self.UNREAD_PATH, mode=dirmode, mask=modemask, gid=portage_gid)
+               ensure_dirs(self.unread_path, mode=dirmode, mask=modemask, gid=portage_gid)
 
        def updateItems(self, repoid):
                """
@@ -61,35 +57,31 @@ class NewsManager(object):
                if repoid not in repos:
                        raise ValueError("Invalid repoID: %s" % repoid)
 
-               timestamp_file = self.TIMESTAMP_PATH + repoid
-               if os.path.exists(timestamp_file):
-                       # Make sure the timestamp has correct permissions.
-                       apply_permissions(filename=timestamp_file, 
-                               uid=int(self.config["PORTAGE_INST_UID"]), gid=portage_gid, mode=0664)
-                       timestamp = os.stat(timestamp_file).st_mtime
-               else:
-                       timestamp = 0
-
-               path = os.path.join(self.portdb.getRepositoryPath(repoid), self.NEWS_PATH)
+               path = os.path.join(self.portdb.getRepositoryPath(repoid), self.news_path)
 
                # Skip reading news for repoid if the news dir does not exist.  Requested by
                # NightMorph :)
                if not os.path.exists(path):
                        return None
                news = os.listdir(path)
+
+               skipfile = os.path.join(self.unread_path, "news-%s.skip" % repoid)
+               skiplist = grabfile(skipfile)
                updates = []
                for itemid in news:
+                       if itemid in skiplist:
+                               continue
                        try:
-                               filename = os.path.join(path, itemid, itemid + "." + self.LANGUAGE_ID + ".txt")
-                               item = NewsItem(filename, itemid, timestamp)
-                       except (TypeError, ValueError):
+                               filename = os.path.join(path, itemid, itemid + "." + self.language_id + ".txt")
+                               item = NewsItem(filename, itemid)
+                       except (TypeError):
                                continue
                        if item.isRelevant(profile=self._profile_path,
                                config=self.config, vardb=self.vdb):
                                updates.append(item)
                del path
                
-               path = os.path.join(self.UNREAD_PATH, 'news-' + repoid + '.unread')
+               path = os.path.join(self.unread_path, 'news-%s.unread' % repoid)
                try:
                        unread_lock = lockfile(path)
                        if not os.path.exists(path):
@@ -103,13 +95,13 @@ class NewsManager(object):
 
                        for item in updates:
                                unread_file.write(item.name + "\n")
+                               skiplist.append(item.name)
                        unread_file.close()
                finally:
                        unlockfile(unread_lock)
-               
-               # Touch the timestamp file
-               f = open(timestamp_file, 'w')
-               f.close()
+                       write_atomic(skipfile, "\n".join(skiplist)+"\n")
+               apply_permissions(filename=skipfile, 
+                               uid=int(self.config["PORTAGE_INST_UID"]), gid=portage_gid, mode=0664)
 
        def getUnreadItems(self, repoid, update=False):
                """
@@ -122,7 +114,7 @@ class NewsManager(object):
                if update:
                        self.updateItems(repoid)
                
-               unreadfile = os.path.join(self.UNREAD_PATH, 'news-' + repoid + '.unread')
+               unreadfile = os.path.join(self.unread_path, 'news-%s.unread' % repoid)
                try:
                        try:
                                unread_lock = lockfile(unreadfile)
@@ -156,15 +148,12 @@ class NewsItem(object):
 
        """
        
-       def __init__(self, path, name, cache_mtime = 0):
+       def __init__(self, path, name):
                """ 
-               For a given news item we only want if it path is a file and it's 
-               mtime is newer than the cache'd timestamp.
+               For a given news item we only want if it path is a file.
                """
                if not os.path.isfile(path):
                        raise TypeError("%s is no regular file" % path)
-               if not os.stat(path).st_mtime > cache_mtime:
-                       raise ValueError("%s / %s timestamp mismatch" % (str(os.stat(path).st_mtime), str(cache_mtime)))
                self.path = path
                self.name = name
                self._parsed = False
index dc281dea3e5b94eb870c192d2c9b80d3910f227a..ac3f4b0cbfe70e4c0ad2a48d140525f6848f44a1 100644 (file)
@@ -179,6 +179,8 @@ class SetConfig(SafeConfigParser):
                self.trees = trees
                self.settings = settings
                self._parsed = False
+               self.active = []
+               self.aliases = {}
 
        def _parse(self):
                if self._parsed:
@@ -230,20 +232,22 @@ class SetConfig(SafeConfigParser):
        
        def getSets(self):
                self._parse()
-               return (self.psets, self.errors)
+               return self.psets
 
        def getSetsWithAliases(self):
                self._parse()
-               shortnames = {}
-               for name in self.psets:
-                       mysplit = name.split("/")
-                       if len(mysplit) > 1 and mysplit[0] == "sets" and mysplit[-1] != "":
-                               if mysplit[-1] in shortnames:
-                                       del shortnames[mysplit[-1]]
-                               else:
-                                       shortnames[mysplit[-1]] = self.psets[name]
-               shortnames.update(self.psets)
-               return (shortnames, self.errors)
+               if not self.aliases:
+                       shortnames = {}
+                       for name in self.psets:
+                               mysplit = name.split("/")
+                               if len(mysplit) > 1 and mysplit[0] == "sets" and mysplit[-1] != "":
+                                       if mysplit[-1] in shortnames:
+                                               del shortnames[mysplit[-1]]
+                                       else:
+                                               shortnames[mysplit[-1]] = self.psets[name]
+                       shortnames.update(self.psets)
+                       self.aliases = shortnames
+               return self.aliases
 
 def make_default_config(settings, trees):
        sc = SetConfig([], settings, trees)
index fbff27054d3222f5d74dcf30134c0d640a1a240e..bc842ae2c3feb7510ebb9e3e74179fdb41f99268 100644 (file)
@@ -113,9 +113,10 @@ class CategorySet(PackageSet):
 class LibraryConsumerSet(PackageSet):
        _operations = ["merge", "unmerge"]
 
-       def __init__(self, vardbapi):
+       def __init__(self, vardbapi, debug=False):
                super(LibraryConsumerSet, self).__init__()
                self.dbapi = vardbapi
+               self.debug = debug
 
        def mapPathsToAtoms(self, paths):
                rValue = set()
@@ -162,11 +163,19 @@ class MissingLibraryConsumerSet(LibraryConsumerSet):
                                        found=True
                                        break
                        if not found:
+                               print "missing library: %s" % lib
+                               print "consumers:"
+                               for x in self.dbapi.libmap.get()[lib]:
+                                       print "    ", x
                                consumers.update(self.dbapi.libmap.get()[lib])
                if not consumers:
                        return
                self._setAtoms(self.mapPathsToAtoms(consumers))
        
        def singleBuilder(cls, options, settings, trees):
-               return MissingLibraryConsumerSet(trees["vartree"].dbapi)
+               if options.get("debug", "true").lower() in ["true", "on", "1", "yes"]:
+                       debug = True
+               else:
+                       debug = False
+               return MissingLibraryConsumerSet(trees["vartree"].dbapi, debug=debug)
        singleBuilder = classmethod(singleBuilder)
index 61ab1bc5303d54ae183dd6937fc4603dede3de74..71a2ecd85361cfbdbac9e98579535334373532de 100644 (file)
@@ -11,6 +11,7 @@ from portage import portage_gid
 from portage.sets import PackageSet, EditablePackageSet, SetConfigError
 from portage.env.loaders import ItemFileLoader, KeyListFileLoader
 from portage.env.validators import ValidAtomValidator
+from portage import dep_getkey, cpv_getkey
 
 __all__ = ["StaticFileSet", "ConfigFileSet", "WorldSet"]
 
@@ -135,6 +136,30 @@ class WorldSet(StaticFileSet):
                unlockfile(self._lock)
                self._lock = None
 
+       def cleanPackage(self, vardb, cpv):
+               self.lock()
+               worldlist = list(self.getAtoms()) # loads latest from disk
+               mykey = cpv_getkey(cpv)
+               newworldlist = []
+               for x in worldlist:
+                       if dep_getkey(x) == mykey:
+                               matches = vardb.match(x, use_cache=0)
+                               if not matches:
+                                       #zap our world entry
+                                       pass
+                               elif len(matches) == 1 and matches[0] == cpv:
+                                       #zap our world entry
+                                       pass
+                               else:
+                                       #others are around; keep it.
+                                       newworldlist.append(x)
+                       else:
+                               #this doesn't match the package we're unmerging; keep it.
+                               newworldlist.append(x)
+
+               self.replace(newworldlist)
+               self.unlock()
+
        def singleBuilder(self, options, settings, trees):
                return WorldSet(settings["ROOT"])
        singleBuilder = classmethod(singleBuilder)