Apply updates from genscripts repo
authorfuzzyray <fuzzyray@gentoo.org>
Wed, 3 Jun 2009 23:04:31 +0000 (23:04 -0000)
committerfuzzyray <fuzzyray@gentoo.org>
Wed, 3 Jun 2009 23:04:31 +0000 (23:04 -0000)
svn path=/trunk/gentoolkit/; revision=659

20 files changed:
pym/gentoolkit/__init__.py
pym/gentoolkit/equery/__init__.py
pym/gentoolkit/equery/belongs.py
pym/gentoolkit/equery/changes.py
pym/gentoolkit/equery/check.py
pym/gentoolkit/equery/depends.py
pym/gentoolkit/equery/depgraph.py
pym/gentoolkit/equery/files.py
pym/gentoolkit/equery/hasuse.py
pym/gentoolkit/equery/list_.py
pym/gentoolkit/equery/meta.py
pym/gentoolkit/equery/size.py
pym/gentoolkit/equery/uses.py
pym/gentoolkit/equery/which.py
pym/gentoolkit/errors.py
pym/gentoolkit/glsa/__init__.py
pym/gentoolkit/helpers.py
pym/gentoolkit/helpers2.py
pym/gentoolkit/package.py
pym/gentoolkit/versionmatch.py [new file with mode: 0644]

index 62e359b225c3fe3b9b690f0b8d6936f4dcc6cd56..35d89aca4bf65045acaf2481d7bcf83c19cfcaac 100644 (file)
@@ -6,47 +6,37 @@
 #
 # $Header$
 
-__author__ = "Karl Trygve Kalleberg"
-__productname__ = "gentoolkit"
-__description__ = "Gentoolkit Common Library"
+# =======
+# Imports 
+# =======
 
-import os
-import sys
-try:
-       import portage
-except ImportError:
-       sys.path.insert(0, "/usr/lib/portage/pym")
-       import portage
-import re
+import portage
 try:
        from threading import Lock
 except ImportError:
        # If we don't have thread support, we don't need to worry about
        # locking the global settings object. So we define a "null" Lock.
-       class Lock:
+       class Lock(object):
                def acquire(self):
                        pass
                def release(self):
                        pass
 
-try:
-       import portage.exception as portage_exception
-except ImportError:
-       import portage_exception
+# =======
+# Globals
+# =======
 
-try:
-       settingslock = Lock()
-       settings = portage.config(clone=portage.settings)
-       porttree = portage.db[portage.root]["porttree"]
-       vartree  = portage.db[portage.root]["vartree"]
-       virtuals = portage.db[portage.root]["virtuals"]
-except portage_exception.PermissionDenied, e:
-       sys.stderr.write("Permission denied: '%s'\n" % str(e))
-       sys.exit(e.errno)
+PORTDB = portage.db[portage.root]["porttree"].dbapi
+VARDB  = portage.db[portage.root]["vartree"].dbapi
+VIRTUALS = portage.db[portage.root]["virtuals"]
 
 Config = {
        "verbosityLevel": 3
 }
 
-from helpers import *
-from package import *
+try:
+       settingslock = Lock()
+       settings = portage.config(clone=portage.settings)
+except portage.exception.PermissionDenied, err:
+       sys.stderr.write("Permission denied: '%s'\n" % str(err))
+       sys.exit(e.errno)
index c270eb9cccdfd072cb527952b7fc9a62fad2a282..8764e1c11a51281c8168927b8bdf62e704999fad 100644 (file)
@@ -25,9 +25,11 @@ import sys
 import time
 from getopt import getopt, GetoptError
 
+from portage import exception
+
 import gentoolkit
 import gentoolkit.pprinter as pp
-from gentoolkit import catpkgsplit, settings, Package, Config
+from gentoolkit import settings, Config
 from gentoolkit.textwrap_ import TextWrapper
 
 __productname__ = "equery"
@@ -123,34 +125,6 @@ def format_options(options):
        return '\n'.join(result)
 
 
-def format_package_names(match_set, status):
-       """Add location and mask status to package names.
-       
-       @type match_set: list of gentoolkit.package.Package
-       @param match_set: packages to format
-       @rtype: list
-       @return: formatted packages
-       """
-
-       arch = gentoolkit.settings["ARCH"]
-       formatted_packages = []
-       pfxmodes = ['---', 'I--', '-P-', '--O']
-       maskmodes = ['  ', ' ~', ' -', 'M ', 'M~', 'M-']
-
-       for pkg in match_set:
-               mask = get_mask_status(pkg, arch)
-               pkgcpv = pkg.get_cpv()
-               slot = pkg.get_env_var("SLOT")
-
-               formatted_packages.append("[%s] [%s] %s (%s)" %
-                       (pfxmodes[status],
-                       pp.maskflag(maskmodes[mask]),
-                       pp.cpv(pkgcpv),
-                       str(slot)))
-       
-       return formatted_packages
-
-
 def format_filetype(path, fdesc, show_type=False, show_md5=False,
                show_timestamp=False):
        """Format a path for printing.
@@ -214,36 +188,6 @@ def format_timestamp(timestamp):
        return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(timestamp)))
 
 
-def get_mask_status(pkg, arch):
-       """Get the mask status of a given package. 
-
-       @type pkg: gentoolkit.package.Package
-       @param pkg: pkg to get mask status of
-       @type arch: str
-       @param arch: output of gentoolkit.settings["ARCH"]
-       @rtype: int
-       @return: an index for this list: ["  ", " ~", " -", "M ", "M~", "M-"]
-               0 = not masked
-               1 = keyword masked
-               2 = arch masked
-               3 = hard masked
-               4 = hard and keyword masked,
-               5 = hard and arch masked
-       """
-
-       # Determining mask status
-       keywords = pkg.get_env_var("KEYWORDS").split()
-       mask_status = 0
-       if pkg.is_masked():
-               mask_status += 3
-       if ("~%s" % arch) in keywords:
-               mask_status += 1
-       elif ("-%s" % arch) in keywords or "-*" in keywords:
-               mask_status += 2
-
-       return mask_status
-
-
 def initialize_configuration():
        """Setup the standard equery config"""
 
@@ -259,6 +203,8 @@ def initialize_configuration():
        # Color handling: -1: Use Portage settings, 0: Force off, 1: Force on
        Config['color'] = -1
 
+       Config['quiet'] = False
+
        # Guess color output
        if (Config['color'] == -1 and (not sys.stdout.isatty() or
                settings["NOCOLOR"] in ("yes", "true")) or
@@ -316,7 +262,7 @@ def parse_global_options(global_opts, args):
                                print_help()
                                sys.exit(0)
                elif opt in ('-q','--quiet'):
-                       Config["verbosityLevel"] = 0
+                       Config["quiet"] = True
                elif opt in ('-C', '--no-color', '--nocolor'):
                        Config['color'] = 0
                        pp.output.nocolor()
@@ -368,7 +314,6 @@ def main():
                print_help(with_description=False)
                sys.exit(2)
 
-
        # Parse global options
        need_help = parse_global_options(global_opts, args)
 
@@ -381,6 +326,11 @@ def main():
        if need_help:
                module_args.append('--help')
 
+       if Config['piping'] or Config['quiet']:
+               Config['verbose'] = False
+       else:
+               Config['verbose'] = True
+
        try:
                expanded_module_name = expand_module_name(module_name)
        except KeyError:
@@ -392,16 +342,10 @@ def main():
                loaded_module = __import__(expanded_module_name, globals(),
                        locals(), [], -1)
                loaded_module.main(module_args)
-       except ValueError, err:
-               if isinstance(err[0], list):
-                       pp.print_error("Ambiguous package name. Use one of: ")
-                       while err[0]:
-                               print " " + err[0].pop()
-               else:
-                       pp.print_error("Internal portage error, terminating")
-                       if err:
-                               pp.print_error(str(err[0]))
-               sys.exit(1)
+       except exception.AmbiguousPackageName, err:
+               pp.print_error("Ambiguous package name. Use one of: ")
+               while err[0]:
+                       print " " + err[0].pop()
        except IOError, err:
                if err.errno != errno.EPIPE:
                        raise
index 6408ec7561c3acd18a635d93d9448e0a8637ae77..215fbd1e8fecab8e3440866cac2eeaf9c19c3200 100644 (file)
@@ -132,7 +132,7 @@ def main(input_args):
 
        query_re = prepare_search_regex(queries)
 
-       if not Config["piping"]:
+       if Config['verbose']:
                pp.print_info(3, " * Searching for %s ... "
                        % (pp.regexpquery(",".join(queries))))
        
@@ -148,13 +148,13 @@ def main(input_args):
                                        pkg_str = pkg.key
                                else:
                                        pkg_str = pkg.cpv
-                               if Config['piping']:
-                                       print pkg_str
-                               else:
+                               if Config['verbose']:
                                        file_str = pp.path(format_filetype(cfile, files[cfile]))
-                                       pp.print_info(0, "%s (%s)" % (pkg_str, file_str))
+                                       print "%s (%s)" % (pkg_str, file_str)
+                               else:
+                                       print pkg_str
 
-                                       found_match = True
+                               found_match = True
 
                if found_match and QUERY_OPTS["earlyOut"]:
                        break
index b7644bed496a590344b87e3cf600d1b077bc8f85..604bb45dacefb053d66b0a154b503c2d586ce908 100644 (file)
@@ -28,7 +28,8 @@ import gentoolkit.pprinter as pp
 from gentoolkit import errors
 from gentoolkit.equery import format_options, mod_usage
 from gentoolkit.helpers2 import find_best_match, find_packages
-from gentoolkit.package import Package, VersionMatch
+from gentoolkit.package import Package
+from gentoolkit.versionmatch import VersionMatch
 
 # =======
 # Globals
@@ -89,7 +90,7 @@ def get_logpath(pkg):
 
        logpath = os.path.join(pkg.get_package_path(), 'ChangeLog')
        if not os.path.isfile(logpath) or not os.access(logpath, os.R_OK):
-               pp.die(1, "%s does not exist or is unreadable"
+               raise errors.GentoolkitFatalError("%s does not exist or is unreadable"
                        % pp.path(logpath))
        
        return logpath
@@ -133,6 +134,8 @@ def index_changelog(entries):
                # Extract the package name from the entry, ex: 
                # *xterm-242 (07 Mar 2009) => xterm-242
                pkg_name = entry.split(' ', 1)[0].lstrip('*')
+               if not pkg_name.strip():
+                       continue
                pkg_split = pkgsplit(pkg_name)
                result.append(
                        (VersionMatch(op="=", ver=pkg_split[1], rev=pkg_split[2]), entry))
@@ -315,6 +318,9 @@ def main(input_args):
                pkg = get_match(query)
                logpath = get_logpath(pkg)
                log_entries = split_changelog(logpath)
+               if not any(log_entries):
+                       raise errors.GentoolkitFatalError(
+                               "%s exists but doesn't contain entries." % pp.path(logpath))
                indexed_entries = index_changelog(log_entries)
 
                #
@@ -328,6 +334,9 @@ def main(input_args):
                        for entry in log_entries[:end]:
                                print entry
                        first_run = False
+               elif log_entries and not indexed_entries:
+                       # We can't match anything, so just print latest:
+                       print log_entries[0].strip()
                else:
                        if ranged_query:
                                pkg = ranged_query
index ffddf72073635f9f9665dd9d2061715dfc76bd71..2531970d28ced4004071ad358cabed0ff6a04b09 100644 (file)
@@ -213,20 +213,20 @@ def main(input_args):
                matches.sort()
 
                for pkg in matches:
-                       if not Config["piping"] and Config["verbosityLevel"] >= 3:
-                               print "[ Checking %s ]" % pp.cpv(pkg.cpv)
+                       if Config['verbose']:
+                               print " * Checking %s ..." % pp.emph(pkg.cpv)
                        else:
                                print "%s:" % pkg.cpv
 
                        passed, checked, errs = run_checks(pkg.get_contents())
 
-                       if not Config["piping"] and Config["verbosityLevel"] >= 3:
+                       if Config['verbose']:
                                for err in errs:
                                        pp.print_error(err)
 
                        passed = pp.number(str(passed))
                        checked = pp.number(str(checked))
-                       info = " * %(passed)s out of %(checked)s files passed"
+                       info = "   %(passed)s out of %(checked)s files passed"
                        print info % locals()
 
                        first_run = False
index 394c35b3cc60a386dbe4792ac64c48b612c259ef..6377f2c18fd9138d7ac4ada60f989e522b3d3a41 100644 (file)
@@ -33,10 +33,8 @@ QUERY_OPTS = {
        "includePortTree": False,
        "includeOverlayTree": False,
        "isRegex": False,
-       "matchExact": True,
        "onlyDirect": True,
-       "onlyInstalled": True,
-       "printMatchInfo": True,
+       "printMatchInfo": (not Config['quiet']),
        "indentLevel": 0,
        "depth": -1
 }
@@ -77,12 +75,11 @@ def cache_package_list(pkg_cache=None):
        """Ensure that the package cache is set."""
 
        if not pkg_cache:
-               if QUERY_OPTS["onlyInstalled"]:
-                       # TODO: move away from using strings here
-                       packages = get_installed_cpvs()
+               if QUERY_OPTS['includePortTree']:
+                       packages = [Package(x) for x in get_cpvs()]
                else:
-                       packages = get_cpvs()
-               packages.sort(compare_package_strings)
+                       packages = [Package(x) for x in get_installed_cpvs()]
+               packages.sort()
                pkg_cache = packages
        else:
                packages = pkg_cache
@@ -108,18 +105,18 @@ def display_dependencies(cpv_is_displayed, dependency, cpv):
 
        if not cpv_is_displayed:
                if dependency[1]:
-                       if not Config["piping"] and Config["verbosityLevel"] >= 3:
+                       if Config['verbose']:
                                print indent + pp.cpv(cpv),
                                print "(" + useflags + " ? " + atom + ")"
                        else:
                                print indent + cpv
                else:
-                       if not Config["piping"] and Config["verbosityLevel"] >= 3:
+                       if Config['verbose']:
                                print indent + pp.cpv(cpv),
                                print "(" + atom + ")"
                        else:
                                print indent + cpv
-       elif not Config["piping"] and Config["verbosityLevel"] >= 3:
+       elif Config['verbose']:
                indent = indent + " " * len(cpv)
                if dependency[1]:
                        print indent + " (" + useflags + " ? " + atom + ")"
@@ -134,7 +131,7 @@ def find_dependencies(matches, pkg_cache):
        @param queries: packages to find the dependencies for
        """
 
