@@ -890,6 +890,7 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
890890
891891 PyObject * message = NULL ;
892892 PyObject * exceptions = NULL ;
893+ PyObject * exceptions_str = NULL ;
893894
894895 if (!PyArg_ParseTuple (args ,
895896 "UO:BaseExceptionGroup.__new__" ,
@@ -905,6 +906,11 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
905906 return NULL ;
906907 }
907908
909+ /* Save initial exceptions sequence as a string incase sequence is mutated */
910+ if (!PyList_Check (exceptions ) && !PyTuple_Check (exceptions )) {
911+ exceptions_str = PyObject_Repr (exceptions );
912+ }
913+
908914 exceptions = PySequence_Tuple (exceptions );
909915 if (!exceptions ) {
910916 return NULL ;
@@ -988,9 +994,11 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
988994
989995 self -> msg = Py_NewRef (message );
990996 self -> excs = exceptions ;
997+ self -> excs_str = exceptions_str ;
991998 return (PyObject * )self ;
992999error :
9931000 Py_DECREF (exceptions );
1001+ Py_XDECREF (exceptions_str );
9941002 return NULL ;
9951003}
9961004
@@ -1029,6 +1037,7 @@ BaseExceptionGroup_clear(PyObject *op)
10291037 PyBaseExceptionGroupObject * self = PyBaseExceptionGroupObject_CAST (op );
10301038 Py_CLEAR (self -> msg );
10311039 Py_CLEAR (self -> excs );
1040+ Py_CLEAR (self -> excs_str );
10321041 return BaseException_clear (op );
10331042}
10341043
@@ -1046,6 +1055,7 @@ BaseExceptionGroup_traverse(PyObject *op, visitproc visit, void *arg)
10461055 PyBaseExceptionGroupObject * self = PyBaseExceptionGroupObject_CAST (op );
10471056 Py_VISIT (self -> msg );
10481057 Py_VISIT (self -> excs );
1058+ Py_VISIT (self -> excs_str );
10491059 return BaseException_traverse (op , visit , arg );
10501060}
10511061
@@ -1068,24 +1078,34 @@ BaseExceptionGroup_repr(PyObject *op)
10681078{
10691079 PyBaseExceptionGroupObject * self = PyBaseExceptionGroupObject_CAST (op );
10701080 assert (self -> msg );
1071- assert (self -> excs );
10721081
1073- /* Use the actual exceptions tuple for accuracy, but make it look like the
1074- * original exception sequence, if possible, for backwards compatibility. */
1075- PyObject * excs_orig = PyTuple_GET_ITEM (self -> args , 1 );
1076- if (PyList_Check (excs_orig )) {
1077- excs_orig = PySequence_List (self -> excs );
1078- }
1079- else {
1080- excs_orig = Py_NewRef (self -> excs );
1082+ PyObject * exceptions_str = Py_XNewRef (self -> excs_str );
1083+
1084+ /* If the initial exceptions string was not saved in the constructor. */
1085+ if (!exceptions_str ) {
1086+ assert (self -> excs );
1087+
1088+ /* Older versions of this code delegated to BaseException's repr, inserting
1089+ * the current value of self.args[1]. However, mutating that sequence makes
1090+ * the repr appear as if the ExceptionGroup itself has changed, which it hasn't.
1091+ * So we use the actual exceptions tuple for accuracy, but make it look like the
1092+ * original exception sequence if possible, for backwards compatibility. */
1093+ if (PyList_Check (PyTuple_GET_ITEM (self -> args , 1 ))) {
1094+ PyObject * exceptions_list = PySequence_List (self -> excs );
1095+ exceptions_str = PyObject_Repr (exceptions_list );
1096+ Py_DECREF (exceptions_list );
1097+ }
1098+ else {
1099+ exceptions_str = PyObject_Repr (self -> excs );
1100+ }
10811101 }
10821102
10831103 const char * name = _PyType_Name (Py_TYPE (self ));
10841104 PyObject * repr = PyUnicode_FromFormat (
1085- "%s(%R, %R )" , name ,
1086- self -> msg , excs_orig );
1105+ "%s(%R, %U )" , name ,
1106+ self -> msg , exceptions_str );
10871107
1088- Py_DECREF (excs_orig );
1108+ Py_DECREF (exceptions_str );
10891109 return repr ;
10901110}
10911111
@@ -1708,6 +1728,8 @@ static PyMemberDef BaseExceptionGroup_members[] = {
17081728 PyDoc_STR ("exception message" )},
17091729 {"exceptions" , _Py_T_OBJECT , offsetof(PyBaseExceptionGroupObject , excs ), Py_READONLY ,
17101730 PyDoc_STR ("nested exceptions" )},
1731+ {"_exceptions_str" , _Py_T_OBJECT , offsetof(PyBaseExceptionGroupObject , excs_str ), Py_READONLY ,
1732+ PyDoc_STR ("private string representation of initial exceptions sequence" )},
17111733 {NULL } /* Sentinel */
17121734};
17131735
0 commit comments