[Python-checkins] python/dist/src/Lib/plat-mac pimp.py, 1.27.4.4, 1.27.4.5

jackjansen at users.sourceforge.net jackjansen at users.sourceforge.net
Fri Jan 7 14:48:56 CET 2005


Update of /cvsroot/python/python/dist/src/Lib/plat-mac
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv6279

Modified Files:
      Tag: release23-maint
	pimp.py 
Log Message:
Backport of 1.38:
Allow relative URLs for included databases and packages.



Index: pimp.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/plat-mac/pimp.py,v
retrieving revision 1.27.4.4
retrieving revision 1.27.4.5
diff -u -d -r1.27.4.4 -r1.27.4.5
--- pimp.py	3 Jan 2005 15:46:30 -0000	1.27.4.4
+++ pimp.py	7 Jan 2005 13:48:53 -0000	1.27.4.5
@@ -1,6 +1,6 @@
 """Package Install Manager for Python.
 
-This is currently a MacOSX-only strawman implementation. 
+This is currently a MacOSX-only strawman implementation.
 Despite other rumours the name stands for "Packman IMPlementation".
 
 Tools to allow easy installation of packages. The idea is that there is
@@ -27,7 +27,7 @@
 import shutil
 import time
 
-__all__ = ["PimpPreferences", "PimpDatabase", "PimpPackage", "main", 
+__all__ = ["PimpPreferences", "PimpDatabase", "PimpPackage", "main",
     "getDefaultDatabase", "PIMP_VERSION", "main"]
 
 _scriptExc_NotInstalled = "pimp._scriptExc_NotInstalled"
@@ -53,12 +53,12 @@
         status = "exp"
     else:
         status = "prod"
-        
+
     major, minor, micro, state, extra = sys.version_info
     pyvers = '%d.%d' % (major, minor)
     if micro == 0 and state != 'final':
         pyvers = pyvers + '%s%d' % (state, extra)
-        
+
     longplatform = distutils.util.get_platform()
     osname, release, machine = longplatform.split('-')
     # For some platforms we may want to differentiate between
@@ -95,7 +95,7 @@
 
 def _cmd(output, dir, *cmditems):
     """Internal routine to run a shell command in a given directory."""
-    
+
     cmd = ("cd \"%s\"; " % dir) + " ".join(cmditems)
     if output:
         output.write("+ %s\n" % cmd)
@@ -113,22 +113,22 @@
 
 class PimpDownloader:
     """Abstract base class - Downloader for archives"""
-    
+
     def __init__(self, argument,
             dir="",
             watcher=None):
         self.argument = argument
         self._dir = dir
         self._watcher = watcher
-                
+
     def download(self, url, filename, output=None):
         return None
-        
+
     def update(self, str):
         if self._watcher:
             return self._watcher.update(str)
         return True
-            
+
 class PimpCurlDownloader(PimpDownloader):
 
     def download(self, url, filename, output=None):
@@ -139,7 +139,7 @@
                     url)
         self.update("Downloading %s: finished" % url)
         return (not exitstatus)
-            
+
 class PimpUrllibDownloader(PimpDownloader):
 
     def download(self, url, filename, output=None):
@@ -151,13 +151,13 @@
             length = long(download.headers['content-length'])
         else:
             length = -1
-        
+
         data = download.read(4096) #read 4K at a time
         dlsize = 0
         lasttime = 0
         while keepgoing:
             dlsize = dlsize + len(data)
-            if len(data) == 0: 
+            if len(data) == 0:
                 #this is our exit condition
                 break
             output.write(data)
@@ -172,12 +172,12 @@
         if keepgoing:
             self.update("Downloading %s: finished" % url)
         return keepgoing
-        
+
 class PimpUnpacker:
     """Abstract base class - Unpacker for archives"""
-    
+
     _can_rename = False
-    
+
     def __init__(self, argument,
             dir="",
             renames=[],
@@ -188,30 +188,30 @@
         self._dir = dir
         self._renames = renames
         self._watcher = watcher
-                
+
     def unpack(self, archive, output=None, package=None):
         return None
-        
+
     def update(self, str):
         if self._watcher:
             return self._watcher.update(str)
         return True
-        
+
 class PimpCommandUnpacker(PimpUnpacker):
     """Unpack archives by calling a Unix utility"""
-    
+
     _can_rename = False
-    
+
     def unpack(self, archive, output=None, package=None):
         cmd = self.argument % archive
         if _cmd(output, self._dir, cmd):
             return "unpack command failed"
-            
+
 class PimpTarUnpacker(PimpUnpacker):
     """Unpack tarfiles using the builtin tarfile module"""
-    
+
     _can_rename = True
-    
+
     def unpack(self, archive, output=None, package=None):
         tf = tarfile.open(archive, "r")
         members = tf.getmembers()
@@ -254,7 +254,7 @@
                 names = package.filterExpectedSkips(names)
             if names:
                 return "Not all files were unpacked: %s" % " ".join(names)
-                        
+
 ARCHIVE_FORMATS = [
     (".tar.Z", PimpTarUnpacker, None),
     (".taz", PimpTarUnpacker, None),
@@ -267,8 +267,8 @@
 class PimpPreferences:
     """Container for per-user preferences, such as the database to use
     and where to install packages."""
-    
-    def __init__(self, 
+
+    def __init__(self,
             flavorOrder=None,
             downloadDir=None,
             buildDir=None,
@@ -288,10 +288,10 @@
         self.buildDir = buildDir
         self.pimpDatabase = pimpDatabase
         self.watcher = None
-        
+
     def setWatcher(self, watcher):
         self.watcher = watcher
-        
+
     def setInstallDir(self, installDir=None):
         if installDir:
             # Installing to non-standard location.
@@ -304,14 +304,14 @@
             installDir = DEFAULT_INSTALLDIR
             self.installLocations = []
         self.installDir = installDir
-        
+
     def isUserInstall(self):
         return self.installDir != DEFAULT_INSTALLDIR
 
     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
         if not os.path.exists(self.downloadDir):
@@ -338,7 +338,7 @@
             else:
                 rv += "Warning: Install directory \"%s\" is not on sys.path\n" % self.installDir
         return rv
-        
+
     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."""
