@@ -531,13 +531,13 @@ PyTypeObject _PyExc_ ## EXCNAME = { \
531531
532532#define ComplexExtendsException (EXCBASE , EXCNAME , EXCSTORE , EXCNEW , \
533533 EXCMETHODS , EXCMEMBERS , EXCGETSET , \
534- EXCSTR , EXCDOC ) \
534+ EXCSTR , EXCREPR , EXCDOC ) \
535535static PyTypeObject _PyExc_ ## EXCNAME = { \
536536 PyVarObject_HEAD_INIT(NULL, 0) \
537537 # EXCNAME, \
538538 sizeof(Py ## EXCSTORE ## Object), 0, \
539- (destructor)EXCSTORE ## _dealloc, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0, \
540- (reprfunc)EXCSTR, 0, 0, 0, \
539+ (destructor)EXCSTORE ## _dealloc, 0, 0, 0, 0, (reprfunc)EXCREPR , 0, 0, 0, \
540+ 0, 0, (reprfunc)EXCSTR, 0, 0, 0, \
541541 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, \
542542 PyDoc_STR(EXCDOC), (traverseproc)EXCSTORE ## _traverse, \
543543 (inquiry)EXCSTORE ## _clear, 0, 0, 0, 0, EXCMETHODS, \
@@ -619,7 +619,7 @@ StopIteration_traverse(PyStopIterationObject *self, visitproc visit, void *arg)
619619}
620620
621621ComplexExtendsException (PyExc_Exception , StopIteration , StopIteration ,
622- 0 , 0 , StopIteration_members , 0 , 0 ,
622+ 0 , 0 , StopIteration_members , 0 , 0 , 0 ,
623623 "Signal the end from iterator.__next__()." );
624624
625625
@@ -682,7 +682,7 @@ static PyMemberDef SystemExit_members[] = {
682682};
683683
684684ComplexExtendsException (PyExc_BaseException , SystemExit , SystemExit ,
685- 0 , 0 , SystemExit_members , 0 , 0 ,
685+ 0 , 0 , SystemExit_members , 0 , 0 , 0 ,
686686 "Request to exit from the interpreter." );
687687
688688/*
@@ -707,6 +707,7 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
707707
708708 PyObject * message = NULL ;
709709 PyObject * exceptions = NULL ;
710+ PyObject * exceptions_str = NULL ;
710711
711712 if (!PyArg_ParseTuple (args ,
712713 "UO:BaseExceptionGroup.__new__" ,
@@ -722,6 +723,18 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
722723 return NULL ;
723724 }
724725
726+ /* Save initial exceptions sequence as a string in case sequence is mutated */
727+ if (!PyList_Check (exceptions ) && !PyTuple_Check (exceptions )) {
728+ exceptions_str = PyObject_Repr (exceptions );
729+ if (exceptions_str == NULL ) {
730+ /* We don't hold a reference to exceptions, so clear it before
731+ * attempting a decref in the cleanup.
732+ */
733+ exceptions = NULL ;
734+ goto error ;
735+ }
736+ }
737+
725738 exceptions = PySequence_Tuple (exceptions );
726739 if (!exceptions ) {
727740 return NULL ;
@@ -805,9 +818,11 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
805818
806819 self -> msg = Py_NewRef (message );
807820 self -> excs = exceptions ;
821+ self -> excs_str = exceptions_str ;
808822 return (PyObject * )self ;
809823error :
810- Py_DECREF (exceptions );
824+ Py_XDECREF (exceptions );
825+ Py_XDECREF (exceptions_str );
811826 return NULL ;
812827}
813828
@@ -846,6 +861,7 @@ BaseExceptionGroup_clear(PyBaseExceptionGroupObject *self)
846861{
847862 Py_CLEAR (self -> msg );
848863 Py_CLEAR (self -> excs );
864+ Py_CLEAR (self -> excs_str );
849865 return BaseException_clear ((PyBaseExceptionObject * )self );
850866}
851867
@@ -863,6 +879,7 @@ BaseExceptionGroup_traverse(PyBaseExceptionGroupObject *self,
863879{
864880 Py_VISIT (self -> msg );
865881 Py_VISIT (self -> excs );
882+ Py_VISIT (self -> excs_str );
866883 return BaseException_traverse ((PyBaseExceptionObject * )self , visit , arg );
867884}
868885
@@ -879,6 +896,61 @@ BaseExceptionGroup_str(PyBaseExceptionGroupObject *self)
879896 self -> msg , num_excs , num_excs > 1 ? "s" : "" );
880897}
881898
899+ static PyObject *
900+ BaseExceptionGroup_repr (PyObject * op )
901+ {
902+ PyBaseExceptionGroupObject * self = PyBaseExceptionGroupObject_CAST (op );
903+ assert (self -> msg );
904+
905+ PyObject * exceptions_str = NULL ;
906+
907+ /* Use the saved exceptions string for custom sequences. */
908+ if (self -> excs_str ) {
909+ exceptions_str = Py_NewRef (self -> excs_str );
910+ }
911+ else {
912+ assert (self -> excs );
913+
914+ /* Older versions delegated to BaseException, inserting the current
915+ * value of self.args[1]; but this can be mutable and go out-of-sync
916+ * with self.exceptions. Instead, use self.exceptions for accuracy,
917+ * making it look like self.args[1] for backwards compatibility. */
918+ if (PyList_Check (PyTuple_GET_ITEM (self -> args , 1 ))) {
919+ PyObject * exceptions_list = PySequence_List (self -> excs );
920+ if (!exceptions_list ) {
921+ return NULL ;
922+ }
923+
924+ exceptions_str = PyObject_Repr (exceptions_list );
925+ Py_DECREF (exceptions_list );
926+ }
927+ else {
928+ exceptions_str = PyObject_Repr (self -> excs );
929+ }
930+
931+ if (!exceptions_str ) {
932+ return NULL ;
933+ }
934+ }
935+
936+ assert (exceptions_str != NULL );
937+
938+ const char * name = _PyType_Name (Py_TYPE (self ));
939+ PyObject * repr = PyUnicode_FromFormat (
940+ "%s(%R, %U)" , name ,
941+ self -> msg , exceptions_str );
942+
943+ Py_DECREF (exceptions_str );
944+ return repr ;
945+ }
946+
947+ /*[clinic input]
948+ @critical_section
949+ BaseExceptionGroup.derive
950+ excs: object
951+ /
952+ [clinic start generated code]*/
953+
882954static PyObject *
883955BaseExceptionGroup_derive (PyObject * self_ , PyObject * excs )
884956{
@@ -1487,7 +1559,7 @@ static PyMethodDef BaseExceptionGroup_methods[] = {
14871559ComplexExtendsException (PyExc_BaseException , BaseExceptionGroup ,
14881560 BaseExceptionGroup , BaseExceptionGroup_new /* new */ ,
14891561 BaseExceptionGroup_methods , BaseExceptionGroup_members ,
1490- 0 /* getset */ , BaseExceptionGroup_str ,
1562+ 0 /* getset */ , BaseExceptionGroup_str , BaseExceptionGroup_repr ,
14911563 "A combination of multiple unrelated exceptions." );
14921564
14931565/*
@@ -2124,7 +2196,7 @@ static PyGetSetDef OSError_getset[] = {
21242196ComplexExtendsException (PyExc_Exception , OSError ,
21252197 OSError , OSError_new ,
21262198 OSError_methods , OSError_members , OSError_getset ,
2127- OSError_str ,
2199+ OSError_str , 0 ,
21282200 "Base class for I/O related errors." );
21292201
21302202
@@ -2255,7 +2327,7 @@ static PyMethodDef NameError_methods[] = {
22552327ComplexExtendsException (PyExc_Exception , NameError ,
22562328 NameError , 0 ,
22572329 NameError_methods , NameError_members ,
2258- 0 , BaseException_str , "Name not found globally." );
2330+ 0 , BaseException_str , 0 , "Name not found globally." );
22592331
22602332/*
22612333 * UnboundLocalError extends NameError
@@ -2377,7 +2449,7 @@ static PyMethodDef AttributeError_methods[] = {
23772449ComplexExtendsException (PyExc_Exception , AttributeError ,
23782450 AttributeError , 0 ,
23792451 AttributeError_methods , AttributeError_members ,
2380- 0 , BaseException_str , "Attribute not found." );
2452+ 0 , BaseException_str , 0 , "Attribute not found." );
23812453
23822454/*
23832455 * SyntaxError extends Exception
@@ -2558,7 +2630,7 @@ static PyMemberDef SyntaxError_members[] = {
25582630
25592631ComplexExtendsException (PyExc_Exception , SyntaxError , SyntaxError ,
25602632 0 , 0 , SyntaxError_members , 0 ,
2561- SyntaxError_str , "Invalid syntax." );
2633+ SyntaxError_str , 0 , "Invalid syntax." );
25622634
25632635
25642636/*
@@ -2616,7 +2688,7 @@ KeyError_str(PyBaseExceptionObject *self)
26162688}
26172689
26182690ComplexExtendsException (PyExc_LookupError , KeyError , BaseException ,
2619- 0 , 0 , 0 , 0 , KeyError_str , "Mapping key not found." );
2691+ 0 , 0 , 0 , 0 , KeyError_str , 0 , "Mapping key not found." );
26202692
26212693
26222694/*
0 commit comments