Gentoo Archives: gentoo-commits

From: "Tomas Chvatal (scarabeus)" <scarabeus@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] gentoolkit r831 - in trunk/gentoolkit: . bin pym/gentoolkit pym/gentoolkit/eshowkw
Date: Thu, 28 Oct 2010 20:13:59
Message-Id: 20101028201352.40A4C20051@flycatcher.gentoo.org
1 Author: scarabeus
2 Date: 2010-10-28 20:13:51 +0000 (Thu, 28 Oct 2010)
3 New Revision: 831
4
5 Added:
6 trunk/gentoolkit/bin/eshowkw
7 trunk/gentoolkit/pym/gentoolkit/eshowkw/
8 trunk/gentoolkit/pym/gentoolkit/eshowkw/__init__.py
9 trunk/gentoolkit/pym/gentoolkit/eshowkw/display_pretty.py
10 trunk/gentoolkit/pym/gentoolkit/eshowkw/keywords_content.py
11 trunk/gentoolkit/pym/gentoolkit/eshowkw/keywords_header.py
12 Modified:
13 trunk/gentoolkit/ChangeLog
14 Log:
15 Initial commit of eshowkw, which is drop-in replacement for eshowkw from gentoolkit-dev.
16
17 Modified: trunk/gentoolkit/ChangeLog
18 ===================================================================
19 --- trunk/gentoolkit/ChangeLog 2010-10-28 20:09:53 UTC (rev 830)
20 +++ trunk/gentoolkit/ChangeLog 2010-10-28 20:13:51 UTC (rev 831)
21 @@ -1,3 +1,7 @@
22 +2010-10-29: Tomáš Chvátal <scarabeus@g.o>
23 + * eshowkw: Add new module as drop-in replacement for eshowkw from
24 + gentoolkit-dev
25 +
26 2010-05-13: Christian Ruppert <idl0r@g.o>
27 * eclean/cli.py: Fix typo, bug 319349, thanks to Ulrich Müller
28 <ulm@g.o>.
29
30 Added: trunk/gentoolkit/bin/eshowkw
31 ===================================================================
32 --- trunk/gentoolkit/bin/eshowkw (rev 0)
33 +++ trunk/gentoolkit/bin/eshowkw 2010-10-28 20:13:51 UTC (rev 831)
34 @@ -0,0 +1,9 @@
35 +#!/usr/bin/python
36 +# vim:fileencoding=utf-8
37 +# Copyright 2010 Gentoo Foundation
38 +# Distributed under the terms of the GNU General Public License v2
39 +
40 +import sys
41 +from eshowkw import main as emain
42 +
43 +emain(sys.argv)
44 \ No newline at end of file
45
46 Added: trunk/gentoolkit/pym/gentoolkit/eshowkw/__init__.py
47 ===================================================================
48 --- trunk/gentoolkit/pym/gentoolkit/eshowkw/__init__.py (rev 0)
49 +++ trunk/gentoolkit/pym/gentoolkit/eshowkw/__init__.py 2010-10-28 20:13:51 UTC (rev 831)
50 @@ -0,0 +1,124 @@
51 +# Copyright 2010 Gentoo Foundation
52 +# Distributed under the terms of the GNU General Public License v2
53 +
54 +__package__ = 'eshowkw'
55 +__version__ = '0.5'
56 +
57 +import portage
58 +
59 +import sys, os, fnmatch
60 +import argparse
61 +from portage import output as porto
62 +from portage import settings as ports
63 +from portage import config as portc
64 +from portage import portdbapi as portdbapi
65 +from portage import db as portdb
66 +
67 +from keywords_header import keywords_header
68 +from keywords_content import keywords_content
69 +from display_pretty import string_rotator
70 +from display_pretty import display
71 +
72 +ignore_slots = False
73 +bold = False
74 +order = 'bottom'
75 +topper = 'versionlist'
76 +
77 +def process_display(package, keywords, dbapi):
78 + portdata = keywords_content(package, keywords.keywords, dbapi, ignore_slots, order, bold, topper)
79 + if topper == 'archlist':
80 + header = string_rotator().rotateContent(keywords.content, keywords.length, bold)
81 + extra = string_rotator().rotateContent(keywords.extra, keywords.length, bold, False)
82 + # -1 : space is taken in account and appended by us
83 + filler = ''.ljust(portdata.slot_length-1)
84 + header = ['%s%s%s' % (x, filler, y) for x, y in zip(header, extra)]
85 + content = portdata.content
86 + header_length = portdata.version_length
87 + content_length = keywords.length
88 + else:
89 + header = string_rotator().rotateContent(portdata.content, portdata.content_length, order, bold)
90 + content = keywords.content
91 + sep = [''.ljust(keywords.length) for x in range(portdata.slot_length-1)]
92 + content.extend(sep)
93 + content.extend(keywords.extra)
94 + header_length = keywords.length
95 + content_length = portdata.version_length
96 + display(content, header, header_length, content_length, portdata.cp, topper)
97 +
98 +def process_args(argv):
99 + """Option parsing via argc"""
100 + parser = argparse.ArgumentParser(prog=__package__,
101 + formatter_class=argparse.ArgumentDefaultsHelpFormatter,
102 + description='Display keywords for specified package or for package that is in pwd.')
103 +
104 + parser.add_argument('-v', '--version', action='version', version=__version__, help='show package version and exit')
105 +
106 + parser.add_argument('package', nargs='*', default=None, help='Packages to check.')
107 +
108 + parser.add_argument('-a', '--arch', nargs='+', default=[], help='Display only specified arch(s)')
109 +
110 + parser.add_argument('-A', '--align', nargs='?', default='bottom', choices=['top', 'bottom'],
111 + help='Specify alignment for descriptions.')
112 + parser.add_argument('-T', '--top-position', nargs='?', default='archlist', choices=['archlist', 'versionlist'],
113 + help='Specify which fields we want to have in top listing.')
114 +
115 + parser.add_argument('-B', '--bold', action='store_true', default=False,
116 + help='Print out each other column in bold for easier visual separation.')
117 + parser.add_argument('-C', '--color', action='store_true', default=False,
118 + help='Force colored output')
119 + parser.add_argument('-O', '--overlays', action='store_true', default=False,
120 + help='Search also overlays')
121 + parser.add_argument('-P', '--prefix', action='store_true', default=False,
122 + help='Display prefix keywords in output.')
123 + parser.add_argument('-S', '--ignore-slot', action='store_true', default=False,
124 + help='Treat slots as irelevant during detection of redundant pacakges.')
125 +
126 + return parser.parse_args(args=argv[1:])
127 +
128 +def main(argv):
129 + global ignore_slots, bold, order, topper
130 +
131 + #opts parsing
132 + opts = process_args(argv)
133 + ignore_slots = opts.ignore_slot
134 + use_overlays = opts.overlays
135 + # user can do both --arch=a,b,c or --arch a b c
136 + if len(opts.arch) > 1:
137 + opts.arch = ','.join(opts.arch)
138 + highlight_arch = ''.join(opts.arch).split(',')
139 + bold = opts.bold
140 + order = opts.align
141 + topper = opts.top_position
142 + prefix = opts.prefix
143 + color = opts.color
144 + package = opts.package
145 + # disable colors when redirected and they are not forced on
146 + if not color and not sys.stdout.isatty():
147 + # disable colors
148 + porto.nocolor()
149 + keywords = keywords_header(prefix, highlight_arch, order)
150 + if len(package) > 0:
151 + dbapi = portdb[ports['ROOT']]['porttree'].dbapi
152 + if not use_overlays:
153 + dbapi.porttrees = [dbapi.porttree_root]
154 + map(lambda x: process_display(x, keywords, dbapi), package)
155 + else:
156 + currdir = os.getcwd()
157 + package=os.path.basename(currdir)
158 + # check if there are actualy some ebuilds
159 + ebuilds = ['%s' % x for x in os.listdir(currdir)
160 + if fnmatch.fnmatch(file, '*.ebuild')]
161 + if len(ebuilds) <= 0:
162 + msg_err = 'No ebuilds at "%s"' % currdir
163 + raise SystemExit(msg_err)
164 + ourtree = os.path.abspath('../../')
165 +
166 + mysettings = portc(env={'PORTDIR_OVERLAY': os.path.abspath('../../')})
167 + dbapi = portdbapi(mysettings=mysettings)
168 + # specify that we want just our nice tree we are in cwd
169 + dbapi.porttrees = [ourtree]
170 + process_display(package, keywords, dbapi)
171 + return 0
172 +
173 +if __name__ == '__main__':
174 + sys.exit(main(sys.argv))
175
176 Added: trunk/gentoolkit/pym/gentoolkit/eshowkw/display_pretty.py
177 ===================================================================
178 --- trunk/gentoolkit/pym/gentoolkit/eshowkw/display_pretty.py (rev 0)
179 +++ trunk/gentoolkit/pym/gentoolkit/eshowkw/display_pretty.py 2010-10-28 20:13:51 UTC (rev 831)
180 @@ -0,0 +1,102 @@
181 +# Copyright 2010 Gentoo Foundation
182 +# Distributed under the terms of the GNU General Public License v2
183 +
184 +from portage.output import colorize
185 +from itertools import izip_longest
186 +
187 +__all__ = ['string_rotator', 'colorize_string', 'align_string', 'rotate_dash', 'print_content', 'display']
188 +
189 +def display(plain_list, rotated_list, plain_width, rotated_height, cp, toplist = 'archlist'):
190 + """Render defauld display to show the keywords listing"""
191 + # header
192 + output = []
193 + output.append('Keywords for %s:' % colorize('blue', cp))
194 + # data
195 + corner_image = [''.ljust(plain_width) for x in range(rotated_height)]
196 + if toplist == 'versionlist':
197 + corner_image.extend(plain_list)
198 + data_printout = ['%s%s' % (x, y)
199 + for x, y in izip_longest(corner_image, rotated_list, fillvalue=corner_image[0])]
200 + if toplist == 'archlist':
201 + data_printout.extend(plain_list)
202 + output.extend(data_printout)
203 + print print_content(output)
204 +
205 +def align_string(string, align, length):
206 + """Align string to the specified alignment (left or right, and after rotation it becames top and bottom)"""
207 + if align == 'top' or align == 'left':
208 + string = string.ljust(length)
209 + else:
210 + string = string.rjust(length)
211 + return string
212 +
213 +def colorize_string(color, string):
214 + """Add coloring for specified string. Due to rotation we need to do that per character rather than per-line"""
215 + tmp = []
216 + for char in list(string):
217 + # % is whitespace separator so we wont color that :)
218 + if char != '%':
219 + tmp.append(colorize(color, char))
220 + else:
221 + tmp.append(char)
222 + return ''.join(tmp)
223 +
224 +def rotate_dash(string):
225 + """Rotate special strings over 90 degrees for better readability."""
226 + chars = ['-', '|']
227 + subs = ['|', '-']
228 + out = string
229 + for x,y in zip(chars, subs):
230 + if string.find(x) != -1:
231 + out = out.replace(x, y)
232 + return out
233 +
234 +def print_content(content):
235 + """Print out content (strip it out of the temporary %)"""
236 + return '\n'.join(content).replace('%','')
237 +
238 +class string_rotator:
239 + __DASH_COUNT = 0
240 + def __getChar(self, string, position, line, bold_separator = False):
241 + """Return specified character from the string position"""
242 +
243 + # first figure out what character we want to work with
244 + # based on order and position in the string
245 + isdash = False
246 + if string.startswith('|') or string.startswith('-') or string.startswith('+'):
247 + split = list(string)
248 + isdash = True
249 + self.__DASH_COUNT += 1
250 + else:
251 + split = string.split('%')
252 + char = split[position]
253 + # bolding
254 + if not isdash and bold_separator \
255 + and (line-self.__DASH_COUNT)%2 == 0 \
256 + and char != ' ':
257 + char = colorize('bold', char)
258 + return char
259 +
260 + def rotateContent(self, elements, length, bold_separator = False, strip = True):
261 + """
262 + Rotate string over 90 degrees:
263 + string -> s
264 + t
265 + r
266 + i
267 + n
268 + g
269 + """
270 + # join used to have list of lines rather than list of chars
271 + tmp = []
272 + for position in range(length):
273 + x = ''
274 + for i, string in enumerate(elements):
275 + x += ' ' + self.__getChar(rotate_dash(string), position, i, bold_separator)
276 + # spaces on dashed line should be dashed too
277 + if x.find('+ -') != -1:
278 + x = x.replace(' ', '-')
279 + # strip all chars and remove empty lines
280 + if not strip or len(x.strip(' |-')) > 0:
281 + tmp.append(x)
282 + return tmp
283
284 Added: trunk/gentoolkit/pym/gentoolkit/eshowkw/keywords_content.py
285 ===================================================================
286 --- trunk/gentoolkit/pym/gentoolkit/eshowkw/keywords_content.py (rev 0)
287 +++ trunk/gentoolkit/pym/gentoolkit/eshowkw/keywords_content.py 2010-10-28 20:13:51 UTC (rev 831)
288 @@ -0,0 +1,290 @@
289 +# Copyright 2010 Gentoo Foundation
290 +# Distributed under the terms of the GNU General Public License v2
291 +
292 +import portage as port
293 +from portage.output import colorize
294 +
295 +__all__ = ['keywords_content']
296 +
297 +from display_pretty import colorize_string
298 +from display_pretty import align_string
299 +
300 +class keywords_content:
301 + class RedundancyChecker:
302 + def __listRedundant(self, keywords, ignoreslots, slots):
303 + """List all redundant packages."""
304 + if ignoreslots:
305 + return self.__listRedundantAll(keywords)
306 + else:
307 + return self.__listRedundantSlots(keywords, slots)
308 +
309 + def __listRedundantSlots(self, keywords, slots):
310 + """Search for redundant packages walking per keywords for specified slot."""
311 + result = [self.__compareSelected([k for k, s in zip(keywords, slots)
312 + if s == slot])
313 + for slot in self.__uniq(slots)]
314 + # this is required because the list itself is not just one level depth
315 + return list(''.join(result))
316 +
317 + def __uniq(self, seq):
318 + """Remove all duplicate elements from list."""
319 + seen = {}
320 + result = []
321 + for item in seq:
322 + if item in seen:
323 + continue
324 + seen[item] = 1
325 + result.append(item)
326 + return result
327 +
328 + def __listRedundantAll(self, keywords):
329 + """Search for redundant packages using all versions ignoring its slotting."""
330 + return list(self.__compareSelected(list(keywords)))
331 +
332 + def __compareSelected(self, kws):
333 + """
334 + Rotate over list of keywords and compare each element with others.
335 + Selectively remove each already compared list from the remaining keywords.
336 + """
337 + result = []
338 + kws.reverse()
339 + for i in range(len(kws)):
340 + kw = kws.pop()
341 + if self.__compareKeywordWithRest(kw, kws):
342 + result.append('#')
343 + else:
344 + result.append('o')
345 + if len(result) == 0:
346 + result.append('o')
347 + return ''.join(result)
348 +
349 + def __compareKeywordWithRest(self, keyword, keywords):
350 + """Compare keywords with list of keywords."""
351 + for key in keywords:
352 + if self.__checkShadow(keyword, key):
353 + return True
354 + return False
355 +
356 + def __checkShadow(self, old, new):
357 + """Check if package version is overshadowed by other package version."""
358 + # remove -* and -arch since they are useless for us
359 + newclean = ["%s" % x for x in new.split()
360 + if x != '-*' and not x.startswith('-')]
361 + oldclean = ["%s" % x for x in old.split()
362 + if x != '-*' and not x.startswith('-')]
363 +
364 + tmp = set(newclean)
365 + tmp.update("~%s" % x for x in newclean
366 + if not x.startswith("~"))
367 + if not set(oldclean).difference(tmp):
368 + return True
369 + else:
370 + return False
371 +
372 + def __init__(self, keywords, slots, ignore_slots = False):
373 + """Query all relevant data for redundancy package checking"""
374 + self.redundant = self.__listRedundant(keywords, ignore_slots, slots)
375 +
376 + class VersionChecker:
377 + def __getVersions(self, packages, vartree):
378 + """Obtain properly aligned version strings without colors."""
379 + return self.__stripStartingSpaces(map(lambda x: self.__separateVersion(x, vartree), packages))
380 +
381 + def __stripStartingSpaces(self, pvs):
382 + """Strip starting whitespace if there is no real reason for it."""
383 + if not self.__require_prepend:
384 + return map(lambda x: x.lstrip(), pvs)
385 + else:
386 + return pvs
387 +
388 + def __separateVersion(self, cpv, vartree):
389 + """Get version string for specfied cpv"""
390 + #pv = port.versions.cpv_getversion(cpv)
391 + return self.__prependVersionInfo(cpv, self.cpv_getversion(cpv), vartree)
392 +
393 + # remove me when portage 2.1.9 is stable
394 + def cpv_getversion(self, mycpv):
395 + """Returns the v (including revision) from an cpv."""
396 + cp = port.versions.cpv_getkey(mycpv)
397 + if cp is None:
398 + return None
399 + return mycpv[len(cp+"-"):]
400 +
401 + def __prependVersionInfo(self, cpv, pv, vartree):
402 + """Prefix version with string based on whether version is installed or masked."""
403 + mask = self.__getMaskStatus(cpv)
404 + install = self.__getInstallStatus(cpv, vartree)
405 +
406 + if mask and install:
407 + pv = '[M][I]%s' % pv
408 + self.__require_longprepend = True
409 + elif mask:
410 + pv = '[M]%s' % pv
411 + self.__require_prepend = True
412 + elif install:
413 + pv = '[I]%s' % pv
414 + self.__require_prepend = True
415 + return pv
416 +
417 + def __getMaskStatus(self, cpv):
418 + """
419 + Figure out if package is pmasked.
420 + This also uses user settings in /etc/ so local changes are important.
421 + """
422 + pmask = False
423 + try:
424 + if port.getmaskingstatus(cpv) == ['package.mask']:
425 + pmask = True
426 + except:
427 + # occurs when package is not known by portdb
428 + # so we consider it unmasked
429 + pass
430 + return pmask
431 +
432 + def __getInstallStatus(self, cpv, vartree):
433 + """Check if package version we test is installed."""
434 + return vartree.cpv_exists(cpv)
435 +
436 + def __init__(self, packages, vartree):
437 + """Query all relevant data for version data formatting"""
438 + self.__require_longprepend = False
439 + self.__require_prepend = False
440 + self.versions = self.__getVersions(packages, vartree)
441 +
442 + def __checkExist(self, pdb, package):
443 + """Check if specified package even exists."""
444 + try:
445 + matches = pdb.xmatch('match-all', package)
446 + except port.exception.AmbiguousPackageName as Arg:
447 + msg_err = 'Ambiguous package name "%s".\n' % package
448 + found = 'Possibilities: %s' % Arg
449 + raise SystemExit('%s%s' % (msg_err, found))
450 + except port.exception.InvalidAtom:
451 + msg_err = 'No such package "%s"' % package
452 + raise SystemExit(msg_err)
453 + if len(matches) <= 0:
454 + msg_err = 'No such package "%s"' % package
455 + raise SystemExit(msg_err)
456 + return matches
457 +
458 + def __getMetadata(self, pdb, packages):
459 + """Obtain all KEYWORDS and SLOT from metadata"""
460 + try:
461 + metadata = map(lambda x: pdb.aux_get(x, ['KEYWORDS', 'SLOT', 'repository']), packages)
462 + except KeyError:
463 + # portage prints out more verbose error for us if we were lucky
464 + raise SystemExit('Failed to obtain metadata')
465 + return list(zip(*metadata))
466 +
467 + def __formatKeywords(self, keywords, keywords_list, usebold = False, toplist = 'archlist'):
468 + """Loop over all keywords and replace them with nice visual identifier"""
469 + # the % is fancy separator, we use it to split keywords for rotation
470 + # so we wont loose the empty spaces
471 + return ['% %'.join([self.__prepareKeywordChar(arch, i, version.split(), usebold, toplist)
472 + for i, arch in enumerate(keywords_list)])
473 + for version in keywords]
474 +
475 + def __prepareKeywordChar(self, arch, field, keywords, usebold = False, toplist = 'archlist'):
476 + """
477 + Convert specified keywords for package into their visual replacements.
478 + # possibilities:
479 + # ~arch -> orange ~
480 + # -arch -> red -
481 + # arch -> green +
482 + # -* -> red *
483 + """
484 + keys = [ '~%s' % arch, '-%s' % arch, '%s' % arch, '-*' ]
485 + nocolor_values = [ '~', '-', '+', '*' ]
486 + values = [
487 + colorize('darkyellow', '~'),
488 + colorize('darkred', '-'),
489 + colorize('darkgreen', '+'),
490 + colorize('darkred', '*')
491 + ]
492 + # check what keyword we have
493 + # here we cant just append space because it would get stripped later
494 + char = colorize('darkgray','o')
495 + for k, v, n in zip(keys, values, nocolor_values):
496 + if k in keywords:
497 + char = v
498 + break
499 + if toplist == 'archlist' and usebold and (field)%2 == 0 and char != ' ':
500 + char = colorize('bold', char)
501 + return char
502 +
503 + def __formatVersions(self, versions, align, length):
504 + """Append colors and align keywords properly"""
505 + # % are used as separators for further split so we wont loose spaces and coloring
506 + tmp = []
507 + for pv in versions:
508 + pv = align_string(pv, align, length)
509 + pv = '%'.join(list(pv))
510 + if pv.find('[%M%][%I%]') != -1:
511 + tmp.append(colorize_string('darkyellow', pv))
512 + elif pv.find('[%M%]') != -1:
513 + tmp.append(colorize_string('darkred', pv))
514 + elif pv.find('[%I%]') != -1:
515 + tmp.append(colorize_string('bold', pv))
516 + else:
517 + tmp.append(pv)
518 + return tmp
519 +
520 + def __formatAdditional(self, additional, color, length):
521 + """Align additional items properly"""
522 + # % are used as separators for further split so we wont loose spaces and coloring
523 + tmp = []
524 + for x in additional:
525 + tmpc = color
526 + x = align_string(x, 'left', length)
527 + x = '%'.join(list(x))
528 + if x == 'o':
529 + # the value is unset so the color is gray
530 + tmpc = 'darkgray'
531 + x = colorize_string(tmpc, x)
532 + tmp.append(x)
533 + return tmp
534 +
535 + def __prepareContentResult(self, versions, keywords, redundant, slots, slot_length, repos, linesep):
536 + """Parse version fields into one list with proper separators"""
537 + content = []
538 + oldslot = ''
539 + fieldsep = '% %|% %'
540 + normsep = '% %'
541 + for v, k, r, s, t in zip(versions, keywords, redundant, slots, repos):
542 + if oldslot != s:
543 + oldslot = s
544 + content.append(linesep)
545 + else:
546 + s = '%'.join(list(''.rjust(slot_length)))
547 + content.append('%s%s%s%s%s%s%s%s%s' % (v, fieldsep, k, fieldsep, r, normsep, s, fieldsep, t))
548 + return content
549 +
550 + def __init__(self, package, keywords_list, porttree, ignoreslots = False, content_align = 'bottom', usebold = False, toplist = 'archlist'):
551 + """Query all relevant data from portage databases."""
552 + vartree = port.db[port.settings['ROOT']]['vartree'].dbapi
553 + packages = self.__checkExist(porttree, package)
554 + self.keywords, self.slots, self.repositories = self.__getMetadata(porttree, packages)
555 + self.slot_length = max([len(x) for x in self.slots])
556 + repositories_length = max([len(x) for x in self.repositories])
557 + self.keyword_length = len(keywords_list)
558 + self.versions = self.VersionChecker(packages, vartree).versions
559 + self.version_length = max([len(x) for x in self.versions])
560 + self.version_count = len(self.versions)
561 + self.redundant = self.RedundancyChecker(self.keywords, self.slots, ignoreslots).redundant
562 + redundant_length = max([len(x) for x in self.redundant])
563 +
564 + ver = self.__formatVersions(self.versions, content_align, self.version_length)
565 + kws = self.__formatKeywords(self.keywords, keywords_list, usebold, toplist)
566 + red = self.__formatAdditional(self.redundant, 'purple', redundant_length)
567 + slt = self.__formatAdditional(self.slots, 'bold', self.slot_length)
568 + rep = self.__formatAdditional(self.repositories, 'yellow', repositories_length)
569 + # those + nubers are spaces in printout. keywords are multiplied also because of that
570 + linesep = '%s+%s+%s+%s' % (''.ljust(self.version_length+1, '-'),
571 + ''.ljust(self.keyword_length*2+1, '-'),
572 + ''.ljust(redundant_length+self.slot_length+3, '-'),
573 + ''.ljust(repositories_length+1, '-')
574 + )
575 +
576 + self.content = self.__prepareContentResult(ver, kws, red, slt, self.slot_length, rep, linesep)
577 + self.content_length = len(linesep)
578 + self.cp = port.cpv_getkey(packages[0])
579
580 Added: trunk/gentoolkit/pym/gentoolkit/eshowkw/keywords_header.py
581 ===================================================================
582 --- trunk/gentoolkit/pym/gentoolkit/eshowkw/keywords_header.py (rev 0)
583 +++ trunk/gentoolkit/pym/gentoolkit/eshowkw/keywords_header.py 2010-10-28 20:13:51 UTC (rev 831)
584 @@ -0,0 +1,99 @@
585 +# Copyright 2001-2010 Gentoo Foundation
586 +# Distributed under the terms of the GNU General Public License v2
587 +
588 +__all__ = ['keywords_header']
589 +
590 +from portage import settings as ports
591 +from portage.output import colorize
592 +from display_pretty import colorize_string
593 +from display_pretty import align_string
594 +
595 +class keywords_header:
596 + __IMPARCHS = [ 'arm', 'amd64', 'x86' ]
597 + __ADDITIONAL_FIELDS = [ 'unused', 'slot' ]
598 + __EXTRA_FIELDS = [ 'repo' ]
599 +
600 + def __readKeywords(self):
601 + """Read all available keywords from portage."""
602 + return [x for x in ports.archlist()
603 + if not x.startswith('~')]
604 +
605 + def __sortKeywords(self, keywords, prefix = False, required_keywords = []):
606 + """Sort keywords with short archs first"""
607 + # user specified only some keywords to display
608 + if len(required_keywords) != 0:
609 + tmpkeywords = [k for k in keywords
610 + if k in required_keywords]
611 + # idiots might specify non-existant archs
612 + if len(tmpkeywords) != 0:
613 + keywords = tmpkeywords
614 +
615 + normal = [k for k in keywords
616 + if len(k.split('-')) == 1]
617 + normal.sort()
618 +
619 + if prefix:
620 + longer = [k for k in keywords
621 + if len(k.split('-')) != 1]
622 + longer.sort()
623 + normal.extend(longer)
624 + return normal
625 +
626 + def __readAdditionalFields(self):
627 + """Prepare list of aditional fileds displayed by eshowkw (2nd part)"""
628 + return self.__ADDITIONAL_FIELDS
629 +
630 + def __readExtraFields(self):
631 + """Prepare list of extra fileds displayed by eshowkw (3rd part)"""
632 + return self.__EXTRA_FIELDS
633 +
634 + def __formatKeywords(self, keywords, align, length):
635 + """Append colors and align keywords properly"""
636 + tmp = []
637 + for keyword in keywords:
638 + tmp2 = keyword
639 + keyword = align_string(keyword, align, length)
640 + # % are used as separators for further split so we wont loose spaces and coloring
641 + keyword = '%'.join(list(keyword))
642 + if tmp2 in self.__IMPARCHS:
643 + tmp.append(colorize_string('darkyellow', keyword))
644 + else:
645 + tmp.append(keyword)
646 + return tmp
647 +
648 + def __formatAdditional(self, additional, align, length):
649 + """Align additional items properly"""
650 + # % are used as separators for further split so we wont loose spaces and coloring
651 + return ['%'.join(align_string(x, align, length)) for x in additional]
652 +
653 + def __prepareExtra(self, extra, align, length):
654 + content = []
655 + content.append(''.ljust(length, '-'))
656 + content.extend(self.__formatAdditional(extra, align, length))
657 + return content
658 +
659 + def __prepareResult(self, keywords, additional, align, length):
660 + """Parse keywords and additional fields into one list with proper separators"""
661 + content = []
662 + content.append(''.ljust(length, '-'))
663 + content.extend(self.__formatKeywords(keywords, align, length))
664 + content.append(''.ljust(length, '-'))
665 + content.extend(self.__formatAdditional(additional, align, length))
666 + return content
667 +
668 + def __init__(self, prefix = False, required_keywords = [], keywords_align = 'bottom'):
669 + """Initialize keywords header."""
670 + additional = self.__readAdditionalFields()
671 + extra = self.__readExtraFields()
672 + self.keywords = self.__sortKeywords(self.__readKeywords(), prefix, required_keywords)
673 + self.length = max(
674 + max([len(x) for x in self.keywords]),
675 + max([len(x) for x in additional]),
676 + max([len(x) for x in extra])
677 + )
678 + #len(max([max(self.keywords, key=len), max(additional, key=len)], key=len))
679 + self.keywords_count = len(self.keywords)
680 + self.additional_count = len(additional)
681 + self.extra_count = len(extra)
682 + self.content = self.__prepareResult(self.keywords, additional, keywords_align, self.length)
683 + self.extra = self.__prepareExtra(extra, keywords_align, self.length)