فهرست منبع

Merge pull request #321 from KevinEady/fix-error-already-set

fix pybind11 implementation for error_already_set
BLUELOVETH 1 سال پیش
والد
کامیت
8024f46b54

+ 1 - 1
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;

+ 2 - 0
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 <typename T>
 constexpr inline bool is_pyobject_v =
     std::is_base_of_v<object, std::decay_t<T>> || std::is_same_v<type, T>;

+ 17 - 2
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 <auto Fn, typename... Args>
 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;

+ 10 - 1
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()); }

+ 31 - 0
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);