+#!/usr/bin/env python
+"""
+This script will search for installed versions of wxPython on OSX and
+allow the user to choose one to uninstall. It then will use the
+metadata stored about the installed package to remove all the files
+associated with that install.
+
+Only the files installed by the main Installer Package will be
+removed. This includes the Python modules and the wxWidgets shared
+libraries. If you also installed the demo or docs by dragging them out
+of the disk image, then you will need to drag them to the Trash
+yourself.
+"""
+
+import sys, os, glob
+import cPickle, urllib
+
+RCPTDIR = "/Library/Receipts"
+RSRCDIR = "Contents/Resources"
+
+# only clean up dirs that have one of these as a prefix. We do this
+# because the file list returned from lsbom will include /, /usr,
+# /usr/local, etc.
+PREFIXES = [ '/Library/Python/2.3/',
+ '/Library/Python/2.4/',
+ '/usr/local/lib/',
+ ]
+
+
+class AccessError(Exception):
+ pass
+
+
+class InstalledReceipt(object):
+ def __init__(self, rcptPath):
+ self.rcptPath = rcptPath
+ self.rsrcPath = os.path.join(rcptPath, RSRCDIR)
+ self.bomFile = glob.glob(os.path.join(self.rsrcPath, "*.bom"))[0]
+ self.findMetaData()
+
+
+ def findMetaData(self):
+ # TODO: Make this be able to also look at Info.plist files
+ infoFile = glob.glob(os.path.join(self.rsrcPath, "*.info"))[0]
+ self.mdata = {}
+ for line in open(infoFile, "r").readlines():
+ line = line.strip()
+ if line and line[0] != '#':
+ ls = line.split()
+ self.mdata[ls[0]] = line[len(ls[0])+1:]
+
+
+ def getFileList(self):
+ p = os.popen("lsbom -s %s" % self.bomFile, "r")
+ data = p.read()
+ data.strip()
+ data = filter(lambda s: s!='' and s!='.', data.split('\n'))
+ loc = self.mdata['DefaultLocation']
+ return [loc+item for item in data]
+
+
+ def walkFiles(self, handleFile, handleDir):
+ dirs = []
+ names = self.getFileList()
+
+ # the plain files
+ for name in names:
+ name = os.path.abspath(name)
+ if os.path.isdir(name):
+ dirs.append(name)
+ else:
+ handleFile(name)
+
+ # the directories
+ dirs.reverse()
+ for dir in dirs:
+ for prefix in PREFIXES:
+ if dir.startswith(prefix):
+ handleDir(dir)
+ break
+
+ # Finally, remove the Receipts package, bottom-up
+ for dirpath, dirname, filenames in os.walk(self.rcptPath, False):
+ for name in filenames:
+ name = os.path.join(dirpath, name)
+ handleFile(name)
+ handleDir(dirpath)
+
+
+
+ def showFiles(self):
+ def show(name):
+ if os.path.exists(name):
+ print "Will remove:", name
+ self.walkFiles(show, show)
+
+
+ def testUninstallAccess(self):
+ def testFile(name):
+ if os.path.exists(name):
+ if not os.access(name, os.W_OK):
+ raise AccessError(name)
+ self.walkFiles(testFile, testFile)
+
+
+ def doUninstall(self):
+ def removeFile(name):
+ if os.path.exists(name):
+ print "Removing:", name
+ os.unlink(name)
+ def removeDir(name):
+ print "Removing:", name
+ if os.path.exists(name):
+ hasFiles = os.listdir(name)
+ if hasFiles: # perhaps some left over symlinks, or .pyc files
+ for file in hasFiles:
+ os.unlink(os.path.join(name, file))
+ os.rmdir(name)
+
+ try:
+ self.testUninstallAccess()
+ except AccessError, e:
+ print "UNABLE TO UNINSTALL!\nNo permission to remove: ", e.args[0]
+ sys.exit()
+
+ self.walkFiles(removeFile, removeDir)
+
+
+
+
+def findInstalled():
+ installed = []
+ for name in glob.glob(os.path.join(RCPTDIR, "wxPython*")):
+ ir = InstalledReceipt(name)
+ installed.append(ir)
+
+ return installed
+
+
+# Just in case a Python < 2.3 is used to run this
+try:
+ enumerate
+except NameError:
+ def enumerate(sequence):
+ return zip(range(len(sequence)), sequence)
+
+
+def main():
+ if len(sys.argv) > 1 and sys.argv[1] == "-doit":
+ inst = cPickle.loads(urllib.unquote(sys.argv[2]))
+ inst.doUninstall()
+ sys.exit()
+
+ print __doc__
+ installed = findInstalled()
+
+ if not installed:
+ print "*** No wxPython installations found! ***"
+ raw_input("Press RETURN...")
+ sys.exit()
+
+ for i, inst in enumerate(installed):
+ print " %d. %s \t%s" % (i+1, inst.mdata["Title"], inst.mdata["Version"])
+
+ print
+ ans = raw_input("Enter the number of the install to examine or 'Q' to quit: ")
+ if ans in ['Q', 'q']:
+ sys.exit()
+ inst = installed[int(ans) - 1]
+
+ while True:
+ print
+ print """
+ Title: %(Title)s
+ Version: %(Version)s
+ Description: %(Description)s
+ """ % inst.mdata
+
+ ans = raw_input("(U)ninstall, (S)how what will be removed, or (Q)uit? [u,s,q] ")
+ if ans in ['Q', 'q']:
+ sys.exit()
+
+ elif ans in ['S', 's']:
+ inst.showFiles()
+
+ elif ans in ['U', 'u']:
+ print
+ print "Launching uninstaller with sudo, please enter your password if prompted:"
+ os.system("sudo %s -doit %s" %
+ (sys.argv[0],
+ urllib.quote(cPickle.dumps(inst))))
+ sys.exit()
+
+
+if __name__ == '__main__':
+ main()