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

jackjansen@users.sourceforge.net jackjansen@users.sourceforge.net
Tue, 15 Apr 2003 07:43:13 -0700


Update of /cvsroot/python/python/dist/src/Lib/plat-mac
In directory sc8-pr-cvs1:/tmp/cvs-serv13694

Modified Files:
	pimp.py 
Log Message:
- Use the tarfile module to unpack tarfiles.
- Allow setting the destination install directory. If this is set then
  it is used for the modules, other items (header files, etc) are not
  installed, and warnings are printed if the package would have liked to.

Unfortunaltey binary installs seem broken due to a tarfile bug (#721871)
or my misunderstanding of how tarfile works.


Index: pimp.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/plat-mac/pimp.py,v
retrieving revision 1.15
retrieving revision 1.16
diff -C2 -d -r1.15 -r1.16
*** pimp.py	9 Apr 2003 13:25:43 -0000	1.15
--- pimp.py	15 Apr 2003 14:43:05 -0000	1.16
***************
*** 23,26 ****
--- 23,29 ----
  import distutils.sysconfig
  import md5
+ import tarfile
+ import tempfile
+ import shutil
  
  __all__ = ["PimpPreferences", "PimpDatabase", "PimpPackage", "main"]
***************
*** 43,53 ****
  DEFAULT_PIMPDATABASE="http://www.cwi.nl/~jack/pimp/pimp-%s.plist" % distutils.util.get_platform()
  
  ARCHIVE_FORMATS = [
!     (".tar.Z", "zcat \"%s\" | tar -xf -"),
!     (".taz", "zcat \"%s\" | tar -xf -"),
!     (".tar.gz", "zcat \"%s\" | tar -xf -"),
!     (".tgz", "zcat \"%s\" | tar -xf -"),
!     (".tar.bz", "bzcat \"%s\" | tar -xf -"),
!     (".zip", "unzip \"%s\""),
  ]
  
--- 46,144 ----
  DEFAULT_PIMPDATABASE="http://www.cwi.nl/~jack/pimp/pimp-%s.plist" % distutils.util.get_platform()
  
+ 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)
+     if NO_EXECUTE:
+         return 0
+     child = popen2.Popen4(cmd)
+     child.tochild.close()
+     while 1:
+         line = child.fromchild.readline()
+         if not line:
+             break
+         if output:
+             output.write(line)
+     return child.wait()
+ 
+ class PimpUnpacker:
+     """Abstract base class - Unpacker for archives"""
+     
+     _can_rename = False
+     
+     def __init__(self, argument,
+             dir="",
+             renames=[]):
+         self.argument = argument
+         if renames and not self._can_rename:
+             raise RuntimeError, "This unpacker cannot rename files"
+         self._dir = dir
+         self._renames = renames
+                 
+     def unpack(self, archive, output=None):
+         return None
+         
+ class PimpCommandUnpacker(PimpUnpacker):
+     """Unpack archives by calling a Unix utility"""
+     
+     _can_rename = False
+     
+     def unpack(self, archive, output=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):
+         tf = tarfile.open(archive, "r")
+         members = tf.getmembers()
+         skip = []
+         if self._renames:
+             for member in members:
+                 for oldprefix, newprefix in self._renames:
+                     if oldprefix[:len(self._dir)] == self._dir:
+                         oldprefix2 = oldprefix[len(self._dir):]
+                     else:
+                         oldprefix2 = None
+                     if member.name[:len(oldprefix)] == oldprefix:
+                         if newprefix is None:
+                             skip.append(member)
+                             #print 'SKIP', member.name
+                         else:
+                             member.name = newprefix + member.name[len(oldprefix):]
+                             print '    ', member.name
+                         break
+                     elif oldprefix2 and member.name[:len(oldprefix2)] == oldprefix2:
+                         if newprefix is None:
+                             skip.append(member)
+                             #print 'SKIP', member.name
+                         else:
+                             member.name = newprefix + member.name[len(oldprefix2):]
+                             #print '    ', member.name
+                         break
+                 else:
+                     skip.append(member)
+                     #print '????', member.name
+         for member in members:
+             if member in skip:
+                 continue
+             tf.extract(member, self._dir)
+         if skip:
+             names = [member.name for member in skip if member.name[-1] != '/']
+             return "Not all files were unpacked: %s" % " ".join(names)
+         
  ARCHIVE_FORMATS = [
!     (".tar.Z", PimpTarUnpacker, None),
!     (".taz", PimpTarUnpacker, None),
!     (".tar.gz", PimpTarUnpacker, None),
!     (".tgz", PimpTarUnpacker, None),
!     (".tar.bz", PimpTarUnpacker, None),
!     (".zip", PimpCommandUnpacker, "unzip \"%s\""),
  ]
  
