Search code examples
pythonpython-c-api

PyRun_File with recursive functions in Python C-API


I am working on embedding python interpreter in a larger system and one of the features is to run a python script on the fly.

Testing with the following code snippet works great, but with recursive function only the first call is executed then crashes

//C++ code
int main()
{

    Py_Initialize();
    PyObject* m_pMainModule = PyImport_AddModule("__main__");
    PyObject* m_pGlobalDict = PyModule_GetDict(m_pMainModule);
    PyObject* m_pLocalDict = PyDict_New();


    PyObject* fd = PyFile_FromString("script.py", "r");

    if (fd == NULL)
    {
        PyErr_SetString(PyExc_IOError, "File not found");
    }


    PyObject * s = PyRun_File(PyFile_AsFile(fd), "script.py", Py_file_input, m_pGlobalDict, m_pLocalDict);
    Py_XDECREF(fd);
    Py_XDECREF(s);

    if (PyErr_Occurred())
    {
        std::string result;
        PyObject* ptype;
        PyObject* pvalue;
        PyObject* ptraceback;
        PyErr_Fetch(&ptype, &pvalue, &ptraceback);
        PyErr_NormalizeException(&ptype, &pvalue, &ptraceback); // in order to convert pvalue from tuples to real objects

                                                                //Attach exception name first
        PyObject* objectStr = PyObject_GetAttrString(ptype, "__name__");
        result = PyString_AS_STRING(objectStr);
        result = "Exception: " + result;;
        Py_XDECREF(objectStr);
        objectStr = PyObject_Str(pvalue);
        if (objectStr != NULL) {
            result = result + " was raised with error message : " + PyString_AS_STRING(objectStr);
            Py_XDECREF(objectStr);

        }
        std::cout << result;

    }


    return 0;
}

Here is the python script I use

def fac(i):
    print "Call to FAC(",i,") !"
    if i <= 1:
        return 1
    else:
        return i*fac(i-1)

print "Hello world"
print fac(4)

And here is the output

Hello world
Call to FAC( 4 ) !
Exception: NameError was raised with error message : global name 'fac' is not defined

While the expected output ( when run directly by invoking the script )

Hello world
Call to FAC( 4 ) !
Call to FAC( 3 ) !
Call to FAC( 2 ) !
Call to FAC( 1 ) !
24

Any ideas on how to proceed ?


Edit:

Platform: Windows 10 x64 Python: 2.7.14


Solution

  • Explanation:

    In the following line, PyRun_File uses two different dictionaries; one for globals and one for locals.

    PyObject * s = PyRun_File(PyFile_AsFile(fd), "script.py", Py_file_input, m_pGlobalDict, m_pLocalDict);
    

    The strange part that fac function name is added to the local one of file. I have no idea Why, But I really wanaa know.

    So the recursive call fails as there is no fac inside locals of the function or the global.

    Solution:

    pass the same dictionary for both local and global

    PyObject * s = PyRun_File(PyFile_AsFile(fd), "script.py", Py_file_input, m_pGlobalDict, m_pGlobalDict);