diff options
author | fuzzyray <fuzzyray@gentoo.org> | 2009-12-08 21:53:45 +0000 |
---|---|---|
committer | fuzzyray <fuzzyray@gentoo.org> | 2009-12-08 21:53:45 +0000 |
commit | acdf616efa73b77936963eaa8b5c715db97646d2 (patch) | |
tree | d08ef2efee8b7edbf8c1df1a8de26439d6b42bd3 /pym/gentoolkit/helpers.py | |
parent | Speedup portageq queries. Include FuzzyRay's patch to respect EMERGE_DEFAULT_... (diff) | |
download | gentoolkit-acdf616efa73b77936963eaa8b5c715db97646d2.tar.gz gentoolkit-acdf616efa73b77936963eaa8b5c715db97646d2.tar.bz2 gentoolkit-acdf616efa73b77936963eaa8b5c715db97646d2.zip |
Merge rev 113 from djanderson's genscripts repo
svn path=/trunk/gentoolkit/; revision=703
Diffstat (limited to 'pym/gentoolkit/helpers.py')
-rw-r--r-- | pym/gentoolkit/helpers.py | 843 |
1 files changed, 699 insertions, 144 deletions
diff --git a/pym/gentoolkit/helpers.py b/pym/gentoolkit/helpers.py index 6d272d3..277e41f 100644 --- a/pym/gentoolkit/helpers.py +++ b/pym/gentoolkit/helpers.py @@ -1,163 +1,718 @@ -#!/usr/bin/python2 -# -# Copyright(c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org> # Copyright(c) 2009, Gentoo Foundation # -# Licensed under the GNU General Public License, v2 +# Licensed under the GNU General Public License, v2 or higher # # $Header$ +"""Improved versions of the original helpers functions. + +As a convention, functions ending in '_packages' or '_match{es}' return +Package objects, while functions ending in 'cpvs' return a sequence of strings. +Functions starting with 'get_' return a set of packages by default and can be +filtered, while functions starting with 'find_' return nothing unless the +query matches one or more packages. +""" + +# Move to Imports section after Python 2.6 is stable +from __future__ import with_statement + +__all__ = ( + 'ChangeLog', + 'FileOwner', + 'compare_package_strings', + 'do_lookup', + 'find_best_match', + 'find_installed_packages', + 'find_packages', + 'get_cpvs', + 'get_installed_cpvs', + 'get_uninstalled_cpvs', + 'uniqify', + 'uses_globbing', + 'split_cpv' +) +__docformat__ = 'epytext' + +# ======= +# Imports +# ======= + +import fnmatch +import os +import re +from functools import partial +from itertools import chain + import portage -from gentoolkit import * -from package import * -from pprinter import print_warn -try: - from portage.util import unique_array -except ImportError: - from portage_util import unique_array - -def find_packages(search_key, masked=False): - """Returns a list of Package objects that matched the search key.""" - try: - if masked: - t = portage.db["/"]["porttree"].dbapi.xmatch("match-all", search_key) - t += portage.db["/"]["vartree"].dbapi.match(search_key) +from portage.versions import catpkgsplit, pkgcmp + +from gentoolkit import pprinter as pp +from gentoolkit import CONFIG +from gentoolkit import errors +from gentoolkit.atom import Atom +from gentoolkit.cpv import CPV +from gentoolkit.dbapi import PORTDB, VARDB +from gentoolkit.versionmatch import VersionMatch +# This has to be imported below to stop circular import. +#from gentoolkit.package import Package + +# ======= +# Classes +# ======= + +class ChangeLog(object): + """Provides methods for working with a Gentoo ChangeLog file. + + Example usage: + >>> from gentoolkit.helpers import ChangeLog + >>> portage = ChangeLog('/usr/portage/sys-apps/portage/ChangeLog') + >>> print portage.latest.strip() + *portage-2.2_rc50 (15 Nov 2009) + + 15 Nov 2009; Zac Medico <zmedico@gentoo.org> +portage-2.2_rc50.ebuild: + 2.2_rc50 bump. This includes all fixes in 2.1.7.5. + >>> len(portage.full) + 75 + >>> len(portage.entries_matching_range( + ... from_ver='2.2_rc40', + ... to_ver='2.2_rc50')) + 11 + + """ + def __init__(self, changelog_path, invalid_entry_is_fatal=False): + if not (os.path.isfile(changelog_path) and + os.access(changelog_path, os.R_OK)): + raise errors.GentoolkitFatalError( + "%s does not exist or is unreadable" % pp.path(changelog_path) + ) + self.changelog_path = changelog_path + self.invalid_entry_is_fatal = invalid_entry_is_fatal + + # Process the ChangeLog: + self.entries = self._split_changelog() + self.indexed_entries = self._index_changelog() + + def __repr__(self): + return "<%s %r>" % (self.__class__.__name__, self.changelog_path) + + @property + def full(self): + """Return the output of L{self._split_changelog}.""" + return self.entries + + @property + def latest(self): + """Return the newest ChangeLog entry.""" + return self.entries[0] + + def entries_matching_atom(self, atom): + """Return entries whose header versions match atom's version. + + @type atom: L{gentoolkit.atom.Atom} or str + @param atom: a atom to find matching entries against + @rtype: list + @return: entries matching atom + @raise errors.GentoolkitInvalidAtom: if atom is a string and malformed + """ + result = [] + + if not isinstance(atom, Atom): + atom = Atom(atom) + + for entry_set in self.indexed_entries: + i, entry = entry_set + # VersionMatch doesn't store .cp, so we'll force it to match here: + i.cpv.cp = atom.cpv.cp + if atom.intersects(i): + result.append(entry) + + return result + + def entries_matching_range(self, from_ver=None, to_ver=None): + """Return entries whose header versions are within a range of versions. + + @type from_ver: str + @param from_ver: valid Gentoo version + @type to_ver: str + @param to_ver: valid Gentoo version + @rtype: list + @return: entries between from_ver and to_ver + @raise errors.GentoolkitFatalError: if neither vers are set + @raise errors.GentoolkitInvalidVersion: if either ver is invalid + """ + result = [] + + # Make sure we have at least one version set + if not (from_ver or to_ver): + raise errors.GentoolkitFatalError( + "Need to specifiy 'from_ver' or 'to_ver'" + ) + + # Create a VersionMatch instance out of from_ver + from_restriction = None + if from_ver: + try: + from_ver_rev = CPV("null-%s" % from_ver) + except errors.GentoolkitInvalidCPV: + raise errors.GentoolkitInvalidVersion(from_ver) + from_restriction = VersionMatch(from_ver_rev, op='>=') + + # Create a VersionMatch instance out of to_ver + to_restriction = None + if to_ver: + try: + to_ver_rev = CPV("null-%s" % to_ver) + except errors.GentoolkitInvalidCPV: + raise errors.GentoolkitInvalidVersion(to_ver) + to_restriction = VersionMatch(to_ver_rev, op='<=') + + # Add entry to result if version ranges intersect it + for entry_set in self.indexed_entries: + i, entry = entry_set + if from_restriction and not from_restriction.match(i): + continue + if to_restriction and not to_restriction.match(i): + continue + result.append(entry) + + return result + + def _index_changelog(self): + """Use the output of L{self._split_changelog} to create an index list + of L{gentoolkit.versionmatch.VersionMatch} objects. + + @rtype: list + @return: tuples containing a VersionMatch instance for the release + version of each entry header as the first item and the entire entry + as the second item + @raise ValueError: if self.invalid_entry_is_fatal is True and we hit an + invalid entry + """ + + result = [] + for entry in self.entries: + # Extract the package name from the entry header, ex: + # *xterm-242 (07 Mar 2009) => xterm-242 + pkg_name = entry.split(' ', 1)[0].lstrip('*') + if not pkg_name.strip(): + continue + try: + entry_ver = CPV(pkg_name) + except errors.GentoolkitInvalidCPV: + if self.invalid_entry_is_fatal: + raise ValueError(entry_ver) + continue + + result.append((VersionMatch(entry_ver, op='='), entry)) + + return result + + def _split_changelog(self): + """Split the ChangeLog into individual entries. + + @rtype: list + @return: individual ChangeLog entries + """ + + result = [] + partial_entries = [] + with open(self.changelog_path) as log: + for line in log: + if line.startswith('#'): + continue + elif line.startswith('*'): + # Append last entry to result... + entry = ''.join(partial_entries) + if entry and not entry.isspace(): + result.append(entry) + # ... and start a new entry + partial_entries = [line] + else: + partial_entries.append(line) + else: + # Append the final entry + entry = ''.join(partial_entries) + result.append(entry) + + return result + + +class FileOwner(object): + """Creates a function for locating the owner of filename queries. + + Example usage: + >>> from gentoolkit.helpers import FileOwner + >>> findowner = FileOwner() + >>> findowner(('/usr/bin/vim',)) + [(<Package app-editors/vim-7.2.182>, '/usr/bin/vim')] + """ + def __init__(self, is_regex=False, early_out=False, printer_fn=None): + """Instantiate function. + + @type is_regex: bool + @param is_regex: funtion args are regular expressions + @type early_out: bool + @param early_out: return when first result is found (safe) + @type printer_fn: callable + @param printer_fn: If defined, will be passed useful information for + printing each result as it is found. + """ + self.is_regex = is_regex + self.early_out = early_out + self.printer_fn = printer_fn + + def __call__(self, queries): + """Run the function. + + @type queries: iterable + @param queries: filepaths or filepath regexes + """ + query_re_string = self._prepare_search_regex(queries) + try: + query_re = re.compile(query_re_string) + except (TypeError, re.error), err: + raise errors.GentoolkitInvalidRegex(err) + + use_match = False + if ((self.is_regex or query_re_string.startswith('^\/')) + and '|' not in query_re_string ): + # If we were passed a regex or a single path starting with root, + # we can use re.match, else use re.search. + use_match = True + + return self.find_owners(query_re, use_match=use_match) + + def find_owners(self, query_re, use_match=False, pkgset=None): + """Find owners and feed data to supplied output function. + + @type query_re: _sre.SRE_Pattern + @param query_re: file regex + @type use_match: bool + @param use_match: use re.match or re.search + @type pkgset: iterable or None + @param pkgset: list of packages to look through + """ + # FIXME: Remove when lazyimport supports objects: + from gentoolkit.package import Package + + if use_match: + query_fn = query_re.match + else: + query_fn = query_re.search + + results = [] + found_match = False + if pkgset is None: + pkgset = get_installed_cpvs() + for pkg in sorted([Package(x) for x in pkgset]): + files = pkg.get_contents() + for cfile in files: + match = query_fn(cfile) + if match: + results.append((pkg, cfile)) + if self.printer_fn is not None: + self.printer_fn(pkg, cfile) + if self.early_out: + found_match = True + break + if found_match: + break + return results + + @staticmethod + def extend_realpaths(paths): + """Extend a list of paths with the realpaths for any symlinks. + + @type paths: list + @param paths: file path strs + @rtype: list + @return: the original list plus the realpaths for any symlinks + so long as the realpath doesn't already exist in the list + @raise AttributeError: if paths does not have attribute 'extend' + """ + + osp = os.path + paths.extend([osp.realpath(x) for x in paths + if osp.islink(x) and osp.realpath(x) not in paths]) + + return paths + + def _prepare_search_regex(self, queries): + """Create a regex out of the queries""" + + queries = list(queries) + if self.is_regex: + return '|'.join(queries) else: - t = portage.db["/"]["porttree"].dbapi.match(search_key) - t += portage.db["/"]["vartree"].dbapi.match(search_key) - # catch the "amgigous package" Exception - except ValueError, e: - if isinstance(e[0],list): - t = [] - for cp in e[0]: - if masked: - t += portage.db["/"]["porttree"].dbapi.xmatch("match-all", cp) - t += portage.db["/"]["vartree"].dbapi.match(cp) + result = [] + # Trim trailing and multiple slashes from queries + slashes = re.compile('/+') + queries = self.extend_realpaths(queries) + for query in queries: + query = slashes.sub('/', query).rstrip('/') + if query.startswith('/'): + query = "^%s$" % re.escape(query) else: - t += portage.db["/"]["porttree"].dbapi.match(cp) - t += portage.db["/"]["vartree"].dbapi.match(cp) + query = "/%s$" % re.escape(query) + result.append(query) + result = "|".join(result) + return result + +# ========= +# Functions +# ========= + +def compare_package_strings(pkg1, pkg2): + """Similar to the builtin cmp, but for package strings. Usually called + as: package_list.sort(compare_package_strings) + + 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) + if pkg1[0] != pkg2[0]: + return cmp(pkg1[0], pkg2[0]) + elif pkg1[1] != pkg2[1]: + return cmp(pkg1[1], pkg2[1]) + else: + return pkgcmp(pkg1[1:], pkg2[1:]) + + +def do_lookup(query, query_opts): + """A high-level wrapper around gentoolkit package-finder functions. + + @type query: str + @param query: pkg, cat/pkg, pkg-ver, cat/pkg-ver, atom, glob or regex + @type query_opts: dict + @param query_opts: user-configurable options from the calling module + Currently supported options are: + + includeInstalled = bool + includePortTree = bool + includeOverlayTree = bool + isRegex = bool + printMatchInfo = bool # Print info about the search + + @rtype: list + @return: Package objects matching query + """ + + 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(e) - except portage_exception.InvalidAtom, e: - print_warn("Invalid Atom: '%s'" % str(e)) + 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: + raise errors.GentoolkitFatalError( + "Not searching in installed, Portage tree, or overlay. " + "Nothing to do." + ) + + 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, simple_package_finder, query_opts) + else: + matches = _do_complex_lookup(query, complex_package_finder, query_opts) + + return matches + + +def _do_complex_lookup(query, package_finder, query_opts): + """Find matches for a query which is a regex or includes globbing.""" + + # FIXME: Remove when lazyimport supports objects: + from gentoolkit.package import Package + + result = [] + + if query_opts["printMatchInfo"] and not CONFIG["piping"]: + print_query_info(query, query_opts) + + cat = split_cpv(query)[0] + + pre_filter = [] + # The "get_" functions can pre-filter against the whole package key, + # but since we allow globbing now, we run into issues like: + # >>> portage.dep.dep_getkey("sys-apps/portage-*") + # 'sys-apps/portage-' + # So the only way to guarantee we don't overrun the key is to + # prefilter by cat only. + if cat: + if query_opts["isRegex"]: + 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) + pre_filter = package_finder(predicate=predicate) + + # Post-filter + if query_opts["isRegex"]: + predicate = lambda x: re.search(query, x) + else: + if cat: + query_re = fnmatch.translate(query) + else: + query_re = fnmatch.translate("*/%s" % query) + predicate = lambda x: re.search(query_re, x) + if pre_filter: + result = [x for x in pre_filter if predicate(x)] + else: + result = package_finder(predicate=predicate) + + return [Package(x) for x in result] + + +def _do_simple_lookup(query, package_finder, query_opts): + """Find matches for a query which is an atom or string.""" + + result = [] + + if query_opts["printMatchInfo"] and CONFIG['verbose']: + print_query_info(query, query_opts) + + result = package_finder(query) + if not query_opts["includeInstalled"]: + result = [x for x in result if not x.is_installed()] + + return result + + +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 + @raise portage.exception.InvalidAtom: if query is not valid input + """ + # FIXME: Remove when lazyimport supports objects: + from gentoolkit.package import Package + + try: + match = PORTDB.xmatch("bestmatch-visible", query) + except portage.exception.InvalidAtom, err: + raise errors.GentoolkitInvalidAtom(err) + + return Package(match) if match else None + + +def find_installed_packages(query): + """Return a list of Package objects that matched the search key.""" + # FIXME: Remove when lazyimport supports objects: + from gentoolkit.package import Package + + 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: + raise errors.GentoolkitInvalidAtom(err) + + return [Package(x) for x in matches] + + +def find_packages(query, include_masked=False): + """Returns a list of Package objects that matched the query. + + @type query: str + @param query: can be of the form: pkg, pkg-ver, cat/pkg, cat/pkg-ver, atom + @type include_masked: bool + @param include_masked: include masked packages + @rtype: list + @return: matching Package objects + """ + # FIXME: Remove when lazyimport supports objects: + from gentoolkit.package import Package + + if not query: return [] - # Make the list of packages unique - t = unique_array(t) - t.sort() - return [Package(x) for x in t] -def find_installed_packages(search_key, masked=False): - """Returns a list of Package objects that matched the search key.""" try: - t = portage.db["/"]["vartree"].dbapi.match(search_key) - # catch the "amgigous package" Exception - except ValueError, e: - if isinstance(e[0],list): - t = [] - for cp in e[0]: - t += portage.db["/"]["vartree"].dbapi.match(cp) + if include_masked: + matches = PORTDB.xmatch("match-all", query) else: - raise ValueError(e) - except portage_exception.InvalidAtom, e: - print_warn("Invalid Atom: '%s'" % str(e)) - return [] - return [Package(x) for x in t] - -def find_best_match(search_key): - """Returns a Package object for the best available candidate that - matched the search key.""" - t = portage.db["/"]["porttree"].dep_bestmatch(search_key) - if t: - return Package(t) - return None - -def find_system_packages(prefilter=None): - """Returns a tuple of lists, first list is resolved system packages, - second is a list of unresolved packages.""" - pkglist = settings.packages - resolved = [] - unresolved = [] - for x in pkglist: - cpv = x.strip() - if len(cpv) and cpv[0] == "*": - pkg = find_best_match(cpv) - if pkg: - resolved.append(pkg) - else: - unresolved.append(cpv) - return (resolved, unresolved) - -def find_world_packages(prefilter=None): - """Returns a tuple of lists, first list is resolved world packages, - seond is unresolved package names.""" - f = open(portage.root+portage.WORLD_FILE) - pkglist = f.readlines() - resolved = [] - unresolved = [] - for x in pkglist: - cpv = x.strip() - if len(cpv) and cpv[0] != "#": - pkg = find_best_match(cpv) - if pkg: - resolved.append(pkg) - else: - unresolved.append(cpv) - return (resolved,unresolved) - -def find_all_installed_packages(prefilter=None): - """Returns a list of all installed packages, after applying the prefilter - function""" - t = vartree.dbapi.cpv_all() - if prefilter: - t = filter(prefilter,t) - return [Package(x) for x in t] - -def find_all_uninstalled_packages(prefilter=None): - """Returns a list of all uninstalled packages, after applying the prefilter - function""" - alist = find_all_packages(prefilter) - return [x for x in alist if not x.is_installed()] - -def find_all_packages(prefilter=None): - """Returns a list of all known packages, installed or not, after applying - the prefilter function""" - t = porttree.dbapi.cp_all() - t += vartree.dbapi.cp_all() - if prefilter: - t = filter(prefilter,t) - t = unique_array(t) - t2 = [] - for x in t: - t2 += porttree.dbapi.cp_list(x) - t2 += vartree.dbapi.cp_list(x) - t2 = unique_array(t2) - return [Package(x) for x in t2] - -def split_package_name(name): - """Returns a list on the form [category, name, version, revision]. Revision will - be 'r0' if none can be inferred. Category and version will be empty, if none can - be inferred.""" - r = portage.catpkgsplit(name) - if not r: - r = name.split("/") - if len(r) == 1: - return ["", name, "", "r0"] + matches = PORTDB.match(query) + matches.extend(VARDB.match(query)) + except portage.exception.InvalidAtom, err: + raise errors.GentoolkitInvalidAtom(str(err)) + + return [Package(x) for x in set(matches)] + + +def get_cpvs(predicate=None, include_installed=True): + """Get all packages in the Portage tree and overlays. Optionally apply a + predicate. + + Example usage: + >>> from gentoolkit.helpers import get_cpvs + >>> len(set(get_cpvs())) + 26065 + >>> fn = lambda x: x.startswith('app-portage') + >>> len(get_cpvs(fn, include_installed=False)) + 112 + + @type predicate: function + @param predicate: a function to filter the package list with + @type include_installed: bool + @param include_installed: + If True: Return the union of all_cpvs and all_installed_cpvs + If False: Return the difference of all_cpvs and all_installed_cpvs + @rtype: generator + @return: a generator that yields unsorted cat/pkg-ver strings from the + Portage tree + """ + + if predicate: + all_cps = iter(x for x in PORTDB.cp_all() if predicate(x)) + else: + all_cps = PORTDB.cp_all() + + all_cpvs = chain.from_iterable(PORTDB.cp_list(x) for x in all_cps) + all_installed_cpvs = get_installed_cpvs(predicate) + + if include_installed: + for cpv in chain(all_cpvs, all_installed_cpvs): + yield cpv + else: + # Consume the smaller pkg set: + installed_cpvs = set(all_installed_cpvs) + for cpv in all_cpvs: + if cpv not in installed_cpvs: + yield cpv + + +# pylint thinks this is a global variable +# pylint: disable-msg=C0103 +get_uninstalled_cpvs = partial(get_cpvs, include_installed=False) + + +def get_installed_cpvs(predicate=None): + """Get all installed packages. Optionally apply a predicate. + + @type predicate: function + @param predicate: a function to filter the package list with + @rtype: generator + @return: a generator that yields unsorted installed cat/pkg-ver strings + from VARDB + """ + + if predicate: + installed_cps = iter(x for x in VARDB.cp_all() if predicate(x)) + else: + installed_cps = VARDB.cp_all() + + for cpv in chain.from_iterable(VARDB.cp_list(x) for x in installed_cps): + yield cpv + + +def print_query_info(query, query_opts): + """Print info about the query to the screen.""" + + cat, pkg = split_cpv(query)[:2] + if cat and not query_opts["isRegex"]: + cat_str = "in %s " % pp.emph(cat.lstrip('><=~!')) + 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_cpv(query): + """Split a cpv into category, name, version and revision. + + @type query: str + @param query: pkg, cat/pkg, pkg-ver, cat/pkg-ver, atom or regex + @rtype: tuple + @return: (category, pkg_name, version, revision) + Each tuple element is a string or empty string (""). + """ + + result = catpkgsplit(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: - return r + ["", "r0"] + result = result + ['', ''] + + if len(result) != 4: + 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: - r = list(r) - if r[0] == 'null': - r[0] = '' - return r + result = list(set(seq)) + + return result + + +def uses_globbing(query): + """Check the query to see if it is using globbing. -# 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 + @type query: str + @param query: user input package query + @rtype: bool + @return: True if query uses globbing, else False + """ -if __name__ == "__main__": - print "This module is for import only" + if set('!*?[]').intersection(query): + # Is query an atom such as '=sys-apps/portage-2.2*'? + if query[0] != '=': + return True + return False +# vim: set ts=4 sw=4 tw=79: |