-       for pkg in [Package(x) for x in cache_package_list(pkg_cache)]:
+       for pkg in cache_package_list(pkg_cache):
                if not pkg.cpv in PKGDEPS:
                        try:
                                deps = pkg.get_runtime_deps() + pkg.get_compiletime_deps()
@@ -192,11 +189,11 @@ def parse_module_options(module_opts):
                        print_help()
                        sys.exit(0)
                elif opt in ('-a', '--all-packages'):
-                       QUERY_OPTS["onlyInstalled"] = False
+                       QUERY_OPTS['includePortTree'] = True
                elif opt in ('-d', '--direct'):
                        continue
                elif opt in ('-D', '--indirect'):
-                       QUERY_OPTS["onlyDirect"] = False
+                       QUERY_OPTS['onlyDirect'] = False
                elif opt in ('--depth'):
                        if posarg.isdigit():
                                depth = int(posarg)
index f4723c2a72833cc707570821787d5f1d6898c4b5..b9cd0a17bbd91be2459f5f919987cd7b7bf92a6b 100644 (file)
@@ -19,7 +19,7 @@ import gentoolkit
 import gentoolkit.pprinter as pp
 from gentoolkit import errors
 from gentoolkit.equery import format_options, mod_usage, Config
-from gentoolkit.helpers2 import do_lookup
+from gentoolkit.helpers2 import do_lookup, find_best_match
 
 # =======
 # Globals
@@ -36,14 +36,9 @@ QUERY_OPTS = {
        "includeMasked": True,
        "isRegex": False,
        "matchExact": True,
-       "printMatchInfo": True
+       "printMatchInfo": (not Config['quiet'])
 }
 
-if not Config["piping"] and Config["verbosityLevel"] >= 3:
-       VERBOSE = True
-else:
-       VERBOSE = False
-
 # =========
 # Functions
 # =========
@@ -98,7 +93,7 @@ def display_graph(pkg, stats, level=0, seen_pkgs=None, suffix=""):
        for dep in deps:
                suffix = ""
                depcpv = dep[2]
-               deppkg = gentoolkit.find_best_match(dep[0] + depcpv)
+               deppkg = find_best_match(dep[0] + depcpv)
                if not deppkg:
                        print (pfx + dep[0] + depcpv),
                        print "(unable to resolve: package masked or removed)"
@@ -178,14 +173,14 @@ def main(input_args):
                for pkg in matches:
                        stats = {"maxdepth": 0, "packages": 0}
 
-                       if VERBOSE:
+                       if Config['verbose']:
                                pp.print_info(3, " * dependency graph for %s:" % pp.cpv(pkg.cpv))
                        else:
                                pp.print_info(0, "%s:" % pkg.cpv)
 
                        stats = display_graph(pkg, stats)[1]
 
-                       if VERBOSE:
+                       if Config['verbose']:
                                info = ''.join(["[ ", pp.cpv(pkg.cpv), " stats: packages (",
                                pp.number(str(stats["packages"])), "), max depth (",
                                pp.number(str(stats["maxdepth"])), ") ]"])
