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
13 'format_package_names',
16 __docformat__ = 'epytext'
17 # version is dynamically set by distutils sdist
28 from getopt import getopt, GetoptError
33 from gentoolkit import CONFIG
34 from gentoolkit import errors
35 from gentoolkit import pprinter as pp
36 from gentoolkit.textwrap_ import TextWrapper
38 __productname__ = "equery"
40 'Karl Trygve Kalleberg - Original author',
41 'Douglas Anderson - 0.3.0 author'
67 def print_help(with_description=True):
68 """Print description, usage and a detailed help message.
70 @param with_description (bool): Option to print module's __doc__ or not
77 print(pp.globaloption("global options"))
78 print(format_options((
79 (" -h, --help", "display this help message"),
80 (" -q, --quiet", "minimal output"),
81 (" -C, --no-color", "turn off colors"),
82 (" -N, --no-pipe", "turn off pipe detection"),
83 (" -V, --version", "display version info")
86 print(pp.command("modules") + " (" + pp.command("short name") + ")")
87 print(format_options((
88 (" (b)elongs", "list what package FILES belong to"),
89 (" (c)hanges", "list changelog entries for ATOM"),
90 (" chec(k)", "verify checksums and timestamps for PKG"),
91 (" (d)epends", "list all packages directly depending on ATOM"),
92 (" dep(g)raph", "display a tree of all dependencies for PKG"),
93 (" (f)iles", "list all files installed by PKG"),
94 (" (h)asuse", "list all packages that have USE flag"),
95 (" (l)ist", "list package matching PKG"),
96 (" (m)eta", "display metadata about PKG"),
97 (" (s)ize", "display total size of all files owned by PKG"),
98 (" (u)ses", "display USE flags for PKG"),
99 (" (w)hich", "print full path to ebuild for PKG")
103 def expand_module_name(module_name):
104 """Returns one of the values of NAME_MAP or raises KeyError"""
106 if module_name == 'list':
107 # list is a Python builtin type, so we must rename our module
109 elif module_name in NAME_MAP.values():
112 return NAME_MAP[module_name]
115 def format_options(options):
116 """Format module options.
119 @param options: [('option 1', 'description 1'), ('option 2', 'des... )]
121 @return: formatted options string
125 twrap = TextWrapper(width=CONFIG['termWidth'])
126 opts = (x[0] for x in options)
127 descs = (x[1] for x in options)
128 for opt, desc in zip(opts, descs):
129 twrap.initial_indent = pp.emph(opt.ljust(25))
130 twrap.subsequent_indent = " " * 25
131 result.append(twrap.fill(desc))
133 return '\n'.join(result)
136 def format_filetype(path, fdesc, show_type=False, show_md5=False,
137 show_timestamp=False):
138 """Format a path for printing.
141 @param path: the path
143 @param fdesc: [file_type, timestamp, MD5 sum/symlink target]
144 file_type is one of dev, dir, obj, sym.
145 If file_type is dir, there is no timestamp or MD5 sum.
146 If file_type is sym, fdesc[2] is the target of the symlink.
147 @type show_type: bool
148 @param show_type: if True, prepend the file's type to the formatted string
150 @param show_md5: if True, append MD5 sum to the formatted string
151 @type show_timestamp: bool
152 @param show_timestamp: if True, append time-of-creation after pathname
154 @return: formatted pathname with optional added information
157 ftype = fpath = stamp = md5sum = ""
159 if fdesc[0] == "obj":
162 stamp = format_timestamp(fdesc[1])
164 elif fdesc[0] == "dir":
166 fpath = pp.path(path)
167 elif fdesc[0] == "sym":
169 stamp = format_timestamp(fdesc[1])
170 tgt = fdesc[2].split()[0]
174 fpath = pp.path_symlink(path + " -> " + tgt)
175 elif fdesc[0] == "dev":
180 pp.error("%s has unknown type: %s" % (path, fdesc[0]))
185 result += "%4s " % ftype
188 result += " " + stamp
190 result += " " + md5sum
195 def format_timestamp(timestamp):
196 """Format a timestamp into, e.g., '2009-01-31 21:19:44' format"""
198 return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(timestamp)))
201 def initialize_configuration():
202 """Setup the standard equery config"""
205 term_width = pp.output.get_term_size()[1]
207 # get_term_size() failed. Set a sane default width:
210 # Terminal size, minus a 1-char margin for text wrapping
211 CONFIG['termWidth'] = term_width - 1
214 if (CONFIG['color'] == -1 and (not sys.stdout.isatty() or
215 os.getenv("NOCOLOR") in ("yes", "true")) or CONFIG['color'] == 0):
219 CONFIG['verbose'] = False
221 CONFIG['debug'] = bool(os.getenv('DEBUG', False))
225 """Return the main usage message for equery"""
227 return "%(usage)s %(product)s [%(g_opts)s] %(mod_name)s [%(mod_opts)s]" % {
228 'usage': pp.emph("Usage:"),
229 'product': pp.productname(__productname__),
230 'g_opts': pp.globaloption("global-options"),
231 'mod_name': pp.command("module-name"),
232 'mod_opts': pp.localoption("module-options")
236 def mod_usage(mod_name="module", arg="pkgspec", optional=False):
237 """Provide a consistent usage message to the calling module.
240 @param arg: what kind of argument the module takes (pkgspec, filename, etc)
242 @param optional: is the argument optional?
245 return "%(usage)s: %(mod_name)s [%(opts)s] %(arg)s" % {
246 'usage': pp.emph("Usage"),
247 'mod_name': pp.command(mod_name),
248 'opts': pp.localoption("options"),
249 'arg': ("[%s]" % pp.emph(arg)) if optional else pp.emph(arg)
253 def parse_global_options(global_opts, args):
254 """Parse global input args and return True if we should display help for
255 the called module, else False (or display help and exit from here).
259 opts = (opt[0] for opt in global_opts)
261 if opt in ('-h', '--help'):
267 elif opt in ('-q','--quiet'):
268 CONFIG['quiet'] = True
269 elif opt in ('-C', '--no-color', '--nocolor'):
272 elif opt in ('-N', '--no-pipe'):
273 CONFIG['piping'] = False
274 elif opt in ('-V', '--version'):
277 elif opt in ('--debug'):
278 CONFIG['debug'] = True
284 """Print the version of this tool to the console."""
286 print("%(product)s (%(version)s) - %(docstring)s" % {
287 "product": pp.productname(__productname__),
288 "version": __version__,
293 def split_arguments(args):
294 """Separate module name from module arguments"""
296 return args.pop(0), args
300 """Parse input and run the program."""
304 'help', 'quiet', 'nocolor', 'no-color', 'no-pipe', 'version', 'debug'
307 initialize_configuration()
310 global_opts, args = getopt(sys.argv[1:], short_opts, long_opts)
311 except GetoptError as err:
312 sys.stderr.write(pp.error("Global %s" % err))
313 print_help(with_description=False)
316 # Parse global options
317 need_help = parse_global_options(global_opts, args)
319 # verbose is shorthand for the very common 'not quiet or piping'
320 if CONFIG['quiet'] or CONFIG['piping']:
321 CONFIG['verbose'] = False
323 CONFIG['verbose'] = True
326 module_name, module_args = split_arguments(args)
332 module_args.append('--help')
335 expanded_module_name = expand_module_name(module_name)
337 sys.stderr.write(pp.error("Unknown module '%s'" % module_name))
338 print_help(with_description=False)
342 loaded_module = __import__(
343 expanded_module_name, globals(), locals(), [], -1
345 loaded_module.main(module_args)
346 except portage.exception.AmbiguousPackageName as err:
347 raise errors.GentoolkitAmbiguousPackage(err.args[0])
348 except IOError as err:
349 if err.errno != errno.EPIPE:
352 if __name__ == '__main__':