views:

111

answers:

1

I've got two different Python extension modules; let's call them A and B. Module A contains a storage class type called container that I want to use within Module B as the return type of a class method.

I can't seem to find any documentation on how I'm supposed to do this. I roughly followed this article to create the modules/classes, except I didn't declare all the methods as static, so they would be accessible: http://nedbatchelder.com/text/whirlext.html

My question is then, how do I go about creating an instance of container that I can pass back as the PyObject* return value of a class method in module B? The container definition looks like this:

typedef struct {
    PyObject_HEAD

    storageclass* cnt_;
} container;

I tried simply doing the following within the method in question, where container_init is the method I have registered as tp_init for the container class:

pycnt::container* retval;
pycnt::container_init(retval, NULL, NULL);
return (PyObject*)retval;

However according to the Python interpreter I'm getting back the class that I called the method on. (i.e., myclassinstance.mymethod() is returning myclassinstance).

I'm obviously going about this the wrong way, but I have no idea what the right way is. Any suggestions? Just to cut anybody off that's going to suggest it - no I am not interested in using SWIG or Boost::Python. I tried that already and the underlying storage class for container didn't play nicely with either (SWIG couldn't even parse it). So far doing the extensions myself has been pretty painless, but I'm stumped on this one.

+2  A: 

Your problem is that type->tp_init doesn't do what you want. type->tp_init is called after an object is allocated to do the instantiation. You'd first have to allocate the object using type->tp_new, then pass that object as the first argument to type->tp_init. Since you pass an uninitialized pointer to tp_init, all bets are off. You typically don't call either function directly, though, and instead call one of PyObject_Call*() on the type object itself to create a new instance. Or provide a normal C function to create a new instance, a la PyFoo_New().

That said, the usual way to do intercommunication between two extension modules is to do it through the Python API. If you want module B to import and use module A, you call PyImport_Import() to get the module object, PyObject_GetAttrString() to get the type object you care about, and one of PyObject_Call*() to instantiate it. Any place you want to store a pointer to that type, you would just take a PyObject *.

Thomas Wouters
I ended up combining the modules and writing PyFoo_New() methods for each type, and everything works great now. One question though, before I combined the modules into one I tried used my PyContainer_New() method inside the other module, and while the interpreter reported that I was getting back a container object, I couldn't call any of it's methods. Why is that? I had imported the container module, but still no luck.
Paul D.
There isn't really enough information there to make an adequate analysis, but off-hand I would guess you were including the first module's code twice, once as a shared object that was being imported, and once through the dependency in the other module (statically or dynamically linked.) Calling the second occurrence's PyContainer_New() would give you all manner of uninitialized datastructures.
Thomas Wouters