1 # Copyright(c) 2009, Gentoo Foundation
3 # Licensed under the GNU General Public License, v2
7 """Gentoo package query tool"""
9 # Move to Imports section after Python 2.6 is stable
10 from __future__ import with_statement
14 'format_package_names',
17 __docformat__ = 'epytext'
18 # version is dynamically set by distutils sdist
29 from getopt import getopt, GetoptError
34 from gentoolkit import CONFIG
35 from gentoolkit import errors
36 from gentoolkit import pprinter as pp
37 from gentoolkit.textwrap_ import TextWrapper
39 __productname__ = "equery"
41 'Karl Trygve Kalleberg - Original author',
42 'Douglas Anderson - 0.3.0 author'
68 def print_help(with_description=True):
69 """Print description, usage and a detailed help message.
71 @param with_description (bool): Option to print module's __doc__ or not
78 print pp.globaloption("global options")
79 print format_options((
80 (" -h, --help", "display this help message"),
81 (" -q, --quiet", "minimal output"),
82 (" -C, --no-color", "turn off colors"),
83 (" -N, --no-pipe", "turn off pipe detection"),
84 (" -V, --version", "display version info")
87 print pp.command("modules") + " (" + pp.command("short name") + ")"
88 print format_options((
89 (" (b)elongs", "list what package FILES belong to"),
90 (" (c)hanges", "list changelog entries for ATOM"),
91 (" chec(k)", "verify checksums and timestamps for PKG"),
92 (" (d)epends", "list all packages directly depending on ATOM"),
93 (" dep(g)raph", "display a tree of all dependencies for PKG"),
94 (" (f)iles", "list all files installed by PKG"),
95 (" (h)asuse", "list all packages that have USE flag"),
96 (" (l)ist", "list package matching PKG"),
97 (" (m)eta", "display metadata about PKG"),
98 (" (s)ize", "display total size of all files owned by PKG"),
99 (" (u)ses", "display USE flags for PKG"),
100 (" (w)hich", "print full path to ebuild for PKG")
104 def expand_module_name(module_name):
105 """Returns one of the values of NAME_MAP or raises KeyError"""
107 if module_name == 'list':
108 # list is a Python builtin type, so we must rename our module
110 elif module_name in NAME_MAP.values():
113 return NAME_MAP[module_name]
116 def format_options(options):
117 """Format module options.
120 @param options: [('option 1', 'description 1'), ('option 2', 'des... )]
122 @return: formatted options string
126 twrap = TextWrapper(width=CONFIG['termWidth'])
127 opts = (x[0] for x in options)
128 descs = (x[1] for x in options)
129 for opt, desc in zip(opts, descs):
130 twrap.initial_indent = pp.emph(opt.ljust(25))
131 twrap.subsequent_indent = " " * 25
132 result.append(twrap.fill(desc))
134 return '\n'.join(result)
137 def format_filetype(path, fdesc, show_type=False, show_md5=False,
138 show_timestamp=False):
139 """Format a path for printing.
142 @param path: the path
144 @param fdesc: [file_type, timestamp, MD5 sum/symlink target]
145 file_type is one of dev, dir, obj, sym.
146 If file_type is dir, there is no timestamp or MD5 sum.
147 If file_type is sym, fdesc[2] is the target of the symlink.
148 @type show_type: bool
149 @param show_type: if True, prepend the file's type to the formatted string
151 @param show_md5: if True, append MD5 sum to the formatted string
152 @type show_timestamp: bool
153 @param show_timestamp: if True, append time-of-creation after pathname
155 @return: formatted pathname with optional added information
158 ftype = fpath = stamp = md5sum = ""
160 if fdesc[0] == "obj":
163 stamp = format_timestamp(fdesc[1])
165 elif fdesc[0] == "dir":
167 fpath = pp.path(path)
168 elif fdesc[0] == "sym":
170 stamp = format_timestamp(fdesc[1])
171 tgt = fdesc[2].split()[0]
175 fpath = pp.path_symlink(path + " -> " + tgt)
176 elif fdesc[0] == "dev":
181 pp.error("%s has unknown type: %s" % (path, fdesc[0]))
186 result += "%4s " % ftype
189 result += " " + stamp
191 result += " " + md5sum
196 def format_timestamp(timestamp):
197 """Format a timestamp into, e.g., '2009-01-31 21:19:44' format"""
199 return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(timestamp)))
202 def initialize_configuration():
203 """Setup the standard equery config"""
206 term_width = pp.output.get_term_size()[1]
208 # get_term_size() failed. Set a sane default width:
211 # Terminal size, minus a 1-char margin for text wrapping
212 CONFIG['termWidth'] = term_width - 1
215 if (CONFIG['color'] == -1 and (not sys.stdout.isatty() or
216 os.getenv("NOCOLOR") in ("yes", "true")) or CONFIG['color'] == 0):
219 CONFIG['verbose'] = not CONFIG['piping']
223 """Return the main usage message for equery"""
225 return "%(usage)s %(product)s [%(g_opts)s] %(mod_name)s [%(mod_opts)s]" % {
226 'usage': pp.emph("Usage:"),
227 'product': pp.productname(__productname__),
228 'g_opts': pp.globaloption("global-options"),
229 'mod_name': pp.command("module-name"),
230 'mod_opts': pp.localoption("module-options")
234 def mod_usage(mod_name="module", arg="pkgspec", optional=False):
235 """Provide a consistent usage message to the calling module.
238 @param arg: what kind of argument the module takes (pkgspec, filename, etc)
240 @param optional: is the argument optional?
243 return "%(usage)s: %(mod_name)s [%(opts)s] %(arg)s" % {
244 'usage': pp.emph("Usage"),
245 'mod_name': pp.command(mod_name),
246 'opts': pp.localoption("options"),
247 'arg': ("[%s]" % pp.emph(arg)) if optional else pp.emph(arg)
251 def parse_global_options(global_opts, args):
252 """Parse global input args and return True if we should display help for
253 the called module, else False (or display help and exit from here).
257 opts = (opt[0] for opt in global_opts)
259 if opt in ('-h', '--help'):
265 elif opt in ('-q','--quiet'):
266 CONFIG['quiet'] = True
267 elif opt in ('-C', '--no-color', '--nocolor'):
270 elif opt in ('-N', '--no-pipe'):
271 CONFIG['piping'] = False
272 elif opt in ('-V', '--version'):
275 elif opt in ('--debug'):
276 CONFIG['debug'] = True
282 """Print the version of this tool to the console."""
284 print "%(product)s (%(version)s) - %(docstring)s" % {
285 "product": pp.productname(__productname__),
286 "version": __version__,
291 def split_arguments(args):
292 """Separate module name from module arguments"""
294 return args.pop(0), args
298 """Parse input and run the program."""
302 'help', 'quiet', 'nocolor', 'no-color', 'no-pipe', 'version', 'debug'
305 initialize_configuration()
308 global_opts, args = getopt(sys.argv[1:], short_opts, long_opts)
309 except GetoptError, err:
310 sys.stderr.write(pp.error("Global %s" % err))
311 print_help(with_description=False)
314 # Parse global options
315 need_help = parse_global_options(global_opts, args)
317 # FIXME: There are a few places that make use of both quiet and verbose.
318 # Consider combining.
320 CONFIG['verbose'] = False
323 module_name, module_args = split_arguments(args)
329 module_args.append('--help')
332 expanded_module_name = expand_module_name(module_name)
334 sys.stderr.write(pp.error("Unknown module '%s'" % module_name))
335 print_help(with_description=False)
339 loaded_module = __import__(
340 expanded_module_name, globals(), locals(), [], -1
342 loaded_module.main(module_args)
343 except portage.exception.AmbiguousPackageName, err:
344 raise errors.GentoolkitAmbiguousPackage(err)
346 if err.errno != errno.EPIPE:
349 if __name__ == '__main__':