diff options
-rw-r--r-- | pym/gentoolkit/equery/has.py | 220 | ||||
-rw-r--r-- | pym/gentoolkit/query.py | 33 |
2 files changed, 243 insertions, 10 deletions
diff --git a/pym/gentoolkit/equery/has.py b/pym/gentoolkit/equery/has.py new file mode 100644 index 0000000..e40a766 --- /dev/null +++ b/pym/gentoolkit/equery/has.py @@ -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: diff --git a/pym/gentoolkit/query.py b/pym/gentoolkit/query.py index c18d19e..9ad9017 100644 --- a/pym/gentoolkit/query.py +++ b/pym/gentoolkit/query.py @@ -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 |