fix: throwing repr caused a segfault (#2389)
* fix: throwing repr caused a segfault
* fixup! ci: include Python 3.9 RC1 (#2387)
diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h
index d34c92c..3a7d7b8 100644
--- a/include/pybind11/pybind11.h
+++ b/include/pybind11/pybind11.h
@@ -776,7 +776,11 @@
for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) {
if (!some_args) some_args = true;
else msg += ", ";
- msg += pybind11::repr(args_[ti]);
+ try {
+ msg += pybind11::repr(args_[ti]);
+ } catch (const error_already_set&) {
+ msg += "<repr raised Error>";
+ }
}
if (kwargs_in) {
auto kwargs = reinterpret_borrow<dict>(kwargs_in);
@@ -787,7 +791,12 @@
for (auto kwarg : kwargs) {
if (first) first = false;
else msg += ", ";
- msg += pybind11::str("{}={!r}").format(kwarg.first, kwarg.second);
+ msg += pybind11::str("{}=").format(kwarg.first);
+ try {
+ msg += pybind11::repr(kwarg.second);
+ } catch (const error_already_set&) {
+ msg += "<repr raised Error>";
+ }
}
}
}
diff --git a/tests/test_exceptions.cpp b/tests/test_exceptions.cpp
index 372d0ae..537819d 100644
--- a/tests/test_exceptions.cpp
+++ b/tests/test_exceptions.cpp
@@ -218,4 +218,7 @@
}
});
+ // Test repr that cannot be displayed
+ m.def("simple_bool_passthrough", [](bool x) {return x;});
+
}
diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py
index 83a46bf..7d7088d 100644
--- a/tests/test_exceptions.py
+++ b/tests/test_exceptions.py
@@ -178,3 +178,14 @@
with pytest.raises(m.MyException5) as excinfo:
m.try_catch(m.MyException, pycatch, m.MyException, m.throws5)
assert str(excinfo.value) == "this is a helper-defined translated exception"
+
+
+# This can often happen if you wrap a pybind11 class in a Python wrapper
+def test_invalid_repr():
+
+ class MyRepr(object):
+ def __repr__(self):
+ raise AttributeError("Example error")
+
+ with pytest.raises(TypeError):
+ m.simple_bool_passthrough(MyRepr())