Recipe 17.1. Implementing a Simple Extension TypeCredit: Alex Martelli ProblemYou want to code and build a C extension type for Python with a minimal amount of hard work. SolutionFirst 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; } DiscussionC-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:
Thereyour new extension module is installed and ready! See AlsoThe 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. |