Importing Python modules from files is relatively easy with the Python C API with PyImport_Import() however I would need to use functions stored in strings. Is there a way to import python modules from strings (to clarify: there is no file; the code is in a string) or will I have to save the strings as temp files?
3 Answers
No need to use temp files. Use this code:
const char *MyModuleName = "blah";
const char *MyModuleCode = "print 'Hello world!'";
PyObject *pyModule = PyModule_New(MyModuleName);
// Set properties on the new module object
PyModule_AddStringConstant(pyModule, "__file__", "");
PyObject *localDict = PyModule_GetDict(pyModule); // Returns a borrowed reference: no need to Py_DECREF() it once we are done
PyObject *builtins = PyEval_GetBuiltins(); // Returns a borrowed reference: no need to Py_DECREF() it once we are done
PyDict_SetItemString(localDict, "__builtins__", builtins);
// Define code in the newly created module
PyObject *pyValue = PyRun_String(MyModuleCode, Py_file_input, localDict, localDict);
if (pyValue == NULL) {
// Handle error
}
else
Py_DECREF(pyValue);
This is code taken from a real commercial application (I've slightly modified it by removing error handling and other non-needed details).
Just set the wanted module name in MyModuleName and the Python code in MyModuleCode and you are done!
Comments
If my understanding is correct, you could use PyImport_ImportModule which takes a const char* name to specify the module to be imported.
Since my understanding was incorrect:
It would generally be better to dump the contents to a .py file and then execute them with PyRun_File but, if you have strings and want to work with those I guess you could use Py_CompileString to compile it to a code object and then feed it to PyEval_EvalCode for evaluation.
6 Comments
I've had success following the strategy laid out by Dimitirs.
Py_Initialize();
PyObject *module = Py_CompileString(
// language: Python
R"EOT(
fake_policy = {
"maxConnections": 4,
"policyDir": "/tmp",
"enableVhostPolicy": True,
"enableVhostNamePatterns": False,
})EOT",
"test_module", Py_file_input);
REQUIRE(module != nullptr);
PyObject *pModuleObj = PyImport_ExecCodeModule("test_module", module);
REQUIRE(pModuleObj != nullptr);
// better to check with an if, use PyErr_Print() or such to read the error
PyObject *pAttrObj = PyObject_GetAttrString(pModuleObj, "fake_policy");
REQUIRE(pAttrObj != nullptr);
auto *entity = reinterpret_cast<qd_entity_t *>(pAttrObj);
REQUIRE(qd_entity_has(entity, "enableVhostNamePatterns")); // sanity check for the test input
// call Py_DecRef s
I used https://awasu.com/weblog/embedding-python/calling-python-code-from-your-program/ as another reference, when writing this.