Search code examples
pythoncpython-3.xpython-c-api

Python/C API - The result is not displayed


I would like to integrate C modules in the Python, so my choice fell on the interface Python.h. Everything compiled without errors and warnings, so I can not understand what the problem is.

C side:

#include <python3.5m/Python.h>
...
#define PyInt_AsLong(x) (PyLong_AsLong((x)))
typedef PyObject* Py;

static Py getSumma(Py self, Py args){
  Py nums;
  if (!PyArg_ParseTuple(args, "O", &nums)){
    return NULL;
  }
  size_t numsAmount = PyList_Size(args);
  int32_t summa = 0;

  for (size_t i = 0; i < numsAmount; i++){
    Py temp = PyList_GetItem(nums, i);
    int32_t num = PyInt_AsLong(temp);
    summa += num;
  }
  return Py_BuildValue("l", summa);
}

static PyMethodDef moduleMethods[] = {
  {"getSumma", (PyCFunction)getSumma, METH_VARARGS, NULL},
  {NULL, NULL, 0, NULL}
};

static PyModuleDef SummaLogic = {
  PyModuleDef_HEAD_INIT,
  "SummaLogic",
  "",
  -1,
  moduleMethods
};

PyMODINIT_FUNC PyInit_SummaLogic(void){
  return PyModule_Create(&SummaLogic);
}

setup.py:

from distutils.core import setup, Extension

SummaLogic = Extension("SummaLogic", sources=['SummaLogic.c'])
setup(ext_modules=[SummaLogic])

Python side:

from SummaLogic import getSumma

if __name__ == "__main__":
    a = [1, 2, 3]
    b = getSumma(a)
    print(b)

It seems right, but when I start it in terminal - nothing happens, just hanging without any activity. What could I miss?


Solution

  • It boils down to PyList_Size and that you don't check for errors there.

    You probably wanted to use it on nums, not args as argument. However you used on args and a very interesting thing happened:

    • args is a tuple,
    • therefore PyList_Size failed and returned -1
    • that -1 which was cast to an unsigned size_t which probably resulted in a very huge number, probably 2**64-1
    • therefore your iteration runs a "very long time" because it takes quite a while to iterate over 2**64-1 items (apart from all the out-of-bound memory accesses).

    The quick fix would be to use:

    Py_ssize_t listlength = PyList_Size(nums);  /* nums instead of args */
    if (listlength == -1) {  /* check for errors */
        return NULL;
    }
    size_t numsAmount = (size_t)listlength /* cast to size_t only after you checked for errors */
    

    However you should check what the error conditions are and test for them after every python C API function call otherwise you'll get a lot of undefined behaviours. Also I probably would stick to the defined return types instead of int32_t (PyInt_AsLong returns long so you might get weird casting errors there as well!), size_t, ... and the typedef PyObject* Py; makes things really tricky for someone who regularly writes C extensions.