gh-141732: Fix `ExceptionGroup` repr changing when original exception… · python/cpython@ff2577f

GitHub

@@ -694,12 +694,12 @@ PyTypeObject _PyExc_ ## EXCNAME = { \

694694695695#defineComplexExtendsException(EXCBASE, EXCNAME, EXCSTORE, EXCNEW, \

696696EXCMETHODS, EXCMEMBERS, EXCGETSET, \

697-EXCSTR, EXCDOC) \

697+EXCSTR, EXCREPR, EXCDOC) \

698698static PyTypeObject _PyExc_ ## EXCNAME = { \

699699 PyVarObject_HEAD_INIT(NULL, 0) \

700700 # EXCNAME, \

701701 sizeof(Py ## EXCSTORE ## Object), 0, \

702- EXCSTORE ## _dealloc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \

702+ EXCSTORE ## _dealloc, 0, 0, 0, 0, EXCREPR, 0, 0, 0, 0, 0, \

703703 EXCSTR, 0, 0, 0, \

704704 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, \

705705 PyDoc_STR(EXCDOC), EXCSTORE ## _traverse, \

@@ -792,7 +792,7 @@ StopIteration_traverse(PyObject *op, visitproc visit, void *arg)

792792}

793793794794ComplexExtendsException(PyExc_Exception, StopIteration, StopIteration,

795-0, 0, StopIteration_members, 0, 0,

795+0, 0, StopIteration_members, 0, 0,0,

796796"Signal the end from iterator.__next__().");

797797798798@@ -865,7 +865,7 @@ static PyMemberDef SystemExit_members[] = {

865865};

866866867867ComplexExtendsException(PyExc_BaseException, SystemExit, SystemExit,

868-0, 0, SystemExit_members, 0, 0,

868+0, 0, SystemExit_members, 0, 0,0,

869869"Request to exit from the interpreter.");

870870871871/*

@@ -890,6 +890,7 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds)

890890891891PyObject*message=NULL;

892892PyObject*exceptions=NULL;

893+PyObject*exceptions_str=NULL;

893894894895if (!PyArg_ParseTuple(args,

895896"UO:BaseExceptionGroup.__new__",

@@ -905,6 +906,18 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds)

905906returnNULL;

906907 }

907908909+/* Save initial exceptions sequence as a string in case sequence is mutated */

910+if (!PyList_Check(exceptions) && !PyTuple_Check(exceptions)) {

911+exceptions_str=PyObject_Repr(exceptions);

912+if (exceptions_str==NULL) {

913+/* We don't hold a reference to exceptions, so clear it before

914+ * attempting a decref in the cleanup.

915+ */

916+exceptions=NULL;

917+ goto error;

918+ }

919+ }

920+908921exceptions=PySequence_Tuple(exceptions);

909922if (!exceptions) {

910923returnNULL;

@@ -988,9 +1001,11 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds)

98810019891002self->msg=Py_NewRef(message);

9901003self->excs=exceptions;

1004+self->excs_str=exceptions_str;

9911005return (PyObject*)self;

9921006error:

993-Py_DECREF(exceptions);

1007+Py_XDECREF(exceptions);

1008+Py_XDECREF(exceptions_str);

9941009returnNULL;

9951010}

9961011@@ -1029,6 +1044,7 @@ BaseExceptionGroup_clear(PyObject *op)

10291044PyBaseExceptionGroupObject*self=PyBaseExceptionGroupObject_CAST(op);

10301045Py_CLEAR(self->msg);

10311046Py_CLEAR(self->excs);

1047+Py_CLEAR(self->excs_str);

10321048returnBaseException_clear(op);

10331049}

10341050@@ -1046,6 +1062,7 @@ BaseExceptionGroup_traverse(PyObject *op, visitproc visit, void *arg)

10461062PyBaseExceptionGroupObject*self=PyBaseExceptionGroupObject_CAST(op);

10471063Py_VISIT(self->msg);

10481064Py_VISIT(self->excs);

1065+Py_VISIT(self->excs_str);

10491066returnBaseException_traverse(op, visit, arg);

10501067}

10511068@@ -1063,6 +1080,54 @@ BaseExceptionGroup_str(PyObject *op)

10631080self->msg, num_excs, num_excs>1 ? "s" : "");

10641081}

106510821083+staticPyObject*

1084+BaseExceptionGroup_repr(PyObject*op)

