The wxPython wx Package

Or, how to survive the new wx namespace changes.

Author: Patrick K. O'Brien
Contact: pobrien@orbtech.com
Organization: Orbtech
Date: 2003-05-08
Revision: 1.1.2.4

Contents

Introduction

Big things sometimes come in small packages. This is certainly true of the new wx package, which is being introduced in wxPython 2.4.1 as a way to allow the "wx" prefix to be dropped from the names of all wxPython classes, functions, and constants. This document should answer all the questions you might have concerning the new wx package. If not, feel free to contact the author. I hope you like the new wx package as much as I do.

Why change anything?

This change is being made for a couple of reasons. The first reason is to discourage the use of import *, which is a dangerous technique that can create name conflicts and bloated namespaces.

The second reason is to remove what some perceive to be a "wart." For example, the following code is rather ugly in that the "wx" prefix on the wxFrame class name is no longer useful when you're using the wx module prefix:

from wxPython import wx

class Frame(wx.wxFrame)

The new wx package allows you to write code like this, instead:

import wx

class Frame(wx.Frame)

The third reason is that the wxWindows project intends to do the same thing (implement a new wx namespace and drop the "wx" prefix) and we want wxPython to lead the way.

What does the new wx package do?

As a way of getting to this new syntax as quickly as possible, the code in this new wx package was created. What it does is alter the existing wx namespace dynamically. By making the changes on-the-fly at runtime, we can try out the new syntax before any permanent changes are made to the underlying class library. The downside of making these changes at runtime is that there is a slight delay when you import wx; the upside is that you can start using the new syntax now.

Will any of this effect my existing code?

No. Your existing code will continue to work and be supported for some time. It will be up to you to decide when to switch to the new syntax. But all new documentation and code examples will use the new syntax. So don't wait too long. You wouldn't want anyone calling you old-fashioned, would you?

How does the new wx package work?

It's pretty simple, and pretty clever. The wx directory contains an __init__.py file, making it a Python package. (In contrast, the old wxPython.wx module is a module, not a package.) When you import wx the code in the __init__.py file is executed, and that's where all the magic takes place. Let's take a look at the code inside the __init__.py file:

"""wx package

Provides a way to drop the wx prefix from wxPython objects."""

__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__revision__ = "$Revision$"[11:-2]

from wxPython import wx

import types

d_new = globals()
d_old = wx.__dict__

for old, obj in d_old.items():
    if type(obj) is types.ModuleType or old.startswith('_'):
        # Skip modules and private names.
        continue
    new = old
    if old.startswith('EVT_'):
        # Leave name unmodified; add to the new wx namespace.
        d_new[new] = obj
    elif old.startswith('wxEVT_'):
        # Leave name unmodified; add to the new wx namespace.
        d_new[new] = obj
    else:
        if old.startswith('wx'):
            # Remove the 'wx' prefix.
            new = old[2:]
        # Add to the new wx package namespace.
        d_new[new] = obj

del d_new
del d_old
del new
del obj
del old
del types

del wx

Namespaces in Python are implemented as dictionaries. The dictionary used to create the wx package's namespace is accessible using the globals() function. The dictionary used to create the old wxPython.wx module's namespace is wx.__dict__. Once we have these two dictionaries, it's a simple matter of iterating through one, changing the names, adding the renamed object to the other dictionary, and cleaning up a few local variables and imported modules. Voila!

What about all the other modules, like grid, html, and stc?

There's more to wxPython than just the wx namespace. And we've got those extra modules covered as well. For each of those modules (as well as the lib package) we've got matching modules in the new wx package. Let's take a look at a few of them.

Here is html.py:

"""Provides a way to drop the wx prefix from wxPython objects."""

__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__revision__ = "$Revision$"[11:-2]

import wx
from wx import prefix

from wxPython import html
prefix.rename(d_new=globals(), d_old=html.__dict__)
del html

del prefix
del wx

And here is lib/dialogs.py:

"""Provides a way to drop the wx prefix from wxPython objects."""

__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__revision__ = "$Revision$"[11:-2]

import wx
from wx import prefix

from wxPython.lib import dialogs
prefix.rename(d_new=globals(), d_old=dialogs.__dict__)
del dialogs

del prefix
del wx

As you can see, they both rely on the prefix.rename() function defined in prefix.py:

"""Renaming utility.

Provides a way to drop the wx prefix from wxPython objects."""

__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__revision__ = "$Revision$"[11:-2]

import types

def rename(d_new, d_old):
    for old, obj in d_old.items():
        if type(obj) is types.ModuleType or old.startswith('_'):
            # Skip modules and private names.
            continue
##         mod = d_old['__name__']
##         if hasattr(obj, '__module__') and not obj.__module__.startswith(mod):
##             # Skip objects imported from other modules, except those
##             # related to the current module, such as stc_.
##             continue
        new = old
        if old.startswith('EVT_') or old.startswith('wxEVT_'):
            # Leave these names unmodified.
            pass 
        elif old.startswith('wx'):
            new = old[2:]
        if new:
            d_new[new] = d_old[old]

Again, the technique is very similar to the one used by the wx package.

How do I use this new wx package?

The wx package is automatically created when you install wxPython version 2.4.1 or higher. So all you have to do is:

import wx

What are the issues with converting old code to use the new wx package?

Obviously, you need to change your import statements from:

from wxPython import wx

or:

from wxPython.wx import *

to:

import wx

Then you need to refer to wx attributes without a "wx" prefix, such as:

class MyFrame(wx.Frame):

In most cases, existing code can be modified with a simple search and replace.

One extra issue you might run into when converting existing code is that the wx.__version__ attribute is no longer available, since the new wx namespace doesn't include any private attributes from the old wxPython.wx namespace. The solution is to use the wx.VERSION_STRING attribute, which was introduced in wxPython 2.4.1.

Where can I find example programs using the new wx syntax?

Example programs are included in the wxPython/samples/wx_examples directory, and are documented in the wxPythonExamples documentation file. Also, all the code in the py package uses the new wx syntax. You can learn more about these in the PyManual.