]> git.saurik.com Git - wxWidgets.git/blob - wxPython/distrib/mac/uninstall_wxPython.py
added missing button state
[wxWidgets.git] / wxPython / distrib / mac / uninstall_wxPython.py
1 #!/usr/bin/env python
2 """
3 This script will search for installed versions of wxPython on OSX and
4 allow the user to choose one to uninstall. It then will use the
5 metadata stored about the installed package to remove all the files
6 associated with that install.
7
8 Only the files installed by the main Installer Package will be
9 removed. This includes the Python modules and the wxWidgets shared
10 libraries. If you also installed the demo or docs by dragging them out
11 of the disk image, then you will need to drag them to the Trash
12 yourself.
13 """
14
15 import sys, os, glob
16 from fnmatch import fnmatchcase
17 import cPickle, urllib
18
19 RCPTDIR = "/Library/Receipts"
20 RSRCDIR = "Contents/Resources"
21
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.
25 PREFIXES = [ '/Library/Python/2.3/',
26 '/Library/Python/2.4/',
27 '/Library/Frameworks/Python.framework/Versions/2.3/lib/python2.3/site-packages/',
28 '/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages/',
29 '/usr/local/lib/',
30 ]
31
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.
35 COMMON_FILES = [ '/usr/local/bin/*',
36 'wx.pth',
37 'wxversion.py',
38 ]
39
40
41
42 class AccessError(Exception):
43 pass
44
45 class ReceiptError(Exception):
46 pass
47
48
49 class InstalledReceipt(object):
50 def __init__(self, rcptPath):
51 self.rcptPath = rcptPath
52 self.rsrcPath = os.path.join(rcptPath, RSRCDIR)
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
59 self.findMetaData()
60
61
62 def findMetaData(self):
63 # TODO: Make this be able to also look at Info.plist files
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
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
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
122
123 def showFiles(self):
124 def show(name):
125 if os.path.exists(name):
126 if not self.lastInstall and self.testCommon(name):
127 return
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):
135 if not self.lastInstall and self.testCommon(name):
136 return
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):
145 if not self.lastInstall and self.testCommon(name):
146 return
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)
153 if hasFiles: # perhaps some stale symlinks, or .pyc files
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
169 def findInstalled():
170 installed = []
171 for name in glob.glob(os.path.join(RCPTDIR, "wxPython*")):
172 try:
173 ir = InstalledReceipt(name)
174 installed.append(ir)
175 except ReceiptError:
176 pass # just skip it...
177
178 return installed
179
180
181 # Just in case a Python < 2.3 is used to run this
182 try:
183 enumerate
184 except NameError:
185 def enumerate(sequence):
186 return zip(range(len(sequence)), sequence)
187
188
189 def 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 " %2d. %-40s %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]
211 inst.lastInstall = len(installed) == 1
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
237 if __name__ == '__main__':
238 main()