Commits

Anonymous committed fbb8233

Initial commit.

  • Participants
  • Parent commits 3fe91f8

Comments (0)

Files changed (21)

+
+
+Rob Cakebread <gentoodev a t gmail . com>
+
+
+
+
+
+This package is still pre-alpha, beware if you're thinking about using yolklib's API just yet.
+
+PSF - Python Software Foundation License
+
+See:
+http://www.opensource.org/licenses/PythonSoftFoundation.php
+
+
+Original author:
+    Rob Cakebread <gentoodev a t gmail . com>
+
+
+
+
+
+Q: Why do you have a FAQ with no questions?
+A: Because nobody has asked any, and more importantly, I'm trying to boost my Cheesecake score!
+
+
+With easy_install:
+  easy_install yolk
+
+With distutils:
+  python setup.py install
+
+
+Lots of changes in 0.0.2 including:
+
+ * PyPI interface 
+ * Much refactoring of yolklib.
+
+
+
+
+Example usage:
+
+yolk -n
+    List only the non-activated (--multi-version) packages installed
+
+yolk -a
+    List only the activated packages installed
+    (Activated packages are normal packages on sys.path you can import)
+
+yolk -l -f License,Author
+    Show the license and author for each installed package
+
+yolk -H twisded
+    Launches your web browser at Twisted's home page
+
+yolk -M Paste 1.0
+    Show all the metadata for Paste version 1.0
+
+yolk -M Paste
+    Show all the metadata for each version of Paste listed on PyPi
+
+yolk -D cheesecake
+    Show all URL's for cheesecake packages you can download
+
+
+
+
+Thanks to Phillib J. Eby for help in understanding the resource_pkg API
+
+
+------------------------------------------------------------------------------
+
+See yolk's Trac:
+
+http://tools.assembla.com/yolk/report/1
+
+------------------------------------------------------------------------------

File docs/tests.txt

+
+tests/rss_feed.py tests interfacing with PyPI's RSS feed
+.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.36.
+.TH YOLK "1" "January 2007" "yolk 0.0.2" "User Commands"
+.SH NAME
+yolk \- manual page for yolk 0.0.2
+.SH DESCRIPTION
+usage: yolk [options] <package_name> <version>
+.SS "options:"
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+show this help message and exit
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+Show yolk version and exit.
+.IP
+Query installed Python packages:
+.IP
+The following options show information about Python packages installed
+by setuptools. Activated packages are normal packages on sys.path that
+can be imported. Non\-activated packages need 'pkg_resources.require()'
+before they can be imported, such as packages installed with
+\&'easy_install \fB\-\-multi\-version\fR'
+.TP
+\fB\-l\fR, \fB\-\-list\fR
+List all packages installed by setuptools.
+.TP
+\fB\-a\fR, \fB\-\-activated\fR
+List only activated packages installed by setuptools.
+.TP
+\fB\-n\fR, \fB\-\-non\-activated\fR
+List only non\-activated packages installed by
+setuptools.
+.TP
+\fB\-m\fR, \fB\-\-metadata\fR
+Show all metadata for packages installed by setuptools
+(use with \fB\-l\fR \fB\-a\fR or \fB\-n\fR)
+.TP
+\fB\-f\fR FIELDS, \fB\-\-fields\fR=\fIFIELDS\fR
+Show specific metadata fields. (use with \fB\-l\fR \fB\-a\fR or \fB\-n\fR)
+.TP
+\fB\-d\fR, \fB\-\-depends\fR
+Show dependencies for a package installed by
+setuptools if they are available. (use with \fB\-l\fR \fB\-a\fR or
+\fB\-n\fR)
+.IP
+PyPI (Cheese Shop) options:
+.IP
+The following options query the Python Package Index:
+.TP
+\fB\-C\fR, \fB\-\-use\-cached\-pkglist\fR
+Use cached package list instead of querying PyPI (Use
+\fB\-F\fR to force retrieving list.)
+.TP
+\fB\-D\fR, \fB\-\-download\-links\fR
+Show download URL's for package listed on PyPI.
+.TP
+\fB\-F\fR, \fB\-\-fetch\-package\-list\fR
+Fetch and cache list of packages from PyPI.
+.TP
+\fB\-H\fR, \fB\-\-browse\-homepage\fR
+Launch web browser at home page for package.
+.TP
+\fB\-L\fR, \fB\-\-latest\fR
+Show last 20 updates on PyPI.
+.TP
+\fB\-M\fR, \fB\-\-query\-metadata\fR
+Show metadata for a package listed on PyPI.
+.TP
+\fB\-S\fR, \fB\-\-search\fR
+Search PyPI by spec and operator.
+.TP
+\fB\-V\fR, \fB\-\-versions\-available\fR
+Show available versions for given package listeded on
+PyPI.
+.SH "SEE ALSO"
+The full documentation for
+.B yolk
+is maintained as a Texinfo manual.  If the
+.B info
+and
+.B yolk
+programs are properly installed at your site, the command
+.IP
+.B info yolk
+.PP
+should give you access to the complete manual.