***************
*** 68,75 ****
          if not buildDir:
              buildDir = DEFAULT_BUILDDIR
-         if not installDir:
-             installDir = DEFAULT_INSTALLDIR
          if not pimpDatabase:
              pimpDatabase = DEFAULT_PIMPDATABASE
          self.flavorOrder = flavorOrder
          self.downloadDir = downloadDir
--- 159,174 ----
          if not buildDir:
              buildDir = DEFAULT_BUILDDIR
          if not pimpDatabase:
              pimpDatabase = DEFAULT_PIMPDATABASE
+         if installDir:
+             # Installing to non-standard location.
+             self.installLocations = [
+                 ('--install-lib', installDir),
+                 ('--install-headers', None),
+                 ('--install-scripts', None),
+                 ('--install-data', None)]
+         else:
+             installDir = DEFAULT_INSTALLDIR
+             self.installLocations = []
          self.flavorOrder = flavorOrder
          self.downloadDir = downloadDir
***************
*** 107,111 ****
              else:
                  rv += "Warning: Install directory \"%s\" is not on sys.path\n" % self.installDir
!         return rv           
          
      def compareFlavors(self, left, right):
--- 206,210 ----
              else:
                  rv += "Warning: Install directory \"%s\" is not on sys.path\n" % self.installDir
!         return rv
          
      def compareFlavors(self, left, right):
***************
*** 389,409 ****
          return rv
              
-     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:
-             output.write("+ %s\n" % cmd)
-         if NO_EXECUTE:
-             return 0
-         child = popen2.Popen4(cmd)
-         child.tochild.close()
-         while 1:
-             line = child.fromchild.readline()
-             if not line:
-                 break
-             if output:
-                 output.write(line)
-         return child.wait()
          
      def downloadPackageOnly(self, output=None):
--- 488,491 ----
***************
*** 426,430 ****
              if scheme == 'manual':
                  return "Please download package manually and save as %s" % self.archiveFilename
