Multi-Version Installs

Introduction

Starting with 2.5.3.0 the wx and wxPython package directories will be installed in a subdirectory of the site-packages directory, instead of directly in site-packages. This is done to help facilitate having multiple versions of wxPython installed side-by-side. Why would you want to do this? One possible scenario is you have an app that requires wxPython 2.4 but you want to use the newest 2.5 to do your development with. Or perhaps you want to be able to test your app with several different versions of wxPython to ensure compatibility. Before everyone panics, rest assured that if you only install one version of wxPython then you should notice no difference in how things work.

In addition to installing wxPython into a "versioned" subdirectory of site-packages, a file named wx.pth is optionally installed that will contain the name of the versioned subdirectory. This will cause that subdirectory to be automatically added to the sys.path and so doing an "import wx" will find the package in the subdirectory like it would have if it was still located in site-packages. I say "optionally" above because that is how you can control which install of wxPython is the default one. Which ever version installs the wx.pth file will be the one that is imported with a plain "import wx" statement. Of course you can always manipulate that by editing the wx.pth file, or by setting PYTHONPATH in the environment, or by the method described in the next paragraph.

Finally, a new module named wxversion.py is installed to the site-packages directory. It can be used to manipulate the sys.path at runtime so your applications can select which version of wxPython they would like to to have imported. You can use it like this:

   1       import wxversion
   2       wxversion.select("2.4")
   3       import wx

Then, even though a 2.5 version of wxPython may be the default the application that does the above the first time that wx is imported will actually get a 2.4 version. NOTE: There isn't actually a 2.4 version of wxPython that supports this, but there will be.

Install Directory Name Format

The site-packages subdirectory used for installing wxPython will have the following format, with some parts being optional depending on platform and build options:

        wx-VERSION [-PORT-CHARTYPE [-FLAVOUR]]

Here are some possible directory names:

    wx-2.5.3
    wx-2.5.3-gtk2-unicode
    wx-2.5.3.0-gtk2-unicode

So as you can see the complexity of the name really depends on the level of granularity that you need to support. By default wxPython will allow you to install all supported versions of wxPython for a single platform at the same time. That means that the middle example above with the short version number and the port and chartype is what the official binaries will use.

How do I use wxversion?

As mentioned above there is a new module called wxversion.py. It is installed directly in site-packages, outside of any of the versioned wxPython directories, and can be used to control at runtime which version of wxPython is imported.

Usage is very simple, you simply import the module at the very beginning of your app and call its select function. It will then go and look at all of the directories in sys.path and check if there are any subdirectories that match wxPython's installation pattern. All of the matches found are compared with the version(s) requested by your app and a score is calculated. The directory containing the version with the best score is inserted at the beginning of the sys.path and then select returns. Now when your app imports the wx package the matching version will be the one that is imported. Pretty slick, eh?

The way the score is calculated is pretty simple and flexible. First, the version number part of the string is converted to a tuple of integers, and the rest of the string is split at the hyphens and stored as a list of options. Finally the version tuple is compared with the installed package and if they don't match the score is an automatic zero. Otherwise it starts with a score of one. Then for every option you ask for that matches an installed version the score will be increased by one more point.

You can be as detailed or as generic as you want in how you ask for a version. For example, all of these are valid requests:

   1     wxversion.select("2.4")
   2     wxversion.select("2.5.3")
   3     wxversion.select("2.5-unicode")
   4     wxversion.select("2.5.3-gtk-ansi")

Whichever installed version has the best score when compared with your request will be the one chosen. This means that you may not get exactly what you ask for, but at least the version will match.

You can also pass a list of versions to select from, and again, whichever installed version has the best score with any of your requests will be the one chosen. Since the installed versions are in reverse sorted order when doing the scoring any ties will be broken by choosing the one with the largest version number. For example:

   1     wxversion.select(["2.5.4", "2.6"])

In the example above if both 2.5.4 and 2.6.1 are installed then 2.6.1 will be chosen.