@@ -349,13 +349,13 @@
         if right in self.flavorOrder:
             return 1
         return cmp(left, right)
-        
+
 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 = []
         self.preferences = prefs
@@ -364,23 +364,23 @@
         self._version = ""
         self._maintainer = ""
         self._description = ""
-        
+
     # Accessor functions
     def url(self): return self._url
     def version(self): return self._version
     def maintainer(self): return self._maintainer
     def description(self): return self._description
-        
+
     def close(self):
         """Clean up"""
         self._packages = []
         self.preferences = None
-        
+
     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
         self._urllist.append(url)
@@ -397,23 +397,26 @@
             if not self._version:
                 sys.stderr.write("Warning: database has no Version information\n")
             elif self._version > PIMP_VERSION:
-                sys.stderr.write("Warning: database version %s newer than pimp version %s\n" 
+                sys.stderr.write("Warning: database version %s newer than pimp version %s\n"
                     % (self._version, PIMP_VERSION))
             self._maintainer = plistdata.get('Maintainer', '')
             self._description = plistdata.get('Description', '').strip()
             self._url = url
-        self._appendPackages(plistdata['Packages'])
+        self._appendPackages(plistdata['Packages'], url)
         others = plistdata.get('Include', [])
-        for url in others:
-            self.appendURL(url, included=1)
-        
-    def _appendPackages(self, packages):
+        for o in others:
+            o = urllib.basejoin(url, o)
+            self.appendURL(o, included=1)
+
+    def _appendPackages(self, packages, url):
         """Given a list of dictionaries containing package
         descriptions create the PimpPackage objects and append them
         to our internal storage."""
-        
+
         for p in packages:
             p = dict(p)
+            if p.has_key('Download-URL'):
+                p['Download-URL'] = urllib.basejoin(url, p['Download-URL'])
             flavor = p.get('Flavor')
             if flavor == 'source':
                 pkg = PimpPackage_source(self, p)
@@ -426,27 +429,27 @@
             else:
                 pkg = PimpPackage(self, dict(p))
             self._packages.append(pkg)
-            
+
     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:
             rv.append(pkg.fullname())
         rv.sort()
         return rv
-        
+
     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:
             packages.append(pkg.dump())
@@ -458,15 +461,15 @@
             }
         plist = plistlib.Plist(**plistdata)
         plist.write(pathOrFile)
