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/keywords_content.py
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/keywords_content.py')
-rw-r--r--pym/gentoolkit/eshowkw/keywords_content.py290
1 files changed, 290 insertions, 0 deletions
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])