File examples/rss_feed.py

+#!/usr/bin/python
+
+import urllib
+import os
+
+from cElementTree import iterparse
+
+import sys
+
+try:
+    from yolklib.pypi import CheeseShop
+except:
+    sys.path.insert(0, os.getcwd())
+    from yolklib.pypi import CheeseShop
+
+
+PYPI_URL = 'http://www.python.org/pypi?:action=rss'
+
+
+def get_pkg_ver(pv, add_quotes=True):
+    """Return package name and version"""
+    n = len(pv.split())
+    if n == 2:
+        #Normal package_name 1.0
+        pkg_name, ver = pv.split()
+    else:
+        parts = pv.split()
+        ver = parts[-1:]
+        if add_quotes:
+            pkg_name = "'%s'" % " ".join(parts[:-1])
+        else:
+            pkg_name = "%s" % " ".join(parts[:-1])
+    return pkg_name, ver
+
+def test_api(pypi_xml):
+    failed = 0
+    failed_msgs =[]
+    for event, elem in iterparse(pypi_xml):
+        if elem.tag == "title":
+            if not elem.text.startswith('Cheese Shop recent updates'):
+                pkg_name, ver = get_pkg_ver(elem.text, False)
+                (pypi_pkg_name, versions) = PyPI.query_versions_pypi(pkg_name, True)
+                try:
+                    assert versions[0] == ver
+                    print "Testing %s... passed" % elem.text
+                except:
+                    failed += 1
+                    failed_msgs.append("%s %s" % (pkg_name, versions))
+                    print "Testing %s... failed" % elem.text
+
+    print "%s tests failed." % failed
+    for msg in failed_msgs:
+        print "\t%s" % msg
+
+def test_cli(pypi_xml):
+    #Fetch master list of package names so we can use cache
+    os.system('./yolk.py -F')
+    for event, elem in iterparse(pypi_xml):
+        if elem.tag == "title":
+            if not elem.text.startswith('Cheese Shop recent updates'):
+                print "Testing %s..." % elem.text
+                pkg_name, ver = get_pkg_ver(elem.text)
+                os.system('./yolk.py -V %s' % pkg_name)
+                os.system('./yolk.py -C -D %s %s' % (pkg_name, ver))
+            elem.clear()
+
+test_cli(urllib.urlopen(PYPI_URL))
+PyPI = CheeseShop()
+test_api(urllib.urlopen(PYPI_URL))
+#!/usr/bin/python
+
+from setuptools import setup
+
+from yolklib import __version__
+
+setup(name="yolk",
+    license = "PSF",
+    version=__version__.version,
+    description="Library and CLI tool for listing installed eggs, their metadata and dependencies and PYPI querying.",
+    long_description="Command-line tool and library for information about Python packages installed by setuptools, and querying of The Cheese Shop (Python Package Index).",
+    maintainer="Rob Cakebread",
+    author="Rob Cakebread",
+    author_email="gentoodev a t gmail . com",
+    url="http://tools.assembla.com/yolk/",
+    keywords="PyPI setuptools cheeseshop distutils eggs package management",
+    classifiers=["Development Status :: 2 - Pre-Alpha",
+                 "Intended Audience :: Developers",
+                 "License :: OSI Approved :: Python Software Foundation License",
+                 "Programming Language :: Python",
+                 "Topic :: Software Development :: Libraries :: Python Modules",
+                 ],
+    py_modules=["yolklib/pypi", "yolklib/metadata", "yolklib/yolklib", "yolklib/__init__", "yolklib/__version__"],
+    packages=['yolklib',],
+    package_dir={'yolklib':''},
+    scripts=['yolk.py',],
+)
+

