Cosmit following @YannickJadoul's comments

Note that detail::error_string() no longer calls PyException_SetTraceback
as it is unncessary for pretty-printing the exception.
This commit is contained in:
Sergei Lebedev 2019-08-29 14:33:18 +01:00
parent c935b73ba5
commit b48bc720e0
2 changed files with 20 additions and 29 deletions

View File

@ -404,39 +404,30 @@ PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_inf
}
PYBIND11_NOINLINE inline std::string error_string(PyObject *type, PyObject *value, PyObject *trace) {
if (!type && !value && !trace) {
if (!type) {
PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred");
return "Unknown internal error occurred";
}
// TODO(superbobry): is it safe to assume that exception has been
// normalized by the caller?
std::string errorString;
if (type) {
errorString += handle(type).attr("__name__").cast<std::string>();
errorString += ": ";
}
if (value)
errorString += str(value).cast<std::string>();
std::string result = handle(type).attr("__name__").cast<std::string>();
result += ": ";
#if PY_MAJOR_VERSION >= 3
if (trace)
PyException_SetTraceback(value, trace);
#endif
if (value)
result += str(value).cast<std::string>();
#if !defined(PYPY_VERSION)
if (trace) {
PyTracebackObject *tb = (PyTracebackObject *) trace;
/* Get the deepest trace possible */
// Get the deepest trace possible.
while (tb->tb_next)
tb = tb->tb_next;
PyFrameObject *frame = tb->tb_frame;
errorString += "\n\nAt:\n";
result += "\n\nAt:\n";
while (frame) {
int lineno = PyFrame_GetLineNumber(frame);
errorString +=
result +=
" " + handle(frame->f_code->co_filename).cast<std::string>() +
"(" + std::to_string(lineno) + "): " +
handle(frame->f_code->co_name).cast<std::string>() + "\n";
@ -445,11 +436,12 @@ PYBIND11_NOINLINE inline std::string error_string(PyObject* type, PyObject* valu
}
#endif
return errorString;
return result;
}
PYBIND11_NOINLINE inline std::string error_string() {
error_scope scope; // Preserve error state
error_scope scope; // Preserve error state.
if (scope.type)
PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace);
return error_string(scope.type, scope.value, scope.trace);
}

View File

@ -333,25 +333,24 @@ public:
inline ~error_already_set();
virtual const char* what() const noexcept {
try {
virtual const char* what() const noexcept override {
if (m_lazy_what.empty()) {
if (m_type || m_value || m_trace)
try {
PyErr_NormalizeException(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr());
m_lazy_what = detail::error_string(m_type.ptr(), m_value.ptr(), m_trace.ptr());
}
return m_lazy_what.c_str();
} catch (...) {
return "Unknown internal error occurred";
}
}
return m_lazy_what.c_str();
}
/// Give the currently-held error back to Python, if any. If there is currently a Python error
/// already set it is cleared first. After this call, the current object no longer stores the
/// error variables (but the `.what()` string is still available).
void restore() {
what(); // Force-build `.what()`.
if (m_type || m_value || m_trace)
if (m_type)
PyErr_Restore(m_type.release().ptr(), m_value.release().ptr(), m_trace.release().ptr());
}