[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():