Search code examples
pythoncpython-c-api

Python C API free() errors after using Py_SetPath() and Py_GetPath()


I'm trying to figure out why I can't simply get and set the python path through its C API. I am using Python3.6, on Ubuntu 17.10 with gcc version 7.2.0. Compiling with:

gcc pytest.c `python3-config --libs` `python3-config --includes`

#include <Python.h>

int main()
{
    Py_Initialize(); // removes error if put after Py_SetPath

    printf("setting path\n"); // prints
    Py_SetPath(L"/usr/lib/python3.6"); // Error in `./a.out': free(): invalid size: 0x00007fd5a8365030 ***
    printf("success\n"); // doesn't print
    return 0;
}

Setting the path works fine, unless I also try to get the path prior to doing so. If I get the path at all, even just to print without modifying the returned value or anything, I get a "double free or corruption" error.

Very confused. Am I doing something wrong or is this a bug? Anyone know a workaround if so?

Edit: Also errors after calling Py_Initialize();. Updated code. Now errors even if I don't call Py_GetPath() first.


Solution

  • From alk it seems related to this bug: https://bugs.python.org/issue31532

    Here is the workaround I am using. Since you can't call Py_GetPath() before Py_Initialize(), and also seemingly you can't call Py_SetPath() after Py_Initialize(), you can add to or get the path like this after calling Py_Initialize():

    #include <Python.h>
    
    int main()
    {
        Py_Initialize();
    
        // get handle to python sys.path object
        PyObject *sys = PyImport_ImportModule("sys");
        PyObject *path = PyObject_GetAttrString(sys, "path");
    
        // make a list of paths to add to sys.path
        PyObject *newPaths = PyUnicode_Split(PyUnicode_FromWideChar(L"a:b:c", -1), PyUnicode_FromWideChar(L":", 1), -1);
    
        // iterate through list and add all paths
        for(int i=0; i<PyList_Size(newPaths); i++) {
            PyList_Append(path, PyList_GetItem(newPaths, i));
        }
    
        // print out sys.path after appends
        PyObject *newlist = PyUnicode_Join(PyUnicode_FromWideChar(L":", -1), path);
        printf("newlist = %ls\n", PyUnicode_AsWideCharString(newlist, NULL));
        return 0;
    }