Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I check if a PyObject is a list?

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.

like image 730
kleka Avatar asked Sep 19 '25 20:09

kleka


1 Answers

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.

like image 146
Abdul Niyas P M Avatar answered Sep 22 '25 09:09

Abdul Niyas P M