2009-09-13 17 views
38

Chcę osadzić python w mojej aplikacji C++. Używam biblioteki Boost - świetne narzędzie. Ale mam jeden problem.Jak uzyskać tekst wyjątku Python

Jeśli funkcja pytona zgłasza wyjątek, chcę go przechwycić i wydrukować błąd w mojej aplikacji lub uzyskać szczegółowe informacje, takie jak numer wiersza w skrypcie python, który spowodował błąd.

Jak mogę to zrobić? Nie mogę znaleźć żadnych funkcji, aby uzyskać szczegółowe informacje o wyjątkach w Pythonie API lub 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() po prostu drukuje tekst błędu na stderr i skasowanie błędu, więc nie może być rozwiązaniem

Odpowiedz

41

Cóż, dowiedziałem się, jak to zrobić.

Bez doładowania (tylko komunikat o błędzie, ponieważ kod, aby wyodrębnić informacje z traceback jest zbyt ciężki, aby opublikować go tutaj):

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

i zwiększyć wersji

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

Niesamowite, dokładnie to czego szukałem ... działa świetnie. –

+0

To jest miłe. Odkryłem w niektórych przypadkach (dla mnie, boost:: python :: import czegoś nie w mojej PYTHONPATH) ptraceback będzie wynosił 0, więc zabezpieczyłbym się przed użyciem ptracebacka, jeśli jest 0. Również, możesz skomentować, co możemy zrobić z extype? Przypuszczam, że drukowanie tekstu typu wyjątku Pythona ma znaczenie. Jak to zrobimy? –

+2

Jedno dodatkowe pytanie: czy nie mamy przeciekać pamięci w górze? Co uwalnia obiekty zwrócone przez PyErr_Fetch? (Nie jestem pewien co do obu przypadków CPython i boost :: pythoon) – elmo

4

W Python C API, PyObject_Str zwraca nowy odwołanie do obiektu Python ciąg z postaci strun obiektu Pythona, który przekazujesz jako argument - podobnie jak str(o) w kodzie Pythona. Zauważ, że obiekt wyjątku nie ma "informacji takich jak numer linii" - jest to obiekt traceback (można użyć obiektu PyErr_Fetch, aby uzyskać zarówno obiekt wyjątku, jak i obiekt traceback). Nie wiem, co (jeśli w ogóle) Boost zapewnia, że ​​te specyficzne funkcje API C są łatwiejsze w użyciu, ale w najgorszym przypadku zawsze można skorzystać z tych funkcji, ponieważ są one oferowane w samym API C.

+0

Wielkie dzięki, Alex. Szukałem sposobu, aby zrobić to bez bezpośredniego wywoływania PyAPI - i dzięki temu Boost może poradzić sobie z wyjątkami, ale Boost nie może :( –

+2

@Anton, cieszę się, że pomogłem, więc co z przegrywaniem i akceptowaniem tej odpowiedzi? -) Użyj ikona znacznika wyboru pod liczbą pobrań dla tej odpowiedzi (obecnie 0 ;-). –

18

Jest to najbardziej wytrzymałe metoda Udało mi się dojść do tej pory:

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

Najwyraźniej można przekazać pusty uchwyt do 'format_exception', więc nie potrzebujesz przypadku'! Tb'. – uckelman

+1

To rozwiązanie działa świetnie, ale musisz wywołać 'PyErr_NormalizeException (& exc, & val, &tb);' jak [ta odpowiedź] (http://stackoverflow.com/a/16806477/3524982) mówi. – DJMcMayhem

Powiązane problemy