2009-09-13 41 views
38

Tôi muốn nhúng python vào ứng dụng C++ của mình. Tôi đang sử dụng thư viện Boost - công cụ tuyệt vời. Nhưng tôi có một vấn đề.Cách lấy văn bản ngoại lệ Python

Nếu hàm python ném ngoại lệ, tôi muốn bắt và in lỗi trong ứng dụng của tôi hoặc nhận một số thông tin chi tiết như số dòng trong tập lệnh python gây ra lỗi.

Tôi có thể làm như thế nào? Tôi không thể tìm thấy bất kỳ chức năng nào để có được thông tin ngoại lệ chi tiết trong Python API hoặc 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() chỉ in văn bản lỗi để stderr và xóa lỗi nên nó không thể là giải pháp

Trả lời

41

Vâng, tôi đã tìm hiểu cách thực hiện.

Nếu không tăng (chỉ thông báo lỗi, bởi vì mã để trích xuất thông tin từ traceback là quá nặng để đăng nó ở đây):

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

Và BOOST phiên bản

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

Tuyệt vời, đây chính xác là những gì tôi đã tìm kiếm ... hoạt động tuyệt vời. –

+0

Điều này thật tuyệt. Tôi đã phát hiện trong một số trường hợp (đối với tôi, một tăng; python :: import của một cái gì đó không phải trong PYTHONPATH) ptraceback của tôi sẽ là 0, vì vậy tôi sẽ bảo vệ chống lại việc sử dụng ptraceback nếu nó là 0. Ngoài ra, bạn có thể bình luận về những gì chúng ta có thể làm với extype? Tôi cho rằng in văn bản của kiểu ngoại lệ python là có ý nghĩa. làm sao chúng ta làm việc đó bây giờ? –

+2

Một câu hỏi bổ sung: chúng tôi có bị rò rỉ bộ nhớ ở trên không? Điều gì giải phóng các đối tượng được trả về bởi PyErr_Fetch? (Tôi không chắc chắn về cả hai CPython và boost :: pythoon trường hợp) – elmo

4

Trong API Python C, PyObject_Str trả về một tham chiếu mới cho một đối tượng chuỗi Python với dạng chuỗi của đối tượng Python bạn đang truyền dưới dạng đối số - giống như str(o) trong mã Python. Lưu ý rằng đối tượng ngoại lệ không có "thông tin giống như số dòng" - đó là trong đối tượng truy nguyên traceback (bạn có thể sử dụng PyErr_Fetch để nhận cả đối tượng ngoại lệ và đối tượng truy nguyên). Không biết cái gì (nếu có) Boost cung cấp các hàm C API cụ thể này dễ sử dụng hơn, nhưng trường hợp xấu nhất, bạn luôn có thể sử dụng các hàm này khi chúng được cung cấp trong chính API C.

+0

cảm ơn rất nhiều, Alex. Tôi đã tìm kiếm một cách để làm cho nó mà không cần gọi trực tiếp của PyAPI - tôi thougth Boost có thể đối phó với ngoại lệ, nhưng Boost không thể: ( –

+2

@Anton, vui vì tôi đã giúp, vì vậy những gì về upvoting và chấp nhận câu trả lời này? -) Sử dụng biểu tượng dấu kiểm theo số lượng upvotes cho câu trả lời này (hiện tại là 0 ;-). –

18

Đây là phương pháp mạnh mẽ nhất Tôi đã có thể xuất hiện cho đến thời điểm này:

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

Có vẻ như ok để chuyển một tay cầm trống sang 'format_exception', vì vậy bạn không cần trường hợp'! Tb'. – uckelman

+1

Giải pháp này hoạt động tốt, bạn sẽ cần phải gọi 'PyErr_NormalizeException (& exc, & val, &tb);' như [câu trả lời này] (http://stackoverflow.com/a/16806477/3524982) nói. – DJMcMayhem

Các vấn đề liên quan