summaryrefslogtreecommitdiffstats
path: root/Objects/exceptions.c
diff options
context:
space:
mode:
authorIrit Katriel <1055913+iritkatriel@users.noreply.github.com>2022-04-16 18:59:52 (GMT)
committerGitHub <noreply@github.com>2022-04-16 18:59:52 (GMT)
commitd4c4a76ed1427c947fcbbe692625b3f644cf3aaf (patch)
tree2e503da40ff6459711ff5730b22e89962b175252 /Objects/exceptions.c
parent7fa3a5a2197896066e3fe53ee325ac6ab54c3414 (diff)
downloadcpython-d4c4a76ed1427c947fcbbe692625b3f644cf3aaf.zip
cpython-d4c4a76ed1427c947fcbbe692625b3f644cf3aaf.tar.gz
cpython-d4c4a76ed1427c947fcbbe692625b3f644cf3aaf.tar.bz2
gh-89770: Implement PEP-678 - Exception notes (GH-31317)
Diffstat (limited to 'Objects/exceptions.c')
-rw-r--r--Objects/exceptions.c123
1 files changed, 80 insertions, 43 deletions
diff --git a/Objects/exceptions.c b/Objects/exceptions.c
index df10a3c..b26a0e9 100644
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -47,7 +47,7 @@ BaseException_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return NULL;
/* the dict is created on the fly in PyObject_GenericSetAttr */
self->dict = NULL;
- self->note = NULL;
+ self->notes = NULL;
self->traceback = self->cause = self->context = NULL;
self->suppress_context = 0;
@@ -83,7 +83,7 @@ BaseException_clear(PyBaseExceptionObject *self)
{
Py_CLEAR(self->dict);
Py_CLEAR(self->args);
- Py_CLEAR(self->note);
+ Py_CLEAR(self->notes);
Py_CLEAR(self->traceback);
Py_CLEAR(self->cause);
Py_CLEAR(self->context);
@@ -108,7 +108,7 @@ BaseException_traverse(PyBaseExceptionObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->dict);
Py_VISIT(self->args);
- Py_VISIT(self->note);
+ Py_VISIT(self->notes);
Py_VISIT(self->traceback);
Py_VISIT(self->cause);
Py_VISIT(self->context);
@@ -186,12 +186,62 @@ PyDoc_STRVAR(with_traceback_doc,
"Exception.with_traceback(tb) --\n\
set self.__traceback__ to tb and return self.");
+static inline PyBaseExceptionObject*
+_PyBaseExceptionObject_cast(PyObject *exc)
+{
+ assert(PyExceptionInstance_Check(exc));
+ return (PyBaseExceptionObject *)exc;
+}
+
+static PyObject *
+BaseException_add_note(PyObject *self, PyObject *note)
+{
+ if (!PyUnicode_Check(note)) {
+ PyErr_Format(PyExc_TypeError,
+ "note must be a str, not '%s'",
+ Py_TYPE(note)->tp_name);
+ return NULL;
+ }
+
+ if (!PyObject_HasAttr(self, &_Py_ID(__notes__))) {
+ PyObject *new_notes = PyList_New(0);
+ if (new_notes == NULL) {
+ return NULL;
+ }
+ if (PyObject_SetAttr(self, &_Py_ID(__notes__), new_notes) < 0) {
+ Py_DECREF(new_notes);
+ return NULL;
+ }
+ Py_DECREF(new_notes);
+ }
+ PyObject *notes = PyObject_GetAttr(self, &_Py_ID(__notes__));
+ if (notes == NULL) {
+ return NULL;
+ }
+ if (!PyList_Check(notes)) {
+ Py_DECREF(notes);
+ PyErr_SetString(PyExc_TypeError, "Cannot add note: __notes__ is not a list");
+ return NULL;
+ }
+ if (PyList_Append(notes, note) < 0) {
+ Py_DECREF(notes);
+ return NULL;
+ }
+ Py_DECREF(notes);
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(add_note_doc,
+"Exception.add_note(note) --\n\
+ add a note to the exception");
static PyMethodDef BaseException_methods[] = {
{"__reduce__", (PyCFunction)BaseException_reduce, METH_NOARGS },
{"__setstate__", (PyCFunction)BaseException_setstate, METH_O },
{"with_traceback", (PyCFunction)BaseException_with_traceback, METH_O,
with_traceback_doc},
+ {"add_note", (PyCFunction)BaseException_add_note, METH_O,
+ add_note_doc},
{NULL, NULL, 0, NULL},
};
@@ -221,33 +271,6 @@ BaseException_set_args(PyBaseExceptionObject *self, PyObject *val, void *Py_UNUS
}
static PyObject *
-BaseException_get_note(PyBaseExceptionObject *self, void *Py_UNUSED(ignored))
-{
- if (self->note == NULL) {
- Py_RETURN_NONE;
- }
- return Py_NewRef(self->note);
-}
-
-static int
-BaseException_set_note(PyBaseExceptionObject *self, PyObject *note,
- void *Py_UNUSED(ignored))
-{
- if (note == NULL) {
- PyErr_SetString(PyExc_TypeError, "__note__ may not be deleted");
- return -1;
- }
- else if (note != Py_None && !PyUnicode_CheckExact(note)) {
- PyErr_SetString(PyExc_TypeError, "__note__ must be a string or None");
- return -1;
- }
-
- Py_INCREF(note);
- Py_XSETREF(self->note, note);
- return 0;
-}
-
-static PyObject *
BaseException_get_tb(PyBaseExceptionObject *self, void *Py_UNUSED(ignored))
{
if (self->traceback == NULL) {
@@ -337,7 +360,6 @@ BaseException_set_cause(PyObject *self, PyObject *arg, void *Py_UNUSED(ignored))
static PyGetSetDef BaseException_getset[] = {
{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},
{"args", (getter)BaseException_get_args, (setter)BaseException_set_args},
- {"__note__", (getter)BaseException_get_note, (setter)BaseException_set_note},
{"__traceback__", (getter)BaseException_get_tb, (setter)BaseException_set_tb},
{"__context__", BaseException_get_context,
BaseException_set_context, PyDoc_STR("exception context")},
@@ -347,14 +369,6 @@ static PyGetSetDef BaseException_getset[] = {
};
-static inline PyBaseExceptionObject*
-_PyBaseExceptionObject_cast(PyObject *exc)
-{
- assert(PyExceptionInstance_Check(exc));
- return (PyBaseExceptionObject *)exc;
-}
-
-
PyObject *
PyException_GetTraceback(PyObject *self)
{
@@ -910,9 +924,32 @@ exceptiongroup_subset(
PyException_SetContext(eg, PyException_GetContext(orig));
PyException_SetCause(eg, PyException_GetCause(orig));
- PyObject *note = _PyBaseExceptionObject_cast(orig)->note;
- Py_XINCREF(note);
- _PyBaseExceptionObject_cast(eg)->note = note;
+ if (PyObject_HasAttr(orig, &_Py_ID(__notes__))) {
+ PyObject *notes = PyObject_GetAttr(orig, &_Py_ID(__notes__));
+ if (notes == NULL) {
+ goto error;
+ }
+ if (PySequence_Check(notes)) {
+ /* Make a copy so the parts have independent notes lists. */
+ PyObject *notes_copy = PySequence_List(notes);
+ Py_DECREF(notes);
+ if (notes_copy == NULL) {
+ goto error;
+ }
+ int res = PyObject_SetAttr(eg, &_Py_ID(__notes__), notes_copy);
+ Py_DECREF(notes_copy);
+ if (res < 0) {
+ goto error;
+ }
+ }
+ else {
+ /* __notes__ is supposed to be a list, and split() is not a
+ * good place to report earlier user errors, so we just ignore
+ * notes of non-sequence type.
+ */
+ Py_DECREF(notes);
+ }
+ }
*result = eg;
return 0;
@@ -1262,7 +1299,7 @@ is_same_exception_metadata(PyObject *exc1, PyObject *exc2)
PyBaseExceptionObject *e1 = (PyBaseExceptionObject *)exc1;
PyBaseExceptionObject *e2 = (PyBaseExceptionObject *)exc2;
- return (e1->note == e2->note &&
+ return (e1->notes == e2->notes &&
e1->traceback == e2->traceback &&
e1->cause == e2->cause &&
e1->context == e2->context);