我想从 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
里面定义的函数。
另一方面,以下代码按预期工作(即,它运行 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()
所需的堆栈帧相关的初始化,但我看不到它在哪里记录在案。
最佳答案
一种解决方案是插入一个使用 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);
}
}
关于python - 从 C 调用 IPython.embed() 方法时为 "ValueError: call stack is not deep enough",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42974139/