如何获取Python异常文本

时间:2009-09-13 15:36:30

标签: c++ python exception boost-python

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

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

我该怎么办?我找不到任何函数来在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()只是将错误文本打印到stderr并清除错误,因此无法解决问题

4 个答案:

答案 0 :(得分:49)

好吧,我发现了怎么做。

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

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);

和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

答案 1 :(得分:19)

这是迄今为止我能够提出的最强大的方法:

    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);
}

答案 2 :(得分:6)

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

答案 3 :(得分:0)

这个线程对我来说非常有用,但是当我尝试不带回溯地提取错误消息本身时,Python C API出现了问题。我找到了很多在Python中执行此操作的方法,但是我找不到在C ++中执行此操作的任何方法。我终于想出了以下版本,该版本尽可能少地使用C API,而更多地依赖于boost python。

>>> random.sample(list(zip(*np.where(x.reshape(10,10,10)))), 20)
[(9, 0, 5), (2, 2, 9), (3, 7, 8), (8, 2, 4), (2, 5, 9), (8, 5, 4), (3, 3, 7), (2, 6, 7), (9, 4, 8), (7, 5, 7), (7, 8, 7), (0, 8, 6), (9, 4, 3), (5, 0, 2), (4, 4, 1), (9, 0, 6), (1, 1, 8), (1, 3, 8), (5, 4, 5), (5, 2, 0)]

之所以起作用,是因为PyErr_Print()还设置了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); sys.last_valuesys.last_type的值。那些设置为与sys.last_traceback相同的值,因此在功能上类似于以下python代码:

sys.exc_info

我希望有人觉得这有用!