如何在 CPython 中获取字符串形式的错误消息

Rel*_*mer 0 c++ python embed cpython

当我PyRun_SimpleString(...)用 C++ 调用并且我的 Python 脚本包含无效语法或错误时,我会收到一条错误消息,该消息会打印到我的控制台。

如何阻止该错误消息刷新到控制台,而是以字符串a 的形式获取该错误消息const char*,并可能以我自己的方式使用该字符串(将其显示为 GUI 文本或其他内容)?

use*_*ica 6

PyRun_SimpleString是 的简化接口PyRun_SimpleStringFlags,并引用的文档PyRun_SimpleStringFlags

如果出现错误,则无法获取异常信息。

也相当重要,

请注意,如果引发未处理的 SystemExit,只要未设置 Py_InspectFlag,该函数就不会返回 -1,而是退出进程。

你可能不想要这样。


您应该改用PyRun_Stringor PyRun_StringFlags。可能是PyRun_StringFlags,因为它可以让您记住导入的影响__future__

PyRun_StringFlags有以下签名:

PyObject* PyRun_StringFlags(const char *str, int start, PyObject *globals, PyObject *locals, PyCompilerFlags *flags)

  • str是要运行的源字符串。

  • startPy_eval_inputPy_file_inputPy_single_input,具体取决于您是否希望其行为类似于eval运行脚本或交互式运行语句。Py_eval_input只允许单个表达式,其值将从调用中返回PyRun_StringFlagsPy_single_input执行诸如表达式自动打印之类的操作,并且只允许一个(可能是复合的)语句。您可能需要Py_file_inputor Py_single_input(如果您使用Py_single_input,您可能需要自定义sys.displayhook)。

  • globalslocals是要使用的全局和本地字典,就像调用execor时一样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);
    
    Run Code Online (Sandbox Code Playgroud)

    并用于d这两个参数。

    PyImport_AddModule两者PyModule_GetDict都返回借用的引用,但我曾经Py_INCREF为 获取新的引用m,并且Py_DECREF当我们完成该引用时。这是因为引用PyImport_AddModule返回是从 借用的sys.modules['__main__'],并且可能会从 中删除 某些内容。( 没有这样的担心,因为它是从 借用的,只要它本身存在就不能被删除或重新分配。只要我们的引用是安全的,它就是安全的。)__main__sys.modulesd__main__.__dict____main__md

  • flags是一个指向具有一个相关字段的结构的指针,一个int表示编译器标志。__future__进口影响这一领域。为了确保__future__一次PyRun_StringFlags调用中的语句在下一次调用中仍然处于活动状态,您应该在某个位置创建一个结构,它不会在运行之间被释放或擦除:

    PyCompilerFlags flags = {0};
    
    Run Code Online (Sandbox Code Playgroud)

    &flags作为这个参数传递。如果您不想记住__future__语句效果,可以传递NULL或 use PyRun_String,它没有此参数。


PyRun_StringFlags成功时将返回一个 Python 对象(通常是None,并记住 decref 引用),异常时返回 NULL。如果出现异常,可以通过异常处理的C级API进行检查,特别是PyErr_FetchPyErr_NormalizeException、 和PyException_SetTraceback

设置异常时运行以下命令:

PyObject *type, *val, *tb;
PyErr_Fetch(&type, &val, &tb);
Run Code Online (Sandbox Code Playgroud)

有点类似于输入except块并获取 的C 级别sys.exc_info(),只不过它val可能是NULL也可能不是 的实例,并且可能未设置type异常的属性。__traceback__下列:

PyErr_NormalizeException(&type, &val, &tb);
if (tb != NULL) {
  PyException_SetTraceback(val, tb);
}
Run Code Online (Sandbox Code Playgroud)

如果需要的话,可以进行标准化。