!             if self._cmd(output, self._db.preferences.downloadDir,
                      "curl",
                      "--output", self.archiveFilename,
--- 508,512 ----
              if scheme == 'manual':
                  return "Please download package manually and save as %s" % self.archiveFilename
!             if _cmd(output, self._db.preferences.downloadDir,
                      "curl",
                      "--output", self.archiveFilename,
***************
*** 452,456 ****
          
          filename = os.path.split(self.archiveFilename)[1]
!         for ext, cmd in ARCHIVE_FORMATS:
              if filename[-len(ext):] == ext:
                  break
--- 534,538 ----
          
          filename = os.path.split(self.archiveFilename)[1]
!         for ext, unpackerClass, arg in ARCHIVE_FORMATS:
              if filename[-len(ext):] == ext:
                  break
***************
*** 458,464 ****
              return "unknown extension for archive file: %s" % filename
          self.basename = filename[:-len(ext)]
!         cmd = cmd % self.archiveFilename
!         if self._cmd(output, self._db.preferences.buildDir, cmd):
!             return "unpack command failed"
              
      def installPackageOnly(self, output=None):
--- 540,547 ----
              return "unknown extension for archive file: %s" % filename
          self.basename = filename[:-len(ext)]
!         unpacker = unpackerClass(arg, dir=self._db.preferences.buildDir)
!         rv = unpacker.unpack(self.archiveFilename, output=output)
!         if rv:
!             return rv
              
      def installPackageOnly(self, output=None):
***************
*** 528,540 ****
          If output is given it should be a file-like object and it
          will receive a log of what happened."""
-         print 'PimpPackage_binary installPackageOnly'
                      
-         msgs = []
-         if self._dict.has_key('Pre-install-command'):
-             msg.append("%s: Pre-install-command ignored" % self.fullname())
          if self._dict.has_key('Install-command'):
!             msgs.append("%s: Install-command ignored" % self.fullname())
!         if self._dict.has_key('Post-install-command'):
!             msgs.append("%s: Post-install-command ignored" % self.fullname())
                      
          self.beforeInstall()
--- 611,622 ----
          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, self._buildDirname, self._dict['Pre-install-command']):
!                 return "pre-install %s: running \"%s\" failed" % \
!                     (self.fullname(), self._dict['Pre-install-command'])
                      
          self.beforeInstall()
***************
*** 542,562 ****
          # Install by unpacking
          filename = os.path.split(self.archiveFilename)[1]
!         for ext, cmd in ARCHIVE_FORMATS:
              if filename[-len(ext):] == ext:
                  break
          else:
!             return "unknown extension for archive file: %s" % filename
          
!         # Extract the files in the root folder.
!         cmd = cmd % self.archiveFilename
!         if self._cmd(output, "/", cmd):
!             return "unpack command failed"
          
          self.afterInstall()
          
          if self._dict.has_key('Post-install-command'):
!             if self._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
          
--- 624,656 ----
          # Install by unpacking
          filename = os.path.split(self.archiveFilename)[1]
!         for ext, unpackerClass, arg in ARCHIVE_FORMATS:
              if filename[-len(ext):] == ext:
                  break
          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:
!                 continue
!             if k == "--install-lib":
!                 oldloc = DEFAULT_INSTALLDIR
!             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)
!         if rv:
!             return rv
          
          self.afterInstall()
          
          if self._dict.has_key('Post-install-command'):
!             if _cmd(output, self._buildDirname, self._dict['Post-install-command']):
!                 return "%s: post-install: running \"%s\" failed" % \
                      (self.fullname(), self._dict['Post-install-command'])
+ 
          return None
          
***************
*** 580,584 ****
                      
          if self._dict.has_key('Pre-install-command'):
!             if self._cmd(output, self._buildDirname, self._dict['Pre-install-command']):
                  return "pre-install %s: running \"%s\" failed" % \
                      (self.fullname(), self._dict['Pre-install-command'])
--- 674,678 ----
                      
          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'])
***************
*** 586,599 ****
          self.beforeInstall()
          installcmd = self._dict.get('Install-command')
          if not installcmd:
!             installcmd = '"%s" setup.py install' % sys.executable
!         if self._cmd(output, self._buildDirname, installcmd):
              return "install %s: running \"%s\" failed" % \
                  (self.fullname(), installcmd)
          
          self.afterInstall()
          
          if self._dict.has_key('Post-install-command'):
!             if self._cmd(output, self._buildDirname, self._dict['Post-install-command']):
                  return "post-install %s: running \"%s\" failed" % \
                      (self.fullname(), self._dict['Post-install-command'])
--- 680,715 ----
          self.beforeInstall()
          installcmd = self._dict.get('Install-command')
+         if installcmd and self._install_renames:
+             return "Package has install-command and can only be installed to standard location"
+         # This is the "bit-bucket" for installations: everything we don't
+         # want. After installation we check that it is actually empty
+         unwanted_install_dir = None
          if not installcmd:
!             extra_args = ""
!             for k, v in self._db.preferences.installLocations:
!                 if not v:
!                     # We don't want these files installed. Send them
!                     # to the bit-bucket.
!                     if not unwanted_install_dir:
!                         unwanted_install_dir = tempfile.mkdtemp()
!                     v = unwanted_install_dir
!                 extra_args = extra_args + " %s \"%s\"" % (k, v)
!             installcmd = '"%s" setup.py install %s' % (sys.executable, extra_args)
!         if _cmd(output, self._buildDirname, installcmd):
              return "install %s: running \"%s\" failed" % \
                  (self.fullname(), installcmd)
+         if unwanted_install_dir and os.path.exists(unwanted_install_dir):
+             unwanted_files = os.listdir(unwanted_install_dir)
+             if unwanted_files:
+                 rv = "Warning: some files were not installed: %s" % " ".join(unwanted_files)
+             else:
+                 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'])
***************
*** 673,681 ****
          
      
! def _run(mode, verbose, force, args):
      """Engine for the main program"""
      
!     prefs = PimpPreferences()
!     prefs.check()
      db = PimpDatabase(prefs)
      db.appendURL(prefs.pimpDatabase)
--- 789,799 ----
          
      
! def _run(mode, verbose, force, args, prefargs):
      """Engine for the main program"""
      
!     prefs = PimpPreferences(**prefargs)
!     rv = prefs.check()
!     if rv:
!         sys.stdout.write(rv)
      db = PimpDatabase(prefs)
      db.appendURL(prefs.pimpDatabase)
***************
*** 698,702 ****
              if verbose:
                  print "\tHome page:\t", pkg.homepage()
!                 print "\tDownload URL:\t", pkg.downloadURL()
      elif mode =='status':
          if not args:
--- 816,823 ----
              if verbose:
                  print "\tHome page:\t", pkg.homepage()
!                 try:
!                     print "\tDownload URL:\t", pkg.downloadURL()
!                 except KeyError:
!                     pass
      elif mode =='status':
          if not args:
***************
*** 752,766 ****
      import getopt
      def _help():
!         print "Usage: pimp [-v] -s [package ...]  List installed status"
!         print "       pimp [-v] -l [package ...]  Show package information"
!         print "       pimp [-vf] -i package ...   Install packages"
!         print "       pimp -d                     Dump database to stdout"
          print "Options:"
!         print "       -v  Verbose"
!         print "       -f  Force installation"
          sys.exit(1)
          
      try:
!         opts, args = getopt.getopt(sys.argv[1:], "slifvd")
      except getopt.Error:
          _help()
--- 873,888 ----
      import getopt
      def _help():
!         print "Usage: pimp [options] -s [package ...]  List installed status"
!         print "       pimp [options] -l [package ...]  Show package information"
!         print "       pimp [options] -i package ...    Install packages"
!         print "       pimp -d                          Dump database to stdout"
          print "Options:"
!         print "       -v     Verbose"
!         print "       -f     Force installation"
!         print "       -D dir Set destination directory (default: site-packages)"
          sys.exit(1)
          
      try:
!         opts, args = getopt.getopt(sys.argv[1:], "slifvdD:")
      except getopt.Error:
          _help()
***************
*** 770,773 ****
--- 892,896 ----
      force = 0
      verbose = 0
+     prefargs = {}
      for o, a in opts:
          if o == '-s':
***************
*** 789,795 ****
          if o == '-v':
              verbose = 1
      if not mode:
          _help()
!     _run(mode, verbose, force, args)
                  
  if __name__ == '__main__':
--- 912,920 ----
          if o == '-v':
              verbose = 1
+         if o == '-D':
+             prefargs['installDir'] = a
      if not mode:
          _help()
!     _run(mode, verbose, force, args, prefargs)
                  
  if __name__ == '__main__':