aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorscarabeus <scarabeus@gentoo.org>2010-10-28 20:13:51 +0000
committerscarabeus <scarabeus@gentoo.org>2010-10-28 20:13:51 +0000
commit4a91692e01bb6d2330f058fd2a31384c6c57bda0 (patch)
tree7f635e2ec9feba50c7ec6da07881120799d513e7 /pym/gentoolkit/eshowkw
parentAdd --binpkgs-missing option to equery list to print installed packages that (diff)
downloadgentoolkit-4a91692e01bb6d2330f058fd2a31384c6c57bda0.tar.gz
gentoolkit-4a91692e01bb6d2330f058fd2a31384c6c57bda0.tar.bz2
gentoolkit-4a91692e01bb6d2330f058fd2a31384c6c57bda0.zip
Initial commit of eshowkw, which is drop-in replacement for eshowkw from gentoolkit-dev.
svn path=/trunk/gentoolkit/; revision=831
Diffstat (limited to 'pym/gentoolkit/eshowkw')
-rw-r--r--pym/gentoolkit/eshowkw/__init__.py124
-rw-r--r--pym/gentoolkit/eshowkw/display_pretty.py102
-rw-r--r--pym/gentoolkit/eshowkw/keywords_content.py290
-rw-r--r--pym/gentoolkit/eshowkw/keywords_header.py99
4 files changed, 615 insertions, 0 deletions
diff --git a/pym/gentoolkit/eshowkw/__init__.py b/pym/gentoolkit/eshowkw/__init__.py
new file mode 100644
index 0000000..efcbfb9
--- /dev/null
+++ b/pym/gentoolkit/eshowkw/__init__.py
@@ -0,0 +1,124 @@
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+__package__ = 'eshowkw'
+__version__ = '0.5'
+
+import portage
+
+import sys, os, fnmatch
+import argparse
+from portage import output as porto
+from portage import settings as ports
+from portage import config as portc
+from portage import portdbapi as portdbapi
+from portage import db as portdb
+
+from keywords_header import keywords_header
+from keywords_content import keywords_content
+from display_pretty import string_rotator
+from display_pretty import display
+
+ignore_slots = False
+bold = False
+order = 'bottom'
+topper = 'versionlist'
+
+def process_display(package, keywords, dbapi):
+ portdata = keywords_content(package, keywords.keywords, dbapi, ignore_slots, order, bold, topper)
+ if topper == 'archlist':
+ header = string_rotator().rotateContent(keywords.content, keywords.length, bold)
+ extra = string_rotator().rotateContent(keywords.extra, keywords.length, bold, False)
+ # -1 : space is taken in account and appended by us
+ filler = ''.ljust(portdata.slot_length-1)
+ header = ['%s%s%s' % (x, filler, y) for x, y in zip(header, extra)]
+ content = portdata.content
+ header_length = portdata.version_length
+ content_length = keywords.length
+ else:
+ header = string_rotator().rotateContent(portdata.content, portdata.content_length, order, bold)
+ content = keywords.content
+ sep = [''.ljust(keywords.length) for x in range(portdata.slot_length-1)]
+ content.extend(sep)
+ content.extend(keywords.extra)
+ header_length = keywords.length
+ content_length = portdata.version_length
+ display(content, header, header_length, content_length, portdata.cp, topper)
+
+def process_args(argv):
+ """Option parsing via argc"""
+ parser = argparse.ArgumentParser(prog=__package__,
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ description='Display keywords for specified package or for package that is in pwd.')
+
+ parser.add_argument('-v', '--version', action='version', version=__version__, help='show package version and exit')
+
+ parser.add_argument('package', nargs='*', default=None, help='Packages to check.')
+
+ parser.add_argument('-a', '--arch', nargs='+', default=[], help='Display only specified arch(s)')
+
+ parser.add_argument('-A', '--align', nargs='?', default='bottom', choices=['top', 'bottom'],
+ help='Specify alignment for descriptions.')
+ parser.add_argument('-T', '--top-position', nargs='?', default='archlist', choices=['archlist', 'versionlist'],
+ help='Specify which fields we want to have in top listing.')
+
+ parser.add_argument('-B', '--bold', action='store_true', default=False,
+ help='Print out each other column in bold for easier visual separation.')
+ parser.add_argument('-C', '--color', action='store_true', default=False,
+ help='Force colored output')
+ parser.add_argument('-O', '--overlays', action='store_true', default=False,
+ help='Search also overlays')
+ parser.add_argument('-P', '--prefix', action='store_true', default=False,
+ help='Display prefix keywords in output.')
+ parser.add_argument('-S', '--ignore-slot', action='store_true', default=False,
+ help='Treat slots as irelevant during detection of redundant pacakges.')
+
+ return parser.parse_args(args=argv[1:])
+
+def main(argv):
+ global ignore_slots, bold, order, topper
+
+ #opts parsing
+ opts = process_args(argv)
+ ignore_slots = opts.ignore_slot
+ use_overlays = opts.overlays
+ # user can do both --arch=a,b,c or --arch a b c
+ if len(opts.arch) > 1:
+ opts.arch = ','.join(opts.arch)
+ highlight_arch = ''.join(opts.arch).split(',')
+ bold = opts.bold
+ order = opts.align
+ topper = opts.top_position
+ prefix = opts.prefix
+ color = opts.color
+ package = opts.package
+ # disable colors when redirected and they are not forced on
+ if not color and not sys.stdout.isatty():
+ # disable colors
+ porto.nocolor()
+ keywords = keywords_header(prefix, highlight_arch, order)
+ if len(package) > 0:
+ dbapi = portdb[ports['ROOT']]['porttree'].dbapi
+ if not use_overlays:
+ dbapi.porttrees = [dbapi.porttree_root]
+ map(lambda x: process_display(x, keywords, dbapi), package)
+ else:
+ currdir = os.getcwd()
+ package=os.path.basename(currdir)
+ # check if there are actualy some ebuilds
+ ebuilds = ['%s' % x for x in os.listdir(currdir)
+ if fnmatch.fnmatch(file, '*.ebuild')]
+ if len(ebuilds) <= 0:
+ msg_err = 'No ebuilds at "%s"' % currdir
+ raise SystemExit(msg_err)
+ ourtree = os.path.abspath('../../')
+
+ mysettings = portc(env={'PORTDIR_OVERLAY': os.path.abspath('../../')})
+ dbapi = portdbapi(mysettings=mysettings)
+ # specify that we want just our nice tree we are in cwd
+ dbapi.porttrees = [ourtree]
+ process_display(package, keywords, dbapi)
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/pym/gentoolkit/eshowkw/display_pretty.py b/pym/gentoolkit/eshowkw/display_pretty.py
new file mode 100644
index 0000000..b29fba1
--- /dev/null
+++ b/pym/gentoolkit/eshowkw/display_pretty.py
@@ -0,0 +1,102 @@
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+from portage.output import colorize
+from itertools import izip_longest
+
+__all__ = ['string_rotator', 'colorize_string', 'align_string', 'rotate_dash', 'print_content', 'display']
+
+def display(plain_list, rotated_list, plain_width, rotated_height, cp, toplist = 'archlist'):
+ """Render defauld display to show the keywords listing"""
+ # header
+ output = []
+ output.append('Keywords for %s:' % colorize('blue', cp))
+ # data
+ corner_image = [''.ljust(plain_width) for x in range(rotated_height)]
+ if toplist == 'versionlist':
+ corner_image.extend(plain_list)
+ data_printout = ['%s%s' % (x, y)
+ for x, y in izip_longest(corner_image, rotated_list, fillvalue=corner_image[0])]
+ if toplist == 'archlist':
+ data_printout.extend(plain_list)
+ output.extend(data_printout)
+ print print_content(output)
+
+def align_string(string, align, length):
+ """Align string to the specified alignment (left or right, and after rotation it becames top and bottom)"""
+ if align == 'top' or align == 'left':
+ string = string.ljust(length)
+ else:
+ string = string.rjust(length)
+ return string
+
+def colorize_string(color, string):
+ """Add coloring for specified string. Due to rotation we need to do that per character rather than per-line"""
+ tmp = []
+ for char in list(string):
+ # % is whitespace separator so we wont color that :)
+ if char != '%':
+ tmp.append(colorize(color, char))
+ else:
+ tmp.append(char)
+ return ''.join(tmp)
+
+def rotate_dash(string):
+ """Rotate special strings over 90 degrees for better readability."""
+ chars = ['-', '|']
+ subs = ['|', '-']
+ out = string
+ for x,y in zip(chars, subs):
+ if string.find(x) != -1:
+ out = out.replace(x, y)
+ return out
+
+def print_content(content):
+ """Print out content (strip it out of the temporary %)"""
+ return '\n'.join(content).replace('%','')
+
+class string_rotator:
+ __DASH_COUNT = 0
+ def __getChar(self, string, position, line, bold_separator = False):
+ """Return specified character from the string position"""
+
+ # first figure out what character we want to work with
+ # based on order and position in the string
+ isdash = False
+ if string.startswith('|') or string.startswith('-') or string.startswith('+'):
+ split = list(string)
+ isdash = True
+ self.__DASH_COUNT += 1
+ else:
+ split = string.split('%')
+ char = split[position]
+ # bolding
+ if not isdash and bold_separator \
+ and (line-self.__DASH_COUNT)%2 == 0 \
+ and char != ' ':
+ char = colorize('bold', char)
+ return char
+
+ def rotateContent(self, elements, length, bold_separator = False, strip = True):
+ """
+ Rotate string over 90 degrees:
+ string -> s
+ t
+ r
+ i
+ n
+ g
+ """
+ # join used to have list of lines rather than list of chars
+ tmp = []
+ for position in range(length):
+ x = ''
+ for i, string in enumerate(elements):
+ x += ' ' + self.__getChar(rotate_dash(string), position, i, bold_separator)
+ # spaces on dashed line should be dashed too
+ if x.find('+ -') != -1:
+ x = x.replace(' ', '-')
+ # strip all chars and remove empty lines
+ if not strip or len(x.strip(' |-')) > 0:
+ tmp.append(x)
+ return tmp
diff --git a/pym/gentoolkit/eshowkw/keywords_content.py b/pym/gentoolkit/eshowkw/keywords_content.py
new file mode 100644
index 0000000..08a728d
--- /dev/null
+++ b/pym/gentoolkit/eshowkw/keywords_content.py
@@ -0,0 +1,290 @@
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+import portage as port
+from portage.output import colorize
+
+__all__ = ['keywords_content']
+
+from display_pretty import colorize_string
+from display_pretty import align_string
+
+class keywords_content:
+ class RedundancyChecker:
+ def __listRedundant(self, keywords, ignoreslots, slots):
+ """List all redundant packages."""
+ if ignoreslots:
+ return self.__listRedundantAll(keywords)
+ else:
+ return self.__listRedundantSlots(keywords, slots)
+
+ def __listRedundantSlots(self, keywords, slots):
+ """Search for redundant packages walking per keywords for specified slot."""
+ result = [self.__compareSelected([k for k, s in zip(keywords, slots)
+ if s == slot])
+ for slot in self.__uniq(slots)]
+ # this is required because the list itself is not just one level depth
+ return list(''.join(result))
+
+ def __uniq(self, seq):
+ """Remove all duplicate elements from list."""
+ seen = {}
+ result = []
+ for item in seq:
+ if item in seen:
+ continue
+ seen[item] = 1
+ result.append(item)
+ return result
+
+ def __listRedundantAll(self, keywords):
+ """Search for redundant packages using all versions ignoring its slotting."""
+ return list(self.__compareSelected(list(keywords)))
+
+ def __compareSelected(self, kws):
+ """
+ Rotate over list of keywords and compare each element with others.
+ Selectively remove each already compared list from the remaining keywords.
+ """
+ result = []
+ kws.reverse()
+ for i in range(len(kws)):
+ kw = kws.pop()
+ if self.__compareKeywordWithRest(kw, kws):
+ result.append('#')
+ else:
+ result.append('o')
+ if len(result) == 0:
+ result.append('o')
+ return ''.join(result)
+
+ def __compareKeywordWithRest(self, keyword, keywords):
+ """Compare keywords with list of keywords."""
+ for key in keywords:
+ if self.__checkShadow(keyword, key):
+ return True
+ return False
+
+ def __checkShadow(self, old, new):
+ """Check if package version is overshadowed by other package version."""
+ # remove -* and -arch since they are useless for us
+ newclean = ["%s" % x for x in new.split()
+ if x != '-*' and not x.startswith('-')]
+ oldclean = ["%s" % x for x in old.split()
+ if x != '-*' and not x.startswith('-')]
+
+ tmp = set(newclean)
+ tmp.update("~%s" % x for x in newclean
+ if not x.startswith("~"))
+ if not set(oldclean).difference(tmp):
+ return True
+ else:
+ return False
+
+ def __init__(self, keywords, slots, ignore_slots = False):
+ """Query all relevant data for redundancy package checking"""
+ self.redundant = self.__listRedundant(keywords, ignore_slots, slots)
+
+ class VersionChecker:
+ def __getVersions(self, packages, vartree):
+ """Obtain properly aligned version strings without colors."""
+ return self.__stripStartingSpaces(map(lambda x: self.__separateVersion(x, vartree), packages))
+
+ def __stripStartingSpaces(self, pvs):
+ """Strip starting whitespace if there is no real reason for it."""
+ if not self.__require_prepend:
+ return map(lambda x: x.lstrip(), pvs)
+ else:
+ return pvs
+
+ def __separateVersion(self, cpv, vartree):
+ """Get version string for specfied cpv"""
+ #pv = port.versions.cpv_getversion(cpv)
+ return self.__prependVersionInfo(cpv, self.cpv_getversion(cpv), vartree)
+
+ # remove me when portage 2.1.9 is stable
+ def cpv_getversion(self, mycpv):
+ """Returns the v (including revision) from an cpv."""
+ cp = port.versions.cpv_getkey(mycpv)
+ if cp is None:
+ return None
+ return mycpv[len(cp+"-"):]
+
+ def __prependVersionInfo(self, cpv, pv, vartree):
+ """Prefix version with string based on whether version is installed or masked."""
+ mask = self.__getMaskStatus(cpv)
+ install = self.__getInstallStatus(cpv, vartree)
+
+ if mask and install:
+ pv = '[M][I]%s' % pv
+ self.__require_longprepend = True
+ elif mask:
+ pv = '[M]%s' % pv
+ self.__require_prepend = True
+ elif install:
+ pv = '[I]%s' % pv
+ self.__require_prepend = True
+ return pv
+
+ def __getMaskStatus(self, cpv):
+ """
+ Figure out if package is pmasked.
+ This also uses user settings in /etc/ so local changes are important.
+ """
+ pmask = False
+ try:
+ if port.getmaskingstatus(cpv) == ['package.mask']:
+ pmask = True
+ except:
+ # occurs when package is not known by portdb
+ # so we consider it unmasked
+ pass
+ return pmask
+
+ def __getInstallStatus(self, cpv, vartree):
+ """Check if package version we test is installed."""
+ return vartree.cpv_exists(cpv)
+
+ def __init__(self, packages, vartree):
+ """Query all relevant data for version data formatting"""
+ self.__require_longprepend = False
+ self.__require_prepend = False
+ self.versions = self.__getVersions(packages, vartree)
+
+ def __checkExist(self, pdb, package):
+ """Check if specified package even exists."""
+ try:
+ matches = pdb.xmatch('match-all', package)
+ except port.exception.AmbiguousPackageName as Arg:
+ msg_err = 'Ambiguous package name "%s".\n' % package
+ found = 'Possibilities: %s' % Arg
+ raise SystemExit('%s%s' % (msg_err, found))
+ except port.exception.InvalidAtom:
+ msg_err = 'No such package "%s"' % package
+ raise SystemExit(msg_err)
+ if len(matches) <= 0:
+ msg_err = 'No such package "%s"' % package
+ raise SystemExit(msg_err)
+ return matches
+
+ def __getMetadata(self, pdb, packages):
+ """Obtain all KEYWORDS and SLOT from metadata"""
+ try:
+ metadata = map(lambda x: pdb.aux_get(x, ['KEYWORDS', 'SLOT', 'repository']), packages)
+ except KeyError:
+ # portage prints out more verbose error for us if we were lucky
+ raise SystemExit('Failed to obtain metadata')
+ return list(zip(*metadata))
+
+ def __formatKeywords(self, keywords, keywords_list, usebold = False, toplist = 'archlist'):
+ """Loop over all keywords and replace them with nice visual identifier"""
+ # the % is fancy separator, we use it to split keywords for rotation
+ # so we wont loose the empty spaces
+ return ['% %'.join([self.__prepareKeywordChar(arch, i, version.split(), usebold, toplist)
+ for i, arch in enumerate(keywords_list)])
+ for version in keywords]
+
+ def __prepareKeywordChar(self, arch, field, keywords, usebold = False, toplist = 'archlist'):
+ """
+ Convert specified keywords for package into their visual replacements.
+ # possibilities:
+ # ~arch -> orange ~
+ # -arch -> red -
+ # arch -> green +
+ # -* -> red *
+ """
+ keys = [ '~%s' % arch, '-%s' % arch, '%s' % arch, '-*' ]
+ nocolor_values = [ '~', '-', '+', '*' ]
+ values = [
+ colorize('darkyellow', '~'),
+ colorize('darkred', '-'),
+ colorize('darkgreen', '+'),
+ colorize('darkred', '*')
+ ]
+ # check what keyword we have
+ # here we cant just append space because it would get stripped later
+ char = colorize('darkgray','o')
+ for k, v, n in zip(keys, values, nocolor_values):
+ if k in keywords:
+ char = v
+ break
+ if toplist == 'archlist' and usebold and (field)%2 == 0 and char != ' ':
+ char = colorize('bold', char)
+ return char
+
+ def __formatVersions(self, versions, align, length):
+ """Append colors and align keywords properly"""
+ # % are used as separators for further split so we wont loose spaces and coloring
+ tmp = []
+ for pv in versions:
+ pv = align_string(pv, align, length)
+ pv = '%'.join(list(pv))
+ if pv.find('[%M%][%I%]') != -1:
+ tmp.append(colorize_string('darkyellow', pv))
+ elif pv.find('[%M%]') != -1:
+ tmp.append(colorize_string('darkred', pv))
+ elif pv.find('[%I%]') != -1:
+ tmp.append(colorize_string('bold', pv))
+ else:
+ tmp.append(pv)
+ return tmp
+
+ def __formatAdditional(self, additional, color, length):
+ """Align additional items properly"""
+ # % are used as separators for further split so we wont loose spaces and coloring
+ tmp = []
+ for x in additional:
+ tmpc = color
+ x = align_string(x, 'left', length)
+ x = '%'.join(list(x))
+ if x == 'o':
+ # the value is unset so the color is gray
+ tmpc = 'darkgray'
+ x = colorize_string(tmpc, x)
+ tmp.append(x)
+ return tmp
+
+ def __prepareContentResult(self, versions, keywords, redundant, slots, slot_length, repos, linesep):
+ """Parse version fields into one list with proper separators"""
+ content = []
+ oldslot = ''
+ fieldsep = '% %|% %'
+ normsep = '% %'
+ for v, k, r, s, t in zip(versions, keywords, redundant, slots, repos):
+ if oldslot != s:
+ oldslot = s
+ content.append(linesep)
+ else:
+ s = '%'.join(list(''.rjust(slot_length)))
+ content.append('%s%s%s%s%s%s%s%s%s' % (v, fieldsep, k, fieldsep, r, normsep, s, fieldsep, t))
+ return content
+
+ def __init__(self, package, keywords_list, porttree, ignoreslots = False, content_align = 'bottom', usebold = False, toplist = 'archlist'):
+ """Query all relevant data from portage databases."""
+ vartree = port.db[port.settings['ROOT']]['vartree'].dbapi
+ packages = self.__checkExist(porttree, package)
+ self.keywords, self.slots, self.repositories = self.__getMetadata(porttree, packages)
+ self.slot_length = max([len(x) for x in self.slots])
+ repositories_length = max([len(x) for x in self.repositories])
+ self.keyword_length = len(keywords_list)
+ self.versions = self.VersionChecker(packages, vartree).versions
+ self.version_length = max([len(x) for x in self.versions])
+ self.version_count = len(self.versions)
+ self.redundant = self.RedundancyChecker(self.keywords, self.slots, ignoreslots).redundant
+ redundant_length = max([len(x) for x in self.redundant])
+
+ ver = self.__formatVersions(self.versions, content_align, self.version_length)
+ kws = self.__formatKeywords(self.keywords, keywords_list, usebold, toplist)
+ red = self.__formatAdditional(self.redundant, 'purple', redundant_length)
+ slt = self.__formatAdditional(self.slots, 'bold', self.slot_length)
+ rep = self.__formatAdditional(self.repositories, 'yellow', repositories_length)
+ # those + nubers are spaces in printout. keywords are multiplied also because of that
+ linesep = '%s+%s+%s+%s' % (''.ljust(self.version_length+1, '-'),
+ ''.ljust(self.keyword_length*2+1, '-'),
+ ''.ljust(redundant_length+self.slot_length+3, '-'),
+ ''.ljust(repositories_length+1, '-')
+ )
+
+ self.content = self.__prepareContentResult(ver, kws, red, slt, self.slot_length, rep, linesep)
+ self.content_length = len(linesep)
+ self.cp = port.cpv_getkey(packages[0])
diff --git a/pym/gentoolkit/eshowkw/keywords_header.py b/pym/gentoolkit/eshowkw/keywords_header.py
new file mode 100644
index 0000000..53105ae
--- /dev/null
+++ b/pym/gentoolkit/eshowkw/keywords_header.py
@@ -0,0 +1,99 @@
+# Copyright 2001-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+__all__ = ['keywords_header']
+
+from portage import settings as ports
+from portage.output import colorize
+from display_pretty import colorize_string
+from display_pretty import align_string
+
+class keywords_header:
+ __IMPARCHS = [ 'arm', 'amd64', 'x86' ]
+ __ADDITIONAL_FIELDS = [ 'unused', 'slot' ]
+ __EXTRA_FIELDS = [ 'repo' ]
+
+ def __readKeywords(self):
+ """Read all available keywords from portage."""
+ return [x for x in ports.archlist()
+ if not x.startswith('~')]
+
+ def __sortKeywords(self, keywords, prefix = False, required_keywords = []):
+ """Sort keywords with short archs first"""
+ # user specified only some keywords to display
+ if len(required_keywords) != 0:
+ tmpkeywords = [k for k in keywords
+ if k in required_keywords]
+ # idiots might specify non-existant archs
+ if len(tmpkeywords) != 0:
+ keywords = tmpkeywords
+
+ normal = [k for k in keywords
+ if len(k.split('-')) == 1]
+ normal.sort()
+
+ if prefix:
+ longer = [k for k in keywords
+ if len(k.split('-')) != 1]
+ longer.sort()
+ normal.extend(longer)
+ return normal
+
+ def __readAdditionalFields(self):
+ """Prepare list of aditional fileds displayed by eshowkw (2nd part)"""
+ return self.__ADDITIONAL_FIELDS
+
+ def __readExtraFields(self):
+ """Prepare list of extra fileds displayed by eshowkw (3rd part)"""
+ return self.__EXTRA_FIELDS
+
+ def __formatKeywords(self, keywords, align, length):
+ """Append colors and align keywords properly"""
+ tmp = []
+ for keyword in keywords:
+ tmp2 = keyword
+ keyword = align_string(keyword, align, length)
+ # % are used as separators for further split so we wont loose spaces and coloring
+ keyword = '%'.join(list(keyword))
+ if tmp2 in self.__IMPARCHS:
+ tmp.append(colorize_string('darkyellow', keyword))
+ else:
+ tmp.append(keyword)
+ return tmp
+
+ def __formatAdditional(self, additional, align, length):
+ """Align additional items properly"""
+ # % are used as separators for further split so we wont loose spaces and coloring
+ return ['%'.join(align_string(x, align, length)) for x in additional]
+
+ def __prepareExtra(self, extra, align, length):
+ content = []
+ content.append(''.ljust(length, '-'))
+ content.extend(self.__formatAdditional(extra, align, length))
+ return content
+
+ def __prepareResult(self, keywords, additional, align, length):
+ """Parse keywords and additional fields into one list with proper separators"""
+ content = []
+ content.append(''.ljust(length, '-'))
+ content.extend(self.__formatKeywords(keywords, align, length))
+ content.append(''.ljust(length, '-'))
+ content.extend(self.__formatAdditional(additional, align, length))
+ return content
+
+ def __init__(self, prefix = False, required_keywords = [], keywords_align = 'bottom'):
+ """Initialize keywords header."""
+ additional = self.__readAdditionalFields()
+ extra = self.__readExtraFields()
+ self.keywords = self.__sortKeywords(self.__readKeywords(), prefix, required_keywords)
+ self.length = max(
+ max([len(x) for x in self.keywords]),
+ max([len(x) for x in additional]),
+ max([len(x) for x in extra])
+ )
+ #len(max([max(self.keywords, key=len), max(additional, key=len)], key=len))
+ self.keywords_count = len(self.keywords)
+ self.additional_count = len(additional)
+ self.extra_count = len(extra)
+ self.content = self.__prepareResult(self.keywords, additional, keywords_align, self.length)
+ self.extra = self.__prepareExtra(extra, keywords_align, self.length)