From 229b3bce971696438c908f4be3d313ab7a740963 Mon Sep 17 00:00:00 2001 From: Yuanyuan Chen Date: Tue, 9 Dec 2025 08:43:36 +0800 Subject: [PATCH 1/2] Fix PyObject_HasAttrString return value Signed-off-by: cyy --- include/pybind11/pytypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index b28692fd74..6b81eff7f7 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -547,7 +547,7 @@ struct error_fetch_and_normalize { // The presence of __notes__ is likely due to exception normalization // errors, although that is not necessarily true, therefore insert a // hint only: - if (PyObject_HasAttrString(m_value.ptr(), "__notes__")) { + if (PyObject_HasAttrString(m_value.ptr(), "__notes__") == 1) { m_lazy_error_string += "[WITH __notes__]"; } #else From 1d1f3434051e80e91af255b6f5f6d16b657d1680 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 12 Dec 2025 22:11:35 -0800 Subject: [PATCH 2/2] [skip ci] Handle PyObject_HasAttrString error when probing __notes__ PyObject_HasAttrString may return -1 to signal an error and set a Python exception. The previous logic only checked for "!= 0", which meant that the error path was treated the same as "attribute exists", causing two problems: misreporting the presence of __notes__ and leaving a spurious exception pending. The earlier PR tightened the condition to "== 1" so that only a successful lookup marks the error string as [WITH __notes__], but it still left the -1 case unhandled. In the context of error_fetch_and_normalize, we are already dealing with an active exception and only want to best-effort detect whether normalization attached any __notes__. If the attribute probe itself fails, we do not want that secondary failure to affect later C-API calls or the error we ultimately report. This change stores the PyObject_HasAttrString return value, treats "== 1" as "has __notes__", and explicitly calls PyErr_Clear() when it returns -1. That way, we avoid leaking a secondary error while still preserving the original exception information and hinting [WITH __notes__] only when we can determine it reliably. --- include/pybind11/pytypes.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index a389f21b69..0ab0b73e1f 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -547,8 +547,13 @@ struct error_fetch_and_normalize { // The presence of __notes__ is likely due to exception normalization // errors, although that is not necessarily true, therefore insert a // hint only: - if (PyObject_HasAttrString(m_value.ptr(), "__notes__") == 1) { + const int has_notes = PyObject_HasAttrString(m_value.ptr(), "__notes__"); + if (has_notes == 1) { m_lazy_error_string += "[WITH __notes__]"; + } else if (has_notes == -1) { + // Ignore secondary errors when probing for __notes__ to avoid leaking a + // spurious exception while still reporting the original error. + PyErr_Clear(); } #else // PyErr_NormalizeException() may change the exception type if there are cascading