52

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

Python関数が例外をスローした場合、それをキャッチしてアプリケーションでエラーを出力するか、エラーの原因となったPythonスクリプトの行番号などの詳細情報を取得します。

どうすればいいですか?PythonAPIまたは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

5 に答える 5

65

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

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

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
于 2009-09-13T20:05:17.273 に答える
23

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

    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);
}
于 2011-07-04T21:22:58.007 に答える
7

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

于 2009-09-13T17:54:12.127 に答える
3

このスレッドは私にとって非常に便利でしたが、トレースバックなしでエラーメッセージ自体を抽出しようとすると、PythonCAPIで問題が発生しました。Pythonでこれを行う方法はたくさん見つかりましたが、C++でこれを行う方法は見つかりませんでした。私はついに次のバージョンを思いつきました。これは、C APIをできるだけ使用せず、代わりにブーストPythonに大きく依存しています。

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

これが機能する理由は、PyErr_Print()sys.last_valueが、、、sys.last_typeおよびの値も設定するためsys.last_tracebackです。それらは与えるのと同じ値に設定されているsys.exc_infoので、これは次のPythonコードと機能的に似ています。

import traceback
import sys

try:
    raise RuntimeError("This is a test")
except:
    err_type = sys.exc_info()[0]
    value = sys.exc_info()[1]
    tb = sys.exc_info()[2]

    stack_trace = "\n".join(traceback.format_exception(err_type, value, tb))
    error_summary = str(value)


print(stack_trace)
print(error_summary)

誰かがこれが役に立つと思ってくれることを願っています!

于 2019-09-11T20:20:53.200 に答える
1

これは、他のいくつかの回答とコメントに基づいた、最新のC++とコメントで適切にフォーマットされたコードです。最小限のテストですが、動作しているようです。

#include <string>
#include <boost/python.hpp>
#include <Python.h>

// Return the current Python error and backtrace as a string, or throw
// an exception if there was none.
std::string python_error_string() {
  using namespace boost::python;

  PyObject* ptype = nullptr;
  PyObject* pvalue = nullptr;
  PyObject* ptraceback = nullptr;

  // Fetch the exception information. If there was no error ptype will be set
  // to null. The other two values might set to null anyway.
  PyErr_Fetch(&ptype, &pvalue, &ptraceback);
  if (ptype == nullptr) {
    throw std::runtime_error("A Python error was detected but when we called "
                             "PyErr_Fetch() it returned null indicating that "
                             "there was no error.");
  }

  // Sometimes pvalue is not an instance of ptype. This converts it. It's
  // done lazily for performance reasons.
  PyErr_NormalizeException(&ptype, &pvalue, &ptraceback);
  if (ptraceback != nullptr) {
    PyException_SetTraceback(pvalue, ptraceback);
  }

  // Get Boost handles to the Python objects so we get an easier API.
  handle<> htype(ptype);
  handle<> hvalue(allow_null(pvalue));
  handle<> htraceback(allow_null(ptraceback));

  // Import the `traceback` module and use it to format the exception.
  object traceback = import("traceback");
  object format_exception = traceback.attr("format_exception");
  object formatted_list = format_exception(htype, hvalue, htraceback);
  object formatted = str("\n").join(formatted_list);
  return extract<std::string>(formatted);
}

handle<>ところで、なぜみんながの代わりに使っているのか気になりましたhandle。どうやらそれはテンプレート引数の推論を無効にします。ここでそれが必要な理由はわかりませんが、とにかく同じではありません。Boostのドキュメントでも使用すると言われているhandle<>ので、正当な理由があると思います。

于 2021-09-27T17:07:58.163 に答える