python - 从 C 调用 IPython.embed() 方法时为 "ValueError: call stack is not deep enough"

标签 python c api ipython

我想从 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

但是,如果我将 IPythonembed 替换为对简单 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/

相关文章:

c - extern char **environ 和 extern char *environ[] 有什么区别

javascript - 如何在谷歌折线图中按年份分隔日期

node.js - 如何在expressjs上将API(路由)转换为CLI

python - 序列化单个对象?

python - 禁用 TreeView 列 "minimum size"行为

python - 没有名为 ‘picamera' 的模块

c - 如何分配结构的结构?

将程序员的记事本配置为 Visual C++ 中的编辑器

java - 如何通过url获取分页中的所有数据

python - 有没有办法只用 pandas 将公式写入 .xlsx 文件,即不使用像 xlsxwriter/openpyxl 这样的工具?