python 如何获取Python异常文本

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1418015/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 22:12:13  来源:igfitidea点击:

How to get Python exception text

c++pythonexceptionboost-python

提问by Anton Kiselev

I want to embed python in my C++ application. I'm using Boost library - great tool. But i have one problem.

我想在我的 C++ 应用程序中嵌入 python。我正在使用 Boost 库 - 很棒的工具。但我有一个问题。

If python function throws an exception, i want to catch it and print error in my application or get some detailed information like line number in python script that caused error.

如果 python 函数抛出异常,我想捕获它并在我的应用程序中打印错误或获取一些详细信息,例如导致错误的 python 脚本中的行号。

How can i do it? I can't find any functions to get detailed exception information in Python API or Boost.

我该怎么做?我在 Python API 或 Boost 中找不到任何函数来获取详细的异常信息。

try {
module=import("MyModule"); //this line will throw excetion if MyModule contains an   error
} catch ( error_already_set const & ) {
//Here i can said that i have error, but i cant determine what caused an error
std::cout << "error!" << std::endl;
}

PyErr_Print() just prints error text to stderr and clears error so it can't be solution

PyErr_Print() 只是将错误文本打印到 stderr 并清除错误,因此无法解决

回答by Anton Kiselev

Well, I found out how to do it.

好吧,我找到了方法。

Without boost (only error message, because code to extract info from traceback is too heavy to post it here):

没有提升(只有错误消息,因为从回溯中提取信息的代码太重,无法在此处发布):

PyObject *ptype, *pvalue, *ptraceback;
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
//pvalue contains error message
//ptraceback contains stack snapshot and many other information
//(see python traceback structure)

//Get error message
char *pStrErrorMessage = PyString_AsString(pvalue);

And BOOST version

和 BOOST 版本

try{
//some code that throws an error
}catch(error_already_set &){

    PyObject *ptype, *pvalue, *ptraceback;
    PyErr_Fetch(&ptype, &pvalue, &ptraceback);

    handle<> hType(ptype);
    object extype(hType);
    handle<> hTraceback(ptraceback);
    object traceback(hTraceback);

    //Extract error message
    string strErrorMessage = extract<string>(pvalue);

    //Extract line number (top entry of call stack)
    // if you want to extract another levels of call stack
    // also process traceback.attr("tb_next") recurently
    long lineno = extract<long> (traceback.attr("tb_lineno"));
    string filename = extract<string>(traceback.attr("tb_frame").attr("f_code").attr("co_filename"));
    string funcname = extract<string>(traceback.attr("tb_frame").attr("f_code").attr("co_name"));
... //cleanup here

回答by mah

This is the most robust method I've been able to come up so far:

这是迄今为止我能想到的最强大的方法:

    try {
        ...
    }
    catch (bp::error_already_set) {
        if (PyErr_Occurred()) {
            msg = handle_pyerror(); 
        }
        py_exception = true;
        bp::handle_exception();
        PyErr_Clear();
    }
    if (py_exception) 
    ....


// decode a Python exception into a string
std::string handle_pyerror()
{
    using namespace boost::python;
    using namespace boost;

    PyObject *exc,*val,*tb;
    object formatted_list, formatted;
    PyErr_Fetch(&exc,&val,&tb);
    handle<> hexc(exc),hval(allow_null(val)),htb(allow_null(tb)); 
    object traceback(import("traceback"));
    if (!tb) {
        object format_exception_only(traceback.attr("format_exception_only"));
        formatted_list = format_exception_only(hexc,hval);
    } else {
        object format_exception(traceback.attr("format_exception"));
        formatted_list = format_exception(hexc,hval,htb);
    }
    formatted = str("\n").join(formatted_list);
    return extract<std::string>(formatted);
}

回答by Alex Martelli

In the Python C API, PyObject_Strreturns a new reference to a Python string object with the string form of the Python object you're passing as the argument -- just like str(o)in Python code. Note that the exception object does not have "information like line number" -- that's in the tracebackobject (you can use PyErr_Fetchto get both the exception object and the traceback object). Don't know what (if anything) Boost provides to make these specific C API functions easier to use, but, worst case, you could always resort to these functions as they are offered in the C API itself.

在 Python C API 中,PyObject_Str以您作为参数传递的 Python 对象的字符串形式返回对 Python 字符串对象的新引用——就像str(o)在 Python 代码中一样。请注意,异常对象没有“行号之类的信息”——这在回溯对象中(您可以使用它PyErr_Fetch来获取异常对象和回溯对象)。不知道 Boost 提供了什么(如果有的话)来使这些特定的 C API 函数更易于使用,但是,在最坏的情况下,您总是可以使用 C API 本身提供的这些函数。

回答by James

This thread has been very useful for me, but I had problems with the Python C API when I tried to extract the error message itself with no traceback. I found plenty of ways to do that in Python, but I couldn't find any way to do this in C++. I finally came up with the following version, which uses the C API as little as possible and instead relies much more on boost python.

这个线程对我非常有用,但是当我尝试在没有回溯的情况下提取错误消息本身时,我遇到了 Python C API 的问题。我在 Python 中找到了很多方法来做到这一点,但在 C++ 中我找不到任何方法来做到这一点。我终于想出了以下版本,它尽可能少地使用 C API,而是更多地依赖于 boost python。

PyErr_Print();

using namespace boost::python;

exec("import traceback, sys", mainNamespace_);
auto pyErr = eval("str(sys.last_value)", mainNamespace_);
auto pyStackTrace = eval("'\n'.join(traceback.format_exception(sys.last_type, sys.last_value, sys.last_traceback))", mainNamespace_);

stackTraceString_ = extract<std::string>(pyStackTrace);
errorSummary_ = extract<std::string>(pyErr);

The reason this works is because PyErr_Print()also sets the value for sys.last_value, sys.last_type, and sys.last_traceback. Those are set to the same values as sys.exc_infowould give, so this is functionally similar to the following python code:

这部作品的原因是因为PyErr_Print()也设定了值sys.last_valuesys.last_typesys.last_traceback。这些设置为与给出的值相同的值sys.exc_info,因此这在功能上类似于以下 python 代码:

import traceback
import sys

try:
    raise RuntimeError("This is a test")
except:
    err_type = sys.exc_info()[0]
    value = sys.exc_info()[1]
    tb = sys.exc_info()[2]

    stack_trace = "\n".join(traceback.format_exception(err_type, value, tb))
    error_summary = str(value)


print(stack_trace)
print(error_summary)

I hope someone finds this useful!

我希望有人觉得这很有用!