[Distutils] Generic uninstall command

M.-A. Lemburg mal@lemburg.com
Fri Feb 16 11:50:03 2001


Here is a different uninstall command which works by checking
.get_outputs(). Rene already hinted towards this idea -- I found
it while looking at the install.py source code (there's even a
flag to write a list of these files somewhere...).

This command should work with all install commands which adhere to
the .get_outputs() logic, meaning that this API should return
a list of absolute filenames of all files which the command wrote
to the file system.

The directory removal is a bit flaky, but works great on systems
which do not allow the removal of directories which aren't empty.
Unfortunately, the .get_outputs() method does not support adding
directories to the list (at least the code using that method
doesn't), so there doesn't seem to be a more elegant way...

#
# Uninstall command
#

class mx_uninstall(Command):

    description = "uninstall the package"

    user_options = []

    def initialize_options(self):
        pass

    def finalize_options(self):
        pass
        
    def run(self):

        # Execute build
        self.announce('determining installation files')
        savevalue = self.distribution.dry_run
        self.distribution.dry_run = 0
        self.run_command('build')

        # Execute install in dry-run mode
        self.distribution.dry_run = 1
        self.run_command('install')
        self.distribution.dry_run = savevalue
        build = self.get_finalized_command('build')
        install = self.get_finalized_command('install')

        # Remove all installed files
        self.announce("removing files")
        dirs = {}
        filenames = install.get_outputs()
        for filename in filenames:
            if not os.path.isabs(filename):
                raise DistutilsError,\
                      'filename "%s" from .get_output() not absolute' % \
                      filename

            if os.path.isfile(filename):
                self.announce("removing '%s'" % filename)
                if not self.dry_run:
                    try:
                        os.remove(filename)
                    except OSError, details:
                        self.warn("Could not remove file: %s" % details)
                    dir = os.path.split(filename)[0]
                    if not dirs.has_key(dir):
                        dirs[dir] = 1
                    if os.path.splitext(filename)[1] == '.py':
                        # Try to remove .pyc and .pyo files also
                        try:
                            os.remove(filename + 'c')
                        except OSError:
                            pass
                        try:
                            os.remove(filename + 'o')
                        except OSError:
                            pass

            elif os.path.isdir(filename):
                if not dirs.has_key(dir):
                    dirs[filename] = 1

            else:
                self.announce("skipping removal of '%s' (not found)" %
                              filename)

        # Remove the installation directories
        self.announce("removing directories")
        dirs = dirs.keys()
        dirs.sort(); dirs.reverse() # sort descending
        for dir in dirs:
            self.announce("removing directory '%s'" % dir)
            if not self.dry_run:
                try:
                    os.rmdir(dir)
                except OSError, details:
                    self.warn("could not remove directory: %s" % details)

-- 
Marc-Andre Lemburg
______________________________________________________________________
Company:                                        http://www.egenix.com/
Consulting:                                    http://www.lemburg.com/
Python Pages:                           http://www.lemburg.com/python/