]> git.saurik.com Git - wxWidgets.git/blame - wxPython/distrib/mac/uninstall_wxPython.py
added a test to check how exception handling works with Win32 SEH; corrected copyright
[wxWidgets.git] / wxPython / distrib / mac / uninstall_wxPython.py
CommitLineData
85245f48
RD
1#!/usr/bin/env python
2"""
3This script will search for installed versions of wxPython on OSX and
4allow the user to choose one to uninstall. It then will use the
5metadata stored about the installed package to remove all the files
6associated with that install.
7
8Only the files installed by the main Installer Package will be
9removed. This includes the Python modules and the wxWidgets shared
10libraries. If you also installed the demo or docs by dragging them out
11of the disk image, then you will need to drag them to the Trash
12yourself.
13"""
14
15import sys, os, glob
45b32352 16from fnmatch import fnmatchcase
85245f48
RD
17import cPickle, urllib
18
19RCPTDIR = "/Library/Receipts"
20RSRCDIR = "Contents/Resources"
21
45b32352
RD
22# Only completly clean out dirs that have one of these as a prefix.
23# We do this because the file list returned from lsbom will include /,
24# /usr, /usr/local, etc.
85245f48
RD
25PREFIXES = [ '/Library/Python/2.3/',
26 '/Library/Python/2.4/',
45b32352
RD
27 '/Library/Frameworks/Python.framework/Versions/2.3/lib/python2.3/site-pacakges/',
28 '/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-pacakges/',
85245f48
RD
29 '/usr/local/lib/',
30 ]
31
45b32352
RD
32# The files that match one of the items in this list will only be
33# removed if the last installation of wxPython on the system is being
34# uninstalled.
35COMMON_FILES = [ '/usr/local/bin/*',
36 'wx.pth',
37 'wxversion.py',
38 ]
39
40
85245f48
RD
41
42class AccessError(Exception):
43 pass
44
156fa22b
RD
45class ReceiptError(Exception):
46 pass
47
85245f48
RD
48
49class InstalledReceipt(object):
50 def __init__(self, rcptPath):
51 self.rcptPath = rcptPath
52 self.rsrcPath = os.path.join(rcptPath, RSRCDIR)
156fa22b
RD
53 bf = glob.glob(os.path.join(self.rsrcPath, "*.bom"))
54 if bf:
55 self.bomFile = bf[0]
56 else:
57 print "WARNING: Unable to find %s/*.bom" % self.rsrcPath
58 raise ReceiptError
85245f48
RD
59 self.findMetaData()
60
61
62 def findMetaData(self):
63 # TODO: Make this be able to also look at Info.plist files
156fa22b
RD
64 infoFiles = glob.glob(os.path.join(self.rsrcPath, "*.info"))
65 if infoFiles:
66 # there should be only one
67 infoFile = infoFiles[0]
68 self.mdata = {}
69 for line in open(infoFile, "r").readlines():
70 line = line.strip()
71 if line and line[0] != '#':
72 ls = line.split()
73 self.mdata[ls[0]] = line[len(ls[0])+1:]
74 else:
75 print "WARNING: Unable to find %s/*.info" % self.rsrcPath
76 raise ReceiptError
77
85245f48
RD
78
79 def getFileList(self):
80 p = os.popen("lsbom -s %s" % self.bomFile, "r")
81 data = p.read()
82 data.strip()
83 data = filter(lambda s: s!='' and s!='.', data.split('\n'))
84 loc = self.mdata['DefaultLocation']
85 return [loc+item for item in data]
86
87
88 def walkFiles(self, handleFile, handleDir):
89 dirs = []
90 names = self.getFileList()
91
92 # the plain files
93 for name in names:
94 name = os.path.abspath(name)
95 if os.path.isdir(name):
96 dirs.append(name)
97 else:
98 handleFile(name)
99
100 # the directories
101 dirs.reverse()
102 for dir in dirs:
103 for prefix in PREFIXES:
104 if dir.startswith(prefix):
105 handleDir(dir)
106 break
107
108 # Finally, remove the Receipts package, bottom-up
109 for dirpath, dirname, filenames in os.walk(self.rcptPath, False):
110 for name in filenames:
111 name = os.path.join(dirpath, name)
112 handleFile(name)
113 handleDir(dirpath)
114
115
45b32352
RD
116 def testCommon(self, name):
117 for cmn in COMMON_FILES:
118 if fnmatchcase(name, cmn) or fnmatchcase(os.path.basename(name), cmn):
119 return True
120 return False
121
85245f48
RD
122
123 def showFiles(self):
124 def show(name):
125 if os.path.exists(name):
45b32352
RD
126 if not self.lastInstall and self.testCommon(name):
127 return
85245f48
RD
128 print "Will remove:", name
129 self.walkFiles(show, show)
130
131
132 def testUninstallAccess(self):
133 def testFile(name):
134 if os.path.exists(name):
45b32352
RD
135 if not self.lastInstall and self.testCommon(name):
136 return
85245f48
RD
137 if not os.access(name, os.W_OK):
138 raise AccessError(name)
139 self.walkFiles(testFile, testFile)
140
141
142 def doUninstall(self):
143 def removeFile(name):
144 if os.path.exists(name):
45b32352
RD
145 if not self.lastInstall and self.testCommon(name):
146 return
85245f48
RD
147 print "Removing:", name
148 os.unlink(name)
149 def removeDir(name):
150 print "Removing:", name
151 if os.path.exists(name):
152 hasFiles = os.listdir(name)
45b32352 153 if hasFiles: # perhaps some stale symlinks, or .pyc files
85245f48
RD
154 for file in hasFiles:
155 os.unlink(os.path.join(name, file))
156 os.rmdir(name)
157
158 try:
159 self.testUninstallAccess()
160 except AccessError, e:
161 print "UNABLE TO UNINSTALL!\nNo permission to remove: ", e.args[0]
162 sys.exit()
163
164 self.walkFiles(removeFile, removeDir)
165
166
167
168
169def findInstalled():
170 installed = []
171 for name in glob.glob(os.path.join(RCPTDIR, "wxPython*")):
156fa22b
RD
172 try:
173 ir = InstalledReceipt(name)
174 installed.append(ir)
175 except ReceiptError:
176 pass # just skip it...
85245f48
RD
177
178 return installed
179
180
181# Just in case a Python < 2.3 is used to run this
182try:
183 enumerate
184except NameError:
185 def enumerate(sequence):
186 return zip(range(len(sequence)), sequence)
187
188
189def main():
190 if len(sys.argv) > 1 and sys.argv[1] == "-doit":
191 inst = cPickle.loads(urllib.unquote(sys.argv[2]))
192 inst.doUninstall()
193 sys.exit()
194
195 print __doc__
196 installed = findInstalled()
197
198 if not installed:
199 print "*** No wxPython installations found! ***"
200 raw_input("Press RETURN...")
201 sys.exit()
202
203 for i, inst in enumerate(installed):
204 print " %d. %s \t%s" % (i+1, inst.mdata["Title"], inst.mdata["Version"])
205
206 print
207 ans = raw_input("Enter the number of the install to examine or 'Q' to quit: ")
208 if ans in ['Q', 'q']:
209 sys.exit()
210 inst = installed[int(ans) - 1]
45b32352 211 inst.lastInstall = len(installed) == 1
85245f48
RD
212
213 while True:
214 print
215 print """
216 Title: %(Title)s
217 Version: %(Version)s
218 Description: %(Description)s
219 """ % inst.mdata
220
221 ans = raw_input("(U)ninstall, (S)how what will be removed, or (Q)uit? [u,s,q] ")
222 if ans in ['Q', 'q']:
223 sys.exit()
224
225 elif ans in ['S', 's']:
226 inst.showFiles()
227
228 elif ans in ['U', 'u']:
229 print
230 print "Launching uninstaller with sudo, please enter your password if prompted:"
231 os.system("sudo %s -doit %s" %
232 (sys.argv[0],
233 urllib.quote(cPickle.dumps(inst))))
234 sys.exit()
235
236
237if __name__ == '__main__':
238 main()