web-dev-qa-db-ja.com

Python例外テキストを取得する方法

pythonをC++アプリケーションに埋め込みたい。Boostライブラリを使用しています。すばらしいツールです。しかし、問題が1つあります。

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に出力し、エラーをクリアするだけなので、解決できません。

45
Anton Kiselev

さて、私はそれを行う方法を見つけました。

ブーストなし(トレースバックから情報を抽出するコードが重すぎてここに投稿できないため、エラーメッセージのみ):

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
52
Anton Kiselev

これは、私がこれまでに思いついた中で最も堅牢な方法です。

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

Python C APIでは、 _PyObject_Str_ は、次の文字列形式のPython文字列オブジェクトへの新しい参照を返します。引数として渡すPythonオブジェクト-Pythonコードのstr(o)と同じように。例外オブジェクトはそうではないことに注意してください。 「行番号のような情報」を持っている-それはtracebackオブジェクトにあります( _PyErr_Fetch_ を使用して取得できます例外オブジェクトとトレースバックオブジェクトの両方)。Boostがこれらの特定のC API関数を使いやすくするために何を提供するか(もしあれば)わからないが、最悪の場合、これらの関数が提供されているので、いつでもこれらの関数に頼ることができます。 CAPI自体。

6
Alex Martelli