Skip to content

Commit 8d6155e

Browse files
iritkatrielErlend Egeberg Aasland
andauthored
bpo-45635: Do not suppress errors in functions called from _PyErr_Display (GH-30073)
Co-authored-by: Erlend Egeberg Aasland <erlend.aasland@innova.no>
1 parent 04deaee commit 8d6155e

File tree

2 files changed

+158
-113
lines changed

2 files changed

+158
-113
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The code called from :c:func:`_PyErr_Display` was refactored to improve error handling. It now exits immediately upon an unrecoverable error.

Python/pythonrun.c

Lines changed: 157 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -629,14 +629,18 @@ parse_syntax_error(PyObject *err, PyObject **message, PyObject **filename,
629629
return 0;
630630
}
631631

632-
static void
633-
print_error_text(PyObject *f, Py_ssize_t offset, Py_ssize_t end_offset, PyObject *text_obj)
632+
static int
633+
print_error_text(PyObject *f, Py_ssize_t offset, Py_ssize_t end_offset,
634+
PyObject *text_obj)
634635
{
635-
size_t caret_repetitions = (end_offset > 0 && end_offset > offset) ? end_offset - offset : 1;
636+
size_t caret_repetitions = (end_offset > 0 && end_offset > offset) ?
637+
end_offset - offset : 1;
638+
636639
/* Convert text to a char pointer; return if error */
637640
const char *text = PyUnicode_AsUTF8(text_obj);
638-
if (text == NULL)
639-
return;
641+
if (text == NULL) {
642+
return -1;
643+
}
640644

641645
/* Convert offset from 1-based to 0-based */
642646
offset--;
@@ -675,27 +679,43 @@ print_error_text(PyObject *f, Py_ssize_t offset, Py_ssize_t end_offset, PyObject
675679
}
676680

677681
/* Print text */
678-
PyFile_WriteString(" ", f);
679-
PyFile_WriteString(text, f);
682+
if (PyFile_WriteString(" ", f) < 0) {
683+
return -1;
684+
}
685+
if (PyFile_WriteString(text, f) < 0) {
686+
return -1;
687+
}
680688

681689
/* Make sure there's a newline at the end */
682690
if (text[len] != '\n') {
683-
PyFile_WriteString("\n", f);
691+
if (PyFile_WriteString("\n", f) < 0) {
692+
return -1;
693+
}
684694
}
685695

686696
/* Don't print caret if it points to the left of the text */
687-
if (offset < 0)
688-
return;
697+
if (offset < 0) {
698+
return 0;
699+
}
689700

690701
/* Write caret line */
691-
PyFile_WriteString(" ", f);
702+
if (PyFile_WriteString(" ", f) < 0) {
703+
return -1;
704+
}
692705
while (--offset >= 0) {
693-
PyFile_WriteString(" ", f);
706+
if (PyFile_WriteString(" ", f) < 0) {
707+
return -1;
708+
}
694709
}
695710
for (size_t caret_iter=0; caret_iter < caret_repetitions ; caret_iter++) {
696-
PyFile_WriteString("^", f);
711+
if (PyFile_WriteString("^", f) < 0) {
712+
return -1;
713+
}
714+
}
715+
if (PyFile_WriteString("\n", f) < 0) {
716+
return -1;
697717
}
698-
PyFile_WriteString("\n", f);
718+
return 0;
699719
}
700720

701721

@@ -953,6 +973,77 @@ print_exception_traceback(struct exception_print_context *ctx, PyObject *value)
953973
return err;
954974
}
955975

