2009-09-13 89 views
38

我想在我的C++應用程序中嵌入蟒蛇。我正在使用Boost庫 - 很棒的工具。但我有一個問題。如何獲得Python異常文本

如果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並清除錯誤,以便它不能溶液

回答

41

好吧,我發現瞭如何做到這一點。

沒有提升(唯一的錯誤消息,因爲代碼提取回溯信息太重它張貼在這裏):

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

太棒了,這正是我一直在尋找的......非常棒的作品。 – 2011-01-04 01:44:55

+0

這很好。我在某些情況下發現的(對我來說,升壓;:蟒蛇::的進口的東西不是在我的PYTHONPATH)ptraceback爲0,所以我會保護免受ptraceback的使用,如果它是0。此外,你可以評論我們可以用extype做什麼?我想打印python異常類型的文本是有意義的。我們如何做到這一點? – 2014-03-06 17:18:32

+2

還有一個問題:我們不是在上面泄漏記憶嗎?什麼釋放由PyErr_Fetch返回的對象? (我不確定CPython和boost :: pythoon的情況) – elmo 2014-04-10 08:41:38

4

在Python C API,PyObject_Str返回一個新的參照Python字符串對象以字符串形式Python參數的Python對象 - 就像Python代碼中的str(o)一樣。需要注意的是異常對象不具有「像行號信息」 - 這是在回溯對象(可以使用PyErr_Fetch兩全異常對象和追蹤對象)。不知道Boost提供了什麼(如果有的話)以使這些特定的C API函數更易於使用,但是,最糟糕的情況是,您可以隨時使用這些函數,因爲它們是在C API本身提供的。

+0

非常感謝,亞歷克斯。我一直在尋找一種方式來度過,即使沒有PyAPI的直接調用 - 我升壓吼聲可以處理異常,但加速不能:( – 2009-09-13 20:14:45

+2

@Anton,很高興我幫助,所以怎麼樣upvoting並接受這個答案 - ?)使用該答案的upvotes數量下的複選標記圖標(當前爲0 ;-)。 – 2009-09-14 00:33:29

18

這是最穩健的方法我已經能夠到目前爲止:

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

很明顯,將一個空柄傳遞給'format_exception'就好了,所以你不需要'!tb'的情況。 – uckelman 2012-11-17 14:29:10

+1

這個解決方案效果很好,你需要調用PyErr_NormalizeException(&exc,&val,&tb);'like this answer(http://stackoverflow.com/a/16806477/3524982)。 – DJMcMayhem 2016-09-19 21:20:17