我想从Python C API调用Python模块中定义的函数。例如,我想致电IPython.embed()
。以下C代码导致运行时错误(安装了IPython的Miniconda 3)。
#include <Python.h>
int
main(int argc, char *argv[])
{
Py_Initialize();
PyObject *ipython = PyImport_ImportModule("IPython");
if (ipython == NULL) {
PyErr_Print();
return 1;
}
PyObject *embed = PyUnicode_FromString("embed");
if (embed == NULL) {
PyErr_Print();
return 1;
}
PyObject *result = PyObject_CallMethodObjArgs(ipython, embed);
if (result == NULL) {
PyErr_Print();
return 1;
}
return 0;
}
观察到错误:
Traceback (most recent call last):
File "[...]/miniconda3/lib/python3.5/site-packages/IPython/terminal/embed.py", line 381, in embed
frame = sys._getframe(1)
ValueError: call stack is not deep enough
但是,如果我将IPython
和embed
替换为对其中定义了test
函数的简单hello
模块,则上述代码可以正常工作。< / p>
另一方面,以下代码按预期工作(即,它运行IPython REPL),但不如上面的代码灵活,不适合我的需要。
PyRun_SimpleString("\n\
from IPython import embed\n\
embed()\n\
");
以下代码也按预期工作,我最初提供的代码现在位于使用PyRun_SimpleString
调用的回调中。
static PyObject *
callback_f(PyObject *obj, PyObject *args)
{
PyObject *ipython = PyImport_ImportModule("IPython");
if (ipython == NULL) {
PyErr_Print();
exit(1);
}
PyObject *embed = PyUnicode_FromString("embed");
if (embed == NULL) {
PyErr_Print();
exit(1);
}
PyObject *result = PyObject_CallMethodObjArgs(ipython, embed);
if (result == NULL) {
PyErr_Print();
exit(1);
}
Py_RETURN_NONE;
}
int
main(int argc, char *argv[])
{
Py_Initialize();
PyObject *module = PyImport_AddModule("test");
if (module == NULL) {
PyErr_Print();
return 1;
}
PyMethodDef method_def;
method_def.ml_name = "callback";
method_def.ml_meth = callback_f;
method_def.ml_flags = 1;
method_def.ml_doc = NULL;
PyObject *callback_obj = PyCFunction_New(&method_def, NULL);
if (callback_obj == NULL) {
PyErr_Print();
return 1;
}
if (PyObject_SetAttrString(module, "callback", callback_obj) != 0) {
PyErr_Print();
return 1;
}
PyRun_SimpleString("\n\
from test import callback\n\
callback()\n\
");
}
因此,我认为PyRun_SimpleString
执行与调用IPython.embed()
所需的堆栈帧相关的初始化,但我看不到它的记录位置。
答案 0 :(得分:1)
解决方案是插入使用PyFrame_New创建的新框架,但它不在文档化的Python C API中。
#include <Python.h>
#include <frameobject.h>
int
main(int argc, char *argv[])
{
Py_Initialize();
PyThreadState *tstate = PyThreadState_GET();
if (tstate == NULL) {
PyErr_Print();
exit(1);
}
PyObject *main_module = PyImport_AddModule("__main__");
if (main_module == NULL) {
PyErr_Print();
exit(1);
}
PyObject *main_dict = PyModule_GetDict(main_module);
if (main_dict == NULL) {
PyErr_Print();
exit(1);
}
PyCodeObject *code_object = PyCode_NewEmpty("foo.py", "f", 0);
if (code_object == NULL) {
PyErr_Print();
exit(1);
}
PyFrameObject *root_frame = PyFrame_New(tstate, code_object, main_dict, main_dict);
if (root_frame == NULL) {
PyErr_Print();
exit(1);
}
tstate->frame = root_frame;
PyObject *ipython = PyImport_ImportModule("IPython");
if (ipython == NULL) {
PyErr_Print();
exit(1);
}
PyObject *embed = PyUnicode_FromString("embed");
if (embed == NULL) {
PyErr_Print();
exit(1);
}
PyObject *result = PyObject_CallMethodObjArgs(ipython, embed);
if (result == NULL) {
PyErr_Print();
exit(1);
}
}