diff options
author | Irit Katriel <1055913+iritkatriel@users.noreply.github.com> | 2022-04-16 18:59:52 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-16 18:59:52 (GMT) |
commit | d4c4a76ed1427c947fcbbe692625b3f644cf3aaf (patch) | |
tree | 2e503da40ff6459711ff5730b22e89962b175252 /Objects/exceptions.c | |
parent | 7fa3a5a2197896066e3fe53ee325ac6ab54c3414 (diff) | |
download | cpython-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.c | 123 |
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); |