-        
+
     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
             if ident[0] == '(' and ident[-1] == ')':
@@ -496,7 +499,7 @@
                 if not found or found < p:
                     found = p
         return found
-        
+
 ALLOWED_KEYS = [
     "Name",
     "Version",
@@ -516,7 +519,7 @@
 
 class PimpPackage:
     """Class representing a single package."""
-    
+
     def __init__(self, db, plistdata):
         self._db = db
         name = plistdata["Name"]
@@ -524,10 +527,10 @@
             if not k in ALLOWED_KEYS:
                 sys.stderr.write("Warning: %s: unknown key %s\n" % (name, k))
         self._dict = plistdata
-    
+
     def __getitem__(self, key):
         return self._dict[key]
-        
+
     def name(self): return self._dict['Name']
     def version(self): return self._dict.get('Version')
     def flavor(self): return self._dict.get('Flavor')
@@ -536,13 +539,13 @@
     def homepage(self): return self._dict.get('Home-page')
     def downloadURL(self): return self._dict.get('Download-URL')
     def systemwideOnly(self): return self._dict.get('Systemwide-only')
-    
+
     def fullname(self):
         """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)."""
-        
+
         rv = self._dict['Name']
         if self._dict.has_key('Version'):
             rv = rv + '-%s' % self._dict['Version']
@@ -552,14 +555,14 @@
             # Pseudo-package, show in parentheses
             rv = '(%s)' % rv
         return rv
-    
+
     def dump(self):
         """Return a dict object containing the information on the package."""
         return self._dict
-        
+
     def __cmp__(self, other):
         """Compare two packages, where the "better" package sorts lower."""
-        
+
         if not isinstance(other, PimpPackage):
             return cmp(id(self), id(other))
         if self.name() != other.name():
@@ -567,15 +570,15 @@
         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,
             "OldInstalled": _scriptExc_OldInstalled,
@@ -607,16 +610,16 @@
             sys.stderr.write("-------------------------------------\n")
             return "bad", "Package install test got exception"
         return "yes", ""
-        
+
     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._dict.get('Download-URL'):
             # For pseudo-packages that are already installed we don't
@@ -624,7 +627,7 @@
             status, _  = self.installed()
             if status == "yes":
                 return []
-            return [(None, 
+            return [(None,
                 "Package %s cannot be installed automatically, see the description" %
                     self.fullname())]
         if self.systemwideOnly() and self._db.preferences.isUserInstall():
@@ -650,28 +653,28 @@
                     descr = pkg.shortdescription()
             rv.append((pkg, descr))
         return rv
-            
-        
+
+
     def downloadPackageOnly(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._dict['Download-URL'])
         path = urllib.url2pathname(path)
         filename = os.path.split(path)[1]
-        self.archiveFilename = os.path.join(self._db.preferences.downloadDir, filename)         
+        self.archiveFilename = os.path.join(self._db.preferences.downloadDir, filename)
         if not self._archiveOK():
             if scheme == 'manual':
                 return "Please download package manually and save as %s" % self.archiveFilename
-            downloader = PimpUrllibDownloader(None, self._db.preferences.downloadDir, 
+            downloader = PimpUrllibDownloader(None, self._db.preferences.downloadDir,
                 watcher=self._db.preferences.watcher)
             if not downloader.download(self._dict['Download-URL'],
                     self.archiveFilename, output):
@@ -680,10 +683,10 @@
             return "archive not found after download"
         if not self._archiveOK():
             return "archive does not have correct MD5 checksum"
-            
+
     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
         if not self._dict.get('MD5Sum'):
@@ -692,10 +695,10 @@
         data = open(self.archiveFilename, 'rb').read()
         checksum = md5.new(data).hexdigest()
         return checksum == self._dict['MD5Sum']
-            
+
     def unpackPackageOnly(self, output=None):
         """Unpack a downloaded package archive."""
-        
+
         filename = os.path.split(self.archiveFilename)[1]
         for ext, unpackerClass, arg in ARCHIVE_FORMATS:
             if filename[-len(ext):] == ext:
@@ -703,43 +706,43 @@
         else:
             return "unknown extension for archive file: %s" % filename
         self.basename = filename[:-len(ext)]
-        unpacker = unpackerClass(arg, dir=self._db.preferences.buildDir, 
+        unpacker = unpackerClass(arg, dir=self._db.preferences.buildDir,
                 watcher=self._db.preferences.watcher)
         rv = unpacker.unpack(self.archiveFilename, output=output)
         if rv:
             return rv
-            
+
     def installPackageOnly(self, output=None):
         """Default install method, to be overridden by subclasses"""
         return "%s: This package needs to be installed manually (no support for flavor=\"%s\")" \
             % (self.fullname(), self._dict.get(flavor, ""))
-            
+
     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._dict.get('Download-URL'):
             return "%s: This package needs to be installed manually (no Download-URL field)" % self.fullname()
         msg = self.downloadPackageOnly(output)
         if msg:
             return "%s: download: %s" % (self.fullname(), msg)
-            
+
         msg = self.unpackPackageOnly(output)
         if msg:
             return "%s: unpack: %s" % (self.fullname(), msg)
-            
+
         return self.installPackageOnly(output)
-        
+
     def beforeInstall(self):
         """Bookkeeping before installation: remember what we have in site-packages"""
         self._old_contents = os.listdir(self._db.preferences.installDir)
-        
+
     def afterInstall(self):
         """Bookkeeping after installation: interpret any new .pth files that have
         appeared"""
-                
+
         new_contents = os.listdir(self._db.preferences.installDir)
         for fn in new_contents:
             if fn in self._old_contents:
@@ -762,7 +765,7 @@
                     line = os.path.join(self._db.preferences.installDir, line)
                 line = os.path.realpath(line)
                 if not line in sys.path:
-                    sys.path.append(line)           
+                    sys.path.append(line)
 
     def filterExpectedSkips(self, names):
         """Return a list that contains only unpexpected skips"""
@@ -785,21 +788,21 @@
     def unpackPackageOnly(self, output=None):
         """We don't unpack binary packages until installing"""
         pass
-            
+
     def installPackageOnly(self, output=None):
         """Install a single source package.
-        
+
         If output is given it should be a file-like object and it
         will receive a log of what happened."""
-                    
+
         if self._dict.has_key('Install-command'):
             return "%s: Binary package cannot have Install-command" % self.fullname()
-                    
+
         if self._dict.has_key('Pre-install-command'):
             if _cmd(output, '/tmp', self._dict['Pre-install-command']):
                 return "pre-install %s: running \"%s\" failed" % \
                     (self.fullname(), self._dict['Pre-install-command'])
-                    
+
         self.beforeInstall()
 
         # Install by unpacking
@@ -810,7 +813,7 @@
         else:
             return "%s: unknown extension for archive file: %s" % (self.fullname(), filename)
         self.basename = filename[:-len(ext)]
-        
+
         install_renames = []
         for k, newloc in self._db.preferences.installLocations:
             if not newloc:
@@ -820,22 +823,22 @@
             else:
                 return "%s: Don't know installLocation %s" % (self.fullname(), k)
             install_renames.append((oldloc, newloc))
-                
+
         unpacker = unpackerClass(arg, dir="/", renames=install_renames)
         rv = unpacker.unpack(self.archiveFilename, output=output, package=self)
         if rv:
             return rv
-        
+
         self.afterInstall()
-        
+
         if self._dict.has_key('Post-install-command'):
             if _cmd(output, '/tmp', self._dict['Post-install-command']):
                 return "%s: post-install: running \"%s\" failed" % \
                     (self.fullname(), self._dict['Post-install-command'])
 
         return None
-        
-    
+
+
 class PimpPackage_source(PimpPackage):
 
     def unpackPackageOnly(self, output=None):
@@ -849,15 +852,15 @@
 
     def installPackageOnly(self, output=None):
         """Install a single source package.
-        
+
         If output is given it should be a file-like object and it
         will receive a log of what happened."""
-                    
+
         if self._dict.has_key('Pre-install-command'):
             if _cmd(output, self._buildDirname, self._dict['Pre-install-command']):
                 return "pre-install %s: running \"%s\" failed" % \
                     (self.fullname(), self._dict['Pre-install-command'])
-                    
+
         self.beforeInstall()
         installcmd = self._dict.get('Install-command')
         if installcmd and self._install_renames:
@@ -887,15 +890,15 @@
                 rv = None
             shutil.rmtree(unwanted_install_dir)
             return rv
-        
+
         self.afterInstall()
-        
+
         if self._dict.has_key('Post-install-command'):
             if _cmd(output, self._buildDirname, self._dict['Post-install-command']):
                 return "post-install %s: running \"%s\" failed" % \
                     (self.fullname(), self._dict['Post-install-command'])
         return None
-        
+
 class PimpPackage_installer(PimpPackage):
 
     def unpackPackageOnly(self, output=None):
@@ -927,36 +930,36 @@
         if _cmd(output, "/tmp", installcmd):
             return '%s: install command failed (use verbose for details)' % self.fullname()
         return '%s: downloaded and opened. Install manually and restart Package Manager' % self.archiveFilename
-    
+
 class PimpInstaller:
     """Installer engine: computes dependencies and installs
     packages in the right order."""
-    
+
     def __init__(self, db):
         self._todo = []
         self._db = db
         self._curtodo = []
         self._curmessages = []
-        
+
     def __contains__(self, package):
         return package in self._todo
-        
+
     def _addPackages(self, packages):
         for package in packages:
             if not package in self._todo:
                 self._todo.append(package)
-            
+
     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()
             if status == "yes":
-                return 
+                return
         if package in self._todo or package in self._curtodo:
             return
         self._curtodo.insert(0, package)
@@ -968,17 +971,17 @@
                 self._prepareInstall(pkg, False, recursive)
             else:
                 self._curmessages.append("Problem with dependency: %s" % descr)
-                
+
     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 = []
         self._prepareInstall(package, force, recursive)
@@ -986,10 +989,10 @@
         self._curtodo = []
         self._curmessages = []
         return rv
-        
+
     def install(self, packages, output):
         """Install a list of packages."""
-        
+
         self._addPackages(packages)
         status = []
         for pkg in self._todo:
@@ -997,12 +1000,12 @@
             if msg:
                 status.append(msg)
         return status
-        
-        
-    
+
+
+
 def _run(mode, verbose, force, args, prefargs, watcher):
     """Engine for the main program"""
-    
+
     prefs = PimpPreferences(**prefargs)
     if watcher:
         prefs.setWatcher(watcher)
@@ -1011,7 +1014,7 @@
         sys.stdout.write(rv)
     db = PimpDatabase(prefs)
     db.appendURL(prefs.pimpDatabase)
-    
+
     if mode == 'dump':
         db.dump(sys.stdout)
     elif mode =='list':
@@ -1086,7 +1089,7 @@
 
 def main():
     """Minimal commandline tool to drive pimp."""
-    
+
     import getopt
     def _help():
         print "Usage: pimp [options] -s [package ...]  List installed status"
@@ -1101,12 +1104,12 @@
         print "              (default: %s)" % DEFAULT_INSTALLDIR
         print "       -u url URL for database"
         sys.exit(1)
-        
+
     class _Watcher:
         def update(self, msg):
             sys.stderr.write(msg + '\r')
             return 1
-        
+
     try:
         opts, args = getopt.getopt(sys.argv[1:], "slifvdD:Vu:")
     except getopt.GetoptError:
@@ -1169,8 +1172,6 @@
                 (pimp_update.PIMP_VERSION, PIMP_VERSION))
         else:
             from pimp_update import *
-    
+
 if __name__ == '__main__':
     main()
-    
-    



More information about the Python-checkins mailing list