Various changes.

This commit is contained in:
Ralf W. Grosse-Kunstleve 2022-05-11 02:35:01 -07:00
parent acdf332cda
commit 58ad49b05e
5 changed files with 35 additions and 35 deletions

View File

@ -473,12 +473,12 @@ PYBIND11_NOINLINE bool isinstance_generic(handle obj, const std::type_info &tp)
PYBIND11_NOINLINE std::string PYBIND11_NOINLINE std::string
error_string(PyObject *exc_type, PyObject *exc_value, PyObject *exc_trace) { error_string(PyObject *exc_type, PyObject *exc_value, PyObject *exc_trace) {
if (!exc_type) { if (!exc_type) {
static const char *msg pybind11_fail(
= "Internal error: error_string() called without a Python error available."; "Internal error: pybind11::detail::error_string() called with exc_type == nullptr");
PyErr_SetString(PyExc_RuntimeError, msg);
return msg;
} }
PyErr_NormalizeException(&exc_type, &exc_value, &exc_trace);
auto result = handle(exc_type).attr("__name__").cast<std::string>(); auto result = handle(exc_type).attr("__name__").cast<std::string>();
result += ": "; result += ": ";
@ -530,10 +530,7 @@ error_string(PyObject *exc_type, PyObject *exc_value, PyObject *exc_trace) {
} }
PYBIND11_NOINLINE std::string error_string() { PYBIND11_NOINLINE std::string error_string() {
error_scope scope; // Preserve error state. error_scope scope; // Fetch error state.
if (scope.type) {
PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace);
}
return error_string(scope.type, scope.value, scope.trace); return error_string(scope.type, scope.value, scope.trace);
} }

View File

@ -2611,8 +2611,8 @@ void print(Args &&...args) {
} }
error_already_set::~error_already_set() { error_already_set::~error_already_set() {
if (m_type) { gil_scoped_acquire gil;
gil_scoped_acquire gil; {
error_scope scope; error_scope scope;
m_type.release().dec_ref(); m_type.release().dec_ref();
m_value.release().dec_ref(); m_value.release().dec_ref();

View File

@ -387,13 +387,15 @@ PYBIND11_NAMESPACE_END(detail)
/// python). /// python).
class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::runtime_error { class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::runtime_error {
public: public:
/// Constructs a new exception from the current Python error indicator, if any. The current /// Constructs a new exception from the current Python error indicator, or substitutes a
/// Python error indicator will be cleared. /// RuntimeError("Internal error: ..."). The current Python error indicator will be cleared.
error_already_set() : std::runtime_error("") { error_already_set() : std::runtime_error("") {
PyErr_Fetch(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr()); if (!PyErr_Occurred()) {
if (m_type) { m_lazy_what = "Internal error: pybind11::detail::error_already_set called while "
PyErr_NormalizeException(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr()); "Python error indicator not set.";
PyErr_SetString(PyExc_RuntimeError, m_lazy_what.c_str());
} }
PyErr_Fetch(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr());
} }
error_already_set(const error_already_set &) = default; error_already_set(const error_already_set &) = default;
@ -411,22 +413,22 @@ public:
// Negate the if condition to test the catch(...) block below. // Negate the if condition to test the catch(...) block below.
if (m_lazy_what.empty()) { if (m_lazy_what.empty()) {
throw std::runtime_error( throw std::runtime_error(
"FATAL failure building pybind11::error_already_set error_string."); "FATAL failure building pybind11::detail::error_already_set what()");
} }
} catch (...) { } catch (...) {
// Terminating the process, to not mask the original error by errors in the error // Terminating the process, to not mask the original error by errors in the error
// handling. Reporting the original error on stderr & stdout. Intentionally using // handling. Reporting the original error on stderr & stdout. Intentionally using
// the Python C API directly, to maximize reliability. // the Python C API directly, to maximize reliability.
std::string msg std::string msg
= "FATAL failure building pybind11::error_already_set error_string: "; = "FATAL failure building pybind11::detail::error_already_set what(): ";
if (m_type.ptr() == nullptr) { if (m_type.ptr() == nullptr) {
msg += "PYTHON_EXCEPTION_TYPE_IS_NULLPTR"; msg += "PYTHON_EXCEPTION_TYPE_IS_NULLPTR";
} else { } else {
const char *tp_name = detail::obj_class_name(m_type.ptr()); const char *class_name = detail::obj_class_name(m_type.ptr());
if (tp_name == nullptr) { if (class_name == nullptr) {
msg += "PYTHON_EXCEPTION_TP_NAME_IS_NULLPTR"; msg += "PYTHON_EXCEPTION_CLASS_NAME_IS_NULLPTR";
} else { } else {
msg += tp_name; msg += class_name;
} }
} }
msg += ": "; msg += ": ";
@ -443,8 +445,8 @@ public:
+ '"'; + '"';
} }
} }
// Intentionally using C calls to maximize reliability (and to avoid #include // Intentionally using C calls to maximize reliability
// <iostream>). // (and to avoid #include <iostream>).
fprintf(stderr, "%s [STDERR]\n", msg.c_str()); fprintf(stderr, "%s [STDERR]\n", msg.c_str());
fflush(stderr); fflush(stderr);
fprintf(stdout, "%s [STDOUT]\n", msg.c_str()); fprintf(stdout, "%s [STDOUT]\n", msg.c_str());
@ -455,15 +457,12 @@ public:
return m_lazy_what.c_str(); return m_lazy_what.c_str();
} }
/// Restores the currently-held Python error, if any (which will clear the Python error /// Restores the currently-held Python error (which will clear the Python error indicator first
/// indicator first if already set). // if already set).
void restore() { void restore() {
if (m_type) { // As long as this type is copyable, there is no point in releasing m_type, m_value,
// As long as this type is copyable, there is no point in releasing m_type, m_value, // m_trace.
// m_trace. PyErr_Restore(m_type.inc_ref().ptr(), m_value.inc_ref().ptr(), m_trace.inc_ref().ptr());
PyErr_Restore(
m_type.inc_ref().ptr(), m_value.inc_ref().ptr(), m_trace.inc_ref().ptr());
}
} }
/// If it is impossible to raise the currently-held error, such as in a destructor, we can /// If it is impossible to raise the currently-held error, such as in a destructor, we can

View File

@ -230,8 +230,8 @@ TEST_SUBMODULE(exceptions, m) {
if ((err && e.what() != std::string("ValueError: foo")) if ((err && e.what() != std::string("ValueError: foo"))
|| (!err || (!err
&& e.what() && e.what()
!= std::string("Internal error: error_string() called without a Python " != std::string("Internal error: pybind11::detail::error_already_set "
"error available."))) { "called while Python error indicator not set."))) {
PyErr_Clear(); PyErr_Clear();
throw std::runtime_error("error message mismatch"); throw std::runtime_error("error message mismatch");
} }

View File

@ -14,9 +14,13 @@ def test_std_exception(msg):
def test_error_already_set(msg): def test_error_already_set(msg):
with pytest.raises(SystemError) as excinfo: with pytest.raises(RuntimeError) as excinfo:
m.throw_already_set(False) m.throw_already_set(False)
assert "without setting" in str(excinfo.value) assert (
msg(excinfo.value)
== "Internal error: pybind11::detail::error_already_set called"
" while Python error indicator not set."
)
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
m.throw_already_set(True) m.throw_already_set(True)