alanc10n alanc10n - 1 month ago 13
C Question

Create and call python function from string via C API

Is it possible to load a python function from a string and then call that function with arguments and get the return value?

I'm using the python C API to run python code from inside my C++ application. I'm able to load a module from a file using

PyImport_Import
, get a function object from that using
PyObject_GetAttrString
, and call the function with
PyObject_CallObject
. What I'd like to do is to load the module/function from a string instead of a file. Is there some equivalent to
PyImport_Import
which would allow me to pass it a string instead of a file? I need to pass arguments to the function I'm calling and I need access to the return value, so I can't just use
PyRun_SimpleString
.




Edit:



I found this solution after getting turned on to
PyRun_String
. I'm creating a new module, getting its dictionary object, passing that along in a call to
PyRun_String
to define a function in my new module, then getting a function object for that newly created function and calling it via
PyObject_CallObject
, passing my args. This is what I've found to solve my problem:
main.cpp



int main()
{
PyObject *pName, *pModule, *pArgs, *pValue, *pFunc;
PyObject *pGlobal = PyDict_New();
PyObject *pLocal;

//Create a new module object
PyObject *pNewMod = PyModule_New("mymod");

Py_Initialize();
PyModule_AddStringConstant(pNewMod, "__file__", "");

//Get the dictionary object from my module so I can pass this to PyRun_String
pLocal = PyModule_GetDict(pNewMod);

//Define my function in the newly created module
pValue = PyRun_String("def blah(x):\n\tprint 5 * x\n\treturn 77\n", Py_file_input, pGlobal, pLocal);
Py_DECREF(pValue);

//Get a pointer to the function I just defined
pFunc = PyObject_GetAttrString(pNewMod, "blah");

//Build a tuple to hold my arguments (just the number 4 in this case)
pArgs = PyTuple_New(1);
pValue = PyInt_FromLong(4);
PyTuple_SetItem(pArgs, 0, pValue);

//Call my function, passing it the number four
pValue = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
printf("Returned val: %ld\n", PyInt_AsLong(pValue));
Py_DECREF(pValue);

Py_XDECREF(pFunc);
Py_DECREF(pNewMod);
Py_Finalize();

return 0;
}





Here is the rest of my original post, left for posterity:



Here's what I was doing originally:
main.cpp
:


#include <Python.h>

int main()
{
PyObject *pName, *pModule, *pArgs, *pValue, *pFunc;

Py_Initialize();
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('')");
pName = PyString_FromString("atest");
pModule = PyImport_Import(pName);
Py_DECREF(pName);

if(pModule == NULL)
{
printf("PMod is null\n");
PyErr_Print();
return 1;
}

pFunc = PyObject_GetAttrString(pModule, "doStuff");
pArgs = PyTuple_New(1);
pValue = PyInt_FromLong(4);
PyTuple_SetItem(pArgs, 0, pValue);

pValue = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
printf("Returned val: %ld\n", PyInt_AsLong(pValue));
Py_DECREF(pValue);

Py_XDECREF(pFunc);
Py_DECREF(pModule);

Py_Finalize();

return 0;
}


And
atest.py
:


def doStuff( x):
print "X is %d\n" % x
return 2 * x

Answer

PyRun_String in the Python C API is probably what you're looking for. See: http://docs.python.org/c-api/veryhigh.html

Comments