1085+{

1086+PyBaseExceptionGroupObject*self=PyBaseExceptionGroupObject_CAST(op);

1087+assert(self->msg);

1088+1089+PyObject*exceptions_str=NULL;

1090+1091+/* Use the saved exceptions string for custom sequences. */

1092+if (self->excs_str) {

1093+exceptions_str=Py_NewRef(self->excs_str);

1094+ }

1095+else {

1096+assert(self->excs);

1097+1098+/* Older versions delegated to BaseException, inserting the current

1099+ * value of self.args[1]; but this can be mutable and go out-of-sync

1100+ * with self.exceptions. Instead, use self.exceptions for accuracy,

1101+ * making it look like self.args[1] for backwards compatibility. */

1102+if (PyList_Check(PyTuple_GET_ITEM(self->args, 1))) {

1103+PyObject*exceptions_list=PySequence_List(self->excs);

1104+if (!exceptions_list) {

1105+returnNULL;

1106+ }

1107+1108+exceptions_str=PyObject_Repr(exceptions_list);

1109+Py_DECREF(exceptions_list);

1110+ }

1111+else {

1112+exceptions_str=PyObject_Repr(self->excs);

1113+ }

1114+1115+if (!exceptions_str) {

1116+returnNULL;

1117+ }

1118+ }

1119+1120+assert(exceptions_str!=NULL);

1121+1122+constchar*name=_PyType_Name(Py_TYPE(self));

1123+PyObject*repr=PyUnicode_FromFormat(

1124+"%s(%R, %U)", name,

1125+self->msg, exceptions_str);

1126+1127+Py_DECREF(exceptions_str);

1128+returnrepr;

1129+}

1130+10661131/*[clinic input]

10671132@critical_section

10681133BaseExceptionGroup.derive

@@ -1697,7 +1762,7 @@ static PyMethodDef BaseExceptionGroup_methods[] = {

16971762ComplexExtendsException(PyExc_BaseException, BaseExceptionGroup,

16981763BaseExceptionGroup, BaseExceptionGroup_new/* new */,

16991764BaseExceptionGroup_methods, BaseExceptionGroup_members,

1700-0/* getset */, BaseExceptionGroup_str,

1765+0/* getset */, BaseExceptionGroup_str,BaseExceptionGroup_repr,

17011766"A combination of multiple unrelated exceptions.");

1702176717031768/*

@@ -2425,7 +2490,7 @@ static PyGetSetDef OSError_getset[] = {

24252490ComplexExtendsException(PyExc_Exception, OSError,

24262491OSError, OSError_new,

24272492OSError_methods, OSError_members, OSError_getset,

2428-OSError_str,

2493+OSError_str,0,

24292494"Base class for I/O related errors.");

2430249524312496@@ -2566,7 +2631,7 @@ static PyMethodDef NameError_methods[] = {

25662631ComplexExtendsException(PyExc_Exception, NameError,

25672632NameError, 0,

25682633NameError_methods, NameError_members,

2569-0, BaseException_str, "Name not found globally.");

2634+0, BaseException_str, 0, "Name not found globally.");

2570263525712636/*

25722637 * UnboundLocalError extends NameError

@@ -2700,7 +2765,7 @@ static PyMethodDef AttributeError_methods[] = {

27002765ComplexExtendsException(PyExc_Exception, AttributeError,

27012766AttributeError, 0,

27022767AttributeError_methods, AttributeError_members,

2703-0, BaseException_str, "Attribute not found.");

2768+0, BaseException_str, 0, "Attribute not found.");

2704276927052770/*

27062771 * SyntaxError extends Exception

@@ -2899,7 +2964,7 @@ static PyMemberDef SyntaxError_members[] = {

2899296429002965ComplexExtendsException(PyExc_Exception, SyntaxError, SyntaxError,

290129660, 0, SyntaxError_members, 0,

2902-SyntaxError_str, "Invalid syntax.");

2967+SyntaxError_str, 0, "Invalid syntax.");

290329682904296929052970/*

@@ -2959,7 +3024,7 @@ KeyError_str(PyObject *op)

29593024}

2960302529613026ComplexExtendsException(PyExc_LookupError, KeyError, BaseException,

2962-0, 0, 0, 0, KeyError_str, "Mapping key not found.");

3027+0, 0, 0, 0, KeyError_str, 0, "Mapping key not found.");

296330282964302929653030/*