Skip to content

Commit 3853d8e

Browse files
ericsnowcurrentlyGlyphack
authored andcommitted
pythongh-76785: Avoid Pickled TracebackException for Propagated Subinterpreter Exceptions (pythongh-113036)
We need the TracebackException of uncaught exceptions for a single purpose: the error display. Thus we only need to pass the formatted error display between interpreters. Passing a pickled TracebackException is overkill.
1 parent 5c4a638 commit 3853d8e

File tree

3 files changed

+90
-154
lines changed

3 files changed

+90
-154
lines changed

Include/internal/pycore_crossinterp.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,7 @@ typedef struct _excinfo {
188188
const char *module;
189189
} type;
190190
const char *msg;
191-
const char *pickled;
192-
Py_ssize_t pickled_len;
191+
const char *errdisplay;
193192
} _PyXI_excinfo;
194193

195194

Lib/test/support/interpreters/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def __init__(self, excinfo):
5656

5757
def __str__(self):
5858
try:
59-
formatted = ''.join(self.excinfo.tbexc.format()).rstrip()
59+
formatted = self.excinfo.errdisplay
6060
except Exception:
6161
return super().__str__()
6262
else:

Python/crossinterp.c

Lines changed: 88 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -945,113 +945,105 @@ _xidregistry_fini(struct _xidregistry *registry)
945945
/*************************/
946946

947947
static const char *
948-
_copy_raw_string(const char *str, Py_ssize_t len)
948+
_copy_string_obj_raw(PyObject *strobj, Py_ssize_t *p_size)
949949
{
950-
size_t size = len + 1;
951-
if (len <= 0) {
952-
size = strlen(str) + 1;
953-
}
954-
char *copied = PyMem_RawMalloc(size);
955-
if (copied == NULL) {
956-
return NULL;
957-
}
958-
if (len <= 0) {
959-
strcpy(copied, str);
960-
}
961-
else {
962-
memcpy(copied, str, size);
963-
}
964-
return copied;
965-
}
966-
967-
static const char *
968-
_copy_string_obj_raw(PyObject *strobj)
969-
{
970-
const char *str = PyUnicode_AsUTF8(strobj);
950+
Py_ssize_t size = -1;
951+
const char *str = PyUnicode_AsUTF8AndSize(strobj, &size);
971952
if (str == NULL) {
972953
return NULL;
973954
}
974955

975-
char *copied = PyMem_RawMalloc(strlen(str)+1);
956+
char *copied = PyMem_RawMalloc(size+1);
976957
if (copied == NULL) {
977958
PyErr_NoMemory();
978959
return NULL;
979960
}
980961
strcpy(copied, str);
962+
if (p_size != NULL) {
963+
*p_size = size;
964+
}
981965
return copied;
982966
}
983967

984968