Although wxversion will allow you to be very specific your choice of version if you really need to, I think that it makes the most sense most of the time to use it more generically and only ask for the ReleaseSeries that your app requires. Please also read the section below on installing upgrades for another reason why just selecting by ReleaseSeries may make the most sense for applications. For example, if you write your app with 2.6.0.1 and expect it to continue working with all 2.6.x releases (since that will be a stable API release series) then you can just use this in your main application module:

   1     import wxversion
   2     wxversion.select("2.6")
   3     import wx
   4     ...

The wxversion.select() function will raise an exception if the requested version is not installed. If you would like to make the selection of the version more of a request, instead of a demand, then you can check first if the selected version is installed, like this:

   1     WXVER = '2.6'
   2     import wxversion
   3     if wxversion.checkInstalled(WXVER):
   4         wxversion.select(WXVER)
   5     import wx
   6     ...

However, if there are compatibility problems because of the difference in version then the user may never have a clue that is the case. So another approach would be to help the user do the right thing if there is a version mismatch. In this example instead of running the normal app a message dialog is shown telling the user why the app won't run, and also opens a browser for them to be able to find the correct version:

   1     WXVER = '2.6'
   2     import wxversion
   3     if wxversion.checkInstalled(WXVER):
   4         wxversion.select(WXVER)
   5     else:
   6         import sys, wx, webbrowser
   7         app = wx.PySimpleApp()
   8         wx.MessageBox("The requested version of wxPython is not installed.\n"
   9                       "Please install version %s" % WXVER,
  10                       "wxPython Version Error")
  11         app.MainLoop()
  12         webbrowser.open("http://wxPython.org/")
  13         sys.exit()
  14 
  15     import wx
  16     ...

Finally, if you would like to just warn the user that there is a version mismatch, but would like to still allow them to continue running the app with their default version of Python, then you could do it like this:

   1     WXVER = '2.6'
   2     import wxversion
   3     if wxversion.checkInstalled(WXVER):
   4         wxversion.select(WXVER)
   5         versionOK = True
   6     else:
   7         versionOK = False
   8 
   9     import wx
  10 
  11     class MainFrame(wx.Frame):
  12         ...
  13 
  14 
  15     class MyApp(wx.App):
  16         def OnInit(self):
  17             if not versionOK:
  18                 result = wx.MessageBox(
  19                     "This application is known to be compatible with\n"
  20                     "wxPython version(s) %s, but you have %s installed.\n"
  21                     "\nWould you like to continue?" % (WXVER, wx.VERSION_STRING),
  22                     "wxPython Version Warning",
  23                     wx.YES_NO)
  24                 if result == wx.NO:
  25                     return True
  26             
  27             frame = MainFrame(None, title="My Main Frame")
  28             self.SetTopWindow(frame)
  29             frame.Show(True)                
  30             return True
  31 
  32     app = MyApp()
  33     app.MainLoop()

You might also want to explain to the user that if the application does run correctly with their currently installed wxPython that they can edit the WXVER assignment, or perhaps you could add a "don't show this message again" checkbox on your dialog and then save the value in a configuration file.

The wxversion module also contains another function that can be used instead of select, that behaves just a bit differently. It is called ensureMinimal and it will allow you to ensure that the version of wxPython used is at least some version that you specify. It first checks the default version and if that is greater than or equal to the specified version it will use it. Otherwise it will look for the newest version that is new enough. If a wxPython version is not found then it will display a message dialog similar to the samples above. It is very easy to use:

   1     import wxversion
   2     wxversion.ensureMinimal('2.5.3')
   3     import wx
   4     ...

The nice thing about ensureMinimal is that it allows you to easily put a lower bound on the versions that you will support, while being flexible on accepting anything above that bound. Also, if the target environment has set a specific default version of wxPython to use then ensureMinimal will respect that if it is able.

What about library modules?

The wxverision module is meant only for use before wxPython is imported the first time. So it should not be used from library modules that are imported by other modules, since you can't guarantee that the other modules haven't already imported wx.

Instead, if you need to restrict the use of the module to certain versions of wxPython, or to warn the user that an unsupported version is being used, then you can use the values of wx.VERSION and wx.PlatformInfo to find out if a compatible version of wxPython is being used. wx.VERSION is a tuple of integers representing the components of the version number, and wx.PlatformInfo is a tuple of strings representing various build options such as the wxWidgets port and character type. For example, to check that your library module is being used on a wxGTK2 unicode build version 2.6 or better you could do this:

   1     if wx.VERSION >= (2,6) and \
   2            'gtk2' in wx.PlatformInfo and \
   3            'unicode' in wx.PlatformInfo:
   4         # do something...