976+
static int
977+
print_exception_file_and_line(struct exception_print_context *ctx,
978+
PyObject **value_p)
979+
{
980+
PyObject *f = ctx->file;
981+
982+
_Py_IDENTIFIER(print_file_and_line);
983+
PyObject *tmp;
984+
int res = _PyObject_LookupAttrId(*value_p, &PyId_print_file_and_line, &tmp);
985+
if (res <= 0) {
986+
if (res < 0) {
987+
PyErr_Clear();
988+
}
989+
return 0;
990+
}
991+
Py_DECREF(tmp);
992+
993+
PyObject *message, *filename, *text;
994+
Py_ssize_t lineno, offset, end_lineno, end_offset;
995+
if (!parse_syntax_error(*value_p, &message, &filename,
996+
&lineno, &offset,
997+
&end_lineno, &end_offset, &text)) {
998+
PyErr_Clear();
999+
return 0;
1000+
}
1001+
1002+
Py_SETREF(*value_p, message);
1003+
1004+
PyObject *line = PyUnicode_FromFormat(" File \"%S\", line %zd\n",
1005+
filename, lineno);
1006+
Py_DECREF(filename);
1007+
if (line == NULL) {
1008+
goto error;
1009+
}
1010+
if (write_indented_margin(ctx, f) < 0) {
1011+
goto error;
1012+
}
1013+
if (PyFile_WriteObject(line, f, Py_PRINT_RAW) < 0) {
1014+
goto error;
1015+
}
1016+
Py_CLEAR(line);
1017+
1018+
if (text != NULL) {
1019+
Py_ssize_t line_size;
1020+
const char *error_line = PyUnicode_AsUTF8AndSize(text, &line_size);
1021+
// If the location of the error spawn multiple lines, we want
1022+
// to just print the first one and highlight everything until
1023+
// the end of that one since we don't support multi-line error
1024+
// messages.
1025+
if (end_lineno > lineno) {
1026+
end_offset = (error_line != NULL) ? line_size : -1;
1027+
}
1028+
// Limit the amount of '^' that we can display to
1029+
// the size of the text in the source line.
1030+
if (error_line != NULL && end_offset > line_size + 1) {
1031+
end_offset = line_size + 1;
1032+
}
1033+
if (print_error_text(f, offset, end_offset, text) < 0) {
1034+
goto error;
1035+
}
1036+
Py_DECREF(text);
1037+
}
1038+
assert(!PyErr_Occurred());
1039+
return 0;
1040+
1041+
error:
1042+
Py_XDECREF(line);
1043+
Py_XDECREF(text);
1044+
return -1;
1045+
}
1046+
9561047
/* Prints the message line: module.qualname[: str(exc)] */
9571048
static int
9581049
print_exception_message(struct exception_print_context *ctx, PyObject *type,
@@ -1072,6 +1163,11 @@ static int
10721163
print_exception_note(struct exception_print_context *ctx, PyObject *value)
10731164
{
10741165
PyObject *f = ctx->file;
1166+
1167+
if (!PyExceptionInstance_Check(value)) {
1168+
return 0;
1169+
}
1170+
10751171
_Py_IDENTIFIER(__note__);
10761172

10771173
PyObject *note = _PyObject_GetAttrId(value, &PyId___note__);
@@ -1112,98 +1208,47 @@ print_exception_note(struct exception_print_context *ctx, PyObject *value)
11121208
return -1;
11131209
}
11141210

1115-
static void
1211+
static int
11161212
print_exception(struct exception_print_context *ctx, PyObject *value)
11171213
{
1118-
int err = 0;
1119-
PyObject *tmp;
11201214
PyObject *f = ctx->file;
11211215

1122-
_Py_IDENTIFIER(print_file_and_line);
1123-
11241216
if (!PyExceptionInstance_Check(value)) {
1125-
if (print_exception_invalid_type(ctx, value) < 0) {
1126-
PyErr_Clear(); /* TODO: change to return -1 */
1127-
}
1128-
return;
1217+
return print_exception_invalid_type(ctx, value);
11291218
}
11301219

11311220
Py_INCREF(value);
11321221
fflush(stdout);
1133-
PyObject *type = (PyObject *) Py_TYPE(value);
1134-
err = print_exception_traceback(ctx, value);
1135-
if (err == 0 &&
1136-
(err = _PyObject_LookupAttrId(value, &PyId_print_file_and_line, &tmp)) > 0)
1137-
{
1138-
PyObject *message, *filename, *text;
1139-
Py_ssize_t lineno, offset, end_lineno, end_offset;
1140-
err = 0;
1141-
Py_DECREF(tmp);
1142-
if (!parse_syntax_error(value, &message, &filename,
1143-
&lineno, &offset,
1144-
&end_lineno, &end_offset, &text)) {
1145-
PyErr_Clear();
1146-
}
1147-
else {
11481222

1149-
Py_DECREF(value);
1150-
value = message;
1151-
1152-
PyObject *line = PyUnicode_FromFormat(" File \"%S\", line %zd\n",
1153-
filename, lineno);
1154-
Py_DECREF(filename);
1155-
if (line != NULL) {
1156-
err = write_indented_margin(ctx, f);
1157-
if (err == 0) {
1158-
err = PyFile_WriteObject(line, f, Py_PRINT_RAW);
1159-
}
1160-
Py_DECREF(line);
1161-
}
1223+
if (print_exception_traceback(ctx, value) < 0) {
1224+
goto error;
1225+
}
11621226

1163-
if (text != NULL) {
1164-
Py_ssize_t line_size;
1165-
const char* error_line = PyUnicode_AsUTF8AndSize(text, &line_size);
1166-
// If the location of the error spawn multiple lines, we want
1167-
// to just print the first one and highlight everything until
1168-
// the end of that one since we don't support multi-line error
1169-
// messages.
1170-
if (end_lineno > lineno) {
1171-
end_offset = (error_line != NULL) ? line_size : -1;
1172-
}
1173-
// Limit the amount of '^' that we can display to
1174-
// the size of the text in the source line.
1175-
if (error_line != NULL && end_offset > line_size + 1) {
1176-
end_offset = line_size + 1;
1177-
}
1178-
print_error_text(f, offset, end_offset, text);
1179-
Py_DECREF(text);
1180-
}
1227+
/* grab the type now because value can change below */
1228+
PyObject *type = (PyObject *) Py_TYPE(value);
11811229

1182-
/* Can't be bothered to check all those
1183-
PyFile_WriteString() calls */
1184-
if (PyErr_Occurred())
1185-
err = -1;
1186-
}
1230+
if (print_exception_file_and_line(ctx, &value) < 0) {
1231+
goto error;
11871232
}
1188-
1189-
if (err == 0) {
1190-
err = print_exception_message(ctx, type, value);
1233+
if (print_exception_message(ctx, type, value) < 0) {
1234+
goto error;
11911235
}
1192-
if (err == 0) {
1193-
err = print_exception_suggestions(ctx, value);
1236+
if (print_exception_suggestions(ctx, value) < 0) {
1237+
goto error;
11941238
}
1195-
if (err == 0) {
1196-
err = PyFile_WriteString("\n", f);
1239+
if (PyFile_WriteString("\n", f) < 0) {
1240+
goto error;
11971241
}
1198-
if (err == 0 && PyExceptionInstance_Check(value)) {
1199-
err = print_exception_note(ctx, value);
1242+
if (print_exception_note(ctx, value) < 0) {
1243+
goto error;
12001244
}
12011245

12021246
Py_DECREF(value);
1203-
/* If an error happened here, don't show it.
1204-
XXX This is wrong, but too many callers rely on this behavior. */
1205-
if (err != 0)
1206-
PyErr_Clear();
1247+
assert(!PyErr_Occurred());
1248+
return 0;
1249+
error:
1250+
Py_DECREF(value);
1251+
return -1;
12071252
}
12081253

12091254
static const char cause_message[] =
@@ -1214,7 +1259,7 @@ static const char context_message[] =
12141259
"During handling of the above exception, "
12151260
"another exception occurred:\n";
12161261

1217-
static void
1262+
static int
12181263
print_exception_recursive(struct exception_print_context*, PyObject*);
12191264

12201265
static int
@@ -1227,10 +1272,12 @@ print_chained(struct exception_print_context* ctx, PyObject *value,
12271272
return -1;
12281273
}
12291274
bool need_close = ctx->need_close;
1230-
print_exception_recursive(ctx, value);
1275+
int res = print_exception_recursive(ctx, value);
12311276
ctx->need_close = need_close;
1232-
12331277
Py_LeaveRecursiveCall();
1278+
if (res < 0) {
1279+
return -1;
1280+
}
12341281

12351282
if (write_indented_margin(ctx, f) < 0) {
12361283
return -1;
@@ -1398,11 +1445,14 @@ print_exception_group(struct exception_print_context *ctx, PyObject *value)
13981445
PyObject *exc = PyTuple_GET_ITEM(excs, i);
13991446

14001447
if (!truncated) {
1401-
if (Py_EnterRecursiveCall(" in print_exception_recursive") != 0) {
1448+
if (Py_EnterRecursiveCall(" in print_exception_group") != 0) {
14021449
return -1;
14031450
}
1404-
print_exception_recursive(ctx, exc);
1451+
int res = print_exception_recursive(ctx, exc);
14051452
Py_LeaveRecursiveCall();
1453+
if (res < 0) {
1454+
return -1;
1455+
}
14061456
}
14071457
else {
14081458
Py_ssize_t excs_remaining = num_excs - ctx->max_group_width;
@@ -1445,33 +1495,25 @@ print_exception_group(struct exception_print_context *ctx, PyObject *value)
14451495
return 0;
14461496
}
14471497

1448-
static void
1498+
static int
14491499
print_exception_recursive(struct exception_print_context *ctx, PyObject *value)
14501500
{
1451-
int err = 0;
14521501
if (ctx->seen != NULL) {
14531502
/* 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;
1503+
if (print_exception_cause_and_context(ctx, value) < 0) {
1504+
return -1;
14681505
}
1469-
else {
1470-
assert(prev_depth == ctx->exception_group_depth);
1506+
}
1507+
if (!_PyBaseExceptionGroup_Check(value)) {
1508+
if (print_exception(ctx, value) < 0) {
1509+
return -1;
14711510
}
14721511
}
1473-
if (err != 0)
1474-
PyErr_Clear();
1512+
else if (print_exception_group(ctx, value) < 0) {
1513+
return -1;
1514+
}
1515+
assert(!PyErr_Occurred());
1516+
return 0;
14751517
}
14761518

14771519
#define PyErr_MAX_GROUP_WIDTH 15
@@ -1505,7 +1547,9 @@ _PyErr_Display(PyObject *file, PyObject *exception, PyObject *value, PyObject *t
15051547
if (ctx.seen == NULL) {
15061548
PyErr_Clear();
15071549
}
1508-
print_exception_recursive(&ctx, value);
1550+
if (print_exception_recursive(&ctx, value) < 0) {
1551+
PyErr_Clear();
1552+
}
15091553
Py_XDECREF(ctx.seen);
15101554

15111555
/* Call file.flush() */

0 commit comments

Comments
 (0)