]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wxversion/wxversion.py
Rearrainged some bits
[wxWidgets.git] / wxPython / wxversion / wxversion.py
1 #----------------------------------------------------------------------
2 # Name: wxversion
3 # Purpose: Allows a wxPython program to search for alternate
4 # installations of the wxPython packages and modify sys.path
5 # so they will be found when "import wx" is done.
6 #
7 # Author: Robin Dunn
8 #
9 # Created: 24-Sept-2004
10 # RCS-ID: $Id$
11 # Copyright: (c) 2004 by Total Control Software
12 # Licence: wxWindows license
13 #----------------------------------------------------------------------
14
15 """
16 If you have more than one version of wxPython installed this module
17 allows your application to choose which version of wxPython will be
18 imported when it does 'import wx'. You use it like this:
19
20 import wxversion
21 wxversion.require('2.4')
22 import wx
23
24 Of course the default wxPython version can also be controlled by
25 setting PYTHONPATH or by editing the wx.pth path configuration file,
26 but using wxversion will allow an application to manage the version
27 selection itself rather than depend on the user to setup the
28 environment correctly.
29
30 It works by searching the sys.path for directories matching wx-* and
31 then comparing them to what was passed to the require function. If a
32 match is found then that path is inserted into sys.path.
33 """
34
35 import sys, os, glob, fnmatch
36
37
38
39
40 def require(versions):
41 """
42 Search for a wxPython installation that matches version.
43
44 :param version: Specifies the version to look for, it can
45 either be a string or a list of strings. Each
46 string is compared to the installed wxPythons
47 and the best match is inserted into the
48 sys.path, allowing an 'import wx' to find that
49 version.
50
51 The version string is composed of the dotted
52 version number (at least 2 of the 4 components)
53 optionally followed by hyphen ('-') separated
54 options (wx port, unicode/ansi, flavour, etc.) A
55 match is determined by how much of the installed
56 version matches what is given in the version
57 parameter. If the version number components don't
58 match then the score is zero, otherwise the score
59 is increased for every specified optional component
60 that is specified and that matches.
61 """
62 assert not sys.modules.has_key('wx') and not sys.modules.has_key('wxPython'), \
63 "wxversion.require() must be called before wxPython is imported"
64
65 bestMatch = None
66 bestScore = 0
67 if type(versions) == str:
68 versions = [versions]
69
70 packages = _find_installed()
71 for pkg in packages:
72 for ver in versions:
73 score = pkg.Score(_wxPackageInfo(ver))
74 if score > bestScore:
75 bestMatch = pkg
76 bestScore = score
77
78 assert bestMatch is not None, \
79 "Required version of wxPython not found"
80
81 sys.path.insert(0, bestMatch.pathname)
82
83
84
85
86 _pattern = "wx-[0-9].*"
87 def _find_installed():
88 installed = []
89 toRemove = []
90 for pth in sys.path:
91
92 # empty means to look in the current dir
93 if not pth:
94 pth = '.'
95
96 # skip it if it's not a package dir
97 if not os.path.isdir(pth):
98 continue
99
100 base = os.path.basename(pth)
101
102 # if it's a wx path that's already in the sys.path then mark
103 # it for removal and then skip it
104 if fnmatch.fnmatchcase(base, _pattern):
105 toRemove.append(pth)
106 continue
107
108 # now look in the dir for matching subdirs
109 for name in glob.glob(os.path.join(pth, _pattern)):
110 # make sure it's a directory
111 if not os.path.isdir(name):
112 continue
113 # and has a wx subdir
114 if not os.path.exists(os.path.join(name, 'wx')):
115 continue
116 installed.append(_wxPackageInfo(name, True))
117
118 for rem in toRemove:
119 del sys.path[sys.path.index(rem)]
120
121 installed.sort()
122 installed.reverse()
123 return installed
124
125
126 class _wxPackageInfo(object):
127 def __init__(self, pathname, stripFirst=False):
128 self.pathname = pathname
129 base = os.path.basename(pathname)
130 segments = base.split('-')
131 if stripFirst:
132 segments = segments[1:]
133 self.version = tuple([int(x) for x in segments[0].split('.')])
134 self.options = segments[1:]
135
136
137 def Score(self, other):
138 score = 0
139 # whatever version components given in other must match exactly
140 if len(self.version) > len(other.version):
141 v = self.version[:len(other.version)]
142 else:
143 v = self.version
144 if v != other.version:
145 return 0
146 score += 1
147 for opt in other.options:
148 if opt in self.options:
149 score += 1
150 return score
151
152
153 # TODO: factor self.options into the sort order?
154 def __lt__(self, other):
155 return self.version < other.version
156 def __gt__(self, other):
157 return self.version > other.version
158 def __eq__(self, other):
159 return self.version == other.version
160
161
162
163
164
165 if __name__ == '__main__':
166 import pprint
167 def test(version):
168 savepath = sys.path[:]
169 require(version)
170 print "Asked for %s:\t got: %s" % (version, sys.path[0])
171 pprint.pprint(sys.path)
172 print
173 sys.path = savepath[:]
174
175
176 # make some test dirs
177 names = ['wx-2.4',
178 'wx-2.5.2',
179 'wx-2.5.2.9-gtk2-unicode',
180 'wx-2.5.2.9-gtk-ansi',
181 'wx-2.5.1',
182 'wx-2.5.2.8-gtk2-unicode',
183 'wx-2.5.3']
184 for name in names:
185 d = os.path.join('/tmp', name)
186 os.mkdir(d)
187 os.mkdir(os.path.join(d, 'wx'))
188
189 # setup sys.path to see those dirs
190 sys.path.append('/tmp')
191
192
193 # now run some tests
194 test("2.4")
195 test("2.5")
196 test("2.5-gtk2")
197 test("2.5.2")
198 test("2.5-ansi")
199 test("2.5-unicode")
200
201 # There isn't a unicode match for this one, but it will give the best
202 # available 2.4. Should it give an error instead? I don't think so...
203 test("2.4-unicode")
204
205 try:
206 # expecting an error on this one
207 test("2.6")
208 except AssertionError:
209 print "Asked for 2.6:\t got: Assertion"
210
211 # Try asking for multiple versions
212 test(["2.6", "2.5.3", "2.5.2-gtk2"])
213
214 # cleanup
215 for name in names:
216 d = os.path.join('/tmp', name)
217 os.rmdir(os.path.join(d, 'wx'))
218 os.rmdir(d)
219
220