Merge from genscripts r459: douglasjanderson
authorfuzzyray <fuzzyray@gentoo.org>
Wed, 22 Sep 2010 21:51:32 +0000 (21:51 -0000)
committerfuzzyray <fuzzyray@gentoo.org>
Wed, 22 Sep 2010 21:51:32 +0000 (21:51 -0000)
Fixing a traceback when Query is passed an invalid atom, and merging some
_incomplete_ work on has.py

Merge from genscripts r454: brian.dolbec
make the needed changes to fix bug 114086 in the has module.

Merge from genscripts r452: douglasjanderson
gentoolkit.query.Query can't subclass gentoolkit.atom.Atom because we can't
control what portage.dep.Atom takes as input.

Merge from genscripts r451: douglasjanderson
Revert r438 because the problem it addresses can be taken care of more robustly.

Merge from genscripts r449: douglasjanderson
Make some modifications to fix bug #309091

Merge from genscripts r444: brian.dolbec
add more help about the env_var's

Merge from genscripts r443: douglasjanderson
Simplify a func for ease of testing

Merge from genscripts r442: brian.dolbec
initial commit of a general purpose equery has module useable for nearly all the
available vardb ENVIRONMENT data"

Merge from genscripts r438: brian.dolbec
rework the dpendencies _parser() tok == '' trap in an attempt to trap the
InvalidAtom error, add '' removal from matches in query, add more detail in the
error from Atom, add a trap to skirt around a null atom in the ChangeLog class.
Hopefully I either solved it or have it printing out more info this time.

Merge from genscripts r436: brian.dolbec
fix the error in the raising of the GentoolkitInvalidAtom error, find and changethe info printed out in the error message to help track down the problem.

