McMillan Enterprises, Inc.

 

SCXX classes

Wrapping Objects

PWOBase

The base class for all PWOxxx classes. You can think of this as a PyObject * which may be NULL. Use it to wrap arbitrary Python objects. Takes care of reference counts.

Most of the PyObject_xxx calls are available as methods (hasAttr, getAttr, setAttr, delAttr, print, cmp, repr, str, isCallable, hash, isTrue, type and cmp). In addition, the comparison operators are mapped to cmp (this version does not go through the new comparison protocol).

Finally, PWOBase::disOwn() allows the wrapper to be destroyed without decrementing the PyObject's refcount (use this to return an object to Python).

All of the other wrapping objects are subclasses of PWOBase.

PWONumber

Wraps numbers at the PyNumber_XXX level. Can be constructed from a PyObject * (PyNumber_Check will be called), or a C int, long, unsigned long or double. Has operator overloads for the arithmetic and bitwise operators as well as abs, divmod, and casts back to C types (conversions performed if necessary).

PWOSequence

Wraps any Python sequence type. Can only be created from a sequence object (use one of the concrete classes to create a new list, tuple or string).

Operators are overloaded in the same way Python does it (eq, + invokes concat, [n] invokes getitem and * invokes repeat). None of the mutating methods are supported - you'll have to use PWOList for that.

Also supports getSlice(int lo, int hi), count, in, index and len.

PWOTuple

Wraps or creates PyTuple objects. When creating a new tuple, you must set the size PWOTuple(sz). Note that the [] operator cannot be used on the LHS, but since we need a way to fill the new tuple, setItem(int ndx, PWOBase& val) is supplied. You can wrap a list object with a PWOTuple, and the tuple conversion will be performed on the list.

PWOList

Wraps or creates a list. In addition to the methods and operators you would expect from above, you can use operator [] on either the LHS or RHS (setItem and getItem). Also, setSice, append, insert, reverse and sort are supported (the latter two return references to this, in the C++ idiom but not the Python idiom).

PWOListMmbr

These are temporary objects which "remember" a PWOList and index. They are there to support using operator [] on either the LHS or RHS of an expression.

PWOString

Wraps or creates a PyString object. Creating a PWOString can take a normal null-terminated string, or a binary string and size. Casting to const char * gets access to the underlying string.

Like a Python string, PWOStrings are immutable. PWOString::format is a static method taking a PWOString& and a PWOTuple& as args (returning a new PWOString).

PWOMapping

Wraps or creates a Python dictionary. Supports operator [] on both LHS and RHS. Also supports hasKey, len, clear, delItem, items, keys and values.

PWOMappingMmbr

These are temporary objects which "remember" a PWOMapping and key. They are there to support using operator [] on either the LHS or RHS of an expression.

PWOCallable

Wraps a callable object (anything that for which PyCallable_Check() returns true). Has one new method, call, which may take void, a tuple of args, or a tuple and dictionary. All overloads return a PWOBase.

PWOModule

Wraps a module object. Has three new methods: getDict(), getName() and getFilename().

Non-wrapping Objects

PWException

These are lightweight C++ exceptions that set the Python exception for you. Casting one to PyObject * will return NULL to Python.

To throw:

       throw PWException(PyExc_TypeError, "Not a whatsit object");
      

Catching:

      PyObject *method(...)
      {
       try { ... }
       catch (PWException e) {
         return e;
       }
      }
      

PWCallbackException

A subclass of PWException that leaves the Python exception (as set by the callback) alone. Used by PWOCallable to transmit the inner Python exception to the outer Python interpreter. You shouldn't need to throw one. Catch them exactly as above.

PWEngine

This class attempts to make sense of the highly confusing area of Python threads. It is a singleton class (in the Borg pattern - all state is shared; creation and destruction are nearly no-ops).

It has one user-callable method: recordThread(). Call this method when you know you have the Python lock and may be getting called on a new Python thread. PWEngine will maintain a set of associations between OS thread ids and Python threadstates. The helper classes below will use PWEngine to figure out what Python threadstate to acquire or release.

Create a static PWEngine in your initmodule method, and immediately call recordThread().

In methods which might get called on a previously unknown Python thread, call PWEngine().recordThread().

When you want to release the Python lock, use the FreeThreadedBlock class (on the stack). Where you need to aquire the Python lock (and the right threadstate), use a (stack based) PythonThreadedBlock.

FreeThreadedBlock

      PyObject *method(...)
      {
        try {
          PWEngine().recordThread();
          {
            FreeThreadedBlock blk;
            // don't touch Python or any Python objects!
          } 
          // however you get here, you reaquire the lock
        }
        // etc..
      }
      

PythonThreadedBlock

The inverse of a FreeThreadedBlock. As long as the OS thread ID has been seen before and the corresponding Python threadstate is still valid, a PythonThreadedBlock will reaquire the lock. Actually, it will create a new threadstate if it can't find one. As long as it knows about the right Python interpreterstate, it will work (with only one interpreter, that means as long as recordThread() has been called at least once). But if you're using mulitple Python interpreters and it can't find an existing threadstate, you might end up in the wrong interpreter!

       // we don't have the Python lock here
       {
         PythonThreadedBlock blk;
         // play with Python here
       } 
       // now the lock is released
      

Why No ParseTuple

I don't like it, that's why. It started out as a handy convenience, but at this point it has developed into a pure line-noise sub-language. SCXX takes care of much of the drudgery for you.

      static PyObject *silly(PyObject *self, PyObject *args_)
      {
          try {
              PWOSequence args(args_);
              PWONumber x(args[0]);
              PWOList   y(args[1]);
              // do stuff with x and y here
              return y[x];
           }
           catch (PWException e) {
              return e;
           }
      }
      

In this very bare example, the user passing wrong args will get a slightly poorer message in his exception than if you used PyArg_ParseTuple. On the other hand, by the time you dressed up your code to be specific about what was wrong with what arg, you might well have less code doing it this way (and, in my opinion, much clearer code).

copyright 2002
McMillan Enterprises, Inc.