-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Python 3.11+: Add __notes__
to error_already_set::what()
output.
#4678
Conversation
This reverts commit 6081628.
…de is so unusual.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks mostly good, with two possible flaws
result += "\n__notes__ (len=" + std::to_string(len_notes) + "):"; | ||
for (ssize_t i = 0; i < len_notes; i++) { | ||
PyObject *note = PyList_GET_ITEM(notes.ptr(), i); | ||
auto note_bytes = reinterpret_steal<object>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be reinterpret_borrow?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is copy-pasted from the existing code further up (btw I didn't see enough meat to factor out).
https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_AsEncodedString
returns a new reference, we have to take ownership.
(Unfortuantely IMO) it's called "steal" instead of "take_ownership", I think this is correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, nvm. Misread this and didn't notice the PyUnicode_AsEncodedString. You are 100% right.
@@ -492,6 +503,14 @@ struct error_fetch_and_normalize { | |||
"of the original active exception type."); | |||
} | |||
m_lazy_error_string = exc_type_name_orig; | |||
#if PY_VERSION_HEX >= 0x030C0000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this version check necessary? I know it should only be true for python 3.11 and above, but the hasattrstring is implicitly a version check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking of it as a very easy runtime optimization, eliding the runtime overhead for the attribute lookup. — A while ago I looked at the implementation and it's actually quite involved. It basically does a full getattr()
equivalent with PyErr_Clear()
.
Thanks for the review @lalaland! I just tested this PR also with Python 3.12.0b1 + two patches to make that possible (references below). I'll merge this now so that it'll be easier to keep up with 3.12 developments.
commit b66198c8345614df7ddf34402cbf5670ae298a6f (HEAD -> wrk)
Author: Ralf W. Grosse-Kunstleve <rwgk@google.com>
Date: Tue May 23 09:46:48 2023 -0700
Check for `t->tp_bases == nullptr`
diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h
index 16387506..eb7ec7a3 100644
--- a/include/pybind11/detail/type_caster_base.h
+++ b/include/pybind11/detail/type_caster_base.h
@@ -104,6 +104,10 @@ all_type_info_get_cache(PyTypeObject *type);
// Populates a just-created cache entry.
PYBIND11_NOINLINE void all_type_info_populate(PyTypeObject *t, std::vector<type_info *> &bases) {
+ if (t->tp_bases == nullptr) {
+ // Since https://github.com/python/cpython/pull/103912 (Python 3.12.0b1)
+ return;
+ }
std::vector<PyTypeObject *> check;
for (handle parent : reinterpret_borrow<tuple>(t->tp_bases)) {
check.push_back((PyTypeObject *) parent.ptr());
diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h
index 28ebc222..db1c5a99 100644
--- a/include/pybind11/pybind11.h
+++ b/include/pybind11/pybind11.h
@@ -1363,6 +1363,10 @@ protected:
/// Helper function which tags all parents of a type using mult. inheritance
void mark_parents_nonsimple(PyTypeObject *value) {
+ if (value->tp_bases == nullptr) {
+ // Since https://github.com/python/cpython/pull/103912 (Python 3.12.0b1)
+ return;
+ }
auto t = reinterpret_borrow<tuple>(value->tp_bases);
for (handle h : t) {
auto *tinfo2 = get_type_info((PyTypeObject *) h.ptr()); |
Description
The original motivation for this PR was to account for a a fairly major behavior change in Python 3.12:
PyErr_Fetch()
behavior change python/cpython#102594Python 3.12
PyErr_Fetch()
normalizes exceptions immediately and tracks any normalization errors under__notes__
:__notes__
were introduced already with Python 3.11, but this was not reflected in pybind11 until now. Adding them to theerror_already_set::what()
output therefore catches up with Python developments in general, and ensures that exception normalization errors continue to be obvious.A highly technical detail for completeness:
The
FAILURE obtaining
andFAILURE formatting
conditions are impractical to exercise in the usual way via unit tests, but a manual pass was made exercising them with temporary tweaks.Suggested changelog entry: