8.1.1 Example Extension Module

To demonstrate the writing of an extension module the source and MMP contents for an example module , 'elemlist' is given below. This module is about extracting the pointers in a cons cell. A cons cell is composed of two pointers. The car and cdr are primitive operations upon linked lists composed of cons cells. The car operation extracts the first pointer, and the cdr operation extracts the second.

#include "Python.h"

/* type-definition & utility-macros */
typedef struct {
    PyObject_HEAD
    PyObject *car, *cdr;
} cons_cell;

staticforward PyTypeObject cons_type;

/* a typetesting macro (we don't use it here) */
#define is_cons(v) ((v)->ob_type == &cons_type)

/* macros to access car & cdr, both as lvalues & rvalues */
#define carof(v) (((cons_cell*)(v))->car)
#define cdrof(v) (((cons_cell*)(v))->cdr)

/* ctor (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(carof(cons)); Py_DECREF(cdrof(cons));
    PyObject_DEL(cons);
}

/* 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 */
};

/* 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 PyMethodDef elemlist_methods[] = {
    {"cons",   cons,   METH_VARARGS},
    {"car",    car,    METH_VARARGS},
    {"cdr",    cdr,    METH_VARARGS},
    {0, 0}
};

/* module entry-point (module-initialization) function */
PyMODINIT_FUNC
initelemlist(void)
{
    /* Create the module and add the functions */
    PyObject *m = Py_InitModule("elemlist", elemlist_methods);
    /* Finish initializing the type-objects */
    cons_type.ob_type = &PyType_Type;
}
A brief description of the concepts involved in writing an extension module is put down below. For more information about writing an extension module refer Extending Python from the mainline Python documentation.

The header file Python.h makes the Python API's accessible in the code. car(), cdr() and cons() are the functions exposed at Python level so that they can be called after importing the elemlist module. The C implementation of these functions take arguments as Python objects. To do anything with them in the C function we have to convert them to C values.

elemlist_methods is the method table for the module. The method table is passed to the interpreter in the module's initialization function, initelemlist(). The initialization function must be named initname(), where name is the name of the module, and should be the only non-static item defined in the module file. The PyMODINIT_FUNC declares the function as void return type, declares any special linkage declarations required by the platform, and for C++ declares the function as extern "C". When the Python program imports module elemlist for the first time, initelemlist() is called.

The MMP file contents for the above source is as follows:

TARGETTYPE  dll
TARGET    kf_elemlist.pyd

CAPABILITY LocalServices NetworkServices ReadUserData WriteUserData UserEnvironment

NOSTRICTDEF
DEFFILE   elemlist.def

/* If global data is present in the extension module then this macro should be
 * defined in the mmp file.
 */
EPOCALLOWDLLDATA

SYSTEMINCLUDE    \epoc32\include\python25
SYSTEMINCLUDE    \epoc32\include\stdapis
SYSTEMINCLUDE    \epoc32\include

LIBRARY     python25.lib

SOURCEPATH  ..\src
SOURCE      elemlist.c

An example usage of the 'elemlist' extension module can be:

from elemlist import *
cell = cons(1, 2)
print "car(cell) :", car(cell)
print "cdr(cell) :", cdr(cell)

See About this document... for information on suggesting changes.