@@ -274,60 +274,72 @@ std::string CPyCppyy::CPPMethod::GetSignatureString(bool fa)
274274// ----------------------------------------------------------------------------
275275void CPyCppyy::CPPMethod::SetPyError_ (PyObject* msg)
276276{
277- // helper to report errors in a consistent format (derefs msg)
278- std::string details{};
277+ // Helper to report errors in a consistent format (derefs msg).
278+ //
279+ // Handles three cases:
280+ // 1. No Python error occured yet:
281+ // Set a new TypeError with the message "msg" and the docstring of this
282+ // C++ method to give some context.
283+ // 2. A C++ exception has occured:
284+ // Augment the exception message with the docstring of this method
285+ // 3. A Python exception has occured:
286+ // Do nothing, Python exceptions are already informative enough
287+
288+ #if PY_VERSION_HEX >= 0x030c0000
289+ PyObject *evalue = PyErr_Occurred () ? PyErr_GetRaisedException () : nullptr ;
290+ PyObject *etype = evalue ? (PyObject *)Py_TYPE (evalue) : nullptr ;
291+ #else
292+ PyObject *etype = nullptr ;
293+ PyObject *evalue = nullptr ;
294+ PyObject *etrace = nullptr ;
279295
280- PyObject *etype = nullptr , *evalue = nullptr ;
281296 if (PyErr_Occurred ()) {
282- PyObject* etrace = nullptr ;
283-
284297 PyErr_Fetch (&etype, &evalue, &etrace);
285-
286- if (evalue) {
287- PyObject* descr = PyObject_Str (evalue);
288- if (descr) {
289- details = CPyCppyy_PyText_AsString (descr);
290- Py_DECREF (descr);
291- }
292- }
293-
294- Py_XDECREF (etrace);
295298 }
299+ #endif
300+
301+ const bool isCppExc = evalue && PyType_IsSubtype ((PyTypeObject*)etype, &CPPExcInstance_Type);
302+ // If the error is not a CPPExcInstance, the error from Python itself is
303+ // already complete and messing with it would only make it less informative.
304+ // Just restore and return.
305+ if (evalue && !isCppExc) {
306+ #if PY_VERSION_HEX >= 0x030c0000
307+ PyErr_SetRaisedException (evalue);
308+ #else
309+ PyErr_Restore (etype, evalue, etrace);
310+ #endif
311+ return ;
312+ }
296313
297314 PyObject* doc = GetDocString ();
298- PyObject* errtype = etype ;
299- if (!errtype)
300- errtype = PyExc_TypeError;
315+ const char * cdoc = CPyCppyy_PyText_AsString (doc) ;
316+ const char * cmsg = msg ? CPyCppyy_PyText_AsString (msg) : nullptr ;
317+ PyObject* errtype = etype ? etype : PyExc_TypeError;
301318 PyObject* pyname = PyObject_GetAttr (errtype, PyStrings::gName );
302319 const char * cname = pyname ? CPyCppyy_PyText_AsString (pyname) : " Exception" ;
303320
304- if (!PyType_IsSubtype ((PyTypeObject*)errtype, &CPPExcInstance_Type)) {
305- if (details.empty ()) {
306- PyErr_Format (errtype, " %s =>\n %s: %s" , CPyCppyy_PyText_AsString (doc),
307- cname, msg ? CPyCppyy_PyText_AsString (msg) : " " );
308- } else if (msg) {
309- PyErr_Format (errtype, " %s =>\n %s: %s (%s)" ,
310- CPyCppyy_PyText_AsString (doc), cname, CPyCppyy_PyText_AsString (msg),
311- details.c_str ());
312- } else {
313- PyErr_Format (errtype, " %s =>\n %s: %s" ,
314- CPyCppyy_PyText_AsString (doc), cname, details.c_str ());
315- }
321+ if (!isCppExc) {
322+ // this is the case where no Python error has occured yet, and we set a new
323+ // error with context info
324+ PyErr_Format (errtype, " %s =>\n %s: %s" , cdoc, cname, cmsg ? cmsg : " " );
316325 } else {
317- Py_XDECREF (((CPPExcInstance*)evalue)->fTopMessage );
326+ // augment the top message with context information
327+ PyObject *&topMessage = ((CPPExcInstance*)evalue)->fTopMessage ;
328+ Py_XDECREF (topMessage);
318329 if (msg) {
319- ((CPPExcInstance*)evalue)->fTopMessage = CPyCppyy_PyText_FromFormat (\
320- " %s =>\n %s: %s | " , CPyCppyy_PyText_AsString (doc), cname, CPyCppyy_PyText_AsString (msg));
330+ topMessage = CPyCppyy_PyText_FromFormat (" %s =>\n %s: %s | " , cdoc, cname, cmsg);
321331 } else {
322- ((CPPExcInstance*)evalue)->fTopMessage = CPyCppyy_PyText_FromFormat (\
323- " %s =>\n %s: " , CPyCppyy_PyText_AsString (doc), cname);
332+ topMessage = CPyCppyy_PyText_FromFormat (" %s =>\n %s: " , cdoc, cname);
324333 }
325- PyErr_SetObject (errtype, evalue);
334+ // restore the updated error
335+ #if PY_VERSION_HEX >= 0x030c0000
336+ PyErr_SetRaisedException (evalue);
337+ #else
338+ PyErr_Restore (etype, evalue, etrace);
339+ #endif
326340 }
327341
328342 Py_XDECREF (pyname);
329- Py_XDECREF (evalue);
330- Py_XDECREF (etype);
331343 Py_DECREF (doc);
332344 Py_XDECREF (msg);
333345}
0 commit comments