1 # Copyright(c) 2009, Gentoo Foundation
3 # Licensed under the GNU General Public License, v2
7 """Gentoo package query tool"""
9 from __future__ import print_function
11 # Move to Imports section after Python 2.6 is stable
16 'format_package_names',
19 __docformat__ = 'epytext'
20 # version is dynamically set by distutils sdist
30 from getopt import getopt, GetoptError
33 from portage import os
36 from gentoolkit import CONFIG
37 from gentoolkit import errors
38 from gentoolkit import pprinter as pp
39 from gentoolkit.textwrap_ import TextWrapper
41 __productname__ = "equery"
43 'Karl Trygve Kalleberg - Original author',
44 'Douglas Anderson - 0.3.0 author'
70 def print_help(with_description=True):
71 """Print description, usage and a detailed help message.
73 @param with_description (bool): Option to print module's __doc__ or not
80 print(pp.globaloption("global options"))
81 print(format_options((
82 (" -h, --help", "display this help message"),
83 (" -q, --quiet", "minimal output"),
84 (" -C, --no-color", "turn off colors"),
85 (" -N, --no-pipe", "turn off pipe detection"),
86 (" -V, --version", "display version info")
89 print(pp.command("modules") + " (" + pp.command("short name") + ")")
90 print(format_options((
91 (" (b)elongs", "list what package FILES belong to"),
92 (" (c)hanges", "list changelog entries for ATOM"),
93 (" chec(k)", "verify checksums and timestamps for PKG"),
94 (" (d)epends", "list all packages directly depending on ATOM"),
95 (" dep(g)raph", "display a tree of all dependencies for PKG"),
96 (" (f)iles", "list all files installed by PKG"),
97 (" (h)asuse", "list all packages that have USE flag"),
98 (" (l)ist", "list package matching PKG"),
99 (" (m)eta", "display metadata about PKG"),
100 (" (s)ize", "display total size of all files owned by PKG"),
101 (" (u)ses", "display USE flags for PKG"),
102 (" (w)hich", "print full path to ebuild for PKG")
106 def expand_module_name(module_name):
107 """Returns one of the values of NAME_MAP or raises KeyError"""
109 if module_name == 'list':
110 # list is a Python builtin type, so we must rename our module
112 elif module_name in NAME_MAP.values():
115 return NAME_MAP[module_name]
118 def format_options(options):
119 """Format module options.
122 @param options: [('option 1', 'description 1'), ('option 2', 'des... )]
124 @return: formatted options string
128 twrap = TextWrapper(width=CONFIG['termWidth'])
129 opts = (x[0] for x in options)
130 descs = (x[1] for x in options)
131 for opt, desc in zip(opts, descs):
132 twrap.initial_indent = pp.emph(opt.ljust(25))
133 twrap.subsequent_indent = " " * 25
134 result.append(twrap.fill(desc))
136 return '\n'.join(result)
139 def format_filetype(path, fdesc, show_type=False, show_md5=False,
140 show_timestamp=False):
141 """Format a path for printing.
144 @param path: the path
146 @param fdesc: [file_type, timestamp, MD5 sum/symlink target]
147 file_type is one of dev, dir, obj, sym.
148 If file_type is dir, there is no timestamp or MD5 sum.
149 If file_type is sym, fdesc[2] is the target of the symlink.
150 @type show_type: bool
151 @param show_type: if True, prepend the file's type to the formatted string
153 @param show_md5: if True, append MD5 sum to the formatted string
154 @type show_timestamp: bool
155 @param show_timestamp: if True, append time-of-creation after pathname
157 @return: formatted pathname with optional added information
160 ftype = fpath = stamp = md5sum = ""
162 if fdesc[0] == "obj":
165 stamp = format_timestamp(fdesc[1])
167 elif fdesc[0] == "dir":
169 fpath = pp.path(path)
170 elif fdesc[0] == "sym":
172 stamp = format_timestamp(fdesc[1])
173 tgt = fdesc[2].split()[0]
177 fpath = pp.path_symlink(path + " -> " + tgt)
178 elif fdesc[0] == "dev":
183 pp.error("%s has unknown type: %s" % (path, fdesc[0]))
188 result += "%4s " % ftype
191 result += " " + stamp
193 result += " " + md5sum
198 def format_timestamp(timestamp):
199 """Format a timestamp into, e.g., '2009-01-31 21:19:44' format"""
201 return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(timestamp)))
204 def initialize_configuration():
205 """Setup the standard equery config"""
208 term_width = pp.output.get_term_size()[1]
210 # get_term_size() failed. Set a sane default width:
213 # Terminal size, minus a 1-char margin for text wrapping
214 CONFIG['termWidth'] = term_width - 1
217 if (CONFIG['color'] == -1 and (not sys.stdout.isatty() or
218 os.getenv("NOCOLOR") in ("yes", "true")) or CONFIG['color'] == 0):
222 CONFIG['verbose'] = False
224 CONFIG['debug'] = bool(os.getenv('DEBUG', False))
228 """Return the main usage message for equery"""
230 return "%(usage)s %(product)s [%(g_opts)s] %(mod_name)s [%(mod_opts)s]" % {
231 'usage': pp.emph("Usage:"),
232 'product': pp.productname(__productname__),
233 'g_opts': pp.globaloption("global-options"),
234 'mod_name': pp.command("module-name"),
235 'mod_opts': pp.localoption("module-options")
239 def mod_usage(mod_name="module", arg="pkgspec", optional=False):
240 """Provide a consistent usage message to the calling module.
243 @param arg: what kind of argument the module takes (pkgspec, filename, etc)
245 @param optional: is the argument optional?
248 return "%(usage)s: %(mod_name)s [%(opts)s] %(arg)s" % {
249 'usage': pp.emph("Usage"),
250 'mod_name': pp.command(mod_name),
251 'opts': pp.localoption("options"),
252 'arg': ("[%s]" % pp.emph(arg)) if optional else pp.emph(arg)
256 def parse_global_options(global_opts, args):
257 """Parse global input args and return True if we should display help for
258 the called module, else False (or display help and exit from here).
262 opts = (opt[0] for opt in global_opts)
264 if opt in ('-h', '--help'):
270 elif opt in ('-q','--quiet'):
271 CONFIG['quiet'] = True
272 elif opt in ('-C', '--no-color', '--nocolor'):
275 elif opt in ('-N', '--no-pipe'):
276 CONFIG['piping'] = False
277 elif opt in ('-V', '--version'):
280 elif opt in ('--debug'):
281 CONFIG['debug'] = True
287 """Print the version of this tool to the console."""
289 print("%(product)s (%(version)s) - %(docstring)s" % {
290 "product": pp.productname(__productname__),
291 "version": __version__,
296 def split_arguments(args):
297 """Separate module name from module arguments"""
299 return args.pop(0), args
303 """Parse input and run the program."""
307 'help', 'quiet', 'nocolor', 'no-color', 'no-pipe', 'version', 'debug'
310 initialize_configuration()
313 global_opts, args = getopt(sys.argv[1:], short_opts, long_opts)
314 except GetoptError as err:
315 sys.stderr.write(pp.error("Global %s" % err))
316 print_help(with_description=False)
319 # Parse global options
320 need_help = parse_global_options(global_opts, args)
322 # verbose is shorthand for the very common 'not quiet or piping'
323 if CONFIG['quiet'] or CONFIG['piping']:
324 CONFIG['verbose'] = False
326 CONFIG['verbose'] = True
329 module_name, module_args = split_arguments(args)
335 module_args.append('--help')
338 expanded_module_name = expand_module_name(module_name)
340 sys.stderr.write(pp.error("Unknown module '%s'" % module_name))
341 print_help(with_description=False)
345 loaded_module = __import__(
346 expanded_module_name, globals(), locals(), [], -1
348 loaded_module.main(module_args)
349 except portage.exception.AmbiguousPackageName as err:
350 raise errors.GentoolkitAmbiguousPackage(err)
351 except IOError as err:
352 if err.errno != errno.EPIPE:
355 if __name__ == '__main__':