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
.
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'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
We can call a C function from Python program using the ctypes module.
Running functions Almost the entire Python interpreter can be summarized into one C-level function: PyEval_EvalFrameEx. This function is the interpreter loop. Consisting of 3k lines of code, its job is to evaluate a frame, or in other words, run it.
The Python/C API allows for compiled pieces of code to be called from Python programs or executed within the CPython interpreter. This process of producing compiled code for use by CPython is generally known as "extending" Python and the compiled pieces of code to be used are known as "extension modules".
PyRun_String
in the Python C API is probably what you're looking for. See: http://docs.python.org/c-api/veryhigh.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With