]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wxversion/wxversion.py
   1 #---------------------------------------------------------------------- 
   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. 
   9 # Created:     24-Sept-2004 
  11 # Copyright:   (c) 2004 by Total Control Software 
  12 # Licence:     wxWindows license 
  13 #---------------------------------------------------------------------- 
  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:: 
  21     wxversion.select('2.4') 
  24 Or additional build options can also be selected, like this:: 
  27     wxversion.select('2.5.3-unicode') 
  30 Of course the default wxPython version can also be controlled by 
  31 setting PYTHONPATH or by editing the wx.pth path configuration file, 
  32 but using wxversion will allow an application to manage the version 
  33 selection itself rather than depend on the user to setup the 
  34 environment correctly. 
  36 It works by searching the sys.path for directories matching wx-* and 
  37 then comparing them to what was passed to the select function.  If a 
  38 match is found then that path is inserted into sys.path. 
  40 NOTE: If you are making a 'bundle' of your application with a tool 
  41 like py2exe then you should *not* use the wxversion module since it 
  42 looks at the filesystem for the directories on sys.path, it will fail 
  43 in a bundled environment.  Instead you should simply ensure that the 
  44 version of wxPython that you want is found by default on the sys.path 
  45 when making the bundled version by setting PYTHONPATH.  Then that 
  46 version will be included in your bundle and your app will work as 
  47 expected.  Py2exe and the others usually have a way to tell at runtime 
  48 if they are running from a bundle or running raw, so you can check 
  49 that and only use wxversion if needed.  For example, for py2exe:: 
  51     if not hasattr(sys, 'frozen'): 
  53         wxversion.select('2.5') 
  56 More documentation on wxversion and multi-version installs can be 
  57 found at: http://wiki.wxpython.org/index.cgi/MultiVersionInstalls 
  61 import sys
, os
, glob
, fnmatch
 
  65 class VersionError(Exception): 
  68 #---------------------------------------------------------------------- 
  72     Search for a wxPython installation that matches version.  If one 
  73     is found then sys.path is modified so that version will be 
  74     imported with a 'import wx', otherwise a VersionError exception is 
  75     raised.  This funciton should only be caled once at the begining 
  76     of the application before wxPython is imported. 
  78         :param version: Specifies the version to look for, it can 
  79                         either be a string or a list of strings.  Each 
  80                         string is compared to the installed wxPythons 
  81                         and the best match is inserted into the 
  82                         sys.path, allowing an 'import wx' to find that 
  85                         The version string is composed of the dotted 
  86                         version number (at least 2 of the 4 components) 
  87                         optionally followed by hyphen ('-') separated 
  88                         options (wx port, unicode/ansi, flavour, etc.)  A 
  89                         match is determined by how much of the installed 
  90                         version matches what is given in the version 
  91                         parameter.  If the version number components don't 
  92                         match then the score is zero, otherwise the score 
  93                         is increased for every specified optional component 
  94                         that is specified and that matches. 
  96     if type(versions
) == str: 
 100     if _selected 
is not None: 
 101         # A version was previously selected, ensure that it matches 
 104             if _selected
.Score(_wxPackageInfo(ver
)) > 0: 
 106         # otherwise, raise an exception 
 107         raise VersionError("A previously selected wx version does not match the new request.") 
 109     # If we get here then this is the first time wxversion is used,  
 110     # ensure that wxPython hasn't been imported yet. 
 111     if sys
.modules
.has_key('wx') or sys
.modules
.has_key('wxPython'): 
 112         raise VersionError("wxversion.select() must be called before wxPython is imported") 
 114     # Look for a matching version and manipulate the sys.path as 
 115     # needed to allow it to be imported. 
 116     installed 
= _find_installed(True) 
 117     bestMatch 
= _get_best_match(installed
, versions
) 
 119     if bestMatch 
is None: 
 120         raise VersionError("Requested version of wxPython not found") 
 122     sys
.path
.insert(0, bestMatch
.pathname
) 
 123     _selected 
= bestMatch
 
 125 #---------------------------------------------------------------------- 
 127 UPDATE_URL 