Merge from genscripts r428: andkit
Fix globbing for python-2.6.5/3.1.2 (fixes #315665)

Apply the regexp only to the category instead of trying to
modify the regexp to match the full package key to avoid relying on
a specific layout of the regexp return by fnmatch.translate.

svn path=/trunk/gentoolkit/; revision=811

pym/gentoolkit/equery/has.py [new file with mode: 0644]
pym/gentoolkit/query.py

diff --git a/pym/gentoolkit/equery/has.py b/pym/gentoolkit/equery/has.py
new file mode 100644 (file)
index 0000000..e40a766
--- /dev/null
@@ -0,0 +1,220 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2 or higher
+#
+# $Header: $
+
+"""List all installed packages that match for a given ENVIRONMENT variable"""
+
+from __future__ import print_function
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import sys
+from getopt import gnu_getopt, GetoptError
+
+from portage import auxdbkeys
+
+import gentoolkit.pprinter as pp
+from gentoolkit import errors
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.package import PackageFormatter, FORMAT_TMPL_VARS
+from gentoolkit.query import Query
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+       "in_installed": True,
+       "in_porttree": False,
+       "in_overlay": False,
+       "include_masked": True,
+       "show_progress": False,
+       "package_format": None,
+       "package_filter": None,
+       "env_var": None
+}
+
+AUXDBKEYS = set(auxdbkeys)
+AUXDBKEYS.difference_update(['UNUSED_0{0}'.format(x) for x in range(6)])
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+       """Print description, usage and a detailed help message.
+
+       @type with_description: bool
+       @param with_description: if true, print module's __doc__ string
+       """
+
+       if with_description:
+               print(__doc__.strip())
+               print()
+       print(mod_usage(mod_name="has", arg="env_var [expr]"))
+       print()
+       print(pp.command("options"))
+       print(format_options((
+               (" -h, --help", "display this help message"),
+               (" -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"),
+               (" -F, --format=TMPL", "specify a custom output format"),
+               ("              TMPL",
+                       "a format template using (see man page):")
+       )))
+       print(" " * 24, ', '.join(pp.emph(x) for x in FORMAT_TMPL_VARS))                        
+       print()
+       print(pp.command("env_var"))
+       print("", ', '.join(pp.emph(x) for x in AUXDBKEYS))                     
+       print(" other env_vars provided by eclasses may be available")
+
+
+def query_in_env(query, env_var, pkg):
+       """Check if the query is in the pkg's environment."""
+       
+       try:
+               if env_var in ("USE", "IUSE"):
+                       results = set(
+                               [x.lstrip("+-") for x in pkg.environment(env_var).split()]
+                       )
+               else:
+                       results = set(pkg.environment(env_var).split())
+       except errors.GentoolkitFatalError:
+               # aux_get KeyError or other unexpected result
+               return False
+
+       if query in results:
+               return True
+
+       return False
+
+
+def display_pkg(query, env_var, pkg):
+       """Display information for a given package."""
+
+       if CONFIG['verbose']:
+               pkgstr = PackageFormatter(
+                       pkg,
+                       do_format=True,
+                       custom_format=QUERY_OPTS["package_format"]
+               )
+       else:
+               pkgstr = PackageFormatter(
+                       pkg,
+                       do_format=False,
+                       custom_format=QUERY_OPTS["package_format"]
+               )
+
+       if (QUERY_OPTS["in_installed"] and
+               not QUERY_OPTS["in_porttree"] and
+               not QUERY_OPTS["in_overlay"]):
+               if not 'I' in  pkgstr.location:
+                       return False
+       if (QUERY_OPTS["in_porttree"] and
+               not QUERY_OPTS["in_overlay"]):
+               if not 'P' in  pkgstr.location:
+                       return False
+       if (QUERY_OPTS["in_overlay"] and
+               not QUERY_OPTS["in_porttree"]):
+               if not 'O' in  pkgstr.location:
+                       return False
+       pp.uprint(pkgstr)
+
+       return True
+
+
+def parse_module_options(module_opts):
+       """Parse module options and update QUERY_OPTS"""
+
+       # Parse module options
+       opts = (x[0] for x in module_opts)
+       posargs = (x[1] for x in module_opts)
+       for opt, posarg in zip(opts, posargs):
+               if opt in ('-h', '--help'):
+                       print_help()
+                       sys.exit(0)
+               elif opt in ('-I', '--exclue-installed'):
+                       QUERY_OPTS['in_installed'] = False
+               elif opt in ('-p', '--portage-tree'):
+                       QUERY_OPTS['in_porttree'] = True
+               elif opt in ('-o', '--overlay-tree'):
+                       QUERY_OPTS['in_overlay'] = True
+               elif opt in ('-F', '--format'):
+                       QUERY_OPTS["package_format"] = posarg
+               elif opt in ('--package'):
+                       QUERY_OPTS["package_filter"] = posarg
+
+
+def main(input_args):
+       """Parse input and run the program"""
+
+       short_opts = "hiIpoF:" # -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', 'format=', 'package=')
+
+       try:
+               module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+       except GetoptError as err:
+               sys.stderr.write(pp.error("Module %s" % err))
+               print()
+               print_help(with_description=False)
+               sys.exit(2)
+
+       parse_module_options(module_opts)
+
+       if not queries:
+               print_help()
+               sys.exit(2)
+
+       query_scope = QUERY_OPTS['package_filter'] or '*'
+       matches = Query(query_scope).smart_find(**QUERY_OPTS)
+       matches.sort()
+
+       # split out the first query since it is suppose to be the env_var
+       QUERY_OPTS['env_var'] = queries.pop(0)
+       env_var = QUERY_OPTS['env_var']
+
+       #
+       # Output
+       #
+
+       if not queries:
+               if not QUERY_OPTS['package_filter']:
+                       err = "Used ENV_VAR without match_expression or --package"
+                       raise errors.GentoolkitFatalError(err, is_serious=False)
+               else:
+                       if len(matches) > 1:
+                               raise errors.AmbiguousPackageName(matches)
+                       for match in matches:
+                               env = QUERY_OPTS['env_var']
+                               print(match.environment(env))
+
+       first_run = True
+       got_match = False
+       for query in queries:
+               if not first_run:
+                       print()
+
+               if CONFIG['verbose']:
+                       status = " * Searching for {0} {1} ... "
+                       pp.uprint(status.format(env_var, pp.emph(query)))
+
+               for pkg in matches:
+                       if query_in_env(query, env_var, pkg):
+                               display_pkg(query, env_var, pkg)
+                               got_match = True
+               first_run = False
+
+       if not got_match:
+               sys.exit(1)
+
+# vim: set ts=4 sw=4 tw=79:
index c18d19e101d338dfa4ebb380c7d3b96632994432..9ad9017a0ab881128da1b3f622f9c6b21720e090 100644 (file)
@@ -27,6 +27,7 @@ from gentoolkit import CONFIG
 from gentoolkit import errors
 from gentoolkit import helpers
 from gentoolkit import pprinter as pp