985969
static int
986-
_pickle_object(PyObject *obj, const char **p_pickled, Py_ssize_t *p_len)
970+
_convert_exc_to_TracebackException(PyObject *exc, PyObject **p_tbexc)
987971
{
988-
assert(!PyErr_Occurred());
989-
PyObject *picklemod = PyImport_ImportModule("_pickle");
990-
if (picklemod == NULL) {
991-
PyErr_Clear();
992-
picklemod = PyImport_ImportModule("pickle");
993-
if (picklemod == NULL) {
994-
return -1;
995-
}
972+
PyObject *args = NULL;
973+
PyObject *kwargs = NULL;
974+
PyObject *create = NULL;
975+
976+
// This is inspired by _PyErr_Display().
977+
PyObject *tbmod = PyImport_ImportModule("traceback");
978+
if (tbmod == NULL) {
979+
return -1;
996980
}
997-
PyObject *dumps = PyObject_GetAttrString(picklemod, "dumps");
998-
Py_DECREF(picklemod);
999-
if (dumps == NULL) {
981+
PyObject *tbexc_type = PyObject_GetAttrString(tbmod, "TracebackException");
982+
Py_DECREF(tbmod);
983+
if (tbexc_type == NULL) {
1000984
return -1;
1001985
}
1002-
PyObject *pickledobj = PyObject_CallOneArg(dumps, obj);
1003-
Py_DECREF(dumps);
1004-
if (pickledobj == NULL) {
986+
create = PyObject_GetAttrString(tbexc_type, "from_exception");
987+
Py_DECREF(tbexc_type);
988+
if (create == NULL) {
1005989
return -1;
1006990
}
1007991

1008-
char *pickled = NULL;
1009-
Py_ssize_t len = 0;
1010-
if (PyBytes_AsStringAndSize(pickledobj, &pickled, &len) < 0) {
1011-
Py_DECREF(pickledobj);
1012-
return -1;
992+
args = PyTuple_Pack(1, exc);
993+
if (args == NULL) {
994+
goto error;
1013995
}
1014-
const char *copied = _copy_raw_string(pickled, len);
1015-
Py_DECREF(pickledobj);
1016-
if (copied == NULL) {
1017-
return -1;
996+
997+
kwargs = PyDict_New();
998+
if (kwargs == NULL) {
999+
goto error;
1000+
}
1001+
if (PyDict_SetItemString(kwargs, "save_exc_type", Py_False) < 0) {
1002+
goto error;
1003+
}
1004+
if (PyDict_SetItemString(kwargs, "lookup_lines", Py_False) < 0) {
1005+
goto error;
1006+
}
1007+
1008+
PyObject *tbexc = PyObject_Call(create, args, kwargs);
1009+
Py_DECREF(args);
1010+
Py_DECREF(kwargs);
1011+
Py_DECREF(create);
1012+
if (tbexc == NULL) {
1013+
goto error;
10181014
}
10191015

1020-
*p_pickled = copied;
1021-
*p_len = len;
1016+
*p_tbexc = tbexc;
10221017
return 0;
1018+
1019+
error:
1020+
Py_XDECREF(args);
1021+
Py_XDECREF(kwargs);
1022+
Py_XDECREF(create);
1023+
return -1;
10231024
}
10241025

1025-
static int
1026-
_unpickle_object(const char *pickled, Py_ssize_t size, PyObject **p_obj)
1026+
1027+
static const char *
1028+
_format_TracebackException(PyObject *tbexc)
10271029
{
1028-
assert(!PyErr_Occurred());
1029-
PyObject *picklemod = PyImport_ImportModule("_pickle");
1030-
if (picklemod == NULL) {
1031-
PyErr_Clear();
1032-
picklemod = PyImport_ImportModule("pickle");
1033-
if (picklemod == NULL) {
1034-
return -1;
1035-
}
1036-
}
1037-
PyObject *loads = PyObject_GetAttrString(picklemod, "loads");
1038-
Py_DECREF(picklemod);
1039-
if (loads == NULL) {
1040-
return -1;
1041-
}
1042-
PyObject *pickledobj = PyBytes_FromStringAndSize(pickled, size);
1043-
if (pickledobj == NULL) {
1044-
Py_DECREF(loads);
1045-
return -1;
1030+
PyObject *lines = PyObject_CallMethod(tbexc, "format", NULL);
1031+
if (lines == NULL) {
1032+
return NULL;
10461033
}
1047-
PyObject *obj = PyObject_CallOneArg(loads, pickledobj);
1048-
Py_DECREF(loads);
1049-
Py_DECREF(pickledobj);
1050-
if (obj == NULL) {
1051-
return -1;
1034+
PyObject *formatted_obj = PyUnicode_Join(&_Py_STR(empty), lines);
1035+
Py_DECREF(lines);
1036+
if (formatted_obj == NULL) {
1037+
return NULL;
10521038
}
1053-
*p_obj = obj;
1054-
return 0;
1039+
1040+
Py_ssize_t size = -1;
1041+
const char *formatted = _copy_string_obj_raw(formatted_obj, &size);
1042+
Py_DECREF(formatted_obj);
1043+
// We remove trailing the newline added by TracebackException.format().
1044+
assert(formatted[size-1] == '\n');
1045+
((char *)formatted)[size-1] = '\0';
1046+
return formatted;
10551047
}
10561048

10571049

@@ -1101,7 +1093,7 @@ _excinfo_init_type(struct _excinfo_type *info, PyObject *exc)
11011093
if (strobj == NULL) {
11021094
return -1;
11031095
}
1104-
info->name = _copy_string_obj_raw(strobj);
1096+
info->name = _copy_string_obj_raw(strobj, NULL);
11051097
Py_DECREF(strobj);
11061098
if (info->name == NULL) {
11071099
return -1;
@@ -1112,7 +1104,7 @@ _excinfo_init_type(struct _excinfo_type *info, PyObject *exc)
11121104
if (strobj == NULL) {
11131105
return -1;
11141106
}
1115-
info->qualname = _copy_string_obj_raw(strobj);
1107+
info->qualname = _copy_string_obj_raw(strobj, NULL);
11161108
Py_DECREF(strobj);
11171109
if (info->name == NULL) {
11181110
return -1;
@@ -1123,7 +1115,7 @@ _excinfo_init_type(struct _excinfo_type *info, PyObject *exc)
11231115
if (strobj == NULL) {
11241116
return -1;
11251117
}
1126-
info->module = _copy_string_obj_raw(strobj);
1118+
info->module = _copy_string_obj_raw(strobj, NULL);
11271119
Py_DECREF(strobj);
11281120
if (info->name == NULL) {
11291121
return -1;
@@ -1188,8 +1180,8 @@ _PyXI_excinfo_Clear(_PyXI_excinfo *info)
11881180
if (info->msg != NULL) {
11891181
PyMem_RawFree((void *)info->msg);
11901182
}
1191-
if (info->pickled != NULL) {
1192-
PyMem_RawFree((void *)info->pickled);
1183+
if (info->errdisplay != NULL) {
1184+
PyMem_RawFree((void *)info->errdisplay);
11931185
}
11941186
*info = (_PyXI_excinfo){{NULL}};
11951187
}
@@ -1226,63 +1218,6 @@ _PyXI_excinfo_format(_PyXI_excinfo *info)
12261218
}
12271219
}
12281220

1229-
static int
1230-
_convert_exc_to_TracebackException(PyObject *exc, PyObject **p_tbexc)
1231-
{
1232-
PyObject *args = NULL;
1233-
PyObject *kwargs = NULL;
1234-
PyObject *create = NULL;
1235-
1236-
// This is inspired by _PyErr_Display().
1237-
PyObject *tbmod = PyImport_ImportModule("traceback");
1238-
if (tbmod == NULL) {
1239-
return -1;
1240-
}
1241-
PyObject *tbexc_type = PyObject_GetAttrString(tbmod, "TracebackException");
1242-
Py_DECREF(tbmod);
1243-
if (tbexc_type == NULL) {
1244-
return -1;
1245-
}
1246-
create = PyObject_GetAttrString(tbexc_type, "from_exception");
1247-
Py_DECREF(tbexc_type);
1248-
if (create == NULL) {
1249-
return -1;
1250-
}
1251-
1252-
args = PyTuple_Pack(1, exc);
1253-
if (args == NULL) {
1254-
goto error;
1255-
}
1256-
1257-
kwargs = PyDict_New();
1258-
if (kwargs == NULL) {
1259-
goto error;
1260-
}
1261-
if (PyDict_SetItemString(kwargs, "save_exc_type", Py_False) < 0) {
1262-
goto error;
1263-
}
1264-
if (PyDict_SetItemString(kwargs, "lookup_lines", Py_False) < 0) {
1265-
goto error;
1266-
}
1267-
1268-
PyObject *tbexc = PyObject_Call(create, args, kwargs);
1269-
Py_DECREF(args);
1270-
Py_DECREF(kwargs);
1271-
Py_DECREF(create);
1272-
if (tbexc == NULL) {
1273-
goto error;
1274-
}
1275-
1276-
*p_tbexc = tbexc;
1277-
return 0;
1278-
1279-
error:
1280-
Py_XDECREF(args);
1281-
Py_XDECREF(kwargs);
1282-
Py_XDECREF(create);
1283-
return -1;
1284-
}
1285-
12861221
static const char *
12871222
_PyXI_excinfo_InitFromException(_PyXI_excinfo *info, PyObject *exc)
12881223
{
@@ -1305,7 +1240,7 @@ _PyXI_excinfo_InitFromException(_PyXI_excinfo *info, PyObject *exc)
13051240
failure = "error while formatting exception";
13061241
goto error;
13071242
}
1308-
info->msg = _copy_string_obj_raw(msgobj);
1243+
info->msg = _copy_string_obj_raw(msgobj, NULL);
13091244
Py_DECREF(msgobj);
13101245
if (info->msg == NULL) {
13111246
failure = "error while copying exception message";
@@ -1321,13 +1256,14 @@ _PyXI_excinfo_InitFromException(_PyXI_excinfo *info, PyObject *exc)
13211256
PyErr_Clear();
13221257
}
13231258
else {
1324-
if (_pickle_object(tbexc, &info->pickled, &info->pickled_len) < 0) {
1259+
info->errdisplay = _format_TracebackException(tbexc);
1260+
Py_DECREF(tbexc);
1261+
if (info->errdisplay == NULL) {
13251262
#ifdef Py_DEBUG
1326-
PyErr_FormatUnraisable("Exception ignored while pickling TracebackException");
1263+
PyErr_FormatUnraisable("Exception ignored while formating TracebackException");
13271264
#endif
13281265
PyErr_Clear();
13291266
}
1330-
Py_DECREF(tbexc);
13311267
}
13321268

13331269
return NULL;
@@ -1342,8 +1278,9 @@ static void
13421278
_PyXI_excinfo_Apply(_PyXI_excinfo *info, PyObject *exctype)
13431279
{
13441280
PyObject *tbexc = NULL;
1345-
if (info->pickled != NULL) {
1346-
if (_unpickle_object(info->pickled, info->pickled_len, &tbexc) < 0) {
1281+
if (info->errdisplay != NULL) {
1282+
tbexc = PyUnicode_FromString(info->errdisplay);
1283+
if (tbexc == NULL) {
13471284
PyErr_Clear();
13481285
}
13491286
}
@@ -1354,9 +1291,9 @@ _PyXI_excinfo_Apply(_PyXI_excinfo *info, PyObject *exctype)
13541291

13551292
if (tbexc != NULL) {
13561293
PyObject *exc = PyErr_GetRaisedException();
1357-
if (PyObject_SetAttrString(exc, "_tbexc", tbexc) < 0) {
1294+
if (PyObject_SetAttrString(exc, "_errdisplay", tbexc) < 0) {
13581295
#ifdef Py_DEBUG
1359-
PyErr_FormatUnraisable("Exception ignored when setting _tbexc");
1296+
PyErr_FormatUnraisable("Exception ignored when setting _errdisplay");
13601297
#endif
13611298
PyErr_Clear();
13621299
}
@@ -1468,13 +1405,13 @@ _PyXI_excinfo_AsObject(_PyXI_excinfo *info)
14681405
goto error;
14691406
}
14701407

1471-
if (info->pickled != NULL) {
1472-
PyObject *tbexc = NULL;
1473-
if (_unpickle_object(info->pickled, info->pickled_len, &tbexc) < 0) {
1408+
if (info->errdisplay != NULL) {
1409+
PyObject *tbexc = PyUnicode_FromString(info->errdisplay);
1410+
if (tbexc == NULL) {
14741411
PyErr_Clear();
14751412
}
14761413
else {
1477-
res = PyObject_SetAttrString(ns, "tbexc", tbexc);
1414+
res = PyObject_SetAttrString(ns, "errdisplay", tbexc);
14781415
Py_DECREF(tbexc);
14791416
if (res < 0) {
14801417
goto error;
@@ -1646,7 +1583,7 @@ _sharednsitem_is_initialized(_PyXI_namespace_item *item)
16461583
static int
16471584
_sharednsitem_init(_PyXI_namespace_item *item, PyObject *key)
16481585
{
1649-
item->name = _copy_string_obj_raw(key);
1586+
item->name = _copy_string_obj_raw(key, NULL);
16501587
if (item->name == NULL) {
16511588
assert(!_sharednsitem_is_initialized(item));
16521589
return -1;

0 commit comments

Comments
 (0)