= "http://wxPython.org/" 
 128 #UPDATE_URL = "http://sourceforge.net/project/showfiles.php?group_id=10718" 
 131 def ensureMinimal(minVersion
): 
 133     Checks to see if the default version of wxPython is greater-than 
 134     or equal to `minVersion`.  If not then it will try to find an 
 135     installed version that is >= minVersion.  If none are available 
 136     then a message is displayed that will inform the user and will 
 137     offer to open their web browser to the wxPython downloads page, 
 138     and will then exit the application. 
 140     assert type(minVersion
) == str 
 142     # ensure that wxPython hasn't been imported yet. 
 143     if sys
.modules
.has_key('wx') or sys
.modules
.has_key('wxPython'): 
 144         raise VersionError("wxversion.ensureMinimal() must be called before wxPython is imported") 
 147     minv 
= _wxPackageInfo(minVersion
) 
 148     defaultPath 
= _find_default() 
 150         defv 
= _wxPackageInfo(defaultPath
, True) 
 154     if bestMatch 
is None: 
 155         installed 
= _find_installed() 
 157             # The list is in reverse sorted order, so if the first one is 
 158             # big enough then choose it 
 159             if installed
[0] >= minv
: 
 160                 bestMatch 
= installed
[0] 
 162     if bestMatch 
is None: 
 163         import wx
, webbrowser
 
 164         versions 
= "\n".join(["      "+ver 
for ver 
in getInstalled()]) 
 165         app 
= wx
.PySimpleApp() 
 166         result 
= wx
.MessageBox("This application requires a version of wxPython " 
 167                                "greater than or equal to %s, but a matching version " 
 169                                "You currently have these version(s) installed:\n%s\n\n" 
 170                                "Would you like to download a new version of wxPython?\n" 
 171                                % (minVersion
, versions
), 
 172                       "wxPython Upgrade Needed", style
=wx
.YES_NO
) 
 174             webbrowser
.open(UPDATE_URL
) 
 178     sys
.path
.insert(0, bestMatch
.pathname
) 
 179     _selected 
= bestMatch
 
 182 #---------------------------------------------------------------------- 
 184 def checkInstalled(versions
): 
 186     Check if there is a version of wxPython installed that matches one 
 187     of the versions given.  Returns True if so, False if not.  This 
 188     can be used to determine if calling `select` will succeed or not. 
 190         :param version: Same as in `select`, either a string or a list 
 191                         of strings specifying the version(s) to check 
 195     if type(versions
) == str: 
 196         versions 
= [versions
] 
 197     installed 
= _find_installed() 
 198     bestMatch 
= _get_best_match(installed
, versions
) 
 199     return bestMatch 
is not None 
 201 #---------------------------------------------------------------------- 
 205     Returns a list of strings representing the installed wxPython 
 206     versions that are found on the system. 
 208     installed 
= _find_installed() 
 209     return [os
.path
.basename(p
.pathname
)[3:] for p 
in installed
] 
 213 #---------------------------------------------------------------------- 
 216 def _get_best_match(installed
, versions
): 
 219     for pkg 
in installed
: 
 221             score 
= pkg
.Score(_wxPackageInfo(ver
)) 
 222             if score 
> bestScore
: 
 228 _pattern 
= "wx-[0-9].*" 
 229 def _find_installed(removeExisting
=False): 
 234         # empty means to look in the current dir 
 238         # skip it if it's not a package dir 
 239         if not os
.path
.isdir(pth
): 
 242         base 
= os
.path
.basename(pth
) 
 244         # if it's a wx path that's already in the sys.path then mark 
 245         # it for removal and then skip it 
 246         if fnmatch
.fnmatchcase(base
, _pattern
): 
 250         # now look in the dir for matching subdirs 
 251         for name 
in glob
.glob(os
.path
.join(pth
, _pattern
)): 
 252             # make sure it's a directory 
 253             if not os
.path
.isdir(name
): 
 255             # and has a wx subdir 
 256             if not os
.path
.exists(os
.path
.join(name
, 'wx')): 
 258             installed
.append(_wxPackageInfo(name
, True)) 
 262             del sys
