diff --git a/3rd/numpy/tests/test_numpy.cpp b/3rd/numpy/tests/test_numpy.cpp index dc75f300..a082c0e3 100644 --- a/3rd/numpy/tests/test_numpy.cpp +++ b/3rd/numpy/tests/test_numpy.cpp @@ -28,7 +28,7 @@ int main() { py::exec(script); std::cout << "Numpy script executed successfully." << std::endl; } - catch (const py::error_already_set& e) { + catch (const py::python_error& e) { // Catch and print Python exceptions std::cerr << "Python error: " << e.what() << std::endl; return 1; diff --git a/include/pybind11/internal/builtins.h b/include/pybind11/internal/builtins.h index 52f65218..1e842bc4 100644 --- a/include/pybind11/internal/builtins.h +++ b/include/pybind11/internal/builtins.h @@ -76,6 +76,8 @@ inline bool isinstance(handle obj, type type) { return py_isinstance(obj.ptr(), inline bool python_error::match(type type) const { return isinstance(m_exception.ptr(), type); } +inline bool error_already_set::match(type type) const { return py_matchexc(type.index()); } + template constexpr inline bool is_pyobject_v = std::is_base_of_v> || std::is_same_v; diff --git a/include/pybind11/internal/error.h b/include/pybind11/internal/error.h index fbacb453..1db80454 100644 --- a/include/pybind11/internal/error.h +++ b/include/pybind11/internal/error.h @@ -25,7 +25,12 @@ private: object m_exception; }; -using error_already_set = python_error; +class error_already_set : public std::exception { +public: + bool match(py_Type type) const { return py_matchexc(type); } + + bool match(type type) const; +}; template inline auto raise_call(Args&&... args) { @@ -48,7 +53,17 @@ inline auto raise_call(Args&&... args) { throw python_error(what, std::move(e)); } -class stop_iteration {}; +class stop_iteration { +public: + stop_iteration() = default; + + stop_iteration(object value) : m_value(std::move(value)) {} + + object value() const { return m_value; } + +private: + object m_value; +}; class cast_error : public std::runtime_error { using std::runtime_error::runtime_error; diff --git a/include/pybind11/internal/function.h b/include/pybind11/internal/function.h index 3138fec0..653b2dad 100644 --- a/include/pybind11/internal/function.h +++ b/include/pybind11/internal/function.h @@ -562,12 +562,21 @@ private: py_exception(tp_IndexError, e.what()); } catch(std::range_error& e) { py_exception(tp_ValueError, e.what()); - } catch(stop_iteration&) { StopIteration(); } catch(index_error& e) { + } catch(stop_iteration& e) { + if(auto value_ptr = e.value().ptr()) { + bool ok = py_tpcall(tp_StopIteration, 1, value_ptr); + if(ok) { py_raise(py_retval()); } + } else { + StopIteration(); + } + } catch(index_error& e) { py_exception(tp_IndexError, e.what()); } catch(key_error& e) { py_exception(tp_KeyError, e.what()); } catch(value_error& e) { py_exception(tp_ValueError, e.what()); } catch(type_error& e) { py_exception(tp_TypeError, e.what()); } catch(import_error& e) { py_exception(tp_ImportError, e.what()); + } catch(error_already_set&) { + // exception already set, do nothing } catch(attribute_error& e) { py_exception(tp_AttributeError, e.what()); } catch(std::exception& e) { py_exception(tp_RuntimeError, e.what()); } diff --git a/include/pybind11/tests/error.cpp b/include/pybind11/tests/error.cpp index 12ea3ee9..8f945b23 100644 --- a/include/pybind11/tests/error.cpp +++ b/include/pybind11/tests/error.cpp @@ -40,6 +40,37 @@ TEST_F(PYBIND11_TEST, exception_cpp_to_python) { throw py::stop_iteration(); }); py::exec("try:\n test_stop_iteration()\nexcept StopIteration as e:\n pass"); + + m.def("test_stop_iteration_value", []() { + throw py::stop_iteration(py::int_(42)); + }); + py::exec( + "try:\n test_stop_iteration_value()\nexcept StopIteration as e:\n assert e.value == 42"); + + m.def("test_error_already_set", []() { + KeyError(none().ptr()); + throw py::error_already_set(); + }); + py::exec("try:\n test_error_already_set()\nexcept KeyError as e:\n pass"); + + m.def("test_error_already_set_matches", []() { + try { + KeyError(none().ptr()); + throw py::error_already_set(); + } catch(py::error_already_set& e) { + if(e.match(tp_KeyError)) { return; } + std::rethrow_exception(std::current_exception()); + } + + try { + StopIteration(); + throw py::error_already_set(); + } catch(py::error_already_set& e) { + if(e.match(type(tp_StopIteration))) { return; } + std::rethrow_exception(std::current_exception()); + } + }); + py::exec("test_error_already_set_matches()"); TEST_EXCEPTION(index_error, IndexError); // FIXME: TEST_EXCEPTION(key_error, KeyError); TEST_EXCEPTION(value_error, ValueError);