Skip to content

Commit

Permalink
Merge pull request #321 from KevinEady/fix-error-already-set
Browse files Browse the repository at this point in the history
fix pybind11 implementation for error_already_set
  • Loading branch information
blueloveTH authored Jan 12, 2025
2 parents a2c0bdd + 30f04b4 commit 8024f46
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 4 deletions.
2 changes: 1 addition & 1 deletion 3rd/numpy/tests/test_numpy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions include/pybind11/internal/builtins.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down
19 changes: 17 additions & 2 deletions include/pybind11/internal/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ class python_error : public std::exception {
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) {
Expand All @@ -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;
Expand Down
11 changes: 10 additions & 1 deletion include/pybind11/internal/function.h
Original file line number Diff line number Diff line change
Expand Up @@ -562,12 +562,21 @@ class cpp_function : public function {
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()); }
Expand Down
31 changes: 31 additions & 0 deletions include/pybind11/tests/error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 8024f46

Please sign in to comment.