File tests/rss_feed.py

+#!/usr/bin/python
+
+import urllib
+import os
+
+from cElementTree import iterparse
+
+import sys
+
+sys.path.insert(0, os.getcwd())
+from yolklib.pypi import CheeseShop
+
+
+PYPI_URL = 'http://www.python.org/pypi?:action=rss'
+
+
+def get_pkg_ver(pv, add_quotes=True):
+    """Return package name and version"""
+    n = len(pv.split())
+    if n == 2:
+        #Normal package_name 1.0
+        pkg_name, ver = pv.split()
+    else:
+        parts = pv.split()
+        ver = parts[-1:]
+        if add_quotes:
+            pkg_name = "'%s'" % " ".join(parts[:-1])
+        else:
+            pkg_name = "%s" % " ".join(parts[:-1])
+    return pkg_name, ver
+
+def test_api(pypi_xml):
+    failed = 0
+    failed_msgs =[]
+    for event, elem in iterparse(pypi_xml):
+        if elem.tag == "title":
+            if not elem.text.startswith('Cheese Shop recent updates'):
+                pkg_name, ver = get_pkg_ver(elem.text, False)
+                (pypi_pkg_name, versions) = PyPI.query_versions_pypi(pkg_name, True)
+                try:
+                    assert versions[0] == ver
+                    print "Testing %s... passed" % elem.text
+                except:
+                    failed += 1
+                    failed_msgs.append("%s %s" % (pkg_name, versions))
+                    print "Testing %s... failed" % elem.text
+
+    print "%s tests failed." % failed
+    for msg in failed_msgs:
+        print "\t%s" % msg
+
+def test_cli(pypi_xml):
+    #Fetch master list of package names so we can use cache
+    os.system('./yolk.py -F')
+    for event, elem in iterparse(pypi_xml):
+        if elem.tag == "title":
+            if not elem.text.startswith('Cheese Shop recent updates'):
+                print "Testing %s..." % elem.text
+                pkg_name, ver = get_pkg_ver(elem.text)
+                os.system('./yolk.py -V %s' % pkg_name)
+                os.system('./yolk.py -C -D %s %s' % (pkg_name, ver))
+            elem.clear()
+
+test_cli(urllib.urlopen(PYPI_URL))
+PyPI = CheeseShop()
+test_api(urllib.urlopen(PYPI_URL))
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+"""
+
+Name: yolk.py
+
+Desc: Command-line tool for listing Python packages installed by setuptools,
+      package metadata, package dependencies, and querying The Cheese Shop
+      (PyPI) for Python package release information.
+
+Author: Rob Cakebread <gentoodev a t gmail.com>
+
+License  : PSF (Python Software Foundation License)
+
+"""
+
+import sys
+import optparse
+import pkg_resources
+import webbrowser
+
+from yolklib import __version__
+from yolklib.metadata import get_metadata
+from yolklib.yolklib import Distributions
+from yolklib.pypi import CheeseShop
+
+
+#Functions for obtaining info about packages installed with setuptools
+##############################################################################
+
+def show_deps(pkg_ver):
+    """Show dependencies for package(s)"""
+
+    try:
+        (pkg_name, ver) = pkg_ver[0].split("=")
+    except ValueError:
+        pkg_name = pkg_ver[0]
+        ver = None
+
+    pkgs = pkg_resources.Environment()
+
+    if not len(pkgs[pkg_name]):
+        print >> sys.stderr, "Can't find package for %s" % pkg_name
+        sys.exit(2)
+
+    for pkg in pkgs[pkg_name]:
+        if not ver:
+            print pkg.project_name, pkg.version
+        #XXX accessing protected member. There's a better way.
+        i = len(pkg._dep_map.values()[0])
+        if i:
+            while i:
+                if not ver or ver and pkg.version == ver:
+                    if ver and i == len(pkg._dep_map.values()[0]):
+                        print pkg.project_name, pkg.version
+                    print "  " + str(pkg._dep_map.values()[0][i - 1])
+                i -= 1
+        else:
+            print >> sys.stderr, \
+                "No dependency information was supplied with the package."
+            sys.exit(2)
+
+def print_metadata(show, metadata, active, show_metadata, fields):
+    """Print out formatted metadata"""
+    version = metadata['Version']
+    #When showing all packages, note which are not active:
+    if show == "all" and not active:
+        active = " *"
+    else:
+        active = ""
+
+    print '%s (%s)%s' % (metadata["Name"], version, active)
+
+    if fields:
+        #Only show specific fields
+        for field in metadata.keys():
+            if field in fields:
+                print "    %s: %s" % (field, metadata[field])
+    elif show_metadata:
+        #Print all available metadata fields
+        for field in metadata.keys():
+            if field != "Name" and field != "Summary":
+                print "    %s: %s" % (field, metadata[field])
+    else:
+        #Default when listing packages
+        print "    %s" % metadata["Summary"]
+    print 
+
+def show_distributions(show, pkg_name, version, show_metadata, fields):
+    """Show list of installed activated OR non-activated packages"""
+    dists = Distributions()
+    results = 0
+    for dist, active in dists.get_distributions(show, pkg_name, version):
+        metadata = get_metadata(dist)
+        print_metadata(show, metadata, active, show_metadata, fields)
+        results += 1
+    if show == "all" and results:
+        print "Versions with '*' are non-active."
+
+#PyPI functions
+##############################################################################
+def get_download_links(package_name, version):
+    """Query PyPI for pkg download URI for a packge"""
+    if version:
+        versions = (version)
+    else:
+        #If they don't specify version, show em all.
+        (package_name, versions) = PYPI.query_versions_pypi(package_name, None)
+    
+    for ver in versions:
+        metadata = PYPI.release_data(package_name, ver)
+        #Try the package's metadata in case there's nothing with release_urls
+        if metadata.has_key('download_url'):
+            print metadata['download_url']
+
+        for urls in PYPI.release_urls(package_name, ver):
+            print urls['url']
+def browse_website(package_name, browser = None):
+    """Launch web browser at project's homepage"""
+    #Get verified name from pypi.
+    (pypi_pkg_name, versions) = PYPI.query_versions_pypi(package_name)
+    if len(versions):
+        metadata = PYPI.release_data(pypi_pkg_name, versions[0])
+        if metadata.has_key("home_page"):
+            print "Launching browser: %s" % metadata['home_page']
+            if browser == 'konqueror':
+                browser = webbrowser.Konqueror()
+            else:
+                browser = webbrowser.get()
+            try:
+                browser.open(metadata['home_page'], 2)
+            except AttributeError:
+                browser.open(metadata['home_page'], 2)
+            return
+
+    print "No homepage URL found."
+
+def show_pkg_metadata_pypi(package_name, version):
+    """Show pkg metadata queried from PyPI"""
+
+    metadata = PYPI.release_data(package_name, version)
+    for key in metadata.keys():
+        print "%s: %s" % (key, metadata[key])
+
+def get_all_versions_pypi(package_name, use_cached_pkglist):
+    """Fetch list of available versions for a package from The Cheese Shop"""
+    (pypi_pkg_name, versions) = PYPI.query_versions_pypi(package_name,
+            use_cached_pkglist)
+
+    #pypi_pkg_name may != package_name; it returns the name with correct case
+    #i.e. You give beautifulsoup but PyPI knows it as BeautifulSoup
+
+    if versions:
+        print_pkg_versions(pypi_pkg_name, versions)
+    else:
+        print >> sys.stderr, "Nothing found on PyPI for %s" % \
+            package_name
+        sys.exit(2)
+
+def pypi_search(spec):
+    """Search PyPI by metadata keyword"""
+    keyword = spec[0]
+    term = " ".join(spec[1:])
+    spec = {}
+    spec[keyword] = term
+    for pkg in PYPI.search(spec): 
+        print "%s (%s):\n    %s\n" % (pkg['name'], pkg['version'], 
+                pkg['summary'])
+
+def get_rss_feed():
+    """Show last 20 package updates from PyPI RSS feed"""
+    rss = PYPI.get_rss()
+    for pkg in rss.keys():
+        print "%s\n    %s\n" % (pkg, rss[pkg])
+
+#Utility functions
+##############################################################################
+
+def parse_pkg_ver(args):
+    """Return tuple with package_name and version from CLI args"""
+
+    version = package = None
+    if len(args) == 1:
+        package = args[0]
+    elif len(args) == 2:
+        package = args[0]
+        version = args[1]
+    return (package, version)
+
+def print_pkg_versions(package_name, versions):
+    """Print list of versions available for a package"""
+
+    for ver in versions:
+        print "%s %s" % (package_name, ver)
+
+def setup_opt_parser():
+    """Setup the optparser"""
+    usage = "usage: %prog [options] <package_name> <version>"
+    opt_parser = optparse.OptionParser(usage=usage)
+    opt_parser.add_option("-v", "--version", action='store_true', dest=
+                         "version", default=False, help=
+                         "Show yolk version and exit.")
+
+    group_local = optparse.OptionGroup(opt_parser, 
+            "Query installed Python packages",
+            "The following options show information about Python packages "
+            "installed by setuptools. Activated packages are normal packages "
+            "on sys.path that can be imported. Non-activated packages need "
+            "'pkg_resources.require()' before they can be imported, such as "
+            "packages installed with 'easy_install --multi-version'")
+
+    group_local.add_option("-l", "--list", action='store_true', dest='all', 
+                         default=False, help=
+                         "List all packages installed by setuptools.")
+
+    group_local.add_option("-a", "--activated", action='store_true', dest=
+                         'active', default=False, help=
+                         "List only activated packages installed by "
+                         "setuptools.")
+
+    group_local.add_option("-n", "--non-activated", action='store_true', 
+                         dest='nonactive', default=False, help=
+                         "List only non-activated packages installed by "
+                         "setuptools.")
+
+    group_local.add_option("-m", "--metadata", action='store_true', dest=
+                         "metadata", default=False, help=
+                         "Show all metadata for packages installed by "
+                         "setuptools (use with -l -a or -n)")
+
+    group_local.add_option("-f", "--fields", action="store", dest="fields", 
+                         default=False, help=
+                         "Show specific metadata fields. "
+                         "(use with -l -a or -n)")
+
+    group_local.add_option("-d", "--depends", action='store_true', dest=
+                         "depends", default=False, help=
+                         "Show dependencies for a package installed by " +
+                         "setuptools if they are available. " +
+                         "(use with -l -a or -n)")
+
+    group_pypi = optparse.OptionGroup(opt_parser, "PyPI (Cheese Shop) options",
+            "The following options query the Python Package Index:")
+
+
+    group_pypi.add_option("-C", "--use-cached-pkglist", action=
+                         'store_true', dest="use_cached_pkglist", 
+                         default=False, help=
+                         "Use cached package list instead of querying PyPI " + 
+                         "(Use -F to force retrieving list.)")
+
+    group_pypi.add_option("-D", "--download-links", action='store_true', 
+                         dest="download_links", default=False, help=
+                         "Show download URL's for package listed on PyPI. ")
+
+    group_pypi.add_option("-F", "--fetch-package-list", action=
+                         'store_true', dest="fetch_package_list", 
+                         default=False, help=
+                         "Fetch and cache list of packages from PyPI.")
+
+    group_pypi.add_option("-H", "--browse-homepage", action=
+                         'store_true', dest="browse_website", 
+                         default=False, help=
+                         "Launch web browser at home page for package.")
+
+    group_pypi.add_option("-L", "--latest", action=
+                         'store_true', dest="rss_feed", 
+                         default=False, help=
+                         "Show last 20 updates on PyPI.")
+
+    group_pypi.add_option("-M", "--query-metadata", action=
+                         'store_true', dest="query_metadata_pypi", 
+                         default=False, help=
+                         "Show metadata for a package listed on PyPI.")
+
+    group_pypi.add_option("-S", "--search", action=
+                         'store_true', dest="search", 
+                         default=False, help=
+                         "Search PyPI by spec and operator.")
+
+    group_pypi.add_option("-V", "--versions-available", action=
+                         'store_true', dest="versions_available", 
+                         default=False, help=
+                         "Show available versions for given package " + 
+                         "listeded on PyPI.")
+    opt_parser.add_option_group(group_local)
+    opt_parser.add_option_group(group_pypi)
+    return opt_parser
+
+
+def main():
+    """Main function"""
+    opt_parser = setup_opt_parser()
+    (options, remaining_args) = opt_parser.parse_args()
+
+    if options.search:
+        if len(remaining_args):
+            pypi_search(remaining_args)
+        else:
+            opt_parser.print_help()
+        sys.exit(0)
+
+    if len(sys.argv) == 1 or len(remaining_args) > 2:
+        opt_parser.print_help()
+        sys.exit(2)
+    (package, version) = parse_pkg_ver(remaining_args)
+
+    if options.version:
+
+        print "Version %s" % __version__.version
+    elif options.depends:
+
+        if not remaining_args:
+            print >> sys.stderr, "I need at least a package name."
+            print >> sys.stderr, \
+                "You can also specify a package name and version:"
+            print >> sys.stderr, "  yolk.py -d kid 0.8"
+            sys.exit(2)
+        show_deps(remaining_args)
+    elif options.all:
+
+        if options.active or options.nonactive:
+            opt_parser.print_help()
+            sys.exit(2)
+        show_distributions("all", package, version, options.metadata,
+                options.fields)
+
+    elif options.active:
+
+        if options.all or options.nonactive:
+            opt_parser.print_help()
+            sys.exit(2)
+        show_distributions("active", package, version, options.metadata,
+                options.fields)
+
+    elif options.nonactive:
+
+        if options.active or options.all:
+            opt_parser.print_help()
+            sys.exit(2)
+        show_distributions("nonactive", package, version, options.metadata,
+                options.fields)
+    elif options.versions_available:
+
+        get_all_versions_pypi(package, options.use_cached_pkglist)
+    elif options.browse_website:
+        browse_website(package)
+
+    elif options.fetch_package_list:
+
+        PYPI.store_pkg_list()
+    elif options.download_links:
+        get_download_links(package, version)
+
+    elif options.rss_feed:
+        get_rss_feed()
+
+    elif options.query_metadata_pypi:
+
+        if version:
+            show_pkg_metadata_pypi(package, version)
+        else:
+
+            #If they don't specify version, show all.
+
+            (package, versions) = PYPI.query_versions_pypi(package, None)
+            for ver in versions:
+                show_pkg_metadata_pypi(package, ver)
+    else:
+        opt_parser.print_help()
+        sys.exit(2)
+
+PYPI = CheeseShop()
+
+if __name__ == "__main__":
+    main()

