Python extension modules

Mark Hammond MarkH at ActiveState.com
Thu Nov 1 12:31:17 CET 2001


[posted and mailed]

emdpek wrote:

 > I need Python objects to contain and "represent" native C
 > structs.  Is there a way to do this *without* defining a new
 > type?


Check out the struct module.  It allows you to pack and unpack "blobs" 
of data (usually in a string), given a format string.

So one approach would be to define a Python class in a .py file, that 
wraps up the struct module.

import struct
class Foo:
   def __init__(self, int1=0, int2=0):
     self.int1=int1
     self.int2=int2
   def __getattr__(self, attr):
     if attr=="_value_":
       return struct.pack("ii", self.int1, self.int2)
     raise AttributeError,  "%r.%s" % (self, attr)

f=Foo(int2=1)
f.int1=255
print "Data is", repr(f._value_)
--
Yields: Data is '\xff\x00\x00\x00\x01\x00\x00\x00'

Your extension module could create instances of this class to return to 
callers.  Something like:

(error checking omitted, but _do_ add it :)
{
...
   CSTRUCT cs;
   PyObject *mod = PyImport_ImportModule("foo");
   PyObject *cls = PyObject_GetAttrString(mod, "Foo");

   PyObject *args = Py_BuildValue(cs.int1, cs.int2);
   PyObject *instance = PyEval_CallObject(cls, args);
   Py_DECREF(args);
   Py_DECREF(cls);

   // instance is a new object ready to return, with a new reference
}

It could accept class instances as params - use something like:
{
...
   PyObject *inst, *str;
   CSTRUCT cs;
   PyArg_ParseTuple("O", &inst);
   str=PyObject_GetAttrString(inst, "_value_");
   if (!PyString_Check(str)) ...
   if (PyString_Length(str)!= sizeof(cs)) // eek...
   memcpy(&cs, PyString_AsString(str), sizeof(cs));
   Py_DECREF(str);
...
}

The other alternative, as you discovered, is what you outline below:

 > Question about defining a new type, then.  Functions
 > (package-scoped functions not tied to a class) are declared
 > via the PyMethodDef array, right?  So, this is where an
 > instance constructor might go?


[snip lots of stuff that looked correct, but I really didn't look too hard]


 > static PyObject*
 > MyNew_getattr(MyNew *self, char *name)


You should use Python declarations exactly - use "PyObject" instead of 
"MyNew".  MyNew may fail in some C++ scenarios.

 > {
 >   return Py_FindMethod(MyNew_methods, (PyObject *)self, name);
 > }
 >
 >
 > So, now you can: "rv = instance.method()"

Yep.  You can also add simple checks to getattr to make it look more 
like a struct:

static PyObject*
MyNew_getattr(PyObject *self, char *name)
{
   if (strcmp(name, "int1")==0)
     return PyInt_FromLong( ((MyNew *)self)->pcs->int1);
   else if ...
   // try methods last.
   return Py_FindMethod(MyNew_methods, (PyObject *)self, name);
}

 > Is this (use of "getattr" method, which calls Py_FindMethod)
 > the accepted convention?
 >
 > Am I on the right track?  Thanks in advance...


Yep :)


 > P.S.  There is perhaps a train-of-thought missing from the
 >   Extending tutorial that I lost hair figuring out this
 >   morning.  I would be more than happy to offer possible
 >   improvements, if there is interest...


Sure is!  If submit patches to the documentation to the source-forge 
patch manager, you would be loved by many :)

Mark.




More information about the Python-list mailing list