I am trying to clear a Python list I pass to a C function using ctypes. The calls I found at this link seem to work sometimes, but when the list I pass in contains other lists or classes as elements I get a segfault.
I have a file foo.py that looks like this:
import ctypes
_libpractice=ctypes.CDLL('./_practice.so', mode=ctypes.RTLD_GLOBAL)
_libpractice.update.argtypes = [ctypes.py_object]
_libpractice.update.restype = ctypes.py_object
def c_update(my_list):
return _libpractice.update(my_list)
and a file practice.c that looks like this:
#include <Python.h>
#include <stdio.h>
PyObject* update(PyObject* list){
Py_INCREF(list);
Py_ssize_t len = PySequence_Length(list);
PySequence_DelSlice(list, 0, len);
return list;
}
Here is an example of it working correctly with various primitive data types.
>>> import foo
>>> a = [1,2,3,4,5.5,'a',6]
>>> foo.c_update(a)
[]
>>>
Here is an example of the segfault:
>>> b = [1,2,3,[],5]
>>> foo.c_update(b)
Segmentation fault (core dumped)
I have figured out a way around this by calling PySequence_GetItem()
on every item in the list and then calling PySequence_DelSlice()
like so:
PyObject* update(PyObject* list){
Py_INCREF(list);
Py_ssize_t len = PySequence_Length(list);
for(Py_ssize_t i = 0; i<len; i++){
PyObject* item = PySequence_GetItem(list,i);
}
PySequence_DelSlice(list, 0, len);
return list;
}
However, if I pass a class as an element of the list, the destructor is not called after it is deleted from the list.
>>> import foo
>>> class Sample:
... def __del__(self):
... print('del called')
...
>>> a = Sample()
>>> a = 3
del called
>>> b = Sample()
>>> my_list = [1,b,3,[]]
>>> foo.c_update(my_list)
[]
>>>
How can I clear the list I am passing in while at the same time making sure I am correctly handling reference counts?
I don't know if this is the direct cause of the segfault, but you cannot use the Python API from within a library loaded as CDLL. You have to use PyDLL. With CDLL, Python will release the GIL before calling functions from that library, and you need the GIL.