Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 34k
gh-141732: Fix ExceptionGroup repr changing when original exception sequence is mutated#141736
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
b1afcb859bc3c7ca2d21d493ab01cd90d51177b535af4f08672918eab45402a62d72a4f93ae8f56eefee79dd615e9fec3df5acfef2fca5924885a6b48013991bcddebccef7c59ca573e9531fe91da9238fcaf3142c67e71d962581ce39eec9211d01737206a8d17ef3383af2f53533409997c013cd38db5feb970d7aec51e21fad079094f6b5fcfa757f8bf69cba6b6d61e6b98e455fdb75a12e85cc2aaFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Ensure the :meth:`~object.__repr__` for :exc:`ExceptionGroup` and :exc:`BaseExceptionGroup` does | ||
| not change when the exception sequence that was original passed in to its constructor is subsequently mutated. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -694,12 +694,12 @@ PyTypeObject _PyExc_ ## EXCNAME ={\ | ||
| #define ComplexExtendsException(EXCBASE, EXCNAME, EXCSTORE, EXCNEW, \ | ||
| EXCMETHODS, EXCMEMBERS, EXCGETSET, \ | ||
| EXCSTR, EXCDOC) \ | ||
| EXCSTR, EXCREPR, EXCDOC) \ | ||
| static PyTypeObject _PyExc_ ## EXCNAME ={\ | ||
| PyVarObject_HEAD_INIT(NULL, 0) \ | ||
| # EXCNAME, \ | ||
| sizeof(Py ## EXCSTORE ## Object), 0, \ | ||
| EXCSTORE ## _dealloc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ | ||
| EXCSTORE ## _dealloc, 0, 0, 0, 0, EXCREPR, 0, 0, 0, 0, 0, \ | ||
| EXCSTR, 0, 0, 0, \ | ||
| Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, \ | ||
| PyDoc_STR(EXCDOC), EXCSTORE ## _traverse, \ | ||
| @@ -792,7 +792,7 @@ StopIteration_traverse(PyObject *op, visitproc visit, void *arg) | ||
| } | ||
| ComplexExtendsException(PyExc_Exception, StopIteration, StopIteration, | ||
| 0, 0, StopIteration_members, 0, 0, | ||
| 0, 0, StopIteration_members, 0, 0, 0, | ||
| "Signal the end from iterator.__next__()."); | ||
| @@ -865,7 +865,7 @@ static PyMemberDef SystemExit_members[] ={ | ||
| }; | ||
| ComplexExtendsException(PyExc_BaseException, SystemExit, SystemExit, | ||
| 0, 0, SystemExit_members, 0, 0, | ||
| 0, 0, SystemExit_members, 0, 0, 0, | ||
| "Request to exit from the interpreter."); | ||
| /* | ||
| @@ -890,6 +890,7 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | ||
| PyObject *message = NULL; | ||
| PyObject *exceptions = NULL; | ||
| PyObject *exceptions_str = NULL; | ||
| if (!PyArg_ParseTuple(args, | ||
| "UO:BaseExceptionGroup.__new__", | ||
| @@ -905,6 +906,18 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | ||
| return NULL; | ||
| } | ||
| /* Save initial exceptions sequence as a string in case sequence is mutated */ | ||
| if (!PyList_Check(exceptions) && !PyTuple_Check(exceptions)){ | ||
| exceptions_str = PyObject_Repr(exceptions); | ||
dr-carlos marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| if (exceptions_str == NULL){ | ||
| /* We don't hold a reference to exceptions, so clear it before | ||
| * attempting a decref in the cleanup. | ||
| */ | ||
| exceptions = NULL; | ||
| goto error; | ||
| } | ||
| } | ||
| exceptions = PySequence_Tuple(exceptions); | ||
| if (!exceptions){ | ||
| return NULL; | ||
| @@ -988,9 +1001,11 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | ||
| self->msg = Py_NewRef(message); | ||
| self->excs = exceptions; | ||
| self->excs_str = exceptions_str; | ||
| return (PyObject*)self; | ||
| error: | ||
| Py_DECREF(exceptions); | ||
| Py_XDECREF(exceptions); | ||
| Py_XDECREF(exceptions_str); | ||
| return NULL; | ||
| } | ||
| @@ -1029,6 +1044,7 @@ BaseExceptionGroup_clear(PyObject *op) | ||
| PyBaseExceptionGroupObject *self = PyBaseExceptionGroupObject_CAST(op); | ||
| Py_CLEAR(self->msg); | ||
| Py_CLEAR(self->excs); | ||
| Py_CLEAR(self->excs_str); | ||
| return BaseException_clear(op); | ||
| } | ||
| @@ -1046,6 +1062,7 @@ BaseExceptionGroup_traverse(PyObject *op, visitproc visit, void *arg) | ||
| PyBaseExceptionGroupObject *self = PyBaseExceptionGroupObject_CAST(op); | ||
| Py_VISIT(self->msg); | ||
| Py_VISIT(self->excs); | ||
| Py_VISIT(self->excs_str); | ||
| return BaseException_traverse(op, visit, arg); | ||
| } | ||
| @@ -1063,6 +1080,54 @@ BaseExceptionGroup_str(PyObject *op) | ||
| self->msg, num_excs, num_excs > 1 ? "s" : ""); | ||
| } | ||
| static PyObject * | ||
| BaseExceptionGroup_repr(PyObject *op) | ||
| { | ||
| PyBaseExceptionGroupObject *self = PyBaseExceptionGroupObject_CAST(op); | ||
| assert(self->msg); | ||
| PyObject *exceptions_str = NULL; | ||
| /* Use the saved exceptions string for custom sequences. */ | ||
| if (self->excs_str){ | ||
| exceptions_str = Py_NewRef(self->excs_str); | ||
| } | ||
| else{ | ||
| assert(self->excs); | ||
| /* Older versions delegated to BaseException, inserting the current | ||
| * value of self.args[1]; but this can be mutable and go out-of-sync | ||
| * with self.exceptions. Instead, use self.exceptions for accuracy, | ||
| * making it look like self.args[1] for backwards compatibility. */ | ||
| if (PyList_Check(PyTuple_GET_ITEM(self->args, 1))){ | ||
| PyObject *exceptions_list = PySequence_List(self->excs); | ||
dr-carlos marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| if (!exceptions_list){ | ||
| return NULL; | ||
| } | ||
| exceptions_str = PyObject_Repr(exceptions_list); | ||
dr-carlos marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| Py_DECREF(exceptions_list); | ||
| } | ||
| else{ | ||
| exceptions_str = PyObject_Repr(self->excs); | ||
dr-carlos marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| if (!exceptions_str){ | ||
| return NULL; | ||
| } | ||
| } | ||
| assert(exceptions_str != NULL); | ||
| const char *name = _PyType_Name(Py_TYPE(self)); | ||
| PyObject *repr = PyUnicode_FromFormat( | ||
| "%s(%R, %U)", name, | ||
| self->msg, exceptions_str); | ||
| Py_DECREF(exceptions_str); | ||
| return repr; | ||
| } | ||
| /*[clinic input] | ||
| @critical_section | ||
| BaseExceptionGroup.derive | ||
| @@ -1697,7 +1762,7 @@ static PyMethodDef BaseExceptionGroup_methods[] ={ | ||
| ComplexExtendsException(PyExc_BaseException, BaseExceptionGroup, | ||
| BaseExceptionGroup, BaseExceptionGroup_new /* new */, | ||
| BaseExceptionGroup_methods, BaseExceptionGroup_members, | ||
| 0 /* getset */, BaseExceptionGroup_str, | ||
| 0 /* getset */, BaseExceptionGroup_str, BaseExceptionGroup_repr, | ||
| "A combination of multiple unrelated exceptions."); | ||
| /* | ||
| @@ -2425,7 +2490,7 @@ static PyGetSetDef OSError_getset[] ={ | ||
| ComplexExtendsException(PyExc_Exception, OSError, | ||
| OSError, OSError_new, | ||
| OSError_methods, OSError_members, OSError_getset, | ||
| OSError_str, | ||
| OSError_str, 0, | ||
| "Base class for I/O related errors."); | ||
| @@ -2566,7 +2631,7 @@ static PyMethodDef NameError_methods[] ={ | ||
| ComplexExtendsException(PyExc_Exception, NameError, | ||
| NameError, 0, | ||
| NameError_methods, NameError_members, | ||
| 0, BaseException_str, "Name not found globally."); | ||
| 0, BaseException_str, 0, "Name not found globally."); | ||
| /* | ||
| * UnboundLocalError extends NameError | ||
| @@ -2700,7 +2765,7 @@ static PyMethodDef AttributeError_methods[] ={ | ||
| ComplexExtendsException(PyExc_Exception, AttributeError, | ||
| AttributeError, 0, | ||
| AttributeError_methods, AttributeError_members, | ||
| 0, BaseException_str, "Attribute not found."); | ||
| 0, BaseException_str, 0, "Attribute not found."); | ||
| /* | ||
| * SyntaxError extends Exception | ||
| @@ -2899,7 +2964,7 @@ static PyMemberDef SyntaxError_members[] ={ | ||
| ComplexExtendsException(PyExc_Exception, SyntaxError, SyntaxError, | ||
| 0, 0, SyntaxError_members, 0, | ||
| SyntaxError_str, "Invalid syntax."); | ||
| SyntaxError_str, 0, "Invalid syntax."); | ||
| /* | ||
| @@ -2959,7 +3024,7 @@ KeyError_str(PyObject *op) | ||
| } | ||
| ComplexExtendsException(PyExc_LookupError, KeyError, BaseException, | ||
| 0, 0, 0, 0, KeyError_str, "Mapping key not found."); | ||
| 0, 0, 0, 0, KeyError_str, 0, "Mapping key not found."); | ||
| /* | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.