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) |