Recipe17.1.Implementing a Simple Extension Type


Recipe 17.1. Implementing a Simple Extension Type

Credit: Alex Martelli

Problem

You want to code and build a C extension type for Python with a minimal amount of hard work.

Solution

First of all, we need to create a setup.py file to use the distutils package to build and install our module:

from distutils.core import setup, Extension setup(name = "elemlist",       version = "1.0",       maintainer = "Alex Martelli",       maintainer_email = "amcx@aleax.it",       description = "Sample, simple Python extension module",       ext_modules = [Extension('elemlist',sources=['elemlist.c'])] )

Then, we need a file elemlist.c with our module's source code:

#include "Python.h" /* type-definition and utility-macros */ typedef struct {     PyObject_HEAD     PyObject *car, *cdr; } cons_cell; staticforward PyTypeObject cons_type; /* a type-testing macro (we don't actually use it here) */ #define is_cons(v) ((v)->ob_type == &cons_type) /* utility macros to access car and cdr, as either lvalues or rvalues */ #define carof(v) (((cons_cell*)(v))->car) #define cdrof(v) (((cons_cell*)(v))->cdr) /* ctor ("internal" factory-function) and dtor */ static cons_cell* cons_new(PyObject *car, PyObject *cdr) {     cons_cell *cons = PyObject_New(cons_cell, &cons_type);     if(cons) {         cons->car = car; Py_INCREF(car); /* INCREF when holding a PyObject */         cons->cdr = cdr; Py_INCREF(cdr); /* ditto */     }     return cons; } static void cons_dealloc(cons_cell* cons) {     /* DECREF when releasing previously-held PyObject*'s */     Py_DECREF(cons->car); Py_DECREF(cons->cdr);     PyObject_Del(cons); } /* A minimal Python type-object */ statichere PyTypeObject cons_type = {     PyObject_HEAD_INIT(0)     /* initialize to 0 to ensure Win32 portability  */     0,                        /* ob_size */     "cons",                   /* tp_name */     sizeof(cons_cell),        /* tp_basicsize */     0,                        /* tp_itemsize */     /* methods */     (destructor)cons_dealloc, /* tp_dealloc */     /* implied by ISO C: all zeros thereafter, i.e., no other method */ }; /* module-functions */ static PyObject* cons(PyObject *self, PyObject *args)    /* the exposed factory-function */ {     PyObject *car, *cdr;     if(!PyArg_ParseTuple(args, "OO", &car, &cdr))         return 0;     return (PyObject*)cons_new(car, cdr); } static PyObject* car(PyObject *self, PyObject *args)     /* car-accessor */ {     PyObject *cons;     if(!PyArg_ParseTuple(args, "O!", &cons_type, &cons)) /* type-checked */         return 0;     return Py_BuildValue("O", carof(cons)); } static PyObject* cdr(PyObject *self, PyObject *args)     /* cdr-accessor */ {     PyObject *cons;     if(!PyArg_ParseTuple(args, "O!", &cons_type, &cons)) /* type-checked */         return 0;     return Py_BuildValue("O", cdrof(cons)); } static PyObject* setcar(PyObject *self, PyObject *args)  /* car-setter */ {     PyObject *cons;     PyObject *value;     if(!PyArg_ParseTuple(args, "O!O", &cons_type, &cons, &value))         return 0;     Py_INCREF(value);     Py_DECREF(carof(cons));     carof(cons) = value;     return Py_BuildValue(""); } static PyObject* setcdr(PyObject *self, PyObject *args)  /* cdr-setter */ {     PyObject *cons;     PyObject *value;     if(!PyArg_ParseTuple(args, "O!O", &cons_type, &cons, &value))         return 0;     Py_INCREF(value);     Py_DECREF(cdrof(cons));     cdrof(cons) = value;     return Py_BuildValue(""); } static PyMethodDef elemlist_module_functions[  ] = {     {"cons",   cons,   METH_VARARGS},     {"car",    car,    METH_VARARGS},     {"cdr",    cdr,    METH_VARARGS},     {"setcar", setcar, METH_VARARGS},     {"setcdr", setcdr, METH_VARARGS},     {0, 0} }; /* module entry-point (module-initialization) function */ void initelemlist(void) {     /* Create the module, with its functions */     PyObject *m = Py_InitModule("elemlist", elemlist_module_functions);     /* Finish initializing the type-objects */     cons_type.ob_type = &PyType_Type; }

Discussion

C-coded Python extension types have an undeserved aura of mystery and difficulty. Sure, it's a lot of work to implement every possible feature, but a minimal yet useful type doesn't necessarily take all that much effort.

This module is roughly equivalent to the Python-coded module:

def cons(car, cdr): return car, cdr def car(conscell): return conscell[0] def cdr(conscell): return conscell[1] def setcar(conscell, value): conscell[0] = value def setcdr(conscell, value): conscell[1] = value

except that the C source is about 25 times larger, even excluding comments and empty lines (and it is not much faster than the Python-coded version, either).

However, the point of this recipe is to demonstrate a minimal C-coded extension type. I'm not even supplying object methods (except the indispensable destructor) but, rather, I am providing module-level functions to build cons cells and to read and write their car and cdr fields. This recipe also shows the utter simplicity of building a C-coded extension module on any platform, thanks to the distutils package, which does all of the hard work.

Lisp-savvy readers will have recognized from the names involved that this little extension offers the core functionality to implement a Lisp-like linked list typeusing some NIL marker (e.g. None), by convention, as the cdr of the last cons-cell of a list, and otherwise "consing up a list" by having every cdr be another cons-cell. You might easily constrain the cdr to be either None or another cons-cell, giving up on generality for a bit of extra error checking.

Because this recipe is meant as an introduction to writing extension modules in C for Python, here are the instructions for building this extension module, assuming you have a Windows machine with Python 2.3 and Microsoft Visual C++ 6 (or the free command-line equivalent that you can download from Microsoft's site as a part of their .NET Framework SDK). You can presumably translate mentally to other platforms such as Linux with gcc, Mac OS X with gcc, and so on. On the other hand, using different C compilers on Windows involves more work, and I'm not going to cover that here (see http://sebsauvage.net/python/mingw.html).

Here are the steps you should follow to build this recipe's extension:

  1. Make a new directoryfor example, C:\Temp\EL.

  2. Open a command-prompt window, and go to the new directory.

  3. In the new directory, create the files setup.py and elemlist.c with the contents of the recipe's text.

  4. Run the following at the command prompt (assuming you've performed a standard Python 2.3 installation, so that your python.exe lives in C:\Python23):

    <m>C:\Temp\EL> C:\Python23\python setup.py install</m>

  5. This command will result in lots of output, which you should examine to check for problems. Presumably, all has gone well, and the new elemlist extension module has been built and installed.

  6. Now try the extension by running the following at the DOS prompt:

    <m>C:\Temp\EL> C:\Python23\python</m>  (snipped: various greeting messages from Python) >>> from elemlist import cons, car, cdr >>> a = cons(1, cons(2, cons(3, ( )))) >>> car(cdr(a)) 2  >>>

Thereyour new extension module is installed and ready!

See Also

The Extending and Embedding manual is available as part of the standard Python documentation set at http://www.python.org/doc/current/ext/ext.html; the section "Distributing Python Modules" of the standard Python documentation set is still incomplete, but it's a reliable source of information on the distutils package. Python in a Nutshell covers the essentials of extending and embedding and of the distutils package.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net