【发布时间】:2020-11-05 15:22:32
【问题描述】:
当我在 C++ 中调用 PyRun_SimpleString(...) 并且我的 Python 脚本包含无效语法或错误时,我会收到一条错误消息并打印到我的控制台。
如何阻止该错误消息刷新到控制台,而是将该错误消息作为 string 或 const char* 并可能以我自己的方式使用该字符串(显示它作为 GUI 文本或其他东西)?
【问题讨论】:
当我在 C++ 中调用 PyRun_SimpleString(...) 并且我的 Python 脚本包含无效语法或错误时,我会收到一条错误消息并打印到我的控制台。
如何阻止该错误消息刷新到控制台,而是将该错误消息作为 string 或 const char* 并可能以我自己的方式使用该字符串(显示它作为 GUI 文本或其他东西)?
【问题讨论】:
PyRun_SimpleString 是PyRun_SimpleStringFlags 的简化接口,并为PyRun_SimpleStringFlags 引用docs:
如果出现错误,则无法获取异常信息。
也很重要,
请注意,如果引发了其他未处理的 SystemExit,只要未设置 Py_InspectFlag,此函数将不会返回 -1,而是退出进程。
你可能不想要那个。
您应该改用PyRun_String 或PyRun_StringFlags。可能是PyRun_StringFlags,因为它可以让您记住__future__ 导入的效果。
PyRun_StringFlags 具有以下签名:
PyObject* PyRun_StringFlags(const char *str, int start, PyObject *globals, PyObject *locals, PyCompilerFlags *flags)
str 是要运行的源字符串。
start 是 Py_eval_input、Py_file_input 或 Py_single_input,具体取决于您是否希望它的行为类似于 eval、运行脚本或交互运行语句。 Py_eval_input 只允许一个表达式,其值将从PyRun_StringFlags 调用返回。 Py_single_input 执行表达式自动打印之类的操作,并且只允许一个(可能是复合的)语句。您可能需要Py_file_input 或Py_single_input(如果您使用Py_single_input,您可能需要自定义sys.displayhook)。
globals 和 locals 是要使用的全局和本地字典,就像调用 exec 或 eval 时一样。您可能想要获取__main__ 模块的__dict__,如下所示:
m = PyImport_AddModule("__main__");
if (m == NULL)
/* bad stuff happened */
do_something_about_that();
Py_INCREF(m);
d = PyModule_GetDict(m);
...
/* later, when you're done with m and d */
Py_DECREF(m);
并为这两个参数使用d。
PyImport_AddModule 和 PyModule_GetDict 都返回借用的引用,但我使用 Py_INCREF 来获取 m 的新引用,并在完成引用后使用 Py_DECREF。这是因为引用 PyImport_AddModule 返回是从 sys.modules['__main__'] 借来的,并且有可能从 sys.modules 中删除 __main__。 (d 没有这种担心,因为这是从__main__.__dict__ 借来的,只要__main__ 本身存在,就不能删除或重新分配。只要我们的m 引用是安全的,d 就是安全的。 )
flags 是指向具有一个相关字段的结构的指针,int 表示编译器标志。 __future__ 导入会影响此字段。为确保来自一次 PyRun_StringFlags 调用的 __future__ 语句在下一次调用中仍然处于活动状态,您应该在运行之间不会被释放或擦除的地方创建一个结构:
PyCompilerFlags flags = {0};
并将&flags 作为此参数传递。如果你不想想记住__future__语句的效果,你可以传递NULL,或者使用PyRun_String,它没有这个参数。
PyRun_StringFlags 将在成功时返回一个 Python 对象(通常是 None,并记得取消引用),在异常时返回 NULL。如果有异常,可以通过C-level API for exception handling,特别是PyErr_Fetch、PyErr_NormalizeException、PyException_SetTraceback进行检查。
在设置异常时运行以下命令:
PyObject *type, *val, *tb;
PyErr_Fetch(&type, &val, &tb);
是一种 C 级别,相当于输入 except 块并获取 sys.exc_info(),除了 val 可能是 NULL 或不是 type 的实例,以及异常的 __traceback__ 属性可能没有设置。以下:
PyErr_NormalizeException(&type, &val, &tb);
if (tb != NULL) {
PyException_SetTraceback(val, tb);
}
如果你需要,进行标准化。
【讨论】: