diff options
author | fuzzyray <fuzzyray@gentoo.org> | 2009-05-05 17:39:24 +0000 |
---|---|---|
committer | fuzzyray <fuzzyray@gentoo.org> | 2009-05-05 17:39:24 +0000 |
commit | c819d146be6bce86d97019494173253e71b85d2f (patch) | |
tree | 200d00c2b9a420540ff9c4e0d8b3080b762fb562 /pym/gentoolkit/equery/__init__.py | |
parent | Add some useful informations when using $EDITOR. (diff) | |
download | gentoolkit-c819d146be6bce86d97019494173253e71b85d2f.tar.gz gentoolkit-c819d146be6bce86d97019494173253e71b85d2f.tar.bz2 gentoolkit-c819d146be6bce86d97019494173253e71b85d2f.zip |
Rearrange trunk to support gentoolkit version 0.3. Split into gentoolkit, gentoolkit-dev, and deprecated. Import djanderson's work on the gentoolkit library and equery
svn path=/trunk/gentoolkit/; revision=589
Diffstat (limited to 'pym/gentoolkit/equery/__init__.py')
-rw-r--r-- | pym/gentoolkit/equery/__init__.py | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/pym/gentoolkit/equery/__init__.py b/pym/gentoolkit/equery/__init__.py new file mode 100644 index 0000000..6bb04a9 --- /dev/null +++ b/pym/gentoolkit/equery/__init__.py @@ -0,0 +1,407 @@ +# Copyright(c) 2009, Gentoo Foundation +# +# Licensed under the GNU General Public License, v2 +# +# $Header: $ + +"""Gentoo package query tool""" + +# Move to Imports section after Python 2.6 is stable +from __future__ import with_statement + +__all__ = ( + 'format_options', + 'format_package_names', + 'mod_usage' +) +__docformat__ = 'epytext' + +# ======= +# Imports +# ======= + +import errno +import sys +import time +from getopt import getopt, GetoptError + +import gentoolkit +import gentoolkit.pprinter as pp +from gentoolkit import catpkgsplit, settings, Package, Config +from gentoolkit.textwrap_ import TextWrapper + +__productname__ = "equery" +__authors__ = """\ +Karl Trygve Kalleberg - Original author +Douglas Anderson - Modular redesign; author of meta, changes""" + +# ========= +# Functions +# ========= + +def print_help(with_description=True): + """Print description, usage and a detailed help message. + + @param with_description (bool): Option to print module's __doc__ or not + """ + + if with_description: + print __doc__ + print main_usage() + print + print pp.globaloption("global options") + print format_options(( + (" -h, --help", "display this help message"), + (" -q, --quiet", "minimal output"), + (" -C, --no-color", "turn off colors"), + (" -N, --no-pipe", "turn off pipe detection"), + (" -V, --version", "display version info") + )) + print + print pp.command("modules") + " (" + pp.command("short name") + ")" + print format_options(( + (" (b)elongs", "list what package FILES belong to"), + (" (c)hanges", "list changelog entries for PKG"), + (" chec(k)", "verify checksums and timestamps for PKG"), + (" (d)epends", "list all packages directly depending on PKG"), + (" dep(g)raph", "display a tree of all dependencies for PKG"), + (" (f)iles", "list all files installed by PKG"), + (" (h)asuse", "list all packages that have USE flag"), + (" (l)ist", "list package matching PKG"), + (" (m)eta", "display metadata about PKG"), + (" (s)ize", "display total size of all files owned by PKG"), + (" (u)ses", "display USE flags for PKG"), + (" (w)hich", "print full path to ebuild for PKG") + )) + + +def expand_module_name(module_name): + """Returns one of the values of name_map or raises KeyError""" + + name_map = { + 'b': 'belongs', + 'c': 'changes', + 'k': 'check', + 'd': 'depends', + 'g': 'depgraph', + 'f': 'files', + 'h': 'hasuse', + 'l': 'list_', + 'm': 'meta', + 's': 'size', + 'u': 'uses', + 'w': 'which' + } + + if module_name == 'list': + # list is a Python builtin type, so we must rename our module + return 'list_' + elif module_name in name_map.values(): + return module_name + else: + return name_map[module_name] + + +def format_options(options): + """Format module options. + + @type options: list + @param options: [('option 1', 'description 1'), ('option 2', 'des... )] + @rtype: str + @return: formatted options string + """ + + result = [] + twrap = TextWrapper(width=Config['termWidth']) + opts = (x[0] for x in options) + descs = (x[1] for x in options) + for opt, desc in zip(opts, descs): + twrap.initial_indent = pp.emph(opt.ljust(25)) + twrap.subsequent_indent = " " * 25 + result.append(twrap.fill(desc)) + + 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. + + @type path: str + @param path: the path + @type fdesc: list + @param fdesc: [file_type, timestamp, MD5 sum/symlink target] + file_type is one of dev, dir, obj, sym. + If file_type is dir, there is no timestamp or MD5 sum. + If file_type is sym, fdesc[2] is the target of the symlink. + @type show_type: bool + @param show_type: if True, prepend the file's type to the formatted string + @type show_md5: bool + @param show_md5: if True, append MD5 sum to the formatted string + @type show_timestamp: bool + @param show_timestamp: if True, append time-of-creation after pathname + @rtype: str + @return: formatted pathname with optional added information + """ + + ftype = fpath = stamp = md5sum = "" + + if fdesc[0] == "obj": + ftype = "file" + fpath = path + stamp = format_timestamp(fdesc[1]) + md5sum = fdesc[2] + elif fdesc[0] == "dir": + ftype = "dir" + fpath = pp.path(path) + elif fdesc[0] == "sym": + ftype = "sym" + stamp = format_timestamp(fdesc[1]) + tgt = fdesc[2].split()[0] + if Config["piping"]: + fpath = path + else: + fpath = pp.path_symlink(path + " -> " + tgt) + elif fdesc[0] == "dev": + ftype = "dev" + fpath = path + else: + pp.print_error("%s has unknown type: %s" % (path, fdesc[0])) + + result = "" + if show_type: + result += "%4s " % ftype + result += fpath + if show_timestamp: + result += " " + stamp + if show_md5: + result += " " + md5sum + + return result + + +def format_timestamp(timestamp): + """Format a timestamp into, e.g., '2009-01-31 21:19:44' format""" + + 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""" + + # Get terminal size + term_width = pp.output.get_term_size()[1] + if term_width == -1: + # get_term_size() failed. Set a sane default width: + term_width = 80 + + # Terminal size, minus a 1-char margin for text wrapping + Config['termWidth'] = term_width - 1 + + # Color handling: -1: Use Portage settings, 0: Force off, 1: Force on + Config['color'] = -1 + + # Guess color output + if (Config['color'] == -1 and (not sys.stdout.isatty() or + settings["NOCOLOR"] in ("yes", "true")) or + Config['color'] == 0): + pp.output.nocolor() + + # Guess piping output + if not sys.stdout.isatty(): + Config["piping"] = True + else: + Config["piping"] = False + + +def main_usage(): + """Print the main usage message for equery""" + + return "%(usage)s %(product)s [%(g_opts)s] %(mod_name)s [%(mod_opts)s]" % { + 'usage': pp.emph("Usage:"), + 'product': pp.productname(__productname__), + 'g_opts': pp.globaloption("global-options"), + 'mod_name': pp.command("module-name"), + 'mod_opts': pp.localoption("module-options") + } + + +def mod_usage(mod_name="module", arg="pkgspec", optional=False): + """Provide a consistant usage message to the calling module. + + @type arg: string + @param arg: what kind of argument the module takes (pkgspec, filename, etc) + @type optional: bool + @param optional: is the argument optional? + """ + + return "%(usage)s: %(mod_name)s [%(opts)s] %(arg)s" % { + 'usage': pp.emph("Usage"), + 'mod_name': pp.command(mod_name), + 'opts': pp.localoption("options"), + 'arg': ("[%s]" % pp.emph(arg)) if optional else pp.emph(arg) + } + + +def parse_global_options(global_opts, args): + """Parse global input args and return True if we should display help for + the called module, else False (or display help and exit from here). + """ + + need_help = False + opts = (opt[0] for opt in global_opts) + for opt in opts: + if opt in ('-h', '--help'): + if args: + need_help = True + else: + print_help() + sys.exit(0) + elif opt in ('-q','--quiet'): + Config["verbosityLevel"] = 0 + elif opt in ('-C', '--no-color', '--nocolor'): + Config['color'] = 0 + pp.output.nocolor() + elif opt in ('-N', '--no-pipe'): + Config["piping"] = False + elif opt in ('-V', '--version'): + print_version() + sys.exit(0) + + return need_help + + +def print_version(): + """Print the version of this tool to the console.""" + + try: + with open('/etc/gentoolkit-version') as gentoolkit_version: + version = gentoolkit_version.read().strip() + except IOError, err: + pp.die(2, str(err)) + + print "%(product)s (%(version)s) - %(docstring)s" % { + "product": pp.productname(__productname__), + "version": version, + "docstring": __doc__ + } + print + print __authors__ + + +def split_arguments(args): + """Separate module name from module arguments""" + + return args.pop(0), args + + +def main(): + """Parse input and run the program.""" + + initialize_configuration() + + short_opts = "hqCNV" + long_opts = ('help', 'quiet', 'nocolor', 'no-color', 'no-pipe', 'version') + + try: + global_opts, args = getopt(sys.argv[1:], short_opts, long_opts) + except GetoptError, err: + pp.print_error("Global %s" % err) + print_help(with_description=False) + sys.exit(2) + + + # Parse global options + need_help = parse_global_options(global_opts, args) + + try: + module_name, module_args = split_arguments(args) + except IndexError: + print_help() + sys.exit(2) + + if need_help: + module_args.append('--help') + + try: + expanded_module_name = expand_module_name(module_name) + except KeyError: + pp.print_error("Unknown module '%s'" % module_name) + print_help(with_description=False) + sys.exit(2) + + try: + 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 IOError, err: + if err.errno != errno.EPIPE: + raise |