Skip to content

Commit 97784da

Browse files
YannickJadoulwjakob
authored andcommitted
[BUGFIX] Fixing pybind11::error_already_set.matches to also work with exception subclasses (#1715)
* Fixing order of arguments in call to PyErr_GivenExceptionMatches in pybind11::error_already_set.matches * Added tests on error_already_set::matches fix for exception base classes
1 parent a0b8f70 commit 97784da

File tree

3 files changed

+33
-3
lines changed

3 files changed

+33
-3
lines changed

include/pybind11/pytypes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ class error_already_set : public std::runtime_error {
344344
/// Check if the currently trapped error type matches the given Python exception class (or a
345345
/// subclass thereof). May also be passed a tuple to search for any exception class matches in
346346
/// the given tuple.
347-
bool matches(handle ex) const { return PyErr_GivenExceptionMatches(ex.ptr(), m_type.ptr()); }
347+
bool matches(handle exc) const { return PyErr_GivenExceptionMatches(m_type.ptr(), exc.ptr()); }
348348

349349
const object& type() const { return m_type; }
350350
const object& value() const { return m_value; }

tests/test_exceptions.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,38 @@ TEST_SUBMODULE(exceptions, m) {
118118
m.def("throws_logic_error", []() { throw std::logic_error("this error should fall through to the standard handler"); });
119119
m.def("exception_matches", []() {
120120
py::dict foo;
121-
try { foo["bar"]; }
121+
try {
122+
// Assign to a py::object to force read access of nonexistent dict entry
123+
py::object o = foo["bar"];
124+
}
122125
catch (py::error_already_set& ex) {
123126
if (!ex.matches(PyExc_KeyError)) throw;
127+
return true;
128+
}
129+
return false;
130+
});
131+
m.def("exception_matches_base", []() {
132+
py::dict foo;
133+
try {
134+
// Assign to a py::object to force read access of nonexistent dict entry
135+
py::object o = foo["bar"];
124136
}
137+
catch (py::error_already_set &ex) {
138+
if (!ex.matches(PyExc_Exception)) throw;
139+
return true;
140+
}
141+
return false;
142+
});
143+
m.def("modulenotfound_exception_matches_base", []() {
144+
try {
145+
// On Python >= 3.6, this raises a ModuleNotFoundError, a subclass of ImportError
146+
py::module::import("nonexistent");
147+
}
148+
catch (py::error_already_set &ex) {
149+
if (!ex.matches(PyExc_ImportError)) throw;
150+
return true;
151+
}
152+
return false;
125153
});
126154

127155
m.def("throw_already_set", [](bool err) {

tests/test_exceptions.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ def test_python_call_in_catch():
4848

4949

5050
def test_exception_matches():
51-
m.exception_matches()
51+
assert m.exception_matches()
52+
assert m.exception_matches_base()
53+
assert m.modulenotfound_exception_matches_base()
5254

5355

5456
def test_custom(msg):

0 commit comments

Comments
 (0)