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