]> git.saurik.com Git - wxWidgets.git/blame - wxPython/wxversion/wxversion.py
Minor manual fixes.
[wxWidgets.git] / wxPython / wxversion / wxversion.py
CommitLineData
d48c1c64
RD
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"""
16If you have more than one version of wxPython installed this module
17allows your application to choose which version of wxPython will be
18imported when it does 'import wx'. You use it like this:
19
20 import wxversion
21 wxversion.require('2.4')
22 import wx
23
24Of course the default wxPython version can also be controlled by
25setting PYTHONPATH or by editing the wx.pth path configuration file,
26but using wxversion will allow an application to manage the version
27selection itself rather than depend on the user to setup the
28environment correctly.
29
30It works by searching the sys.path for directories matching wx-* and
31then comparing them to what was passed to the require function. If a
32match is found then that path is inserted into sys.path.
33"""
34
35import sys, os, glob, fnmatch
36
37
38
39
40def require(versions):
41 """
42 Search for a wxPython installation that matches version.
43
93ba536a
RD
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.
d48c1c64
RD
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].*"
87def _find_installed():
88 installed = []
17f3e530 89 toRemove = []
d48c1c64
RD
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
17f3e530
RD
102 # if it's a wx path that's already in the sys.path then mark
103 # it for removal and then skip it
d48c1c64 104 if fnmatch.fnmatchcase(base, _pattern):
17f3e530 105 toRemove.append(pth)
d48c1c64
RD
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
17f3e530
RD
118 for rem in toRemove:
119 del sys.path[sys.path.index(rem)]
120
d48c1c64
RD
121 installed.sort()
122 installed.reverse()
123 return installed
124
125
126class _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
165if __name__ == '__main__':
17f3e530 166 import pprint
d48c1c64
RD
167 def test(version):
168 savepath = sys.path[:]
169 require(version)
170 print "Asked for %s:\t got: %s" % (version, sys.path[0])
17f3e530
RD
171 pprint.pprint(sys.path)
172 print
d48c1c64
RD
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