Skip to content

Commit 0fe104f

Browse files
iritkatrielErlend Egeberg Aasland
andauthored
bpo-45635: refactor print_exception_recursive into smaller functions to standardize error handling (pythonGH-30015)
Co-authored-by: Erlend Egeberg Aasland <erlend.aasland@innova.no>
1 parent c1051e0 commit 0fe104f

File tree

1 file changed

+187
-149
lines changed

1 file changed

+187
-149
lines changed

Python/pythonrun.c

Lines changed: 187 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -956,7 +956,7 @@ print_exception_traceback(struct exception_print_context *ctx, PyObject *value)
956956
/* Prints the message line: module.qualname[: str(exc)] */
957957
static int
958958
print_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

Comments
 (0)