What about py2exe or similar tools?

There are several tools available that can create a standalone executable bundle from a Python application and its dependencies, such as wxPython, that can run independently of any existing Python installation. Typically, these tools inspect your application's bytecode and detect any packages, modules, and extensions referenced by "import" statements. In some cases, these tools will even detect and include shared libraries used by Python extensions. A typical bundled application will include everything necessary to run on any machine of similar platform.

From a bundled environment, a particular version of wxPython should be included in the bundle, so using wxversion is not typically necessary. If your application will be bundled using one of these tools, wxversion should be used conditionally or not at all. py2exe, py2app, and similar tools should set the "frozen" attribute of the sys module, which is normally not present. The following main script code snippet will conditionally use wxversion if not running in a bundled environment:

   1     import sys
   2     if not hasattr(sys, "frozen"):
   3         import wxversion
   4         wxversion.select("2.5")
   5     import wx

If you have multiple versions of wxPython installed and your application requires a specific version, then you should ensure that version is found by the application packager. There are several methods to ensure that this occurs:

How do I manually convert an existing install to a multi-version?

Until there is a new release of wxPython 2.4 that supports multi-version installs you can use the instructions in this section to manually convert your existing 2.4 install into something that can be supported by wxversion. You can also use this technique to keep older 2.5 versions of wxPython installed if you wish. Simply substitute the version numbers as needed below.

WARNING: There will be side-effects of doing this, one of them being breaking the uninstall tool for removing this build of wxPython. So be prepared down the road to either undo these changes before attempting to uninstall the old version, or to manually cleanup the installation after a failed uninstall...

Windows

Since on Windows installs all of the runtime files needed for wxPython are contained within the package directories it is very easy to adapt it to be compatible with a multi-version install. In addition to the uninstall issues listed above, doing this conversion will cause the wxPython Start Menu shortcuts to be broken but since you'll be getting a new set of them when you install the 2.5 packages that shouldn't matter too much. You can just delete that old group from the Start Menu if you want.

  1. Find the site-packages folder. It is probably something like c:\Python23\Lib\site-packages.

  2. Make a new subfolder that conforms to the name specification

    shown above, such as wx-2.4-msw-ansi or wx-2.4-msw-unicode.

  3. Move the wx and wxPython folders out of the site-packages folder and into the new subfolder that you just created.

  4. Install one or more of the wxPython2.5-win32 runtime packages.

    They will install wxPython to other subfolders of site-packages as well as the wxversion.py and wx.pth files.

  5. Test which is the default version, for example:
    [C:\] python
    Python 2.3.4 (#53, May 25 2004, 21:17:02) [MSC v.1200 32 bit (Intel)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import wx
    >>> wx.VERSION_STRING
    '2.5.3.1rc1'
    >>>  
  6. Test using wxversion to select the old version, for example:

    [C:\] python
    Python 2.3.4 (#53, May 25 2004, 21:17:02) [MSC v.1200 32 bit (Intel)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import wxversion
    >>> wxversion.select("2.4")
    >>> import wx
    >>> wx.VERSION_STRING
    '2.4.2.4'
    >>> 
  7. If you would like the old version of wxPython to be the default

    then you can edit site-packages\wx.pth so it contains the name of the subfolder containing the old version.

OSX

Follow the instruction from Windows with the mention that the names of the new folders should be like: wx-2.4-osx-ansi or wx-2.4-osx-unicode.

Linux or other unix-like systems

Follow the instruction from Windows with the mention that the names of the new folders should be like: wx-2.4-gtk-ansi or wx-2.4-gtk-unicode.

What happens when I install a new version of wxPython?

New versions of wxPython should be aware of wxversion. When the wxversion aware 2.4 version appears you should probably delete the created wx-2.4-[os]-[build] directories and install this new version.

MultiVersionInstalls (last edited 2008-03-11 10:50:18 by localhost)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.