File yolklib/__init__.py

+
+
+__all__ = ['__version__', 'yolklib', 'metadata', 'pypi']

File yolklib/__version__.py

+version = '0.0.2'

File yolklib/metadata.py

+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+"""
+
+Name     : metadata.py
+
+Author   : Rob Cakebread <gentoodev@gmail.com>
+
+License  : PSF (Python Software Foundation License)
+
+Desc     : Return metadata for Python distribution installed by setuptools
+           in a dict
+
+           Note: The metadata uses RFC 2822-based message documents.
+
+"""
+
+
+import email
+
+
+def get_metadata(dist):
+    """Return dictionary of metadata for given dist"""
+    if not dist.has_metadata('PKG-INFO'):
+        return
+
+    md = dist.get_metadata('PKG-INFO')
+    msg = email.message_from_string(md) 
+    metadata = {}
+    for header in [l for l in msg._headers]:
+        metadata[header[0]] = header[1]
+
+    return metadata
+

File yolklib/pypi.py

+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+
+"""
+
+Name: pypi.py
+
+Desc: Library for getting information about Python packages by querying
+      The CheeseShop (PYPI a.k.a. Python Package Index).
+
+
+Author: Rob Cakebread <gentoodev a t gmail.com>
+
+License  : PSF (Python Software Foundation License)
+
+"""
+
+import xmlrpclib
+import cPickle
+import urllib2
+import os
+
+import __version__
+
+#XXX celementree is part of the stdlib in Python 2.5 but it uses a different
+#namespace. Add check for it before this:
+try:
+    from cElementTree import parse
+except ImportError:
+    from elementtree.ElementTree import parse
+
+PYPI_SERVER = xmlrpclib.Server('http://cheeseshop.python.org/pypi')
+PYPI_URL = 'http://www.python.org/pypi?:action=rss'
+VERSION = __version__.version
+
+
+class CheeseShop:
+
+    """Interface to Python Package Index"""
+
+    def __init__(self):
+
+        self.yolk_dir = self.get_yolk_dir()
+
+    def get_rss(self):
+        """Fetch last 20 package release items from PyPI RSS feed"""
+        rss = {}
+        request = urllib2.Request(PYPI_URL)
+        request.add_header('User-Agent', "yolk/%s (Python-urllib)" % VERSION)
+        root = parse(urllib2.urlopen(request)).getiterator()
+        #XXX It'd be nicer if we did a reverse chronological sort. 
+        for element in root:
+            if element.tag == "title":
+                if not element.text.startswith("Cheese Shop recent updates"):
+                    title = element.text
+            elif element.tag == "description":
+                if element.text:
+                    if not element.text.startswith("Updates to the Python Cheese Shop"):
+                        rss[title] = element.text
+                        element.clear()
+        return rss
+
+    def query_versions_pypi(self, package_name, use_cached_pkglist=None):
+        """Fetch list of available versions for a package from The CheeseShop"""
+
+        versions = self.package_releases(package_name)
+        if not versions:
+
+            #The search failed, maybe they used the wrong case.
+            #Check entire list of packages using case-insensitive search.
+
+            if use_cached_pkglist:
+                package_list = query_cached_package_list()
+            else:
+
+                #Download package list from PYPI
+
+                package_list = self.list_packages()
+            for pypi_pkg in package_list:
+                if pypi_pkg.lower() == package_name.lower():
+                    versions = self.package_releases(pypi_pkg)
+                    package_name = pypi_pkg
+                    break
+        return (package_name, versions)
+
+    def get_yolk_dir(self):
+        """Return location we store config files and data"""
+        app_data_dir = "%s/.yolk" % os.path.expanduser("~")
+        if not os.path.exists(app_data_dir):
+            os.mkdir(app_data_dir)
+        return app_data_dir
+
+    def query_cached_package_list(self):
+        """Return list of pickled package names from PYPI"""
+
+        pickle_file = '%s/package_list.pkl' % self.yolk_dir
+        return cPickle.load(open(pickle_file, "r"))
+
+    def store_pkg_list(self):
+        """Cache master list of package names from PYPI"""
+
+        package_list = self.list_packages()
+        pickle_file = '%s/package_list.pkl' % self.yolk_dir
+        cPickle.dump(package_list, open(pickle_file, "w"))
+
+    def search(self, spec):
+        '''Query PYPI via XMLRPC interface using search spec'''
+        #XXX Allow AND/OR searches.
+        return PYPI_SERVER.search(spec, "and")
+
+    def list_packages(self):
+        """Query PYPI via XMLRPC interface for a a list of all package names"""
+
+        return PYPI_SERVER.list_packages()
+
+    def release_urls(self, package_name, version):
+        """Query PYPI via XMLRPC interface for a pkg's available versions"""
+
+        return PYPI_SERVER.release_urls(package_name, version)
+
+    def release_data(self, package_name, version):
+        """Query PYPI via XMLRPC interface for a pkg's metadata"""
+
+        return PYPI_SERVER.release_data(package_name, version)
+
+    def package_releases(self, package_name):
+        """Query PYPI via XMLRPC interface for a pkg's available versions"""
+
+        return PYPI_SERVER.package_releases(package_name)
+