index f25ae5a9cce51f3561b8ca9fc2eb7f9b7e6ab131..80caf1d35a348530ce59d63c1b55c7e973c789e2 100644 (file)
@@ -35,7 +35,7 @@ QUERY_OPTS = {
        "isRegex": False,
        "matchExact": True,
        "outputTree": False,
-       "printMatchInfo": True,
+       "printMatchInfo": (not Config['quiet']),
        "showType": False,
        "showTimestamp": False,
        "showMD5": False,
@@ -45,11 +45,6 @@ QUERY_OPTS = {
 FILTER_RULES = ('dir', 'obj', 'sym', 'dev', 'path', 'conf', 'cmd', 'doc',
        'man', 'info')
 
-if not Config["piping"] and Config["verbosityLevel"] >= 3:
-       VERBOSE = True
-else:
-       VERBOSE = False
-
 # =========
 # Functions
 # =========
@@ -302,8 +297,8 @@ def main(input_args):
                        pp.print_error("No matching packages found for %s" % query)
 
                for pkg in matches:
-                       if VERBOSE:
-                               pp.print_info(1, " * Contents of %s:" % pp.cpv(pkg.cpv))
+                       if Config['verbose']:
+                               print " * Contents of %s:" % pp.cpv(pkg.cpv)
 
                        contents = pkg.get_contents()
                        display_files(filter_contents(contents))
index 6580bbffddea92151099be57bd02eb5e147b9245..c110a228a67e65f51e920e4ae58adaf96777ae42 100644 (file)
@@ -17,10 +17,9 @@ from getopt import gnu_getopt, GetoptError
 
 import gentoolkit
 import gentoolkit.pprinter as pp
-from gentoolkit.equery import format_options, format_package_names, \
-       mod_usage, Config
-from gentoolkit.helpers2 import do_lookup, get_installed_cpvs
-from gentoolkit.package import Package
+from gentoolkit.equery import format_options, mod_usage, Config
+from gentoolkit.helpers2 import do_lookup, get_installed_cpvs, print_sequence
+from gentoolkit.package import Package, PackageFormatter
 
 # =======
 # Globals
@@ -28,7 +27,7 @@ from gentoolkit.package import Package
 
 QUERY_OPTS = {
        "categoryFilter": None,
-       "includeInstalled": False,
+       "includeInstalled": True,
        "includePortTree": False,
        "includeOverlayTree": False,
        "includeMasked": True,
@@ -55,8 +54,8 @@ def print_help(with_description=True):
        print pp.command("options")
        print format_options((
                (" -h, --help", "display this help message"),
-               (" -i, --installed",
-                       "include installed packages in search path (default)"),
+               (" -I, --exclude-installed",
+                       "exclude installed packages from search path"),
                (" -o, --overlay-tree", "include overlays in search path"),
                (" -p, --portage-tree", "include entire portage tree in search path")
        ))
@@ -71,63 +70,19 @@ def parse_module_options(module_opts):
                if opt in ('-h', '--help'):
                        print_help()
                        sys.exit(0)
-               elif opt in ('-i', '--installed'):
-                       QUERY_OPTS['includeInstalled'] = True
+               elif opt in ('-I', '--exclue-installed'):
+                       QUERY_OPTS['includeInstalled'] = False
                elif opt in ('-p', '--portage-tree'):
                        QUERY_OPTS['includePortTree'] = True
                elif opt in ('-o', '--overlay-tree'):
                        QUERY_OPTS['includeOverlayTree'] = True
 
 
-def print_sequence(seq):
-       """Print every item of a sequence."""
-
-       for item in seq:
-               print item
-
-
-def sort_by_location(query, matches):
-       """Take a list of packages and sort them by location.
-       
-       @rtype: tuple
-       @return:
-               installed: list of all packages in matches that are in the vdb
-               overlay: list of all packages in matches that reside in an overlay
-               porttree: list of all packages that are not in the vdb or an overlay
-       """
-
-       all_installed_packages = set()
-       if QUERY_OPTS["includeInstalled"]:
-               all_installed_packages = set(Package(x) for x in get_installed_cpvs())
-
-       # Cache package sets
-       installed = []
-       overlay = []
-       porttree = []
-
-       for pkg in matches:
-               useflags = [f.lstrip("+-") for f in pkg.get_env_var("IUSE").split()]
-               if query not in useflags:
-                       continue 
-
-               if QUERY_OPTS["includeInstalled"]:
-                       if pkg in all_installed_packages:
-                               installed.append(pkg)
-                               continue
-               if pkg.is_overlay():
-                       if QUERY_OPTS["includeOverlayTree"]:
-                               overlay.append(pkg)
-                       continue
-               if QUERY_OPTS["includePortTree"]:
-                       porttree.append(pkg)
-
-       return installed, overlay, porttree
-
-
 def main(input_args):
        """Parse input and run the program"""
 
-       short_opts = "hiIpo"
+       short_opts = "hiIpo" # -i was option for default action
+       # --installed is no longer needed, kept for compatibility (djanderson '09)
        long_opts = ('help', 'installed', 'exclude-installed', 'portage-tree',
                'overlay-tree')
 
@@ -144,10 +99,6 @@ def main(input_args):
        if not queries:
                print_help()
                sys.exit(2)
-       elif not (QUERY_OPTS['includeInstalled'] or
-               QUERY_OPTS['includePortTree'] or QUERY_OPTS['includeOverlayTree']):
-               # Got queries but no search path; set a sane default
-               QUERY_OPTS['includeInstalled'] = True
 
        matches = do_lookup("*", QUERY_OPTS)
        matches.sort()
@@ -161,29 +112,33 @@ def main(input_args):
                if not first_run:
                        print
 
-               if not Config["piping"]:
-                       print " * Searching for USE flag %s ... " % pp.useflag(query)
-
-               installed, overlay, porttree = sort_by_location(query, matches)
-
-               if QUERY_OPTS["includeInstalled"]:
-                       print " * installed packages:"
-                       if not Config["piping"]:
-                               installed = format_package_names(installed, 1)
-                       print_sequence(installed)
-
-               if QUERY_OPTS["includePortTree"]:
-                       portdir = pp.path(gentoolkit.settings["PORTDIR"])
-                       print " * Portage tree (%s):" % portdir
-                       if not Config["piping"]:
-                               porttree = format_package_names(porttree, 2)
-                       print_sequence(porttree)
-
-               if QUERY_OPTS["includeOverlayTree"]:
-                       portdir_overlay = pp.path(gentoolkit.settings["PORTDIR_OVERLAY"])
-                       print " * overlay tree (%s):" % portdir_overlay
-                       if not Config["piping"]:
-                               overlay = format_package_names(overlay, 3)
-                       print_sequence(overlay)
+               if Config['verbose']:
+                       print " * Searching for USE flag %s ... " % pp.emph(query)
+
+               for pkg in matches:
+
+                       useflags = [x.lstrip("+-") for x in pkg.get_env_var("IUSE").split()]
+                       if query not in useflags:
+                               continue
+
+                       if Config['verbose']:
+                               pkgstr = PackageFormatter(pkg, format=True)
+                       else:
+                               pkgstr = PackageFormatter(pkg, format=False)
+
+                       if (QUERY_OPTS["includeInstalled"] and
+                               not QUERY_OPTS["includePortTree"] and
+                               not QUERY_OPTS["includeOverlayTree"]):
+                               if not 'I' in pkgstr.location:
+                                       continue
+                       if (QUERY_OPTS["includePortTree"] and
+                               not QUERY_OPTS["includeOverlayTree"]):
+                               if not 'P' in pkgstr.location:
+                                       continue
+                       if (QUERY_OPTS["includeOverlayTree"] and
+                               not QUERY_OPTS["includePortTree"]):
+                               if not 'O' in pkgstr.location:
+                                       continue
+                       print pkgstr
 
                first_run = False
index 20cd376a246e18679f92fd54453a3492aa98702d..dd1302944a7b4d61fb60f3f447a40aa9234a5076 100644 (file)
@@ -17,10 +17,9 @@ from getopt import gnu_getopt, GetoptError
 
 import gentoolkit
 import gentoolkit.pprinter as pp
-from gentoolkit.equery import format_options, format_package_names, \
-       mod_usage, Config
+from gentoolkit.equery import format_options, mod_usage, Config
 from gentoolkit.helpers2 import do_lookup, get_installed_cpvs
-from gentoolkit.package import Package
+from gentoolkit.package import Package, PackageFormatter
 
 # =======
 # Globals
@@ -29,12 +28,12 @@ from gentoolkit.package import Package
 QUERY_OPTS = {
        "categoryFilter": None,
        "duplicates": False,
-       "includeInstalled": False,
+       "includeInstalled": True,
        "includePortTree": False,
        "includeOverlayTree": False,
        "includeMasked": True,
        "isRegex": False,
-       "printMatchInfo": True
+       "printMatchInfo": (not Config['quiet'])
 }
 
 # =========
@@ -66,38 +65,13 @@ def print_help(with_description=True):
                (" -c, --category CAT", "only search in the category CAT"),
                (" -d, --duplicates", "list only installed duplicate packages"),
                (" -f, --full-regex", "query is a regular expression"),
-               (" -i, --installed", "list installed packages matching query"),
+               (" -I, --exclude-installed",
+                       "exclude installed packages from output"),
                (" -o, --overlay-tree", "list packages in overlays"),
                (" -p, --portage-tree", "list packages in the main portage tree")
        ))
 
 
-def adjust_query_environment(queries):
-       """Make sure the search environment is good to go."""
-
-       if not queries and not (QUERY_OPTS["duplicates"] or
-               QUERY_OPTS["includeInstalled"] or QUERY_OPTS["includePortTree"] or 
-               QUERY_OPTS["includeOverlayTree"]):
-               print_help()
-               sys.exit(2)
-       elif queries and not (QUERY_OPTS["duplicates"] or
-               QUERY_OPTS["includeInstalled"] or QUERY_OPTS["includePortTree"] or 
-               QUERY_OPTS["includeOverlayTree"]):
-               QUERY_OPTS["includeInstalled"] = True
-       elif not queries and (QUERY_OPTS["duplicates"] or
-               QUERY_OPTS["includeInstalled"] or QUERY_OPTS["includePortTree"] or 
-               QUERY_OPTS["includeOverlayTree"]):
-               queries = ["*"]
-
-       # Only search installed packages when listing duplicate packages
-       if QUERY_OPTS["duplicates"]:
-               QUERY_OPTS["includeInstalled"] = True
-               QUERY_OPTS["includePortTree"] = False
-               QUERY_OPTS["includeOverlayTree"] = False
-
-       return queries
-
-
 def get_duplicates(matches):
        """Return only packages that have more than one version installed."""
 
@@ -129,8 +103,8 @@ def parse_module_options(module_opts):
                        QUERY_OPTS['listAllPackages'] = True
                elif opt in ('-c', '--category'):
                        QUERY_OPTS['categoryFilter'] = posarg
-               elif opt in ('-i', '--installed'):
-                       QUERY_OPTS['includeInstalled'] = True
+               elif opt in ('-I', '--exclude-installed'):
+                       QUERY_OPTS['includeInstalled'] = False
                elif opt in ('-p', '--portage-tree'):
                        QUERY_OPTS['includePortTree'] = True
                elif opt in ('-o', '--overlay-tree'):
@@ -145,55 +119,13 @@ def parse_module_options(module_opts):
                        QUERY_OPTS['duplicates'] = True
 
 
-def print_sequence(seq):
-       """Print every item of a sequence."""
-
-       for item in seq:
-               print item
-
-
-def sort_by_location(matches):
-       """Take a list of packages and sort them by location.
-       
-       @rtype: tuple
-       @return:
-               installed: list of all packages in matches that are in the vdb
-               overlay: list of all packages in matches that reside in an overlay
-               porttree: list of all packages that are not in the vdb or an overlay
-       """
-
-       all_installed_packages = set()
-       if QUERY_OPTS["includeInstalled"]:
-               all_installed_packages = set(Package(x) for x in get_installed_cpvs())
-
-       # Cache package sets
-       installed = []
-       overlay = []
-       porttree = []
-
-       for pkg in matches:
-               if QUERY_OPTS["includeInstalled"]:
-                       if pkg in all_installed_packages:
-                               installed.append(pkg)
-                               continue
-               if pkg.is_overlay():
-                       if QUERY_OPTS["includeOverlayTree"]:
-                               overlay.append(pkg)
-                       continue
-               if QUERY_OPTS["includePortTree"]:
-                       porttree.append(pkg)
-
-       return installed, overlay, porttree
-
-
 def main(input_args):
        """Parse input and run the program"""
 
-       short_opts = "hc:defiIop" # -I was used to turn off -i when it was
-                                 # the default action, -e is now default
+       short_opts = "hc:defiIop" # -i, -e were options for default actions
 
        # 04/09: djanderson
-       # --exclude-installed is no longer needed. Kept for compatibility.
+       # --installed is no longer needed. Kept for compatibility.
        # --exact-name is no longer needed. Kept for compatibility.
        long_opts = ('help', 'all', 'category=', 'installed', 'exclude-installed',
        'portage-tree', 'overlay-tree', 'full-regex', 'exact-name', 'duplicates')
@@ -207,7 +139,16 @@ def main(input_args):
                sys.exit(2)
 
        parse_module_options(module_opts)
-       queries = adjust_query_environment(queries)
+
+       # Only search installed packages when listing duplicate packages
+       if QUERY_OPTS["duplicates"]:
+               QUERY_OPTS["includeInstalled"] = True
+               QUERY_OPTS["includePortTree"] = False
+               QUERY_OPTS["includeOverlayTree"] = False
+
+       if not queries:
+               print_help()
+               sys.exit(2)
 
        first_run = True
        for query in queries:
@@ -222,30 +163,29 @@ def main(input_args):
 
                matches.sort()
 
-               installed, overlay, porttree = sort_by_location(matches)
-
                #
                # Output
                #
 
-               if QUERY_OPTS["includeInstalled"]:
-                       print " * installed packages:"
-                       if not Config["piping"]:
-                               installed = format_package_names(installed, 1)
-                       print_sequence(installed)
-
-               if QUERY_OPTS["includePortTree"]:
-                       portdir = pp.path(gentoolkit.settings["PORTDIR"])
-                       print " * Portage tree (%s):" % portdir 
-                       if not Config["piping"]:
-                               porttree = format_package_names(porttree, 2)
-                       print_sequence(porttree)
-
-               if QUERY_OPTS["includeOverlayTree"]:
-                       portdir_overlay = pp.path(gentoolkit.settings["PORTDIR_OVERLAY"])
-                       print " * overlay tree (%s):" % portdir_overlay
-                       if not Config["piping"]:
-                               overlay = format_package_names(overlay, 3)
-                       print_sequence(overlay)
+               for pkg in matches:
+                       if Config['verbose']:
+                               pkgstr = PackageFormatter(pkg, format=True)
+                       else:
+                               pkgstr = PackageFormatter(pkg, format=False)
+
+                       if (QUERY_OPTS["includeInstalled"] and
+                               not QUERY_OPTS["includePortTree"] and
+                               not QUERY_OPTS["includeOverlayTree"]):
+                               if not 'I' in pkgstr.location:
+                                       continue
+                       if (QUERY_OPTS["includePortTree"] and
+                               not QUERY_OPTS["includeOverlayTree"]):
+                               if not 'P' in pkgstr.location:
+                                       continue
+                       if (QUERY_OPTS["includeOverlayTree"] and
+                               not QUERY_OPTS["includePortTree"]):
+                               if not 'O' in pkgstr.location:
+                                       continue
+                       print pkgstr
 
                first_run = False
index 8148329ad3a95aa212bbed6d142ed9f4b1a021d0..a531537c0bc64acf421ddec2abd5456539d50e25 100644 (file)
@@ -27,7 +27,8 @@ from portage import settings
 import gentoolkit.pprinter as pp
 from gentoolkit import errors
 from gentoolkit.equery import format_options, mod_usage, Config
-from gentoolkit.helpers2 import find_packages
+from gentoolkit.helpers2 import find_packages, print_sequence, print_file, \
+       uniqify
 from gentoolkit.textwrap_ import TextWrapper
 
 # =======
@@ -54,11 +55,6 @@ PORTDIR = [settings["PORTDIR"] or os.path.join(os.sep, "usr", "portage")]
 if settings["PORTDIR_OVERLAY"]:
        PORTDIR.extend(settings["PORTDIR_OVERLAY"].split())
 
-if not Config["piping"] and Config["verbosityLevel"] >= 3:
-       VERBOSE = True
-else:
-       VERBOSE = False
-
 # =========
 # Functions
 # =========
@@ -91,7 +87,7 @@ def print_help(with_description=True):
 def call_get_functions(metadata_path, package_dir, QUERY_OPTS):
        """Call information gathering functions and display the results."""
        
-       if VERBOSE:
+       if Config['verbose']:
                print get_overlay_name(package_dir)
 
        try:
@@ -141,7 +137,7 @@ def call_get_functions(metadata_path, package_dir, QUERY_OPTS):
                print_sequence(format_list(useflags))
 
        if QUERY_OPTS["xml"]:
-               print_file(meta)
+               print_file(metadata_path)
 
 
 def format_line(line, first="", subsequent="", force_quiet=False):
@@ -233,7 +229,7 @@ def format_list(lst, first="", subsequent="", force_quiet=False):
                                # We don't want to send a blank line to format_line()
                                result.append("")
        else:
-               if VERBOSE:
+               if Config['verbose']:
                        if force_quiet:
                                result = None
                        else:
@@ -374,17 +370,16 @@ def _get_upstream_maintainer(node):
        maintainer = node.findall("maintainer")
        maint = []
        for elem in maintainer:
-               name = elem.find("name")
-               email = elem.find("email")
+               if elem.find("name") != None:
+                       maint.append(elem.find("name").text)
+               if elem.find("email") != None:
+                       maint.append(elem.find("email").text)
                if elem.get("status") == "active":
-                       status = "(%s)" % pp.output.green("active")
+                       maint.append("(%s)" % pp.output.green("active"))
                elif elem.get("status") == "inactive":
-                       status = "(%s)" % pp.output.red("inactive")
-               elif elem.get("status"):
-                       status = "(" + elem.get("status") + ")"
-               else:
-                       status = ""
-               maint.append(" ".join([name.text, email.text, status]))
+                       maint.append("(%s)" % pp.output.red("inactive"))
+               elif elem.get("status") != None:
+                       maint.append("(" + elem.get("status") + ")")
 
        return format_list(maint, "Maintainer: ", " " * 12, force_quiet=True)
 
@@ -432,33 +427,6 @@ def get_upstream(xml_tree):
        return result
 
 
-def print_sequence(seq):
-       """Print each element of a sequence."""
-
-       for elem in seq:
-               print elem
-
-
-def uniqify(seq, preserve_order=True): 
-       """Return a uniqified list. Optionally preserve order."""
-
-       if preserve_order:
-               seen = set()
-               result = [x for x in seq if x not in seen and not seen.add(x)]
-       else:
-               result = list(set(seq))
-
-       return result
-
-
-def print_file(path):
-       """Display the contents of a file."""
-
-       with open(path) as open_file:
-               lines = open_file.read()
-               print lines.strip()
-
-
 def parse_module_options(module_opts):
        """Parse module options and update GLOBAL_OPTS"""
 
@@ -526,4 +494,4 @@ def main(input_args):
                                
                        call_get_functions(metadata_path, package_dir, QUERY_OPTS)
        
-                       first_run = False
\ No newline at end of file
+                       first_run = False
index 59f45d88297f26ccec4a9ae85b77ec4a0ec691a0..9cb6bc98d636b6c9faebac372c6679d4b2621e47 100644 (file)
@@ -78,10 +78,7 @@ def display_size(match_set):
        for pkg in match_set:
                (size, files, uncounted) = pkg.size()
 
-               if Config["piping"]:
-                       info = "%s: total(%d), inaccessible(%d), size(%s)"
-                       print info % (pkg.cpv, files, uncounted, size)
-               else:
+               if Config['verbose']:
                        print " * %s" % pp.cpv(pkg.cpv)
                        print "Total files : %s".rjust(25) % pp.number(str(files))
 
@@ -95,6 +92,9 @@ def display_size(match_set):
                                size_str = "%s %s" % format_bytes(size)
 
                        pp.print_info(0, "Total size  : %s".rjust(25) % size_str)
+               else:
+                       info = "%s: total(%d), inaccessible(%d), size(%s)"
+                       print info % (pkg.cpv, files, uncounted, size)
 
 
 def format_bytes(bytes_, precision=2):
index 27186138d23cf9d6d1272ba695e2b571e699112a..56fc78b23acd5dac4c22c4f50724932e4ad15e6c 100644 (file)
@@ -82,9 +82,7 @@ def display_useflags(output):
        markers = ("-", "+")
        color = [pp.useflagoff, pp.useflagon]
        for in_makeconf, in_installed, flag, desc, restrict in output:
-               if Config["piping"]:
-                       pp.print_info(0, markers[in_makeconf] + flag)
-               else:
+               if Config['verbose']:
                        flag_name = ""
                        if in_makeconf != in_installed:
                                flag_name += pp.emph(" %s %s" % 
@@ -101,20 +99,22 @@ def display_useflags(output):
                                restrict = "(%s %s)" % (pp.emph("Restricted to"), 
                                        pp.cpv(restrict))
                                twrap.initial_indent = flag_name
-                               pp.print_info(0, twrap.fill(restrict))
+                               print twrap.fill(restrict)
                                if desc:
                                        twrap.initial_indent = twrap.subsequent_indent
-                                       pp.print_info(0, twrap.fill(desc))
+                                       print twrap.fill(desc)
                                else:
-                                       pp.print_info(0, " : <unknown>")
+                                       print " : <unknown>"
                        else:
                                if desc:
                                        twrap.initial_indent = flag_name
                                        desc = twrap.fill(desc)
-                                       pp.print_info(0, desc)
+                                       print desc
                                else:
                                        twrap.initial_indent = flag_name
-                                       pp.print_info(0, twrap.fill("<unknown>"))
+                                       print twrap.fill("<unknown>")
+               else:
+                       print markers[in_makeconf] + flag
 
 
 def get_global_useflags():
@@ -277,15 +277,10 @@ def parse_module_options(module_opts):
 def print_legend(query):
        """Print a legend to explain the output format."""
 
-       if not Config['piping']:
-               pp.print_info(3, " * Searching for packages matching %s ..." %
-                       pp.pkgquery(query))
-               pp.print_info(3, "[ Legend : %s - flag is set in make.conf       ]"
-                       % pp.emph("U"))
-               pp.print_info(3, "[        : %s - package is installed with flag ]"
-                       % pp.emph("I"))
-               pp.print_info(3, "[ Colors : %s, %s                         ]" %
-                       (pp.useflagon("set"), pp.useflagoff("unset")))
+       print "[ Legend : %s - flag is set in make.conf       ]" % pp.emph("U")
+       print "[        : %s - package is installed with flag ]" % pp.emph("I")
+       print "[ Colors : %s, %s                         ]" % (
+               pp.useflagon("set"), pp.useflagoff("unset"))
 
 
 def main(input_args):
@@ -317,7 +312,8 @@ def main(input_args):
                if not first_run:
                        print
 
-               print_legend(query)
+               if Config['verbose']:
+                       print " * Searching for %s ..." % pp.pkgquery(query)
 
                matches = get_matches(query)
                matches.sort()
@@ -327,14 +323,15 @@ def main(input_args):
 
                        output = get_output_descriptions(pkg, global_usedesc)
                        if output:
-                               if not Config['piping']:
-                                       pp.print_info(3, "[ Found these USE flags for %s ]" %
+                               if Config['verbose']:
+                                       print_legend(query)
+                                       print (" * Found these USE flags for %s:" %
                                                pp.cpv(pkg.cpv))
-                                       pp.print_info(3, pp.emph(" U I"))
+                                       print pp.emph(" U I")
                                display_useflags(output)
                        else:
-                               if not Config['piping']:
-                                       pp.print_info(3, "[ No USE flags found for %s ]" %
+                               if Config['verbose']:
+                                       pp.print_warn("No USE flags found for %s" %
                                                pp.cpv(pkg.cpv))
 
                first_run = False
index 4cae712d6356f175c3d017c7a0c293c7362be8e2..828dae1c6d29ababc8b168657be6c0e35ec81192 100644 (file)
@@ -91,7 +91,7 @@ def main(input_args):
                        pkg = sorted(matches).pop()
                        ebuild_path = pkg.get_ebuild_path()
                        if ebuild_path:
-                               pp.print_info(0, os.path.normpath(ebuild_path))
+                               print os.path.normpath(ebuild_path)
                        else:
                                pp.print_warn("No ebuilds to satisfy %s" % pkg.name)
                else:
index c63519211f5dd11af43ae933fe63df53d214c169..9843b6b007360b9e54a1ab20607ba04f521be81e 100644 (file)
@@ -12,6 +12,7 @@ __all__ = [
        'GentoolkitInvalidPackageName',
        'GentoolkitInvalidCPV',
        'GentoolkitInvalidRegex',
+       'GentoolkitInvalidVersion',
        'GentoolkitNoMatches'
 ]
 
@@ -77,6 +78,13 @@ class GentoolkitInvalidRegex(GentoolkitException):
                sys.exit(2)
 
 
+class GentoolkitInvalidVersion(GentoolkitException):
+       """Got a malformed version"""
+       def __init__(self, version):
+               pp.print_error("Malformed version: '%s'" % version)
+               sys.exit(2)
+
+
 class GentoolkitNoMatches(GentoolkitException):
        """No packages were found matching the search query"""
        def __init__(self, query):
index 1ddcd41fdb6fceada16ae54764fdb753f1dd5733..d28383527c3da42b9a3aec7ae42f9eb6c3e7ddb7 100644 (file)
@@ -11,7 +11,7 @@
 # - getting GLSAs from http/ftp servers (not really useful without the fixed ebuilds)
 # - GPG signing/verification (until key policy is clear)
 
-__author__ = "Marius Mauch <genone@gentoo.org>, Robert Buchholz <rbu@gentoo.org>"
+__author__ = "Marius Mauch <genone@gentoo.org>"
 
 import os
 import sys
@@ -19,10 +19,10 @@ import urllib
 import codecs
 import re
 import operator
-import xml.etree.cElementTree as ET
+import xml.dom.minidom
 from StringIO import StringIO
 
-if sys.version_info[0:2] < (2, 3):
+if sys.version_info[0:2] < (2,3):
        raise NotImplementedError("Python versions below 2.3 have broken XML code " \
                                                                +"and are not supported")
 
@@ -33,8 +33,8 @@ except ImportError:
        import portage
 
 # Note: the space for rgt and rlt is important !!
-opMapping = {"le": "<=", "lt": "<", "eq": "=", "gt": ">", "ge": ">=",
-               "rge": ">=~", "rle": "<=~", "rgt": " >~", "rlt": " <~"}
+opMapping = {"le": "<=", "lt": "<", "eq": "=", "gt": ">", "ge": ">=", 
+                        "rge": ">=~", "rle": "<=~", "rgt": " >~", "rlt": " <~"}
 NEWLINE_ESCAPE = "!;\\n"       # some random string to mark newlines that should be preserved
 SPACE_ESCAPE = "!;_"           # some random string to mark spaces that should be preserved
 
@@ -164,15 +164,16 @@ def getListElements(listnode):
        """
        Get all <li> elements for a given <ol> or <ul> node.
        
-       @type   listnode: ElementTree
+       @type   listnode: xml.dom.Node
        @param  listnode: <ul> or <ol> list to get the elements for
        @rtype:         List of Strings
        @return:        a list that contains the value of the <li> elements
        """
-       if not listnode.tag in ["ul", "ol"]:
+       if not listnode.nodeName in ["ul", "ol"]:
                raise GlsaFormatException("Invalid function call: listnode is not <ul> or <ol>")
        rValue = [getText(li, format="strip") \
-               for li in listnode.getchildren()]
+               for li in listnode.childNodes \
+               if li.nodeType == xml.dom.Node.ELEMENT_NODE]
        return rValue
 
 def getText(node, format, textfd = None):
@@ -184,7 +185,7 @@ def getText(node, format, textfd = None):
        tabs and spaces. This function is only useful for the GLSA DTD,
        it's not applicable for other DTDs.
        
-       @type   node: ElementTree
+       @type   node: xml.dom.Node
        @param  node: the root node to start with the parsing
        @type   format: String
        @param  format: this should be either I{strip}, I{keep} or I{xml}
@@ -199,45 +200,45 @@ def getText(node, format, textfd = None):
        @return:        the (formatted) content of the node and its subnodes
                        except if textfd was not none
        """
-       if node == None:
-               return ""
        if not textfd:
                textfd = StringIO()
                returnNone = False
        else:
                returnNone = True
        if format in ["strip", "keep"]:
-               if node.tag in ["uri", "mail"]:
-                       textfd.write(node.text+": "+(node.get("link") or ""))
+               if node.nodeName in ["uri", "mail"]:
+                       textfd.write(node.childNodes[0].data+": "+node.getAttribute("link"))
                else:
-                       textfd.write(node.text)
-                       for subnode in node.getchildren():
-                               getText(subnode, format, textfd)
-                               textfd.write(subnode.tail)
+                       for subnode in node.childNodes:
+                               if subnode.nodeName == "#text":
+                                       textfd.write(subnode.data)
+                               else:
+                                       getText(subnode, format, textfd)
        else: # format = "xml"
-               textfd.write(node.text)
-               for subnode in node.getchildren():
-                       if subnode.tag == "p":
-                               ptext = subnode.text
-                               for p_subnode in subnode.getchildren():
-                                       ptext += (p_subnode.text or "").strip()
-                                       if p_subnode.tag in ["uri", "mail"]:
-                                               ptext += " <"+(p_subnode.get("link") or "")+">"
-                                       ptext += p_subnode.tail
-                               textfd.write(ptext.strip())
+               for subnode in node.childNodes:
+                       if subnode.nodeName == "p":
+                               for p_subnode in subnode.childNodes:
+                                       if p_subnode.nodeName == "#text":
+                                               textfd.write(p_subnode.data.strip())
+                                       elif p_subnode.nodeName in ["uri", "mail"]:
+                                               textfd.write(p_subnode.childNodes[0].data)
+                                               textfd.write(" ( "+p_subnode.getAttribute("link")+" )")
                                textfd.write(NEWLINE_ESCAPE)
-                       elif subnode.tag == "ul":
+                       elif subnode.nodeName == "ul":
                                for li in getListElements(subnode):
                                        textfd.write("-"+SPACE_ESCAPE+li+NEWLINE_ESCAPE+" ")
-                       elif subnode.tag == "ol":
-                               for i, li in enumerate(getListElements(subnode)):
-                                       textfd.write(str(i+1)+"."+SPACE_ESCAPE+li+NEWLINE_ESCAPE+" ")
-                       elif subnode.tag == "code":
+                       elif subnode.nodeName == "ol":
+                               i = 0
+                               for li in getListElements(subnode):
+                                       i = i+1
+                                       textfd.write(str(i)+"."+SPACE_ESCAPE+li+NEWLINE_ESCAPE+" ")
+                       elif subnode.nodeName == "code":
                                textfd.write(getText(subnode, format="keep").lstrip().replace("\n", NEWLINE_ESCAPE))
                                textfd.write(NEWLINE_ESCAPE)
+                       elif subnode.nodeName == "#text":
+                               textfd.write(subnode.data)
                        else:
-                               raise GlsaFormatException("Invalid Tag found: ", subnode.tag)
-                       textfd.write(subnode.tail)
+                               raise GlsaFormatException("Invalid Tag found: ", subnode.nodeName)
        if returnNone:
                return None
        rValue = textfd.getvalue()
@@ -251,7 +252,7 @@ def getMultiTagsText(rootnode, tagname, format):
        Returns a list with the text of all subnodes of type I{tagname}
        under I{rootnode} (which itself is not parsed) using the given I{format}.
        
-       @type   rootnode: ElementTree
+       @type   rootnode: xml.dom.Node
        @param  rootnode: the node to search for I{tagname}
        @type   tagname: String
        @param  tagname: the name of the tags to search for
@@ -261,7 +262,7 @@ def getMultiTagsText(rootnode, tagname, format):
        @return:        a list containing the text of all I{tagname} childnodes
        """
        rValue = [getText(e, format) \
-               for e in rootnode.findall(tagname)]
+               for e in rootnode.getElementsByTagName(tagname)]
        return rValue
 
 def makeAtom(pkgname, versionNode):
@@ -271,18 +272,22 @@ def makeAtom(pkgname, versionNode):
        
        @type   pkgname: String
        @param  pkgname: the name of the package for this atom
-       @type   versionNode: ElementTree
+       @type   versionNode: xml.dom.Node
        @param  versionNode: a <vulnerable> or <unaffected> Node that
                                                 contains the version information for this atom
        @rtype:         String
        @return:        the portage atom
        """
-       rValue = opMapping[versionNode.get("range")] \
+       rValue = opMapping[versionNode.getAttribute("range")] \
                                + pkgname \
                                + "-" + getText(versionNode, format="strip")
-       slot = versionNode.get("slot")
-       if slot and slot != "*":
-               rValue += ":" + slot.strip()
+       try:
+               slot = versionNode.getAttribute("slot").strip()
+       except KeyError:
+               pass
+       else:
+               if slot and slot != "*":
+                       rValue += ":" + slot
        return str(rValue)
 
 def makeVersion(versionNode):
@@ -290,17 +295,21 @@ def makeVersion(versionNode):
        creates from the information in the I{versionNode} a 
        version string (format <op><version>).
        
-       @type   versionNode: ElementTree
+       @type   versionNode: xml.dom.Node
        @param  versionNode: a <vulnerable> or <unaffected> Node that
                                                 contains the version information for this atom
        @rtype:         String
        @return:        the version string
        """
-       rValue = opMapping[versionNode.get("range")] \
+       rValue = opMapping[versionNode.getAttribute("range")] \
                        +getText(versionNode, format="strip")
-       slot = versionNode.get("slot")
-       if slot and slot != "*":
-               rValue += ":" + slot.strip()
+       try:
+               slot = versionNode.getAttribute("slot").strip()
+       except KeyError:
+               pass
+       else:
+               if slot and slot != "*":
+                       rValue += ":" + slot
        return rValue
 
 def match(atom, portdbname, match_type="default"):
@@ -517,72 +526,81 @@ class Glsa:
                @rtype:         None
                @returns:       None
                """
-               self.DOM = ET.parse(myfile)
-               #elif self.DOM.doctype.systemId == "http://www.gentoo.org/dtd/glsa.dtd":
-                       #self.dtdversion = 0
-               #elif self.DOM.doctype.systemId == "http://www.gentoo.org/dtd/glsa-2.dtd":
-                       #self.dtdversion = 2
-               #else:
-                       #raise GlsaTypeException(self.DOM.doctype.systemId)
-               myroot = self.DOM.getroot()
-               if myroot.tag != "glsa":
-                       raise GlsaFormatException("Root tag was not 'glsa', but '%s' in %s:" % (self.tag, self.nr))
-               if self.type == "id" and myroot.get("id") != self.nr:
-                       raise GlsaFormatException("filename and internal id don't match:" + myroot.get("id") + " != " + self.nr)
+               self.DOM = xml.dom.minidom.parse(myfile)
+               if not self.DOM.doctype:
+                       raise GlsaTypeException(None)
+               elif self.DOM.doctype.systemId == "http://www.gentoo.org/dtd/glsa.dtd":
+                       self.dtdversion = 0
+               elif self.DOM.doctype.systemId == "http://www.gentoo.org/dtd/glsa-2.dtd":
+                       self.dtdversion = 2
+               else:
+                       raise GlsaTypeException(self.DOM.doctype.systemId)
+               myroot = self.DOM.getElementsByTagName("glsa")[0]
+               if self.type == "id" and myroot.getAttribute("id") != self.nr:
+                       raise GlsaFormatException("filename and internal id don't match:" + myroot.getAttribute("id") + " != " + self.nr)
 
                # the simple (single, required, top-level, #PCDATA) tags first
-               self.title = getText(myroot.find("title"), format="strip")
-               self.synopsis = getText(myroot.find("synopsis"), format="strip")
-               self.announced = format_date(getText(myroot.find("announced"), format="strip"))
+               self.title = getText(myroot.getElementsByTagName("title")[0], format="strip")
+               self.synopsis = getText(myroot.getElementsByTagName("synopsis")[0], format="strip")
+               self.announced = format_date(getText(myroot.getElementsByTagName("announced")[0], format="strip"))
                
+               count = 1
                # Support both formats of revised:
-               # <revised>December 30, 2007: 02</revised>   (old style)
-               # <revised count="2">2007-12-30</revised>    (new style)
-               revisedEl = myroot.find("revised")
+               # <revised>December 30, 2007: 02</revised>
+               # <revised count="2">2007-12-30</revised>
+               revisedEl = myroot.getElementsByTagName("revised")[0]
                self.revised = getText(revisedEl, format="strip")
-               if (self.revised.find(":") >= 0): # old style
+               if (revisedEl.attributes.has_key("count")):
+                       count = revisedEl.getAttribute("count")
+               elif (self.revised.find(":") >= 0):
                        (self.revised, count) = self.revised.split(":")
-               else:                             #new style
-                       count = revisedEl.get("count")
-
+               
                self.revised = format_date(self.revised)
                
                try:
                        self.count = int(count)
-               except (ValueError, TypeError):
+               except ValueError:
                        # TODO should this rais a GlsaFormatException?
                        self.count = 1
                
                # now the optional and 0-n toplevel, #PCDATA tags and references
-               self.access = getText(myroot.find("access"), format="strip")
-               # TODO
+               try:
+                       self.access = getText(myroot.getElementsByTagName("access")[0], format="strip")
+               except IndexError:
+                       self.access = ""
                self.bugs = getMultiTagsText(myroot, "bug", format="strip")
-               self.references = getMultiTagsText(myroot.find("references"), "uri", format="keep")
+               self.references = getMultiTagsText(myroot.getElementsByTagName("references")[0], "uri", format="keep")
                
                # and now the formatted text elements
-               self.description = getText(myroot.find("description"), format="xml")
-               self.workaround = getText(myroot.find("workaround"), format="xml")
-               self.resolution = getText(myroot.find("resolution"), format="xml")
-               self.impact_text = getText(myroot.find("impact"), format="xml")
-               self.impact_type = myroot.find("impact").get("type")
-               self.background = getText(myroot.find("background"), format="xml")
+               self.description = getText(myroot.getElementsByTagName("description")[0], format="xml")
+               self.workaround = getText(myroot.getElementsByTagName("workaround")[0], format="xml")
+               self.resolution = getText(myroot.getElementsByTagName("resolution")[0], format="xml")
+               self.impact_text = getText(myroot.getElementsByTagName("impact")[0], format="xml")
+               self.impact_type = myroot.getElementsByTagName("impact")[0].getAttribute("type")
+               try:
+                       self.background = getText(myroot.getElementsByTagName("background")[0], format="xml")
+               except IndexError:
+                       self.background = ""                                    
 
                # finally the interesting tags (product, affected, package)
-               self.glsatype = myroot.find("product").get("type")
-               self.product = getText(myroot.find("product"), format="strip")
+               self.glsatype = myroot.getElementsByTagName("product")[0].getAttribute("type")
+               self.product = getText(myroot.getElementsByTagName("product")[0], format="strip")
+               self.affected = myroot.getElementsByTagName("affected")[0]
                self.packages = {}
-               affected = myroot.find("affected")
-               for p in affected.findall("package"):
-                       name = p.get("name")
-                       new_entry = {}
-                       new_entry["arch"] = p.get("arch")
-                       new_entry["auto"] = (p.get("auto") == "yes")
-                       new_entry["vul_vers"] = [makeVersion(v) for v in p.findall("vulnerable")]
-                       new_entry["unaff_vers"] = [makeVersion(v) for v in p.findall("unaffected")]
-                       new_entry["vul_atoms"] = [makeAtom(name, v) for v in p.findall("vulnerable")]
-                       new_entry["unaff_atoms"] = [makeAtom(name, v) for v in p.findall("unaffected")]
-                       package_entries = self.packages.setdefault(name, [])
-                       package_entries.append(new_entry)
+               for p in self.affected.getElementsByTagName("package"):
+                       name = p.getAttribute("name")
+                       if not self.packages.has_key(name):
+                               self.packages[name] = []
+                       tmp = {}
+                       tmp["arch"] = p.getAttribute("arch")
+                       tmp["auto"] = (p.getAttribute("auto") == "yes")
+                       tmp["vul_vers"] = [makeVersion(v) for v in p.getElementsByTagName("vulnerable")]
+                       tmp["unaff_vers"] = [makeVersion(v) for v in p.getElementsByTagName("unaffected")]
+                       tmp["vul_atoms"] = [makeAtom(name, v) for v in p.getElementsByTagName("vulnerable")]
+                       tmp["unaff_atoms"] = [makeAtom(name, v) for v in p.getElementsByTagName("unaffected")]
+                       self.packages[name].append(tmp)
+               # TODO: services aren't really used yet
+               self.services = self.affected.getElementsByTagName("service")
                return None
 
        def dump(self, outstream=sys.stdout, encoding="utf-8"):
index 69acc1df0d456d63779e76c4a5d1975d087e9d83..6d272d3ae61dfa44c0a961d2c277f1bf079ee771 100644 (file)
@@ -150,11 +150,12 @@ def split_package_name(name):
                r[0] = ''
        return r
 
-def sort_package_list(pkglist):
-       """Returns the list ordered in the same way portage would do with lowest version
-       at the head of the list."""
-       pkglist.sort(Package.compare_version)
-       return pkglist
+# XXX: Defunct: use helpers2.compare_package_strings
+#def sort_package_list(pkglist):
+#      """Returns the list ordered in the same way portage would do with lowest version
+#      at the head of the list."""
+#      pkglist.sort(Package.compare_version)
+#      return pkglist
 
 if __name__ == "__main__":
        print "This module is for import only"
index 1a2530a0c05ccd210aca226a391118d0ddb15d26..f9d0c494e43f5fcbd0455a980030103bbe8bab38 100644 (file)
@@ -13,16 +13,19 @@ query matches one or more packages.
 This should be merged into helpers when a clean path is found.
 """
 
+# Move to Imports section after Python 2.6 is stable
+from __future__ import with_statement
+
 __all__ = (
        'compare_package_strings',
+       'do_lookup',
        'find_best_match',
        'find_installed_packages',
        'find_packages',
        'get_cpvs',
        'get_installed_cpvs',
        'get_uninstalled_cpvs',
-       'uses_globbing',
-       'do_lookup'
+       'uses_globbing'
 )
 __author__ = 'Douglas Anderson'
 __docformat__ = 'epytext'
@@ -40,17 +43,10 @@ from portage.util import unique_array
 
 import gentoolkit
 import gentoolkit.pprinter as pp
-from gentoolkit import catpkgsplit, Config
+from gentoolkit import Config, PORTDB, VARDB
 from gentoolkit import errors
 from gentoolkit.package import Package
 
-# =======
-# Globals
-# =======
-
-PORTDB = portage.db[portage.root]["porttree"].dbapi
-VARDB  = portage.db[portage.root]["vartree"].dbapi
-
 # =========
 # Functions
 # =========
@@ -62,12 +58,12 @@ def compare_package_strings(pkg1, pkg2):
        An alternative is to use the Package descriptor from gentoolkit.package
        >>> pkgs = [Package(x) for x in package_list]
        >>> pkgs.sort()
-       
+
        @see: >>> help(cmp)
        """
 
-       pkg1 = catpkgsplit(pkg1)
-       pkg2 = catpkgsplit(pkg2)
+       pkg1 = portage.versions.catpkgsplit(pkg1)
+       pkg2 = portage.versions.catpkgsplit(pkg2)
        # Compare categories
        if pkg1[0] != pkg2[0]:
                return cmp(pkg1[0], pkg2[0])
@@ -79,75 +75,59 @@ def compare_package_strings(pkg1, pkg2):
                return portage.versions.pkgcmp(pkg1[1:], pkg2[1:])
 
 
-def find_best_match(query):
-       """Return the highest unmasked version of a package matching query.
-       
-       @type query: str
-       @param query: can be of the form: pkg, pkg-ver, cat/pkg, cat/pkg-ver, atom
-       @rtype: str or None
-       """
-
-       match = PORTDB.xmatch("bestmatch-visible", query)
+def do_lookup(query, query_opts):
+       """A high-level wrapper around gentoolkit package-finder functions.
 
-       return Package(match) if match else None
+       @type query: str
+       @param query: pkg, cat/pkg, pkg-ver, cat/pkg-ver, atom or regex
+       @type query_opts: dict
+       @param query_opts: user-configurable options from the calling module
+               Currently supported options are:
 
+               categoryFilter     = str or None
+               includeInstalled   = bool
+               includePortTree    = bool
+               includeOverlayTree = bool
+               isRegex            = bool
+               printMatchInfo     = bool           # Print info about the search
 
-def find_installed_packages(query):
-       """Return a list of Package objects that matched the search key."""
+       @rtype: list
+       @return: Package objects matching query
+       """
 
-       try:
-               matches = VARDB.match(query)
-       # catch the ambiguous package Exception
-       except ValueError, err:
-               if isinstance(err[0], list):
-                       matches = []
-                       for pkgkey in err[0]:
-                               matches.append(VARDB.match(pkgkey))
+       if query_opts["includeInstalled"]:
+               if query_opts["includePortTree"] or query_opts["includeOverlayTree"]:
+                       simple_package_finder = partial(find_packages, include_masked=True)
+                       complex_package_finder = get_cpvs
                else:
-                       raise ValueError(err)
-       except portage.exception.InvalidAtom, err:
-               pp.print_warn("Invalid Atom: '%s'" % str(err))
-               return []
-
-       return [Package(x) for x in matches]
-
-
-def uses_globbing(query):
-       """Check the query to see if it is using globbing.
+                       simple_package_finder = find_installed_packages
+                       complex_package_finder = get_installed_cpvs
+       elif query_opts["includePortTree"] or query_opts["includeOverlayTree"]:
+               simple_package_finder = partial(find_packages, include_masked=True)
+               complex_package_finder = get_uninstalled_cpvs
+       else:
+               pp.die(2, "Not searching in installed, portage tree or overlay."
+                       " Nothing to do.")
 
-       @rtype: bool
-       @return: True if query uses globbing, else False
-       """
+       is_simple_query = True
+       if query_opts["isRegex"] or uses_globbing(query):
+               is_simple_query = False
 
-       if set('!*?[]').intersection(set(query)):
-               if portage.dep.get_operator(query):
-                       # Query may be an atom such as '=sys-apps/portage-2.2*'
-                       pass
-               else:
-                       return True
+       if is_simple_query:
+               matches = _do_simple_lookup(query, simple_package_finder, query_opts)
+       else:
+               matches = _do_complex_lookup(query, complex_package_finder, query_opts)
 
-       return False
+       return matches
 
 
-def _do_complex_lookup(query, query_opts):
+def _do_complex_lookup(query, package_finder, query_opts):
        """Find matches for a query which is a regex or includes globbing."""
 
        # pylint: Too many branches (18/12)
        # pylint: disable-message=R0912
        result = []
 
-       if query_opts["includeInstalled"]:
-               if query_opts["includePortTree"] or query_opts["includeOverlayTree"]:
-                       package_finder = get_cpvs
-               else:
-                       package_finder = get_installed_cpvs
-       elif query_opts["includePortTree"] or query_opts["includeOverlayTree"]:
-               package_finder = get_uninstalled_cpvs
-       else:
-               pp.print_error("Not searching in installed, portage tree or overlay." +
-                       " Nothing to do.")
-               pp.die(2, "This is an internal error. Please report this.")
-
        if query_opts["printMatchInfo"] and not Config["piping"]:
                print_query_info(query, query_opts)
 
@@ -194,28 +174,7 @@ def _do_complex_lookup(query, query_opts):
        return [Package(x) for x in result]
 
 
-def print_query_info(query, query_opts):
-       """Print info about the query to the screen."""
-
-       cats = prepare_categories(query_opts["categoryFilter"])
-       cat, pkg, ver, rev = split_query(query)
-       del ver, rev
-       if cats:
-               cat_str = "in %s " % ', '.join([pp.emph(x) for x in cats])
-       elif cat and not query_opts["isRegex"]:
-               cat_str = "in %s " % pp.emph(cat)
-       else:
-               cat_str = ""
-       
-       if query_opts["isRegex"]:
-               pkg_str = query
-       else:
-               pkg_str = pkg
-
-       print " * Searching for %s %s..." % (pp.emph(pkg_str), cat_str)
-
-
-def _do_simple_lookup(query, query_opts):
+def _do_simple_lookup(query, package_finder, query_opts):
        """Find matches for a query which is an atom or string."""
 
        result = []
@@ -223,11 +182,6 @@ def _do_simple_lookup(query, query_opts):
        cats = prepare_categories(query_opts["categoryFilter"])
        if query_opts["printMatchInfo"] and not Config["piping"]:
                print_query_info(query, query_opts)
-               
-       if query_opts["includePortTree"] or query_opts["includeOverlayTree"]:
-               package_finder = find_packages
-       else:
-               package_finder = find_installed_packages
 
        result = package_finder(query)
        if not query_opts["includeInstalled"]:
@@ -239,36 +193,34 @@ def _do_simple_lookup(query, query_opts):
        return result
 
 
-def do_lookup(query, query_opts):
-       """A high-level wrapper around gentoolkit package-finder functions.
-
+def find_best_match(query):
+       """Return the highest unmasked version of a package matching query.
+       
        @type query: str
-       @param query: pkg, cat/pkg, pkg-ver, cat/pkg-ver, atom or regex
-       @type query_opts: dict
-       @param query_opts: user-configurable options from the calling module
-               Currently supported options are:
+       @param query: can be of the form: pkg, pkg-ver, cat/pkg, cat/pkg-ver, atom
+       @rtype: str or None
+       """
 
-               categoryFilter     = str or None
-               includeInstalled   = bool
-               includePortTree    = bool
-               includeOverlayTree = bool
-               isRegex            = bool
-               printMatchInfo     = bool           # Print info about the search
+       match = PORTDB.xmatch("bestmatch-visible", query)
 
-       @rtype: list
-       @return: Package objects matching query
-       """
+       return Package(match) if match else None
 
-       is_simple_query = True
-       if query_opts["isRegex"] or uses_globbing(query):
-               is_simple_query = False
 
-       if is_simple_query:
-               matches = _do_simple_lookup(query, query_opts)
-       else:
-               matches = _do_complex_lookup(query, query_opts)
+def find_installed_packages(query):
+       """Return a list of Package objects that matched the search key."""
 
-       return matches
+       try:
+               matches = VARDB.match(query)
+       # catch the ambiguous package Exception
+       except portage.exception.AmbiguousPackageName, err:
+               matches = []
+               for pkgkey in err[0]:
+                       matches.extend(VARDB.match(pkgkey))
+       except portage.exception.InvalidAtom, err:
+               pp.print_warn("Invalid Atom: '%s'" % str(err))
+               return []
+
+       return [Package(x) for x in matches]
 
 
 def find_packages(query, include_masked=False):
@@ -289,18 +241,6 @@ def find_packages(query, include_masked=False):
                else:
                        matches = PORTDB.match(query)
                matches.extend(VARDB.match(query))
-       # Catch ambiguous packages
-       except ValueError, err:
-               if isinstance(err[0], list):
-                       matches = []
-                       for pkgkey in err[0]:
-                               if include_masked:
-                                       matches.extend(PORTDB.xmatch("match-all", pkgkey))
-                               else:
-                                       matches.extend(PORTDB.match(pkgkey))
-                               matches.extend(VARDB.match(pkgkey))
-               else:
-                       raise ValueError(err)
        except portage.exception.InvalidAtom, err:
                raise errors.GentoolkitInvalidAtom(str(err))
 
@@ -400,6 +340,42 @@ def prepare_categories(category_filter):
        return tuple(good_cats)
 
 
+def print_query_info(query, query_opts):
+       """Print info about the query to the screen."""
+
+       cats = prepare_categories(query_opts["categoryFilter"])
+       cat, pkg, ver, rev = split_query(query)
+       del ver, rev
+       if cats:
+               cat_str = "in %s " % ', '.join([pp.emph(x) for x in cats])
+       elif cat and not query_opts["isRegex"]:
+               cat_str = "in %s " % pp.emph(cat)
+       else:
+               cat_str = ""
+       
+       if query_opts["isRegex"]:
+               pkg_str = query
+       else:
+               pkg_str = pkg
+
+       print " * Searching for %s %s..." % (pp.emph(pkg_str), cat_str)
+
+
+def print_file(path):
+       """Display the contents of a file."""
+
+       with open(path) as open_file:
+               lines = open_file.read()
+               print lines.strip()
+
+
+def print_sequence(seq):
+       """Print every item of a sequence."""
+
+       for item in seq:
+               print item
+
+
 def split_query(query):
        """Split a query into category, name, version and revision.
 
@@ -410,16 +386,52 @@ def split_query(query):
                Each tuple element is a string or empty string ("").
        """
 
-       cat = name = ver = rev = ""
+       result = portage.versions.catpkgsplit(query)
 
-       try:
-               (cat, name, ver, rev) = gentoolkit.split_package_name(query)
-       except ValueError, err:
-               # FIXME: Not hitting this error anymore... but we should be?
-               if str(err) == 'too many values to unpack':
-                       pp.print_error("Too many slashes ('/').")
-                       raise errors.GentoolkitInvalidPackageName(query)
+       if result:
+               result = list(result)
+               if result[0] == 'null':
+                       result[0] = ''
+               if result[3] == 'r0':
+                       result[3] = ''
+       else:
+               result = query.split("/")
+               if len(result) == 1:
+                       result = ['', query, '', '']
                else:
-                       raise ValueError(err)
-       
-       return (cat, name, ver, rev)
+                       result = result + ['', '']
+
+       if len(result) != 4:
+               pp.print_error("Too many slashes ('/').")
+               raise errors.GentoolkitInvalidPackageName(query)
+
+       return tuple(result)
+
+
+def uniqify(seq, preserve_order=True): 
+       """Return a uniqified list. Optionally preserve order."""
+
+       if preserve_order:
+               seen = set()
+               result = [x for x in seq if x not in seen and not seen.add(x)]
+       else:
+               result = list(set(seq))
+
+       return result
+
+
+def uses_globbing(query):
+       """Check the query to see if it is using globbing.
+
+       @rtype: bool
+       @return: True if query uses globbing, else False
+       """
+
+       if set('!*?[]').intersection(set(query)):
+               if portage.dep.get_operator(query):
+                       # Query may be an atom such as '=sys-apps/portage-2.2*'
+                       pass
+               else:
+                       return True
+
+       return False
index 340a512376fd00dc10a8f4819488b8ee32885b7c..857470a1b8205d334c4dc0f873ea821898d1485d 100644 (file)
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+#!/usr/bin/python
 #
 # Copyright(c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
 # Copyright(c) 2004-2009, Gentoo Foundation
 import os
 
 import portage
-from portage import catpkgsplit
-from portage.versions import vercmp
+from portage.versions import catpkgsplit, vercmp
 
-from gentoolkit import *
+import gentoolkit.pprinter as pp
+from gentoolkit import settings, settingslock, PORTDB, VARDB
 from gentoolkit import errors
-
-# =======
-# Globals
-# =======
-
-PORTDB = portage.db[portage.root]["porttree"].dbapi
-VARDB  = portage.db[portage.root]["vartree"].dbapi
+from gentoolkit.versionmatch import VersionMatch
 
 # =======
 # Classes
@@ -98,27 +92,33 @@ class Package(object):
        def __repr__(self):
                return "<%s %s @%#8x>" % (self.__class__.__name__, self._cpv, id(self))
 
-       def __cmp__(self, other):
-               # FIXME: __cmp__ functions dissallowed in py3k; need __lt__, __gt__.
+       def __eq__(self, other):
+               return hash(self) == hash(other)
+
+       def __ne__(self, other):
+               return hash(self) != hash(other)
+
+       def __lt__(self, other):
                if not isinstance(other, self.__class__):
                        raise TypeError("other isn't of %s type, is %s" %
                                (self.__class__, other.__class__))
 
                if self.category != other.category:
-                       return cmp(self.category, other.category)
+                       return self.category < other.category
                elif self.name != other.name:
-                       return cmp(self.name, other.name)
+                       return self.name < other.name
                else:
                        # FIXME: this cmp() hack is for vercmp not using -1,0,1
                        # See bug 266493; this was fixed in portage-2.2_rc31
                        #return portage.vercmp(self.fullversion, other.fullversion)
-                       return cmp(portage.vercmp(self.fullversion, other.fullversion), 0)
-
-       def __eq__(self, other):
-               return hash(self) == hash(other)
+                       result = cmp(portage.vercmp(self.fullversion, other.fullversion), 0)
+                       if result == -1:
+                               return True
+                       else:
+                               return False
 
-       def __ne__(self, other):
-               return hash(self) != hash(other)
+       def __gt__(self, other):
+               return not self.__lt__(other)
 
        def __hash__(self):
                return hash(self._cpv)
@@ -144,11 +144,13 @@ class Package(object):
        def get_settings(self, key):
                """Returns the value of the given key for this package (useful 
                for package.* files."""
-               self._settingslock.acquire()
-               self._settings.setcpv(self.cpv)
-               v = self._settings[key]
-               self._settingslock.release()
-               return v
+               try:
+                       self._settingslock.acquire()
+                       self._settings.setcpv(self.cpv)
+                       result = self._settings[key]
+               finally:
+                       self._settingslock.release()
+               return result
 
        def get_cpv(self):
                """Returns full Category/Package-Version string"""
@@ -156,14 +158,14 @@ class Package(object):
 
        def get_provide(self):
                """Return a list of provides, if any"""
-               if not self.is_installed():
+               if self.is_installed():
+                       result = VARDB.get_provide(self.cpv)
+               else:
                        try:
-                               x = [self.get_env_var('PROVIDE')]
+                               result = [self.get_env_var('PROVIDE')]
                        except KeyError:
-                               x = []
-                       return x
-               else:
-                       return vartree.get_provide(self.cpv)
+                               result = []
+               return result
 
        def get_dependants(self):
                """Retrieves a list of CPVs for all packages depending on this one"""
@@ -176,11 +178,10 @@ class Package(object):
                # Try to use the portage tree first, since emerge only uses the tree 
                # when calculating dependencies
                try:
-                       cd = self.get_env_var("RDEPEND", porttree).split()
+                       rdepends = self.get_env_var("RDEPEND", PORTDB).split()
                except KeyError:
-                       cd = self.get_env_var("RDEPEND", vartree).split()
-               r,i = self._parse_deps(cd)
-               return r
+                       rdepends = self.get_env_var("RDEPEND", VARDB).split()
+               return self._parse_deps(rdepends)[0]
 
        def get_compiletime_deps(self):
                """Returns a linearised list of first-level compile time dependencies
@@ -189,11 +190,10 @@ class Package(object):
                # Try to use the portage tree first, since emerge only uses the tree 
                # when calculating dependencies
                try:
-                       rd = self.get_env_var("DEPEND", porttree).split()
+                       depends = self.get_env_var("DEPEND", PORTDB).split()
                except KeyError:
-                       rd = self.get_env_var("DEPEND", vartree).split()
-               r,i = self._parse_deps(rd)
-               return r
+                       depends = self.get_env_var("DEPEND", VARDB).split()
+               return self._parse_deps(depends)[0]
 
        def get_postmerge_deps(self):
                """Returns a linearised list of first-level post merge dependencies 
@@ -202,11 +202,10 @@ class Package(object):
                # Try to use the portage tree first, since emerge only uses the tree 
                # when calculating dependencies
                try:
-                       pd = self.get_env_var("PDEPEND", porttree).split()
+                       postmerge_deps = self.get_env_var("PDEPEND", PORTDB).split()
                except KeyError:
-                       pd = self.get_env_var("PDEPEND", vartree).split()
-               r,i = self._parse_deps(pd)
-               return r
+                       postmerge_deps = self.get_env_var("PDEPEND", VARDB).split()
+               return self._parse_deps(postmerge_deps)[0]
 
        def intersects(self, other):
                """Check if a passed in package atom "intersects" this atom.
@@ -221,7 +220,7 @@ class Package(object):
                        ">=dev-lang/python-2.4" and "dev-lang/python" but not
                        "<dev-lang/python-2.3"
 
-               @type other: gentoolkit.package.Package
+               @type other: L{gentoolkit.package.Package}
                @param other: other package to compare
                @see: pkgcore.ebuild.atom.py
                """
@@ -238,11 +237,11 @@ class Package(object):
                if self.operator == '=':
                        if other.operator == '=*':
                                return self.fullversion.startswith(other.fullversion)
-                       return VersionMatch(frompkg=other).match(self)
+                       return VersionMatch(other).match(self)
                if other.operator == '=':
                        if self.operator == '=*':
                                return other.fullversion.startswith(self.fullversion)
-                       return VersionMatch(frompkg=self).match(other)
+                       return VersionMatch(self).match(other)
 
                # If we are both ~ matches we match if we are identical:
                if self.operator == other.operator == '~':
@@ -273,13 +272,13 @@ class Package(object):
                        # match the other's endpoint (just checking one endpoint
                        # is not enough, it would give a false positive on <=2 vs >2)
                        return (
-                               VersionMatch(frompkg=other).match(ranged) and
-                               VersionMatch(frompkg=ranged).match(other))
+                               VersionMatch(other).match(ranged) and
+                               VersionMatch(ranged).match(other))
 
                if other.operator == '~':
                        # Other definitely matches its own version. If ranged also
                        # does we're done:
-                       if VersionMatch(frompkg=ranged).match(other):
+                       if VersionMatch(ranged).match(other):
                                return True
                        # The only other case where we intersect is if ranged is a
                        # > or >= on other's version and a nonzero revision. In
@@ -362,8 +361,8 @@ class Package(object):
 
        def is_overlay(self):
                """Returns True if the package is in an overlay."""
-               dir,ovl = portage.portdb.findname2(self.cpv)
-               return ovl != self._portdir_path
+               ebuild, tree = portage.portdb.findname2(self.cpv)
+               return tree != self._portdir_path
 
        def is_masked(self):
                """Returns true if this package is masked against installation. 
@@ -372,80 +371,52 @@ class Package(object):
                unmasked = portage.portdb.xmatch("match-visible", self.cpv)
                return self.cpv not in unmasked
 
-       def get_ebuild_path(self,in_vartree=0):
+       def get_ebuild_path(self, in_vartree=False):
                """Returns the complete path to the .ebuild file"""
                if in_vartree:
-                       return vartree.getebuildpath(self.cpv)
-               else:
-                       return portage.portdb.findname(self.cpv)
+                       return VARDB.getebuildpath(self.cpv)
+               return PORTDB.findname(self.cpv)
 
        def get_package_path(self):
                """Returns the path to where the ChangeLog, Manifest, .ebuild files
                reside"""
-               p = self.get_ebuild_path()
-               sp = p.split("/")
-               if sp:
-                       # FIXME: use os.path.join
-                       return "/".join(sp[:-1])
+               ebuild_path = self.get_ebuild_path()
+               path_split = ebuild_path.split("/")
+               if path_split:
+                       return os.sep.join(path_split[:-1])
 
-       def get_env_var(self, var, tree=""):
+       def get_env_var(self, var, tree=None):
                """Returns one of the predefined env vars DEPEND, RDEPEND,
                SRC_URI,...."""
-               if tree == "":
-                       mytree = vartree
+               if tree == None:
+                       tree = VARDB
                        if not self.is_installed():
-                               mytree = porttree
-               else:
-                       mytree = tree
-               try:
-                       r = mytree.dbapi.aux_get(self.cpv,[var])
-               except KeyError:
-                       # aux_get raises KeyError if it encounters a bad digest, etc
-                       raise
-               if not r:
+                               tree = PORTDB
+               result = tree.aux_get(self.cpv, [var])
+               if not result:
                        raise errors.GentoolkitFatalError("Could not find the package tree")
-               if len(r) != 1:
+               if len(result) != 1:
                        raise errors.GentoolkitFatalError("Should only get one element!")
-               return r[0]
+               return result[0]
 
        def get_use_flags(self):
                """Returns the USE flags active at time of installation"""
                self._initdb()
                if self.is_installed():
                        return self._db.getfile("USE")
-               return ""
 
        def get_contents(self):
                """Returns the full contents, as a dictionary, in the form
-               [ '/bin/foo' : [ 'obj', '1052505381', '45ca8b89751...' ], ... ]"""
+               ['/bin/foo' : [ 'obj', '1052505381', '45ca8b89751...' ], ... ]"""
                self._initdb()
                if self.is_installed():
                        return self._db.getcontents()
                return {}               
 
-       # XXX >
-       def compare_version(self,other):
-               """Compares this package's version to another's CPV; returns -1, 0, 1.
-               
-               Deprecated in favor of __cmp__.
-               """
-               v1 = self.cpv_split
-               v2 = catpkgsplit(other.get_cpv())
-               # if category is different
-               if v1[0] != v2[0]:
-                       return cmp(v1[0],v2[0])
-               # if name is different
-               elif v1[1] != v2[1]:
-                       return cmp(v1[1],v2[1])
-               # Compare versions
-               else:
-                       return portage.pkgcmp(v1[1:],v2[1:])
-       # < XXX
-
        def size(self):
                """Estimates the installed size of the contents of this package,
                if possible.
-               Returns [size, number of files in total, number of uncounted files]
+               Returns (size, number of files in total, number of uncounted files)
                """
                contents = self.get_contents()
                size = 0
@@ -457,7 +428,7 @@ class Package(object):
                                files += 1
                        except OSError:
                                uncounted += 1
-               return [size, files, uncounted]
+               return (size, files, uncounted)
 
        def _initdb(self):
                """Internal helper function; loads package information from disk,
@@ -470,113 +441,106 @@ class Package(object):
                                settings["ROOT"],
                                settings
                        )
-                       
 
-class VersionMatch(object):
-       """Package restriction implementing Gentoo ebuild version comparison rules.
-       From pkgcore.ebuild.atom_restricts.
 
-       Any overriding of this class *must* maintain numerical order of
-       self.vals, see intersect for reason why. vals also must be a tuple.
+class PackageFormatter(object):
+       """When applied to a L{gentoolkit.package.Package} object, determine the
+       location (Portage Tree vs. overlay), install status and masked status. That
+       information can then be easily formatted and displayed.
+       
+       Example usage:
+               >>> from gentoolkit.helpers2 import find_packages
+               >>> from gentoolkit.package import PackageFormatter
+               >>> pkgs = [PackageFormatter(x) for x in find_packages('gcc')]
+               >>> for pkg in pkgs:
+               ...     # Only print packages that are installed and from the Portage
+               ...     # tree
+               ...     if set('IP').issubset(pkg.location):
+               ...             print pkg
+               ... 
+               [IP-] [  ] sys-devel/gcc-4.3.2-r3 (4.3)
+
+       @type pkg: L{gentoolkit.package.Package}
+       @param pkg: package to format
+       @type format: L{bool}
+       @param format: Whether to format the package name or not. 
+               Essentially C{format} should be set to False when piping or when
+               quiet output is desired. If C{format} is False, only the location
+               attribute will be created to save time.
        """
-       _convert_op2int = {(-1,):"<", (-1, 0): "<=", (0,):"=",
-               (0, 1):">=", (1,):">"}
-
-       _convert_int2op = dict([(v, k) for k, v in _convert_op2int.iteritems()])
-       del k, v
-
-       def __init__(self, **kwargs):
-               """This class will either create a VersionMatch instance out of
-               a Package instance, or from explicitly passed in operator, version,
-               and revision.
 
-               Possible args:
-                       frompkg=<gentoolkit.package.Package> instance
+       def __init__(self, pkg, format=True):
+               location = ''
+               maskmodes = ['  ', ' ~', ' -', 'M ', 'M~', 'M-']
 
-                       OR
+               self.pkg = pkg
+               self.format = format
+               if format:
+                       self.arch = settings["ARCH"]
+                       self.mask = maskmodes[self.get_mask_status()]
+                       self.slot = pkg.get_env_var("SLOT")
+               self.location = self.get_package_location()
 
-                       op=str: version comparison to do,
-                               valid operators are ('<', '<=', '=', '>=', '>', '~')
-                       ver=str: version to base comparison on
-                       rev=str: revision to base comparison on
-               """
-               if 'frompkg' in kwargs and kwargs['frompkg']:
-                       self.operator = kwargs['frompkg'].operator
-                       self.version = kwargs['frompkg'].version
-                       self.revision = kwargs['frompkg'].revision
-                       self.fullversion = kwargs['frompkg'].fullversion
-               elif set(('op', 'ver', 'rev')) == set(kwargs):
-                       self.operator = kwargs['op']
-                       self.version = kwargs['ver']
-                       self.revision = kwargs['rev']
-                       if not self.revision:
-                               self.fullversion = self.version
-                       else:
-                               self.fullversion = "%s-%s" % (self.version, self.revision)
-               else:
-                       raise TypeError('__init__() takes either a Package instance '
-                               'via frompkg= or op=, ver= and rev= all passed in')
-
-               if self.operator != "~" and self.operator not in self._convert_int2op:
-                       # FIXME: change error
-                       raise errors.InvalidVersion(self.ver, self.rev,
-                               "invalid operator, '%s'" % operator)
-
-               if self.operator == "~":
-                       if not self.version:
-                               raise ValueError(
-                                       "for ~ op, version must be specified")
-                       self.droprevision = True
-                       self.values = (0,)
-               else:
-                       self.droprevision = False
-                       self.values = self._convert_int2op[self.operator]
-
-       def match(self, pkginst):
-               if self.droprevision:
-                       ver1, ver2 = self.version, pkginst.version
-               else:
-                       ver1, ver2 = self.fullversion, pkginst.fullversion
-
-               #print "== VersionMatch.match DEBUG START =="
-               #print "ver1:", ver1
-               #print "ver2:", ver2
-               #print "vercmp(ver2, ver1):", vercmp(ver2, ver1)
-               #print "self.values:", self.values
-               #print "vercmp(ver2, ver1) in values?",
-               #print "vercmp(ver2, ver1) in self.values"
-               #print "==  VersionMatch.match DEBUG END  =="
-
-               return vercmp(ver2, ver1) in self.values
+       def __repr__(self):
+               return "<%s %s @%#8x>" % (self.__class__.__name__, self.pkg, id(self))
 
        def __str__(self):
-               s = self._convert_op2int[self.values]
+               if self.format:
+                       return "[%(location)s] [%(mask)s] %(package)s (%(slot)s)" % {
+                               'location': self.location,
+                               'mask': pp.maskflag(self.mask),
+                               'package': pp.cpv(self.pkg.cpv),
+                               'slot': self.slot
+                       }
+               else:
+                       return self.pkg.cpv
 
-               if self.droprevision or not self.revision:
-                       return "ver %s %s" % (s, self.version)
-               return "ver-rev %s %s-%s" % (s, self.version, self.revision)
+       def get_package_location(self):
+               """Get the physical location of a package on disk.
 
-       def __repr__(self):
-               return "<%s %s @%#8x>" % (self.__class__.__name__, str(self), id(self))
+               @rtype: str
+               @return: one of:
+                       '-P-' : Not installed and from the Portage tree
+                       '--O' : Not installed and from an overlay
+                       'IP-' : Installed and from the Portage tree
+                       'I-O' : Installed and from an overlay
+               """
 
-       @staticmethod
-       def _convert_ops(inst):
-               if inst.droprevision:
-                       return inst.values
-               return tuple(sorted(set((-1, 0, 1)).difference(inst.values)))
+               result = ['-', '-', '-']
 
-       def __eq__(self, other):
-               if self is other:
-                       return True
-               if isinstance(other, self.__class__):
-                       if (self.droprevsion != other.droprevsion or
-                               self.version != other.version or
-                               self.revision != other.revision):
-                               return False
-                       return self._convert_ops(self) == self._convert_ops(other)
+               if self.pkg.is_installed():
+                       result[0] = 'I'
+               if self.pkg.is_overlay():
+                       result[2] = 'O'
+               else:
+                       result[1] = 'P'
+
+               return ''.join(result)
+
+       def get_mask_status(self):
+               """Get the mask status of a given package. 
+
+               @type pkg: L{gentoolkit.package.Package}
+               @param pkg: pkg to get mask status of
+               @type arch: str
+               @param arch: output of gentoolkit.settings["ARCH"]
+               @rtype: int
+               @return: an index for this list: ["  ", " ~", " -", "M ", "M~", "M-"]
+                       0 = not masked
+                       1 = keyword masked
+                       2 = arch masked
+                       3 = hard masked
+                       4 = hard and keyword masked,
+                       5 = hard and arch masked
+               """
 
-               return False
+               keywords = self.pkg.get_env_var("KEYWORDS").split()
+               mask_status = 0
+               if self.pkg.is_masked():
+                       mask_status += 3
+               if ("~%s" % self.arch) in keywords:
+                       mask_status += 1
+               elif ("-%s" % self.arch) in keywords or "-*" in keywords:
+                       mask_status += 2
 
-       def __hash__(self):
-               return hash((self.droprevision, self.version, self.revision,
-                       self.values))
+               return mask_status
diff --git a/pym/gentoolkit/versionmatch.py b/pym/gentoolkit/versionmatch.py
new file mode 100644 (file)
index 0000000..0048882
--- /dev/null
@@ -0,0 +1,190 @@
+#! /usr/bin/python
+#
+# Copyright(c) 2009, Gentoo Foundation
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+"""Gentoo package version comparison object from pkgcore.ebuild.atom_restricts.
+
+The VersionMatch class allows you to compare package versions according to
+Gentoo's versioning rules.
+
+The simplest way to use it is to test simple equality. In this example I've
+passed in the keyword arguments op (operator), ver (version), and
+rev (revision) explicitly:
+>>> from gentoolkit.versionmatch import VersionMatch
+>>> VersionMatch(op='=',ver='1',rev='') == VersionMatch(op='=',ver='1',rev='')
+True
+>>> VersionMatch(op='=',ver='1',rev='') == VersionMatch(op='=',ver='2',rev='')
+False
+
+A more flexible way to use it is to pass it a single gentoolkit.package.Package
+instance which it uses to determine op, ver and rev:
+>>> from gentoolkit.package import Package
+>>> from gentoolkit.versionmatch import VersionMatch
+>>> pkg1 = Package('sys-apps/portage-2.2')                 
+>>> pkg2 = Package('sys-apps/portage-1.6')
+>>> VersionMatch(pkg1) == VersionMatch(pkg2)
+False
+
+Simple equality tests aren't actually very useful because they don't understand
+different operators:
+>>> VersionMatch(op='>', ver='1.5', rev='') == \
+... VersionMatch(op='=', ver='2', rev='')
+False
+
+For more complicated comparisons, we can use the match method:
+>>> from gentoolkit.package import Package
+>>> from gentoolkit.versionmatch import VersionMatch
+>>> pkg1 = Package('>=sys-apps/portage-2.2')
+>>> pkg2 = Package('=sys-apps/portage-2.2_rc30')
+>>> # An "rc" (release candidate) version compares less than a non "rc" version
+... VersionMatch(pkg1).match(pkg2)
+False
+>>> pkg2 = Package('=sys-apps/portage-2.2-r6')
+>>> # But an "r" (revision) version compares greater than a non "r" version
+... VersionMatch(pkg1).match(pkg2)
+True
+
+@see: gentoolkit.equery.changes for examples of use in gentoolkit.
+@see: gentoolkit.package.Package.intersects for a higher level version
+      comparison method.
+"""
+
+# =======
+# Imports
+# =======
+
+from portage.versions import vercmp
+
+import gentoolkit
+from gentoolkit import errors
+
+# =======
+# Classes
+# =======
+
+class VersionMatch(object):
+       """Gentoo package version comparison object from pkgcore.ebuild.atom_restricts.
+
+       Any overriding of this class *must* maintain numerical order of
+       self.vals, see intersect for reason why. vals also must be a tuple.
+       """
+       _convert_op2int = {(-1,):"<", (-1, 0): "<=", (0,):"=",
+               (0, 1):">=", (1,):">"}
+
+       _convert_int2op = dict([(v, k) for k, v in _convert_op2int.iteritems()])
+       del k, v
+
+       def __init__(self, *args, **kwargs):
+               """This class will either create a VersionMatch instance out of
+               a Package instance, or from explicitly passed in operator, version,
+               and revision.
+
+               Takes EITHER one arg:
+                       <gentoolkit.package.Package> instance
+
+                       OR
+
+               three keyword args:
+                       op=str: version comparison to do,
+                               valid operators are ('<', '<=', '=', '>=', '>', '~')
+                       ver=str: version to base comparison on
+                       rev=str: revision to base comparison on
+               """
+               if args and isinstance(args[0], (gentoolkit.package.Package,
+                               self.__class__)):
+                       self.operator = args[0].operator
+                       self.version = args[0].version
+                       self.revision = args[0].revision
+                       self.fullversion = args[0].fullversion
+               elif set(('op', 'ver', 'rev')) == set(kwargs):
+                       self.operator = kwargs['op']
+                       self.version = kwargs['ver']
+                       self.revision = kwargs['rev']
+                       if not self.revision:
+                               self.fullversion = self.version
+                       else:
+                               self.fullversion = "%s-%s" % (self.version, self.revision)
+               else:
+                       raise TypeError('__init__() takes either a Package instance '
+                               'argument or op=, ver= and rev= all passed in as keyword args')
+
+               if self.operator != "~" and self.operator not in self._convert_int2op:
+                       raise errors.GentoolkitInvalidVersion(
+                               "invalid operator '%s'" % self.operator)
+
+               if self.operator == "~":
+                       if not self.version:
+                               raise errors.GentoolkitInvalidVersion(
+                                       "for ~ op, version must be specified")
+                       self.droprevision = True
+                       self.values = (0,)
+               else:
+                       self.droprevision = False
+                       self.values = self._convert_int2op[self.operator]
+
+       def match(self, pkginst):
+               """See whether a passed in VersionMatch or Package instance matches
+               self.
+
+               Example usage:
+                       >>> from gentoolkit.versionmatch import VersionMatch
+                       >>> VersionMatch(op='>',ver='1.5',rev='').match(
+                       ... VersionMatch(op='=',ver='2.0',rev=''))
+                       True
+
+               @type pkginst: gentoolkit.versionmatch.VersionMatch OR
+                              gentoolkit.package.Package
+               @param pkginst: version to compare with self's version
+               @rtype: bool
+               """
+
+               if self.droprevision:
+                       ver1, ver2 = self.version, pkginst.version
+               else:
+                       ver1, ver2 = self.fullversion, pkginst.fullversion
+
+               #print "== VersionMatch.match DEBUG START =="
+               #print "ver1:", ver1
+               #print "ver2:", ver2
+               #print "vercmp(ver2, ver1):", vercmp(ver2, ver1)
+               #print "self.values:", self.values
+               #print "vercmp(ver2, ver1) in values?",
+               #print "vercmp(ver2, ver1) in self.values"
+               #print "==  VersionMatch.match DEBUG END  =="
+
+               return vercmp(ver2, ver1) in self.values
+
+       def __str__(self):
+               operator = self._convert_op2int[self.values]
+
+               if self.droprevision or not self.revision:
+                       return "ver %s %s" % (operator, self.version)
+               return "ver-rev %s %s-%s" % (operator, self.version, self.revision)
+
+       def __repr__(self):
+               return "<%s %s @%#8x>" % (self.__class__.__name__, str(self), id(self))
+
+       @staticmethod
+       def _convert_ops(inst):
+               if inst.droprevision:
+                       return inst.values
+               return tuple(sorted(set((-1, 0, 1)).difference(inst.values)))
+
+       def __eq__(self, other):
+               if self is other:
+                       return True
+               if isinstance(other, self.__class__):
+                       if (self.droprevision != other.droprevision or
+                               self.version != other.version or
+                               self.revision != other.revision):
+                               return False
+                       return self._convert_ops(self) == self._convert_ops(other)
+
+               return False
+
+       def __hash__(self):
+               return hash((self.droprevision, self.version, self.revision,
+                       self.values))