pythoncpython-3.xpython-itertoolsextension-modules

How to call a builtin function (or method) from C code of a Python extension module?


What I currently want to accomplish is to tweak Pythons itertools module function combinations to sort the passed iterable before creating combinations out of it with the goal of having the returned combinations sorted.

I am working on a Python extension module for the first time and my only experience up to now was to write and compile a "Hello World" like Python extension module, but I hope that my overall programming experience in a couple of programming languages is a solid enough foundation I can build upon to succeed on this challenge.

I know that there is a builtin Python function sorted() which can sort the iterable passed to combinations but I don't know how to call it from within the C code of the extension module.

I have tried just to write iterable = sorted(iterable); but even if the module compiles (with a warning) the import of the compiled module fails with ImportError: cgitertools.cpython-36m-x86_64-linux-gnu.so: undefined symbol: sorted

My question is:

How to call the Pythons builtin method (using as example sorted()) from within C code of a Python extension module?

Below all the details of what I have tried and why it didn't work:

combinations_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    combinationsobject *co;
    Py_ssize_t n;
    Py_ssize_t r;
    PyObject *pool = NULL;
    PyObject *iterable = NULL;
    Py_ssize_t *indices = NULL;
    Py_ssize_t i;
    static char *kwargs[] = {"iterable", "r", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "On:combinations", kwargs,
                                     &iterable, &r))
        return NULL;

    // iterable.sort(); doesn't work ... cgitertoolsmodule.c:2398:13: error: request for member ‘sort’ in something not a structure or union
    // iterable.__sort__(); doesn't work either with same error
    // COMPILES, but gives ERROR on import in Python: 
    iterable = sorted(iterable);

$ python3.6 cgitertoolsmodule-setup.py build
running build
running build_ext
building 'cgitertools' extension
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/usr/local/include/python3.6m -c cgitertoolsmodule.c -o build/temp.linux-x86_64-3.6/cgitertoolsmodule.o
cgitertoolsmodule.c: In function ‘combinations_new’:
cgitertoolsmodule.c:2400:16: warning: implicit declaration of function ‘sorted’ [-Wimplicit-function-declaration]
     iterable = sorted(iterable);
                ^
cgitertoolsmodule.c:2400:14: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
     iterable = sorted(iterable);
              ^
gcc -pthread -shared build/temp.linux-x86_64-3.6/cgitertoolsmodule.o -o build/lib.linux-x86_64-3.6/cgitertools.cpython-36m-x86_64-linux-gnu.so

$ python3.6
Python 3.6.1 (default, Apr 18 2017, 23:00:41) 
[GCC 5.4.1 20160904] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from cgitertools import combinations
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: cgitertools.cpython-36m-x86_64-linux-gnu.so: undefined symbol: sorted

Solution

  • You should get the sorted function out of the builtins and than call it:

    PyObject *builtins = PyEval_GetBuiltins(); 
    PyObject *sorted = PyDict_GetItemString(builtins , "sorted");
    PyObject *sorted_list = PyEval_CallFunction(sorted, "(O)", iterable);
    
    //... do something with the sorted_list
    
    Py_DECREF(sorted_list);