.path
[sys
.path
.index(rem
)] 
 269 # Scan the sys.path looking for either a directory matching _pattern, 
 273         # empty means to look in the current dir 
 277         # skip it if it's not a package dir 
 278         if not os
.path
.isdir(pth
): 
 281         # does it match the pattern? 
 282         base 
= os
.path
.basename(pth
) 
 283         if fnmatch
.fnmatchcase(base
, _pattern
): 
 289         if not os
.path
.isdir(pth
): 
 291         if os
.path
.exists(os
.path
.join(pth
, 'wx.pth')): 
 292             base 
= open(os
.path
.join(pth
, 'wx.pth')).read() 
 293             return os
.path
.join(pth
, base
) 
 298 class _wxPackageInfo(object): 
 299     def __init__(self
, pathname
, stripFirst
=False): 
 300         self
.pathname 
= pathname
 
 301         base 
= os
.path
.basename(pathname
) 
 302         segments 
= base
.split('-') 
 304             segments 
= segments
[1:] 
 305         self
.version 
= tuple([int(x
) for x 
in segments
[0].split('.')]) 
 306         self
.options 
= segments
[1:] 
 309     def Score(self
, other
): 
 312         # whatever number of version components given in other must 
 314         minlen 
= min(len(self
.version
), len(other
.version
)) 
 315         if self
.version
[:minlen
] != other
.version
[:minlen
]: 
 319         for opt 
in other
.options
: 
 320             if opt 
in self
.options
: 
 326     def __lt__(self
, other
): 
 327         return self
.version 
< other
.version 
or \
 
 328                (self
.version 
== other
.version 
and self
.options 
< other
.options
) 
 329     def __le__(self
, other
): 
 330         return self
.version 
<= other
.version 
or \
 
 331                (self
.version 
== other
.version 
and self
.options 
<= other
.options
) 
 333     def __gt__(self
, other
): 
 334         return self
.version 
> other
.version 
or \
 
 335                (self
.version 
== other
.version 
and self
.options 
> other
.options
) 
 336     def __ge__(self
, other
): 
 337         return self
.version 
>= other
.version 
or \
 
 338                (self
.version 
== other
.version 
and self
.options 
>= other
.options
) 
 340     def __eq__(self
, other
): 
 341         return self
.version 
== other
.version 
and self
.options 
== other
.options
 
 345 #---------------------------------------------------------------------- 
 347 if __name__ 
== '__main__': 
 350     #ensureMinimal('2.5') 
 351     #pprint.pprint(sys.path) 
 357         savepath 
= sys
.path
[:] 
 361         print "Asked for %s:\t got: %s" % (version
, sys
.path
[0]) 
 362         pprint
.pprint(sys
.path
) 
 366         sys
.path 
= savepath
[:] 
 371     # make some test dirs 
 374              'wx-2.5.2.9-gtk2-unicode', 
 375              'wx-2.5.2.9-gtk-ansi', 
 377              'wx-2.5.2.8-gtk2-unicode', 
 380         d 
= os
.path
.join('/tmp', name
) 
 382         os
.mkdir(os
.path
.join(d
, 'wx')) 
 384     # setup sys.path to see those dirs 
 385     sys
.path
.append('/tmp') 
 389     pprint
.pprint( getInstalled()) 
 390     print checkInstalled("2.4") 
 391     print checkInstalled("2.5-unicode") 
 392     print checkInstalled("2.99-bogus") 
 393     print "Current sys.path:" 
 394     pprint
.pprint(sys
.path
) 
 404     # There isn't a unicode match for this one, but it will give the best 
 405     # available 2.4.  Should it give an error instead?  I don't think so... 
 408     # Try asking for multiple versions 
 409     test(["2.6", "2.5.3", "2.5.2-gtk2"]) 
 412         # expecting an error on this one 
 414     except VersionError
, e
: 
 415         print "Asked for 2.6:\t got Exception:", e 
 
 417     # check for exception when incompatible versions are requested 
 421     except VersionError
, e
: 
 422         print "Asked for incompatible versions, got Exception:", e 
 
 426         d 
= os
.path
.join('/tmp', name
) 
 427         os
.rmdir(os
.path
.join(d
, 'wx'))