[Python-checkins] python/dist/src/Lib/plat-mac pimp.py,1.4,1.5
jackjansen@users.sourceforge.net
jackjansen@users.sourceforge.net
Mon, 10 Feb 2003 07:55:57 -0800
Update of /cvsroot/python/python/dist/src/Lib/plat-mac
In directory sc8-pr-cvs1:/tmp/cvs-serv3675
Modified Files:
pimp.py
Log Message:
Added docstrings.
Index: pimp.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/plat-mac/pimp.py,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** pimp.py 10 Feb 2003 14:19:14 -0000 1.4
--- pimp.py 10 Feb 2003 15:55:51 -0000 1.5
***************
*** 1,2 ****
--- 1,16 ----
+ """Package Install Manager for Python.
+
+ This is currently a MacOSX-only strawman implementation.
+ Motto: "He may be shabby, but he gets you what you need" :-)
+
+ Tools to allow easy installation of packages. The idea is that there is
+ an online XML database per (platform, python-version) containing packages
+ known to work with that combination. This module contains tools for getting
+ and parsing the database, testing whether packages are installed, computing
+ dependencies and installing packages.
+
+ There is a minimal main program that works as a command line tool, but the
+ intention is that the end user will use this through a GUI.
+ """
import sys
import os
***************
*** 7,10 ****
--- 21,26 ----
import md5
+ __all__ = ["PimpPreferences", "PimpDatabase", "PimpPackage", "main"]
+
_scriptExc_NotInstalled = "pimp._scriptExc_NotInstalled"
_scriptExc_OldInstalled = "pimp._scriptExc_OldInstalled"
***************
*** 33,36 ****
--- 49,55 ----
class PimpPreferences:
+ """Container for per-user preferences, such as the database to use
+ and where to install packages"""
+
def __init__(self,
flavorOrder=None,
***************
*** 56,59 ****
--- 75,81 ----
def check(self):
+ """Check that the preferences make sense: directories exist and are
+ writable, the install directory is on sys.path, etc."""
+
rv = ""
RWX_OK = os.R_OK|os.W_OK|os.X_OK
***************
*** 84,87 ****
--- 106,111 ----
def compareFlavors(self, left, right):
+ """Compare two flavor strings. This is part of your preferences
+ because whether the user prefers installing from source or binary is."""
if left in self.flavorOrder:
if right in self.flavorOrder:
***************
*** 93,96 ****
--- 117,125 ----
class PimpDatabase:
+ """Class representing a pimp database. It can actually contain
+ information from multiple databases through inclusion, but the
+ toplevel database is considered the master, as its maintainer is
+ "responsible" for the contents"""
+
def __init__(self, prefs):
self._packages = []
***************
*** 102,105 ****
--- 131,138 ----
def appendURL(self, url, included=0):
+ """Append packages from the database with the given URL.
+ Only the first database should specify included=0, so the
+ global information (maintainer, description) get stored."""
+
if url in self._urllist:
return
***************
*** 112,121 ****
self._maintainer = dict.get('maintainer', '')
self._description = dict.get('description', '')
! self.appendPackages(dict['packages'])
others = dict.get('include', [])
for url in others:
self.appendURL(url, included=1)
! def appendPackages(self, packages):
for p in packages:
pkg = PimpPackage(self, **dict(p))
--- 145,158 ----
self._maintainer = dict.get('maintainer', '')
self._description = dict.get('description', '')
! self._appendPackages(dict['packages'])
others = dict.get('include', [])
for url in others:
self.appendURL(url, included=1)
! def _appendPackages(self, packages):
! """Given a list of dictionaries containing package
! descriptions create the PimpPackage objects and append them
! to our internal storage."""
!
for p in packages:
pkg = PimpPackage(self, **dict(p))
***************
*** 123,129 ****
--- 160,170 ----
def list(self):
+ """Return a list of all PimpPackage objects in the database."""
+
return self._packages
def listnames(self):
+ """Return a list of names of all packages in the database."""
+
rv = []
for pkg in self._packages:
***************
*** 132,135 ****
--- 173,181 ----
def dump(self, pathOrFile):
+ """Dump the contents of the database to an XML .plist file.
+
+ The file can be passed as either a file object or a pathname.
+ All data, including included databases, is dumped."""
+
packages = []
for pkg in self._packages:
***************
*** 145,148 ****
--- 191,201 ----
def find(self, ident):
+ """Find a package. The package can be specified by name
+ or as a dictionary with name, version and flavor entries.
+
+ Only name is obligatory. If there are multiple matches the
+ best one (higher version number, flavors ordered according to
+ users' preference) is returned."""
+
if type(ident) == str:
# Remove ( and ) for pseudo-packages
***************
*** 176,179 ****
--- 229,234 ----
class PimpPackage:
+ """Class representing a single package."""
+
def __init__(self, db, name,
version=None,
***************
*** 201,204 ****
--- 256,260 ----
def dump(self):
+ """Return a dict object containing the information on the package."""
dict = {
'name': self.name,
***************
*** 227,230 ****
--- 283,288 ----
def __cmp__(self, other):
+ """Compare two packages, where the "better" package sorts lower."""
+
if not isinstance(other, PimpPackage):
return cmp(id(self), id(other))
***************
*** 232,239 ****
return cmp(self.name, other.name)
if self.version != other.version:
! return cmp(self.version, other.version)
return self._db.preferences.compareFlavors(self.flavor, other.flavor)
def installed(self):
namespace = {
"NotInstalled": _scriptExc_NotInstalled,
--- 290,304 ----
return cmp(self.name, other.name)
if self.version != other.version:
! return -cmp(self.version, other.version)
return self._db.preferences.compareFlavors(self.flavor, other.flavor)
def installed(self):
+ """Test wheter the package is installed.
+
+ Returns two values: a status indicator which is one of
+ "yes", "no", "old" (an older version is installed) or "bad"
+ (something went wrong during the install test) and a human
+ readable string which may contain more details."""
+
namespace = {
"NotInstalled": _scriptExc_NotInstalled,
***************
*** 260,263 ****
--- 325,336 ----
def prerequisites(self):
+ """Return a list of prerequisites for this package.
+
+ The list contains 2-tuples, of which the first item is either
+ a PimpPackage object or None, and the second is a descriptive
+ string. The first item can be None if this package depends on
+ something that isn't pimp-installable, in which case the descriptive
+ string should tell the user what to do."""
+
rv = []
if not self.downloadURL:
***************
*** 279,282 ****
--- 352,357 ----
def _cmd(self, output, dir, *cmditems):
+ """Internal routine to run a shell command in a given directory."""
+
cmd = ("cd \"%s\"; " % dir) + " ".join(cmditems)
if output:
***************
*** 294,298 ****
return rv
! def downloadSinglePackage(self, output):
scheme, loc, path, query, frag = urlparse.urlsplit(self.downloadURL)
path = urllib.url2pathname(path)
--- 369,384 ----
return rv
! def downloadSinglePackage(self, output=None):
! """Download a single package, if needed.
!
! An MD5 signature is used to determine whether download is needed,
! and to test that we actually downloaded what we expected.
! If output is given it is a file-like object that will receive a log
! of what happens.
!
! If anything unforeseen happened the method returns an error message
! string.
! """
!
scheme, loc, path, query, frag = urlparse.urlsplit(self.downloadURL)
path = urllib.url2pathname(path)
***************
*** 313,316 ****
--- 399,404 ----
def _archiveOK(self):
+ """Test an archive. It should exist and the MD5 checksum should be correct."""
+
if not os.path.exists(self.archiveFilename):
return 0
***************
*** 322,326 ****
return checksum == self._MD5Sum
! def unpackSinglePackage(self, output):
filename = os.path.split(self.archiveFilename)[1]
for ext, cmd in ARCHIVE_FORMATS:
--- 410,416 ----
return checksum == self._MD5Sum
! def unpackSinglePackage(self, output=None):
! """Unpack a downloaded package archive."""
!
filename = os.path.split(self.archiveFilename)[1]
for ext, cmd in ARCHIVE_FORMATS:
***************
*** 338,342 ****
return "no setup.py found after unpack of archive"
! def installSinglePackage(self, output):
if not self.downloadURL:
return "%s: This package needs to be installed manually" % _fmtpackagename(self)
--- 428,437 ----
return "no setup.py found after unpack of archive"
! def installSinglePackage(self, output=None):
! """Download, unpack and install a single package.
!
! If output is given it should be a file-like object and it
! will receive a log of what happened."""
!
if not self.downloadURL:
return "%s: This package needs to be installed manually" % _fmtpackagename(self)
***************
*** 360,363 ****
--- 455,461 ----
class PimpInstaller:
+ """Installer engine: computes dependencies and installs
+ packages in the right order."""
+
def __init__(self, db):
self._todo = []
***************
*** 375,378 ****
--- 473,482 ----
def _prepareInstall(self, package, force=0, recursive=1):
+ """Internal routine, recursive engine for prepareInstall.
+
+ Test whether the package is installed and (if not installed
+ or if force==1) prepend it to the temporary todo list and
+ call ourselves recursively on all prerequisites."""
+
if not force:
status, message = package.installed()
***************
*** 392,395 ****
--- 496,508 ----
def prepareInstall(self, package, force=0, recursive=1):
+ """Prepare installation of a package.
+
+ If the package is already installed and force is false nothing
+ is done. If recursive is true prerequisites are installed first.
+
+ Returns a list of packages (to be passed to install) and a list
+ of messages of any problems encountered.
+ """
+
self._curtodo = []
self._curmessages = []
***************
*** 401,404 ****
--- 514,519 ----
def install(self, packages, output):
+ """Install a list of packages."""
+
self._addPackages(packages)
status = []
***************
*** 411,414 ****
--- 526,534 ----
def _fmtpackagename(dict):
+ """Return the full name "name-version-flavor" of a package.
+
+ If the package is a pseudo-package, something that cannot be
+ installed through pimp, return the name in (parentheses)."""
+
if isinstance(dict, PimpPackage):
dict = dict.dump()
***************
*** 424,427 ****
--- 544,549 ----
def _run(mode, verbose, force, args):
+ """Engine for the main program"""
+
prefs = PimpPreferences()
prefs.check()
***************
*** 496,499 ****
--- 618,623 ----
def main():
+ """Minimal commandline tool to drive pimp."""
+
import getopt
def _help():