+from gentoolkit.atom import Atom
 from gentoolkit.cpv import CPV
 from gentoolkit.dbapi import PORTDB, VARDB
 from gentoolkit.package import Package
@@ -36,7 +37,7 @@ from gentoolkit.sets import get_set_atoms, SETPREFIX
 # Classes
 # =======
 
-class Query(object):
+class Query(CPV):
        """Provides common methods on a package query."""
 
        def __init__(self, query, is_regex=False):
@@ -60,6 +61,16 @@ class Query(object):
                self.is_regex = is_regex
                self.query_type = self._get_query_type()
 
+               # Name the rest of the chunks, if possible
+               if self.query_type != "set":
+                       try:
+                               atom = Atom(self.query)
+                               self.__dict__.update(atom.__dict__)
+                       except errors.GentoolkitInvalidAtom:
+                               CPV.__init__(self, self.query)
+                               self.operator = ''
+                               self.atom = self.cpv
+
        def __repr__(self):
                rx = ''
                if self.is_regex:
@@ -79,8 +90,7 @@ class Query(object):
                        cat_str = ""
                        pkg_str = pp.emph(self.query)
                else:
-                       cpv = CPV(self.query)
-                       cat, pkg = cpv.category, cpv.name + cpv.fullversion
+                       cat, pkg = self.category, self.name + self.fullversion
                        if cat and not self.is_regex:
                                cat_str = "in %s " % pp.emph(cat.lstrip('><=~!'))
                        else:
@@ -185,7 +195,9 @@ class Query(object):
                        if in_installed:
                                matches.extend(VARDB.match(self.query))
                except portage.exception.InvalidAtom as err:
-                       raise errors.GentoolkitInvalidAtom(str(err))
+                       message = "query.py: find(), query=%s, InvalidAtom=%s" %(
+                               self.query, str(err))
+                       raise errors.GentoolkitInvalidAtom(message)
 
                return [Package(x) for x in set(matches)]
 
@@ -221,7 +233,9 @@ class Query(object):
                try:
                        best = PORTDB.xmatch("bestmatch-visible", self.query)
                except portage.exception.InvalidAtom as err:
-                       raise errors.GentoolkitInvalidAtom(err)
+                       message = "query.py: find_best(), bestmatch-visible, " + \
+                               "query=%s, InvalidAtom=%s" %(self.query, str(err))
+                       raise errors.GentoolkitInvalidAtom(message)
                # xmatch can return an empty string, so checking for None is not enough
                if not best:
                        if not (include_keyworded or include_masked):
@@ -229,7 +243,9 @@ class Query(object):
                        try:
                                matches = PORTDB.xmatch("match-all", self.query)
                        except portage.exception.InvalidAtom as err:
-                               raise errors.GentoolkitInvalidAtom(err)
+                               message = "query.py: find_best(), match-all, query=%s, InvalidAtom=%s" %(
+                                       self.query, str(err))
+                               raise errors.GentoolkitInvalidAtom(message)
                        masked = portage.best(matches)
                        keywordable = []
                        for m in matches:
@@ -302,10 +318,7 @@ class Query(object):
                                cat_re = cat
                        else:
                                cat_re = fnmatch.translate(cat)
-                               # [::-1] reverses a sequence, so we're emulating an ".rreplace()"
-                               # except we have to put our "new" string on backwards
-                               cat_re = cat_re[::-1].replace('$', '*./', 1)[::-1]
-                       predicate = lambda x: re.match(cat_re, x)
+                       predicate = lambda x: re.match(cat_re, x.split("/", 1)[0])
                        pre_filter = self.package_finder(predicate=predicate)
 
                # Post-filter