I am new to the Python/C API and while I got some basic functions to work, I am struggling with this one.
PyObject* sum_elements(PyObject*, PyObject *o)
{
Py_ssize_t n = PyList_Size(o);
long total = 0;
if (n < 0)
{
return PyLong_FromLong(total);
}
PyObject* item;
for (int i = 0; i < n; i++)
{
item = PyList_GetItem(o, i);
if (!PyLong_Check(item)) continue;
total += PyLong_AsLong(item);
}
return PyLong_FromLong(total);
}
Basically this is the function from the introduction on the doc page. It should receive a python list and return the sum of all elements. The function works fine if i pass a list, if I pass something else however i get the error message
SystemError: c:\_work\5\s\objects\listobject.c:187: bad argument to internal function
This situation should be handled by the if (n<0)
statement, as n is -1 if the passed object is not a list.
I am binding the function the following way:
static PyMethodDef example_module_methods[] = {
{ "sum_list", (PyCFunction)sum_elements, METH_O, nullptr},
{ nullptr, nullptr, 0, nullptr }
};
Thanks.
The error
SystemError: c:\_work\5\s\objects\listobject.c:187: bad argument to internal function
is actually occurs at
Py_ssize_t n = PyList_Size(o)
Because PyList_Size
has an extra check to see whether the object of list type, If not it will call PyErr_BadInternalCall
to raise the SystemError
. See the implementation of PyList_Size
in listobject.c
PyList_Size(PyObject *op)
{
if (!PyList_Check(op)) {
PyErr_BadInternalCall();
return -1;
}
else
return Py_SIZE(op);
}
The PyErr_BadInternalCall
is a shorthand for PyErr_SetString(PyExc_SystemError, message)
, where message indicates that an internal operation (e.g. a Python/C API function) was invoked with an illegal argument.
You should use PyList_Check
API to check whether the object is of list
type . As per the doc it Return true if object is a list object or an instance of a subtype of the list type.
PyObject* sum_elements(PyObject*, PyObject *o)
{
// Check if `o` is of `list` type, if not raise `TypeError`.
if (!PyList_Check(o)) {
PyErr_Format(PyExc_TypeError, "The argument must be of list or subtype of list");
return NULL;
}
// The argument is list type, perform the remaining calculations.
Py_ssize_t n = PyList_Size(o);
long total = 0;
if (n < 0)
{
return PyLong_FromLong(total);
}
PyObject* item;
for (int i = 0; i < n; i++)
{
item = PyList_GetItem(o, i);
if (!PyLong_Check(item)) continue;
total += PyLong_AsLong(item);
}
return PyLong_FromLong(total);
}
Once this extra check is added, the function call will raise
TypeError: The argument must be of list or sub type of list
when the argument other than list
type is supplied.