Skip to content

Commit 9ca573e

Browse files
committed
Save repr of non-standard sequences in BaseExceptionGroup constructor
1 parent ccef7c5 commit 9ca573e

File tree

2 files changed

+35
-12
lines changed

2 files changed

+35
-12
lines changed

Include/cpython/pyerrors.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ typedef struct {
1818
PyException_HEAD
1919
PyObject *msg;
2020
PyObject *excs;
21+
PyObject *excs_str;
2122
} PyBaseExceptionGroupObject;
2223

2324
typedef struct {

Objects/exceptions.c

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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;
992999
error:
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

Comments
 (0)