From b55ea9e05172e5cfd91ca00dda38895024104ce1 Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Sat, 28 Jan 2023 07:09:26 -0600 Subject: [PATCH] Improve type error messages. Refs #330 --- src/greenlet/greenlet_exceptions.hpp | 12 ++++++++++++ src/greenlet/greenlet_internal.hpp | 9 +++++++-- src/greenlet/greenlet_refs.hpp | 4 +++- src/greenlet/tests/test_cpp.py | 13 +++++++++++-- src/greenlet/tests/test_greenlet.py | 5 ++++- 5 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/greenlet/greenlet_exceptions.hpp b/src/greenlet/greenlet_exceptions.hpp index 46a63cfe..697df002 100644 --- a/src/greenlet/greenlet_exceptions.hpp +++ b/src/greenlet/greenlet_exceptions.hpp @@ -4,6 +4,7 @@ #define PY_SSIZE_T_CLEAN #include #include +#include #ifdef __clang__ # pragma clang diagnostic push @@ -25,6 +26,13 @@ namespace greenlet { { PyErr_SetString(exc_kind, msg); } + PyErrOccurred(PyObject* exc_kind, const std::string msg) + : std::runtime_error(msg) + { + // This copies the c_str, so we don't have any lifetime + // issues to worry about. + PyErr_SetString(exc_kind, msg.c_str()); + } }; class TypeError : public PyErrOccurred @@ -34,6 +42,10 @@ namespace greenlet { : PyErrOccurred(PyExc_TypeError, what) { } + TypeError(const std::string what) + : PyErrOccurred(PyExc_TypeError, what) + { + } }; class ValueError : public PyErrOccurred diff --git a/src/greenlet/greenlet_internal.hpp b/src/greenlet/greenlet_internal.hpp index c53ecc56..4f7fe6bb 100644 --- a/src/greenlet/greenlet_internal.hpp +++ b/src/greenlet/greenlet_internal.hpp @@ -21,6 +21,7 @@ #include "greenlet_allocator.hpp" #include +#include #define GREENLET_MODULE struct _greenlet; @@ -45,7 +46,9 @@ greenlet::refs::MainGreenletExactChecker(void *p) } // We control the class of the main greenlet exactly. if (Py_TYPE(p) != &PyGreenlet_Type) { - throw greenlet::TypeError("Expected a greenlet"); + std::string err("MainGreenlet: Expected exactly a greenlet, not a "); + err += Py_TYPE(p)->tp_name; + throw greenlet::TypeError(err); } // Greenlets from dead threads no longer respond to main() with a @@ -56,7 +59,9 @@ greenlet::refs::MainGreenletExactChecker(void *p) return; } if (!dynamic_cast(g)) { - throw greenlet::TypeError("Expected a main greenlet"); + std::string err("MainGreenlet: Expected exactly a main greenlet, not a "); + err += Py_TYPE(p)->tp_name; + throw greenlet::TypeError(err); } } diff --git a/src/greenlet/greenlet_refs.hpp b/src/greenlet/greenlet_refs.hpp index 36f2991c..ed1ef195 100644 --- a/src/greenlet/greenlet_refs.hpp +++ b/src/greenlet/greenlet_refs.hpp @@ -57,7 +57,9 @@ namespace greenlet } if (!PyObject_TypeCheck(p, &PyGreenlet_Type)) { - throw TypeError("Expected a greenlet"); + std::string err("GreenletChecker: Expected any type of greenlet, not "); + err += Py_TYPE(p)->tp_name; + throw TypeError(err); } } diff --git a/src/greenlet/tests/test_cpp.py b/src/greenlet/tests/test_cpp.py index 955566b4..7aaeb0bb 100644 --- a/src/greenlet/tests/test_cpp.py +++ b/src/greenlet/tests/test_cpp.py @@ -54,8 +54,17 @@ def _do_test_unhandled_exception(self, target): # https://devblogs.microsoft.com/oldnewthing/20110519-00/?p=10623 # and # https://docs.microsoft.com/en-us/previous-versions/k089yyh0(v=vs.140)?redirectedfrom=MSDN - expected_exit = -signal.SIGABRT if not WIN else 3 - self.assertEqual(p.exitcode, expected_exit) + expected_exit = ( + -signal.SIGABRT, + # But beginning on Python 3.11, the faulthandler + # that prints the C backtraces sometimes segfaults after + # reporting the exception but before printing the stack. + # This has only been seen on linux/gcc. + -signal.SIGSEGV + ) if not WIN else ( + 3, + ) + self.assertIn(p.exitcode, expected_exit) def test_unhandled_exception_aborts(self): # verify that plain unhandled throw aborts diff --git a/src/greenlet/tests/test_greenlet.py b/src/greenlet/tests/test_greenlet.py index 6057185a..3185b39b 100644 --- a/src/greenlet/tests/test_greenlet.py +++ b/src/greenlet/tests/test_greenlet.py @@ -972,7 +972,10 @@ def check(glet): for p in None, 1, self, "42": with self.assertRaises(TypeError) as exc: glet.parent = p - self.assertEqual(str(exc.exception), "Expected a greenlet") + + self.assertEqual( + str(exc.exception), + "GreenletChecker: Expected any type of greenlet, not " + type(p).__name__) # First, not running g = greenlet(bg)