]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wxversion/wxversion.py
DMC fix.
[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 either be
45 a sting or a list of strings. Each string is
46 compared to the installed wxPythons and the best
47 match is added to the sys.path, allowing an 'import
48 wx' to find that version.
49
50 The version string is composed of the dotted
51 version number (at least 2 of the 4 components)
52 optionally followed by hyphen ('-') separated
53 options (wx port, unicode/ansi, flavour, etc.) A
54 match is determined by how much of the installed
55 version matches what is given in the version
56 parameter. If the version number components don't
57 match then the score is zero, otherwise the score
58 is increased for every specified optional component
59 that is specified and that matches.
60 """
61 assert not sys.modules.has_key('wx') and not sys.modules.has_key('wxPython'), \
62 "wxversion.require() must be called before wxPython is imported"
63
64 bestMatch = None
65 bestScore = 0
66 if type(versions) == str:
67 versions = [versions]
68
69 packages = _find_installed()
70 for pkg in packages:
71 for ver in versions:
72 score = pkg.Score(_wxPackageInfo(ver))
73 if score > bestScore:
74 bestMatch = pkg
75 bestScore = score
76
77 assert bestMatch is not None, \
78 "Required version of wxPython not found"
79
80 sys.path.insert(0, bestMatch.pathname)
81
82
83
84
85 _pattern = "wx-[0-9].*"
86 def _find_installed():
87 installed = []
88 for pth in sys.path:
89
90 # empty means to look in the current dir
91 if not pth:
92 pth = '.'
93
94 # skip it if it's not a package dir
95 if not os.path.isdir(pth):
96 continue
97
98 base = os.path.basename(pth)
99
100 # if it's a wx path that's already in the sys.path then skip it
101 if fnmatch.fnmatchcase(base, _pattern):
102 continue
103
104 # now look in the dir for matching subdirs
105 for name in glob.glob(os.path.join(pth, _pattern)):
106 # make sure it's a directory
107 if not os.path.isdir(name):
108 continue
109 # and has a wx subdir
110 if not os.path.exists(os.path.join(name, 'wx')):
111 continue
112 installed.append(_wxPackageInfo(name, True))
113
114 installed.sort()
115 installed.reverse()
116 return installed
117
118
119 class _wxPackageInfo(object):
120 def __init__(self, pathname, stripFirst=False):
121 self.pathname = pathname
122 base = os.path.basename(pathname)
123 segments = base.split('-')
124 if stripFirst:
125 segments = segments[1:]
126 self.version = tuple([int(x) for x in segments[0].split('.')])
127 self.options = segments[1:]
128
129
130 def Score(self, other):
131 score = 0
132 # whatever version components given in other must match exactly
133 if len(self.version) > len(other.version):
134 v = self.version[:len(other.version)]
135 else:
136 v = self.version
137 if v != other.version:
138 return 0
139 score += 1
140 for opt in other.options:
141 if opt in self.options:
142 score += 1
143 return score
144
145
146 # TODO: factor self.options into the sort order?
147 def __lt__(self, other):
148 return self.version < other.version
149 def __gt__(self, other):
150 return self.version > other.version
151 def __eq__(self, other):
152 return self.version == other.version
153
154
155
156
157
158 if __name__ == '__main__':
159 def test(version):
160 savepath = sys.path[:]
161 require(version)
162 print "Asked for %s:\t got: %s" % (version, sys.path[0])
163 sys.path = savepath[:]
164
165
166 # make some test dirs
167 names = ['wx-2.4',
168 'wx-2.5.2',
169 'wx-2.5.2.9-gtk2-unicode',
170 'wx-2.5.2.9-gtk-ansi',
171 'wx-2.5.1',
172 'wx-2.5.2.8-gtk2-unicode',
173 'wx-2.5.3']
174 for name in names:
175 d = os.path.join('/tmp', name)
176 os.mkdir(d)
177 os.mkdir(os.path.join(d, 'wx'))
178
179 # setup sys.path to see those dirs
180 sys.path.append('/tmp')
181
182
183 # now run some tests
184 test("2.4")
185 test("2.5")
186 test("2.5-gtk2")
187 test("2.5.2")
188 test("2.5-ansi")
189 test("2.5-unicode")
190
191 # There isn't a unicode match for this one, but it will give the best
192 # available 2.4. Should it give an error instead? I don't think so...
193 test("2.4-unicode")
194
195 try:
196 # expecting an error on this one
197 test("2.6")
198 except AssertionError:
199 print "Asked for 2.6:\t got: Assertion"
200
201 # Try asking for multiple versions
202 test(["2.6", "2.5.3", "2.5.2-gtk2"])
203
204 # cleanup
205 for name in names:
206 d = os.path.join('/tmp', name)
207 os.rmdir(os.path.join(d, 'wx'))
208 os.rmdir(d)
209
210