lash
lash

Reputation: 756

Destroying a PyCapsule object

According to documentation, the third argument to PyCapsule_New() can specify a destructor, which I assume should be called when the capsule is destroyed.

void mapDestroy(PyObject *capsule) {

    lash_map_simple_t *map;
    fprintf(stderr, "Entered destructor\n");
    map = (lash_map_simple_t*)PyCapsule_GetPointer(capsule, "MAP_C_API");
    if (map == NULL)
         return;
    fprintf(stderr, "Destroying map %p\n", map);
    lashMapSimpleFree(map);
    free(map);

}

static PyObject * mapSimpleInit_func(PyObject *self, PyObject *args) {

    unsigned int w;
    unsigned int h;
    PyObject *pymap;

    lash_map_simple_t *map = (lash_map_simple_t*)malloc(sizeof(lash_map_simple_t));

    pymap = PyCapsule_New((void *)map, "MAP_C_API", mapDestroy);

    if (!PyArg_ParseTuple(args, "II", &w, &h))
        return NULL;

    lashMapSimpleInit(map, &w, &h);

    return Py_BuildValue("O", pymap);

} 

However, when I instantiate the object and delete it or exit from Python console, the destructor doesn't seem to be called:

>>> a = mapSimpleInit(10,20)
>>> a
<capsule object "MAP_C_API" at 0x7fcf4959f930>
>>> del(a)
>>> a = mapSimpleInit(10,20)
>>> a
<capsule object "MAP_C_API" at 0x7fcf495186f0>
>>> quit()
lash@CANTANDO ~/programming/src/liblashgame $ 

My guess is that it has something to do with the Py_BuildValue() returning a new reference to the "capsule", which upon deletion doesn't affect the original. Anyway, how would I go about ensuring that the object is properly destroyed?

Using Python 3.4.3 [GCC 4.8.4] (on linux)

Upvotes: 5

Views: 1648

Answers (2)

Amaury
Amaury

Reputation: 216

The code above has a reference leak: pymap = PyCapsule_New() returns a new object (its refcount is 1), but Py_BuildValue("O", pymap) creates a new reference to the same object, and its refcount is now 2.

Just return pymap;.

Upvotes: 2

fish2000
fish2000

Reputation: 4435

Py_BuildValue("O", thingy) will just increment the refcount for thingy and return it – the docs say that it returns a “new reference” but that is not quite true when you pass it an existing PyObject*.

If these functions of yours – the ones in your question, that is – are all defined in the same translation unit, the destructor function will likely have to be declared static (so its full signature would be static void mapDestroy(PyObject* capsule);) to ensure that the Python API can look up the functions’ address properly when it comes time to call the destructor.

… You don’t have to use a static function, as long as the destructor’s address in memory is valid. For example, I’ve successfully used a C++ non-capturing lambda as a destructor, as non-capturing C++ lambdas can be converted to function pointers; if you want to use another way of obtaining and handing off a function pointer for your capsule destructor that works better for you, by all means go for it.

Upvotes: 0

Related Questions