File yolklib/yolklib.py

+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+
+'''
+
+Name: yolklib.py
+
+Desc: Library for getting information about Python packages installed by
+      setuptools, package metadata, package dependencies, and querying
+      The CheeseShop (PYPI) for Python package release information.
+
+
+Author: Rob Cakebread <gentoodev a t gmail.com>
+
+License  : PSF (Python Software Foundation License)
+
+'''
+
+import pkg_resources
+
+
+
+class Distributions:
+
+    def __init__(self):
+
+        self.environment = pkg_resources.Environment()
+        self.working_set = pkg_resources.WorkingSet()
+
+    def query_activated(self, dist):
+        """Return True if distribution is active"""
+        if dist in self.working_set:
+            return True
+
+    def get_distributions(self, show, pkg_name="", version=""):
+        """List installed packages"""
+        for name, dist in self.get_alpha(show, pkg_name, version):
+            ver = dist.version
+            for p in self.environment[dist.project_name]:
+                if ver == p.version:
+                    if show == "nonactive" and dist not in self.working_set:
+                        yield (dist, self.query_activated(dist))
+                    elif show == "active" and dist in self.working_set:
+                        yield (dist, self.query_activated(dist))
+                    elif show == "all":
+                        yield (dist, self.query_activated(dist))
+
+    def get_alpha(self, show, pkg_name="", version=""):
+        """Return list of alphabetized packages"""
+        alpha_list = []
+        for dist in self.get_packages(show):
+            if pkg_name and dist.project_name != pkg_name:
+                #Only checking for a single package name
+                pass
+            elif version and dist.version != version:
+                #Only checking for a single version of a package
+                pass
+            else:
+                alpha_list.append((dist.project_name + dist.version, dist))
+        alpha_list.sort()
+        return alpha_list
+    
+    def get_packages(self, show):
+        """Return list of Distributions"""
+
+        if show == 'nonactive' or show == "all":
+            all_packages = []
+            for package in self.environment:
+                #There may be multiple versions of same packages
+                for i in range(len(self.environment[package])):
+                    if self.environment[package][i]:
+                        all_packages.append(self.environment[package][i])
+            return all_packages
+        else:
+            # Only activated packages
+            return self.working_set
+