Merge genscripts rev 137, this revision makes the output of equery meta compatible...
authorfuzzyray <fuzzyray@gentoo.org>
Mon, 14 Dec 2009 19:37:49 +0000 (19:37 -0000)
committerfuzzyray <fuzzyray@gentoo.org>
Mon, 14 Dec 2009 19:37:49 +0000 (19:37 -0000)
svn path=/trunk/gentoolkit/; revision=722

12 files changed:
DEVELOPING
NEWS
TODO
man/equery.1
pym/gentoolkit/equery/__init__.py
pym/gentoolkit/equery/changes.py
pym/gentoolkit/equery/depends.py
pym/gentoolkit/equery/meta.py
pym/gentoolkit/package.py
pym/gentoolkit/pprinter.py
pym/gentoolkit/query.py [new file with mode: 0644]
pym/gentoolkit/test/test_cpv.py [new file with mode: 0644]

index ea5f457a6d0848eb50aaf749917f27876eabbd0a..0a6c3099406963ba3cff6ee521fca421ec8b5b7f 100644 (file)
@@ -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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5e349be82db4416f42ba6c76778f398314177ef5 100644 (file)
--- 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 859e8df5d1b06a2810c97994ff40fc9007ede67e..161546bed833e68d045cbb041cd3f677e6881116 100644 (file)
--- 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)
index c3be6e996ce2b75e0385cdb48361d3570ed3d6c1..62df6fd8dcc6790c748d861855bf1b34262f1403 100644 (file)
@@ -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
index d99395360ef95c5f1cd9475228cee62a015d0b90..07eb3dd20d5466dacff22d04b835e3b85bae7950 100644 (file)
@@ -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"),
index ef35ec9d4747585a4dea5f1cbb4e6189e6d8fe68..28611f9e7c8e9558818ed2101356c0eb1a71d8ba 100644 (file)
@@ -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
index b60a0ccbb6269776dc1bfb8ed5cc71facda8b10d..18e08afbd373110ad85b0b9953104c6d331ef8a7 100644 (file)
@@ -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'
 
index 763e30d9f2167283bf404d0a1f30ac2110e718ec..1fc896429f47b7c6ccb7f070158c5d323a3ffbf5 100644 (file)
@@ -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)
index 1759a8cebc37bcf5069df11aba5ef6592c239592..c020f63be881e880746928b0cf5212855927d357 100644 (file)
@@ -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"))
                        }
index 8f619e3766996d2a3b470b01ea495cf19cdfb379..bd5e6cb4ba6d0f18866a1b34566a7914c9fcc406 100644 (file)
@@ -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-<version> string."""
+       """Returns a category/package-<version> 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 (file)
index 0000000..24f8c18
--- /dev/null
@@ -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 (file)
index 0000000..9b36cc3
--- /dev/null
@@ -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()