From ae34620db130bd8d94d68af73571e5df1bd4d258 Mon Sep 17 00:00:00 2001 From: fuzzyray Date: Mon, 14 Dec 2009 19:37:49 +0000 Subject: [PATCH] Merge genscripts rev 137, this revision makes the output of equery meta compatible with epkginfo svn path=/trunk/gentoolkit/; revision=722 --- DEVELOPING | 4 +- NEWS | 40 ++++++++++++++ TODO | 18 +++++-- man/equery.1 | 20 ++++--- pym/gentoolkit/equery/__init__.py | 4 +- pym/gentoolkit/equery/changes.py | 2 +- pym/gentoolkit/equery/depends.py | 2 +- pym/gentoolkit/equery/meta.py | 37 +++++++++---- pym/gentoolkit/package.py | 86 +++++++++++++++++++------------ pym/gentoolkit/pprinter.py | 56 ++++++++++---------- pym/gentoolkit/query.py | 34 ++++++++++++ pym/gentoolkit/test/test_cpv.py | 59 +++++++++++++++++++++ 12 files changed, 276 insertions(+), 86 deletions(-) create mode 100644 pym/gentoolkit/query.py create mode 100644 pym/gentoolkit/test/test_cpv.py diff --git a/DEVELOPING b/DEVELOPING index ea5f457..0a6c309 100644 --- a/DEVELOPING +++ b/DEVELOPING @@ -1,7 +1,7 @@ -Code Guidelines +Python Code Guidelines --------------- These are a few guidelines to stick to when modifying or adding code to -Gentoolkit. +Gentoolkit. These guidelines do not apply to pym/gentoolkit/test/*. First, read Python's PEP 8: http://www.python.org/dev/peps/pep-0008/ Next, read Portage's DEVELOPING file. diff --git a/NEWS b/NEWS index e69de29..5e349be 100644 --- a/NEWS +++ b/NEWS @@ -0,0 +1,40 @@ +News (new features/major bug fixes) + +gentoolkit-0.3.0 +---------------- + +epkginfo: + * is now a link to equery meta and has all the features equery meta has + +equery: + * --help menus cleaned up, using notations (b)elongs instead of belongs(b). + * man page has been rewritten. + * --quiet effects more modules. + * 2 new modules: + * changes - Gentoo ChangeLog viewer, try: + `equery changes portage` to see entry for portage version that emerge + wants to install; + `equery changes portage --from=2.2_rc20 --to=2.2_rc30` to see all entries + between the specified versions. + * meta - Displays information available in metadata.xml and keyword info. + Try `equery meta boost` to list herd, maintainers, keywords, and more. + * Modules which are meant to run on multiple packages (check, list, size) now + allow category and package name globbing, (so no more need for --exact-name + or --category). + + # Exact name matching by default: + $ equery l zilla + * Searching for zilla ... + + # Use globs to fuzzy match + $ equery l *zilla* + * Searching for *zilla* ... + [IP-] [ ] www-client/mozilla-firefox-3.5.4:0 + + # Use globs to 'category filter' + $ equery l www-client/* + * Searching for * in www-client ... + [I--] [XX] www-client/chromium-4.0.223.5:0 + [IP-] [ ] www-client/epiphany-2.26.3-r2:0 + [IP-] [ ] www-client/links-2.2:2 + [IP-] [ ] www-client/mozilla-firefox-3.5.4:0 diff --git a/TODO b/TODO index 859e8df..161546b 100644 --- a/TODO +++ b/TODO @@ -40,9 +40,21 @@ Ebuild changes: "${python}" setup.py test || die "testing returned non zero" } - Add: - DEPEND on python 2.5 (needed for 'from __future__ import with' and others) + DEPEND on python 2.5 (needed for 'from __future__ import with_statement' and others) For Next Release: - write NEWS file - - write a docstring for Package which details especially the change in have - cpv as an object, not a string. + - make CPV.__init__ more strict, it allows some silly stuff + - $ equery uses '>=sys-apps/portage-2' + * Searching for >=sys-apps/portage-2 ... + * Found these USE flags for sys-apps/portage-2.1.6.13: + - belongs doesn't properly match atom syntax + + +For following release: + - transition package query backend to using Query class. + Query class should accept any kind package of input accepted by equery. + Most of the functions in helpers should be able to moved out, either in + to query (many of those functions pertain to finding packages matching a + query) or into other appropriate modules (split_cpv, + compare_package_strings into cpv, as they also requires a cpv string) diff --git a/man/equery.1 b/man/equery.1 index c3be6e9..62df6fd 100644 --- a/man/equery.1 +++ b/man/equery.1 @@ -409,21 +409,27 @@ http://www.gentoo.org/proj/en/devrel/handbook/handbook.xml?part=2&chap=4 .IR "LOCAL OPTIONS" ":" .HP -.B \-c, \-\-current -.br -Parse a metadata.xml file in the current directory. Useful for package maintainers to review formatting. -.HP .B \-d, \-\-description .br Show an extended package description. .HP .B \-H, \-\-herd .br -Show the herd(s) for the package. When not piping and not passing \fB--quiet\fP as a global option, also show the herd's email address. +Show the herd(s) for the package. When not piping and not passing \fB--quiet\fP as a global option, also show the herd's email address. (shown by default) +.HP +.B \-k, \-\-keywords +.br +Show keywords for all matching versions. \fBkeywords\fP does not list all keywords for all versions. Instead, it filters the list to make it easier to spot versions that need bumping or are okay to remove from the tree. It filters by slot. For example: +.br +Keywords: 1.35.0-r3:\fB0\fP: +.br +Keywords: 1.35.0-r5:\fB0\fP: amd64 hppa ppc x86 ~alpha ~arm ~ia64 ~mips ~ppc64 ~s390 ~sh ~sparc +.br +In this output from \fBequery meta boost\fP, -r5 is the highest available version in slot 0, so all keywords are listed. The actual keywords for -r3 are "~amd64 ~hppa ~ppc ~x86", but since a higher version in the same slot has the same or more stable keywording, they are filtered out. Arch mask keywords (-*) are always shown. .HP .B \-m, \-\-maintainer .br -Show the package maintainer(s) email address. If the metadata is available, also show the maitainer's name and/or job description. +Show the package maintainer(s) email address. If the metadata is available, also show the maintainer's name and/or job description. (shown by default) .HP .B \-u, \-\-useflags .br @@ -431,7 +437,7 @@ Show per-package USE flag descriptions. Per-package USE flag descriptions are so .HP .B \-U, \-\-upstream .br -Show information about the package's upstream project. This information can be extremely useful for finding an author's email, upstream bug tracker or upstream documentation. However, most maintainers do not currently provide this information. +Show information about the package's upstream project, including the author's email, upstream bug tracker or upstream documentation. At the time of writing, most maintainers do not provide this information. (shown by default) .HP .B \-x, \-\-xml .br diff --git a/pym/gentoolkit/equery/__init__.py b/pym/gentoolkit/equery/__init__.py index d993953..07eb3dd 100644 --- a/pym/gentoolkit/equery/__init__.py +++ b/pym/gentoolkit/equery/__init__.py @@ -87,9 +87,9 @@ def print_help(with_description=True): print pp.command("modules") + " (" + pp.command("short name") + ")" print format_options(( (" (b)elongs", "list what package FILES belong to"), - (" (c)hanges", "list changelog entries for PKG"), + (" (c)hanges", "list changelog entries for ATOM"), (" chec(k)", "verify checksums and timestamps for PKG"), - (" (d)epends", "list all packages directly depending on PKG"), + (" (d)epends", "list all packages directly depending on ATOM"), (" dep(g)raph", "display a tree of all dependencies for PKG"), (" (f)iles", "list all files installed by PKG"), (" (h)asuse", "list all packages that have USE flag"), diff --git a/pym/gentoolkit/equery/changes.py b/pym/gentoolkit/equery/changes.py index ef35ec9..28611f9 100644 --- a/pym/gentoolkit/equery/changes.py +++ b/pym/gentoolkit/equery/changes.py @@ -4,7 +4,7 @@ # # $Header: $ -"""Displays the ChangeLog entry for the latest installable version of a package""" +"""Displays the ChangeLog entry for the latest installable version of an atom""" # Move to Imports sections when Python 2.6 is stable from __future__ import with_statement diff --git a/pym/gentoolkit/equery/depends.py b/pym/gentoolkit/equery/depends.py index b60a0cc..18e08af 100644 --- a/pym/gentoolkit/equery/depends.py +++ b/pym/gentoolkit/equery/depends.py @@ -4,7 +4,7 @@ # # $Header: $ -"""List all packages that depend on a given query""" +"""List all packages that depend on a atom given query""" __docformat__ = 'epytext' diff --git a/pym/gentoolkit/equery/meta.py b/pym/gentoolkit/equery/meta.py index 763e30d..1fc8964 100644 --- a/pym/gentoolkit/equery/meta.py +++ b/pym/gentoolkit/equery/meta.py @@ -76,9 +76,11 @@ def print_help(with_description=True, with_usage=True): def filter_keywords(matches): - """Filter non-unique keywords per slot. + """Filters non-unique keywords per slot. - This view apparently makes version bumps easier for package maintainers. + Does not filter arch mask keywords (-). Besides simple non-unique keywords, + also remove unstable keywords (~) if a higher version in the same slot is + stable. This view makes version bumps easier for package maintainers. @type matches: array @param matches: set of L{gentoolkit.package.Package} instances whose @@ -88,19 +90,31 @@ def filter_keywords(matches): 'array of keywords not found in a higher version of pkg within the same slot' values. """ + def del_archmask(keywords): + """Don't add arch_masked to filter set.""" + return [x for x in keywords if not x.startswith('-')] + + def add_unstable(keywords): + """Add unstable keyword for all stable keywords to filter set.""" + result = list(keywords) + result.extend( + ['~%s' % x for x in keywords if not x.startswith(('-', '~'))] + ) + return result result = {} slot_map = {} # Start from the newest rev_matches = reversed(matches) for pkg in rev_matches: - keywords_str, slot = pkg.environment(('KEYWORDS', 'SLOT')) + keywords_str, slot = pkg.environment(('KEYWORDS', 'SLOT'), + prefer_vdb=False) keywords = keywords_str.split() result[pkg] = [x for x in keywords if x not in slot_map.get(slot, [])] try: - slot_map[slot].update(keywords) + slot_map[slot].update(del_archmask(add_unstable(keywords))) except KeyError: - slot_map[slot] = set(keywords) + slot_map[slot] = set(del_archmask(add_unstable(keywords))) return result @@ -200,12 +214,15 @@ def format_keywords(keywords): result = [] for kw in sorted(keywords): - if kw.startswith(('~', '-')): - # keyword (~) or arch (-) masked - kw = pp.useflag(kw, enabled=False) + if kw.startswith('-'): + # arch masked + kw = pp.keyword(kw, stable=False, hard_masked=True) + elif kw.startswith('~'): + # keyword masked + kw = pp.keyword(kw, stable=False, hard_masked=False) else: - # unmasked - kw = pp.useflag(kw, enabled=True) + # stable + kw = pp.keyword(kw, stable=True, hard_masked=False) result.append(kw) return ' '.join(result) diff --git a/pym/gentoolkit/package.py b/pym/gentoolkit/package.py index 1759a8c..c020f63 100644 --- a/pym/gentoolkit/package.py +++ b/pym/gentoolkit/package.py @@ -9,9 +9,19 @@ """Provides an interface to package information stored by package managers. -The package class is the heart of much of Gentoolkit. Given a CPV -(category/package-version string), -TODO: finish this docstring +The Package class is the heart of much of Gentoolkit. Given a CPV +(category/package-version) string, it can reveal the package's status in the +tree and VARDB (/var/db/), provide rich comparison and sorting, and expose +important parts of Portage's back-end. + +Example usage: + >>> portage = Package('sys-apps/portage-2.1.6.13') + >>> portage.ebuild_path() + '/usr/portage/sys-apps/portage/portage-2.1.6.13.ebuild' + >>> portage.is_masked() + False + >>> portage.is_installed() + True """ __all__ = ( @@ -40,7 +50,7 @@ from gentoolkit.metadata import MetaData # ======= class Package(CPV): - """Provides methods for ascertaining the state of a given CPV.""" + """Exposes the state of a given CPV.""" def __init__(self, cpv): if isinstance(cpv, CPV): @@ -86,19 +96,6 @@ class Package(CPV): def __str__(self): return str(self.cpv) - def _get_trees(self): - """Return dbapi objects for each repository that contains self.""" - - result = [] - if self.is_installed(): - result.append(VARDB) - if self.exists(): - result.append(PORTDB) - if not result: - raise errors.GentoolkitFatalError("Could not find package tree") - - return result - @property def metadata(self): """Instantiate a L{gentoolkit.metadata.MetaData} object here.""" @@ -134,7 +131,7 @@ class Package(CPV): return self._deps - def environment(self, envvars, tree=None): + def environment(self, envvars, prefer_vdb=True, no_fallback=False): """Returns one or more of the predefined environment variables. Available envvars are: @@ -156,21 +153,43 @@ class Package(CPV): @type envvars: str or array @param envvars: one or more of (DEPEND, SRC_URI, etc.) + @type prefer_vdb: bool + @keyword prefer_vdb: if True, look in the vardb before portdb, else + reverse order. Specifically KEYWORDS will get more recent + information by preferring portdb. + @type no_fallback: bool + @keyword no_fallback: query only the preferred db @rtype: str or list @return: str if envvars is str, list if envvars is array + @raise KeyError: if key is not found in requested db(s) """ - if tree is None: - tree = self._get_trees()[0] got_string = False if isinstance(envvars, basestring): got_string = True envvars = (envvars,) - try: - result = tree.aux_get(str(self.cpv), envvars) - except (KeyError, errors.GentoolkitFatalError): - err = "aux_get returned unexpected results" - raise errors.GentoolkitFatalError(err) + if prefer_vdb: + try: + result = VARDB.aux_get(str(self.cpv), envvars) + except KeyError: + try: + if no_fallback: + raise KeyError + result = PORTDB.aux_get(str(self.cpv), envvars) + except KeyError: + err = "aux_get returned unexpected results" + raise errors.GentoolkitFatalError(err) + else: + try: + result = PORTDB.aux_get(str(self.cpv), envvars) + except KeyError: + try: + if no_fallback: + raise KeyError + result = VARDB.aux_get(str(self.cpv), envvars) + except KeyError: + err = "aux_get returned unexpected results" + raise errors.GentoolkitFatalError(err) if got_string: return result[0] @@ -255,14 +274,12 @@ class Package(CPV): return VARDB.findname(str(self.cpv)) return PORTDB.findname(str(self.cpv)) - def package_path(self): + def package_path(self, in_vartree=False): """Return the path to where the ebuilds and other files reside.""" - if self._package_path is None: - path_split = self.ebuild_path().split(os.sep) - self._package_path = os.sep.join(path_split[:-1]) - - return self._package_path + if in_vartree: + return self.dblink.getpath() + return os.sep.join(self.ebuild_path().split(os.sep)[:-1]) def repo_id(self): """Using the package path, determine the repository id. @@ -365,9 +382,14 @@ class PackageFormatter(object): def __str__(self): if self.do_format: maskmodes = [' ', ' ~', ' -', 'M ', 'M~', 'M-', 'XX'] + maskmode = maskmodes[self.format_mask_status()[0]] return "[%(location)s] [%(mask)s] %(package)s:%(slot)s" % { 'location': self.location, - 'mask': pp.maskflag(maskmodes[self.format_mask_status()[0]]), + 'mask': pp.keyword( + maskmode, + stable=not maskmode.strip(), + hard_masked=set(('M', 'X', '-')).intersection(maskmode) + ), 'package': pp.cpv(str(self.pkg.cpv)), 'slot': pp.slot(self.pkg.environment("SLOT")) } diff --git a/pym/gentoolkit/pprinter.py b/pym/gentoolkit/pprinter.py index 8f619e3..bd5e6cb 100644 --- a/pym/gentoolkit/pprinter.py +++ b/pym/gentoolkit/pprinter.py @@ -17,7 +17,6 @@ __all__ = ( 'globaloption', 'installedflag', 'localoption', - 'maskflag', 'number', 'path', 'path_symlink', @@ -48,84 +47,85 @@ import portage.output as output # pylint: disable-msg=E1101 def command(string): - """Print a program command string.""" + """Returns a program command string.""" return output.green(string) def cpv(string): - """Print a category/package- string.""" + """Returns a category/package- string.""" return output.green(string) def die(err, string): - """Print an error string and die with an error code.""" + """Returns an error string and die with an error code.""" sys.stderr.write(error(string)) sys.exit(err) def emph(string): - """Print a string as emphasized.""" + """Returns a string as emphasized.""" return output.bold(string) def error(string): - """Prints an error string to stderr.""" + """Prints an error string.""" return output.red("!!! ") + string + "\n" def globaloption(string): - """Print a global option string, i.e. the program global options.""" + """Returns a global option string, i.e. the program global options.""" return output.yellow(string) -def installedflag(string): - """Print an installed flag string""" - return output.bold(string) - def localoption(string): - """Print a local option string, i.e. the program local options.""" + """Returns a local option string, i.e. the program local options.""" return output.green(string) -def maskflag(string): - """Print a masking flag string""" - return output.red(string) - def number(string): - """Print a number string""" + """Returns a number string.""" return output.turquoise(string) def path(string): - """Print a file or directory path string""" + """Returns a file or directory path string.""" return output.bold(string) def path_symlink(string): - """Print a symlink string.""" + """Returns a symlink string.""" return output.turquoise(string) def pkgquery(string): - """Print a package query string.""" + """Returns a package query string.""" return output.bold(string) def productname(string): - """Print a product name string, i.e. the program name.""" + """Returns a product name string, i.e. the program name.""" return output.turquoise(string) def regexpquery(string): - """Print a regular expression string""" + """Returns a regular expression string.""" return output.bold(string) def section(string): - """Print a string as a section header.""" + """Returns a string as a section header.""" return output.turquoise(string) def slot(string): - """Print a slot string""" + """Returns a slot string""" return output.bold(string) def subsection(string): - """Print a string as a subsection header.""" + """Returns a string as a subsection header.""" return output.turquoise(string) def useflag(string, enabled=True): - """Print a USE flag string""" - return output.green(string) if enabled else output.blue(string) + """Returns a USE flag string.""" + return output.blue(string) if enabled else output.red(string) + +def keyword(string, stable=True, hard_masked=False): + """Returns a keyword string.""" + if stable: + return output.green(string) + if hard_masked: + return output.red(string) + # keyword masked: + return output.blue(string) def warn(string): - """Print a warning string to stderr.""" + """Returns a warning string.""" return "!!! " + string + "\n" # vim: set ts=4 sw=4 tw=79: diff --git a/pym/gentoolkit/query.py b/pym/gentoolkit/query.py new file mode 100644 index 0000000..24f8c18 --- /dev/null +++ b/pym/gentoolkit/query.py @@ -0,0 +1,34 @@ +#!/usr/bin/python +# +# Copyright(c) 2004-2009, Gentoo Foundation +# +# Licensed under the GNU General Public License, v2 +# +# $Header$ + +"""Provides common methods on a package query.""" + +__all__ = ( + 'Query', +) + +# ======= +# Imports +# ======= + +from gentoolkit.cpv import CPV +#from gentoolkit.helpers import * + +# ======= +# Classes +# ======= + +class Query(CPV): + """Provides common methods on a package query.""" + + def __init__(self, cpv): + if isinstance(cpv, CPV): + self.cpv = cpv + else: + self.cpv = CPV(cpv) + del cpv diff --git a/pym/gentoolkit/test/test_cpv.py b/pym/gentoolkit/test/test_cpv.py new file mode 100644 index 0000000..9b36cc3 --- /dev/null +++ b/pym/gentoolkit/test/test_cpv.py @@ -0,0 +1,59 @@ +#!/usr/bin/python +# +# Copyright(c) 2009, Gentoo Foundation +# +# Licensed under the GNU General Public License, v2 +# +# $Header$ + +import unittest +from test import test_support + +from gentoolkit.cpv import * + +class TestGentoolkitCPV(unittest.TestCase): + + def assertEqual2(self, o1, o2): + # logic bugs hidden behind short circuiting comparisons for metadata + # is why we test the comparison *both* ways. + self.assertEqual(o1, o2) + c = cmp(o1, o2) + self.assertEqual(c, 0, + msg="checking cmp for %r, %r, aren't equal: got %i" % (o1, o2, c)) + self.assertEqual(o2, o1) + c = cmp(o2, o1) + self.assertEqual(c, 0, + msg="checking cmp for %r, %r,aren't equal: got %i" % (o2, o1, c)) + + def assertNotEqual2(self, o1, o2): + # is why we test the comparison *both* ways. + self.assertNotEqual(o1, o2) + c = cmp(o1, o2) + self.assertNotEqual(c, 0, + msg="checking cmp for %r, %r, not supposed to be equal, got %i" + % (o1, o2, c)) + self.assertNotEqual(o2, o1) + c = cmp(o2, o1) + self.assertNotEqual(c, 0, + msg="checking cmp for %r, %r, not supposed to be equal, got %i" + % (o2, o1, c)) + + def test_comparison(self): + self.assertEqual2(CPV('pkg'), CPV('pkg')) + self.assertNotEqual2(CPV('pkg'), CPV('pkg1')) + self.assertEqual2(CPV('cat/pkg'), CPV('cat/pkg')) + self.assertNotEqual2(CPV('cat/pkg'), CPV('cat/pkgb')) + self.assertNotEqual2(CPV('cata/pkg'), CPV('cat/pkg')) + self.assertEqual2(CPV('cat/pkg-0.1'), CPV('cat/pkg-0.1')) + self.assertNotEqual2(CPV('cat/pkg-1.0'), CPV('cat/pkg-1')) + self.assertEqual2(CPV('cat/pkg-0'), CPV('cat/pkg-0')) + self.assertEqual2(CPV('cat/pkg-1-r1'), CPV('cat/pkg-1-r1')) + self.assertNotEqual2(CPV('cat/pkg-2-r1'), CPV('cat/pkg-2-r10')) + self.assertEqual2(CPV('cat/pkg-1_rc2'), CPV('cat/pkg-1_rc2')) + self.assertNotEqual2(CPV('cat/pkg-2_rc2-r1'), CPV('cat/pkg-2_rc1-r1')) + +def test_main(): + test_support.run_unittest(TestGentoolkitCPV) + +if __name__ == '__main__': + test_main() -- 2.26.2