@@ -956,7 +956,7 @@ print_exception_traceback(struct exception_print_context *ctx, PyObject *value)
956956/* Prints the message line: module.qualname[: str(exc)] */
957957static int
958958print_exception_message (struct exception_print_context * ctx , PyObject * type ,
959- PyObject * value )
959+ PyObject * value )
960960{
961961 PyObject * f = ctx -> file ;
962962
@@ -1253,183 +1253,221 @@ print_chained(struct exception_print_context* ctx, PyObject *value,
12531253 return 0 ;
12541254}
12551255
1256- static void
1257- print_exception_recursive (struct exception_print_context * ctx , PyObject * value )
1256+ /* Return true if value is in seen or there was a lookup error.
1257+ * Return false if lookup succeeded and the item was not found.
1258+ * We suppress errors because this makes us err on the side of
1259+ * under-printing which is better than over-printing irregular
1260+ * exceptions (e.g., unhashable ones).
1261+ */
1262+ static bool
1263+ print_exception_seen_lookup (struct exception_print_context * ctx ,
1264+ PyObject * value )
12581265{
1259- int err = 0 , res ;
1260- PyObject * cause , * context ;
1266+ PyObject * check_id = PyLong_FromVoidPtr (value );
1267+ if (check_id == NULL ) {
1268+ PyErr_Clear ();
1269+ return true;
1270+ }
12611271
1262- if (ctx -> seen != NULL ) {
1263- /* Exception chaining */
1264- PyObject * value_id = PyLong_FromVoidPtr (value );
1265- if (value_id == NULL || PySet_Add (ctx -> seen , value_id ) == -1 )
1266- PyErr_Clear ();
1267- else if (PyExceptionInstance_Check (value )) {
1268- PyObject * check_id = NULL ;
1269-
1270- cause = PyException_GetCause (value );
1271- context = PyException_GetContext (value );
1272- if (cause ) {
1273- check_id = PyLong_FromVoidPtr (cause );
1274- if (check_id == NULL ) {
1275- res = -1 ;
1276- } else {
1277- res = PySet_Contains (ctx -> seen , check_id );
1278- Py_DECREF (check_id );
1279- }
1280- if (res == -1 )
1281- PyErr_Clear ();
1282- if (res == 0 ) {
1283- err = print_chained (ctx , cause , cause_message , "cause" );
1284- }
1285- }
1286- else if (context &&
1287- !((PyBaseExceptionObject * )value )-> suppress_context ) {
1288- check_id = PyLong_FromVoidPtr (context );
1289- if (check_id == NULL ) {
1290- res = -1 ;
1291- } else {
1292- res = PySet_Contains (ctx -> seen , check_id );
1293- Py_DECREF (check_id );
1294- }
1295- if (res == -1 )
1296- PyErr_Clear ();
1297- if (res == 0 ) {
1298- err = print_chained (ctx , context , context_message , "context" );
1299- }
1300- }
1301- Py_XDECREF (context );
1302- Py_XDECREF (cause );
1303- }
1272+ int in_seen = PySet_Contains (ctx -> seen , check_id );
1273+ Py_DECREF (check_id );
1274+ if (in_seen == -1 ) {
1275+ PyErr_Clear ();
1276+ return true;
1277+ }
1278+
1279+ if (in_seen == 1 ) {
1280+ /* value is in seen */
1281+ return true;
1282+ }
1283+ return false;
1284+ }
1285+
1286+ static int
1287+ print_exception_cause_and_context (struct exception_print_context * ctx ,
1288+ PyObject * value )
1289+ {
1290+ PyObject * value_id = PyLong_FromVoidPtr (value );
1291+ if (value_id == NULL || PySet_Add (ctx -> seen , value_id ) == -1 ) {
1292+ PyErr_Clear ();
13041293 Py_XDECREF (value_id );
1294+ return 0 ;
13051295 }
1306- if (err ) {
1307- /* don't do anything else */
1296+ Py_DECREF (value_id );
1297+
1298+ if (!PyExceptionInstance_Check (value )) {
1299+ return 0 ;
13081300 }
1309- else if (!_PyBaseExceptionGroup_Check (value )) {
1310- print_exception (ctx , value );
1301+
1302+ PyObject * cause = PyException_GetCause (value );
1303+ if (cause ) {
1304+ int err = 0 ;
1305+ if (!print_exception_seen_lookup (ctx , cause )) {
1306+ err = print_chained (ctx , cause , cause_message , "cause" );
1307+ }
1308+ Py_DECREF (cause );
1309+ return err ;
13111310 }
1312- else if (ctx -> exception_group_depth > ctx -> max_group_depth ) {
1313- /* exception group but depth exceeds limit */
1311+ if (((PyBaseExceptionObject * )value )-> suppress_context ) {
1312+ return 0 ;
1313+ }
1314+ PyObject * context = PyException_GetContext (value );
1315+ if (context ) {
1316+ int err = 0 ;
1317+ if (!print_exception_seen_lookup (ctx , context )) {
1318+ err = print_chained (ctx , context , context_message , "context" );
1319+ }
1320+ Py_DECREF (context );
1321+ return err ;
1322+ }
1323+ return 0 ;
1324+ }
1325+
1326+ static int
1327+ print_exception_group (struct exception_print_context * ctx , PyObject * value )
1328+ {
1329+ PyObject * f = ctx -> file ;
13141330
1315- PyObject * line = PyUnicode_FromFormat (
1316- "... (max_group_depth is %d)\n" , ctx -> max_group_depth );
1331+ if ( ctx -> exception_group_depth > ctx -> max_group_depth ) {
1332+ /* depth exceeds limit */
13171333
1318- if (line ) {
1319- PyObject * f = ctx -> file ;
1320- if (err == 0 ) {
1321- err = write_indented_margin (ctx , f );
1322- }
1323- if (err == 0 ) {
1324- err = PyFile_WriteObject (line , f , Py_PRINT_RAW );
1325- }
1326- Py_DECREF (line );
1334+ if (write_indented_margin (ctx , f ) < 0 ) {
1335+ return -1 ;
13271336 }
1328- else {
1329- err = -1 ;
1337+
1338+ PyObject * line = PyUnicode_FromFormat ("... (max_group_depth is %d)\n" ,
1339+ ctx -> max_group_depth );
1340+ if (line == NULL ) {
1341+ return -1 ;
13301342 }
1343+ int err = PyFile_WriteObject (line , f , Py_PRINT_RAW );
1344+ Py_DECREF (line );
1345+ return err ;
1346+ }
1347+
1348+ if (ctx -> exception_group_depth == 0 ) {
1349+ ctx -> exception_group_depth += 1 ;
1350+ }
1351+ print_exception (ctx , value );
1352+
1353+ PyObject * excs = ((PyBaseExceptionGroupObject * )value )-> excs ;
1354+ assert (excs && PyTuple_Check (excs ));
1355+ Py_ssize_t num_excs = PyTuple_GET_SIZE (excs );
1356+ assert (num_excs > 0 );
1357+ Py_ssize_t n ;
1358+ if (num_excs <= ctx -> max_group_width ) {
1359+ n = num_excs ;
13311360 }
13321361 else {
1333- /* format exception group */
1362+ n = ctx -> max_group_width + 1 ;
1363+ }
13341364
1335- if (ctx -> exception_group_depth == 0 ) {
1336- ctx -> exception_group_depth += 1 ;
1365+ ctx -> need_close = false;
1366+ for (Py_ssize_t i = 0 ; i < n ; i ++ ) {
1367+ bool last_exc = (i == n - 1 );
1368+ if (last_exc ) {
1369+ // The closing frame may be added in a recursive call
1370+ ctx -> need_close = true;
13371371 }
1338- print_exception (ctx , value );
13391372
1340- PyObject * excs = ((PyBaseExceptionGroupObject * )value )-> excs ;
1341- assert (excs && PyTuple_Check (excs ));
1342- Py_ssize_t num_excs = PyTuple_GET_SIZE (excs );
1343- assert (num_excs > 0 );
1344- Py_ssize_t n ;
1345- if (num_excs <= ctx -> max_group_width ) {
1346- n = num_excs ;
1373+ if (_Py_WriteIndent (EXC_INDENT (ctx ), f ) < 0 ) {
1374+ return -1 ;
1375+ }
1376+ bool truncated = (i >= ctx -> max_group_width );
1377+ PyObject * line ;
1378+ if (!truncated ) {
1379+ line = PyUnicode_FromFormat (
1380+ "%s+---------------- %zd ----------------\n" ,
1381+ (i == 0 ) ? "+-" : " " , i + 1 );
13471382 }
13481383 else {
1349- n = ctx -> max_group_width + 1 ;
1384+ line = PyUnicode_FromFormat (
1385+ "%s+---------------- ... ----------------\n" ,
1386+ (i == 0 ) ? "+-" : " " );
1387+ }
1388+ if (line == NULL ) {
1389+ return -1 ;
1390+ }
1391+ int err = PyFile_WriteObject (line , f , Py_PRINT_RAW );
1392+ Py_DECREF (line );
1393+ if (err < 0 ) {
1394+ return -1 ;
13501395 }
13511396
1352- PyObject * f = ctx -> file ;
1397+ ctx -> exception_group_depth += 1 ;
1398+ PyObject * exc = PyTuple_GET_ITEM (excs , i );
13531399
1354- ctx -> need_close = false;
1355- for (Py_ssize_t i = 0 ; i < n ; i ++ ) {
1356- int last_exc = (i == n - 1 );
1357- if (last_exc ) {
1358- // The closing frame may be added in a recursive call
1359- ctx -> need_close = true;
1400+ if (!truncated ) {
1401+ if (Py_EnterRecursiveCall (" in print_exception_recursive" ) != 0 ) {
1402+ return -1 ;
13601403 }
1361- PyObject * line ;
1362- bool truncated = (i >= ctx -> max_group_width );
1363- if (!truncated ) {
1364- line = PyUnicode_FromFormat (
1365- "%s+---------------- %zd ----------------\n" ,
1366- (i == 0 ) ? "+-" : " " , i + 1 );
1404+ print_exception_recursive (ctx , exc );
1405+ Py_LeaveRecursiveCall ();
1406+ }
1407+ else {
1408+ Py_ssize_t excs_remaining = num_excs - ctx -> max_group_width ;
1409+
1410+ if (write_indented_margin (ctx , f ) < 0 ) {
1411+ return -1 ;
13671412 }
1368- else {
1369- line = PyUnicode_FromFormat (
1370- "%s+---------------- ... ----------------\n" ,
1371- (i == 0 ) ? "+-" : " " );
1413+
1414+ PyObject * line = PyUnicode_FromFormat (
1415+ "and %zd more exception%s\n" ,
1416+ excs_remaining , excs_remaining > 1 ? "s" : "" );
1417+
1418+ if (line == NULL ) {
1419+ return -1 ;
13721420 }
13731421
1374- if (line ) {
1375- if (err == 0 ) {
1376- err = _Py_WriteIndent (EXC_INDENT (ctx ), f );
1377- }
1378- if (err == 0 ) {
1379- err = PyFile_WriteObject (line , f , Py_PRINT_RAW );
1380- }
1381- Py_DECREF (line );
1422+ int err = PyFile_WriteObject (line , f , Py_PRINT_RAW );
1423+ Py_DECREF (line );
1424+ if (err < 0 ) {
1425+ return -1 ;
13821426 }
1383- else {
1384- err = -1 ;
1427+ }
1428+
1429+ if (last_exc && ctx -> need_close ) {
1430+ if (_Py_WriteIndent (EXC_INDENT (ctx ), f ) < 0 ) {
1431+ return -1 ;
13851432 }
1433+ if (PyFile_WriteString (
1434+ "+------------------------------------\n" , f ) < 0 ) {
1435+ return -1 ;
1436+ }
1437+ ctx -> need_close = false;
1438+ }
1439+ ctx -> exception_group_depth -= 1 ;
1440+ }
13861441
1387- if (err == 0 ) {
1388- ctx -> exception_group_depth += 1 ;
1389- PyObject * exc = PyTuple_GET_ITEM (excs , i );
1390-
1391- if (!truncated ) {
1392- if (!Py_EnterRecursiveCall (" in print_exception_recursive" )) {
1393- print_exception_recursive (ctx , exc );
1394- Py_LeaveRecursiveCall ();
1395- }
1396- else {
1397- err = -1 ;
1398- }
1399- }
1400- else {
1401- Py_ssize_t excs_remaining = num_excs - ctx -> max_group_width ;
1402- PyObject * line = PyUnicode_FromFormat (
1403- "and %zd more exception%s\n" ,
1404- excs_remaining , excs_remaining > 1 ? "s" : "" );
1405-
1406- if (line ) {
1407- if (err == 0 ) {
1408- err = write_indented_margin (ctx , f );
1409- }
1410- if (err == 0 ) {
1411- err = PyFile_WriteObject (line , f , Py_PRINT_RAW );
1412- }
1413- Py_DECREF (line );
1414- }
1415- else {
1416- err = -1 ;
1417- }
1418- }
1442+ if (ctx -> exception_group_depth == 1 ) {
1443+ ctx -> exception_group_depth -= 1 ;
1444+ }
1445+ return 0 ;
1446+ }
14191447
1420- if (err == 0 && last_exc && ctx -> need_close ) {
1421- err = _Py_WriteIndent (EXC_INDENT (ctx ), f );
1422- if (err == 0 ) {
1423- err = PyFile_WriteString (
1424- "+------------------------------------\n" , f );
1425- }
1426- ctx -> need_close = false;
1427- }
1428- ctx -> exception_group_depth -= 1 ;
1429- }
1448+ static void
1449+ print_exception_recursive (struct exception_print_context * ctx , PyObject * value )
1450+ {
1451+ int err = 0 ;
1452+ if (ctx -> seen != NULL ) {
1453+ /* Exception chaining */
1454+ err = print_exception_cause_and_context (ctx , value );
1455+ }
1456+ if (err ) {
1457+ /* don't do anything else */
1458+ }
1459+ else if (!_PyBaseExceptionGroup_Check (value )) {
1460+ print_exception (ctx , value );
1461+ }
1462+ else {
1463+ int prev_depth = ctx -> exception_group_depth ;
1464+ err = print_exception_group (ctx , value );
1465+ if (err < 0 ) {
1466+ /* restore the depth as long as we're ignoring errors */
1467+ ctx -> exception_group_depth = prev_depth ;
14301468 }
1431- if ( ctx -> exception_group_depth == 1 ) {
1432- ctx -> exception_group_depth -= 1 ;
1469+ else {
1470+ assert ( prev_depth == ctx -> exception_group_depth ) ;
14331471 }
14341472 }
14351473 if (err != 0 )
0 commit comments