diff options
author | Irit Katriel <1055913+iritkatriel@users.noreply.github.com> | 2022-01-02 23:22:42 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-02 23:22:42 (GMT) |
commit | 65e7c1f90e9136fc61f4af029b065d9f6c5664c3 (patch) | |
tree | bf322965684bd0eeef7c220c5d1cb8307933cbb7 /Objects/exceptions.c | |
parent | 8e75c6b49b7cb8515b917f01b32ece8c8ea2c0a0 (diff) | |
download | cpython-65e7c1f90e9136fc61f4af029b065d9f6c5664c3.zip cpython-65e7c1f90e9136fc61f4af029b065d9f6c5664c3.tar.gz cpython-65e7c1f90e9136fc61f4af029b065d9f6c5664c3.tar.bz2 |
bpo-46219, 46221: simplify except* implementation following exc_info changes. Move helpers to exceptions.c. Do not assume that exception groups are truthy. (GH-30289)
Diffstat (limited to 'Objects/exceptions.c')
-rw-r--r-- | Objects/exceptions.c | 120 |
1 files changed, 118 insertions, 2 deletions
diff --git a/Objects/exceptions.c b/Objects/exceptions.c index d82340b..403d2d4 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1207,8 +1207,8 @@ collect_exception_group_leaves(PyObject *exc, PyObject *leaves) * of eg which contains all leaf exceptions that are contained * in any exception group in keep. */ -PyObject * -_PyExc_ExceptionGroupProjection(PyObject *eg, PyObject *keep) +static PyObject * +exception_group_projection(PyObject *eg, PyObject *keep) { assert(_PyBaseExceptionGroup_Check(eg)); assert(PyList_CheckExact(keep)); @@ -1245,6 +1245,122 @@ _PyExc_ExceptionGroupProjection(PyObject *eg, PyObject *keep) return result; } +static bool +is_same_exception_metadata(PyObject *exc1, PyObject *exc2) +{ + assert(PyExceptionInstance_Check(exc1)); + assert(PyExceptionInstance_Check(exc2)); + + PyBaseExceptionObject *e1 = (PyBaseExceptionObject *)exc1; + PyBaseExceptionObject *e2 = (PyBaseExceptionObject *)exc2; + + return (e1->note == e2->note && + e1->traceback == e2->traceback && + e1->cause == e2->cause && + e1->context == e2->context); +} + +/* + This function is used by the interpreter to calculate + the exception group to be raised at the end of a + try-except* construct. + + orig: the original except that was caught. + excs: a list of exceptions that were raised/reraised + in the except* clauses. + + Calculates an exception group to raise. It contains + all exceptions in excs, where those that were reraised + have same nesting structure as in orig, and those that + were raised (if any) are added as siblings in a new EG. + + Returns NULL and sets an exception on failure. +*/ +PyObject * +_PyExc_PrepReraiseStar(PyObject *orig, PyObject *excs) +{ + assert(PyExceptionInstance_Check(orig)); + assert(PyList_Check(excs)); + + Py_ssize_t numexcs = PyList_GET_SIZE(excs); + + if (numexcs == 0) { + return Py_NewRef(Py_None); + } + + if (!_PyBaseExceptionGroup_Check(orig)) { + /* a naked exception was caught and wrapped. Only one except* clause + * could have executed,so there is at most one exception to raise. + */ + + assert(numexcs == 1 || (numexcs == 2 && PyList_GET_ITEM(excs, 1) == Py_None)); + + PyObject *e = PyList_GET_ITEM(excs, 0); + assert(e != NULL); + return Py_NewRef(e); + } + + PyObject *raised_list = PyList_New(0); + if (raised_list == NULL) { + return NULL; + } + PyObject* reraised_list = PyList_New(0); + if (reraised_list == NULL) { + Py_DECREF(raised_list); + return NULL; + } + + /* Now we are holding refs to raised_list and reraised_list */ + + PyObject *result = NULL; + + /* Split excs into raised and reraised by comparing metadata with orig */ + for (Py_ssize_t i = 0; i < numexcs; i++) { + PyObject *e = PyList_GET_ITEM(excs, i); + assert(e != NULL); + if (Py_IsNone(e)) { + continue; + } + bool is_reraise = is_same_exception_metadata(e, orig); + PyObject *append_list = is_reraise ? reraised_list : raised_list; + if (PyList_Append(append_list, e) < 0) { + goto done; + } + } + + PyObject *reraised_eg = exception_group_projection(orig, reraised_list); + if (reraised_eg == NULL) { + goto done; + } + + if (!Py_IsNone(reraised_eg)) { + assert(is_same_exception_metadata(reraised_eg, orig)); + } + Py_ssize_t num_raised = PyList_GET_SIZE(raised_list); + if (num_raised == 0) { + result = reraised_eg; + } + else if (num_raised > 0) { + int res = 0; + if (!Py_IsNone(reraised_eg)) { + res = PyList_Append(raised_list, reraised_eg); + } + Py_DECREF(reraised_eg); + if (res < 0) { + goto done; + } + result = _PyExc_CreateExceptionGroup("", raised_list); + if (result == NULL) { + goto done; + } + } + +done: + Py_XDECREF(raised_list); + Py_XDECREF(reraised_list); + return result; +} + static PyMemberDef BaseExceptionGroup_members[] = { {"message", T_OBJECT, offsetof(PyBaseExceptionGroupObject, msg), READONLY, PyDoc_STR("exception message")}, |