mirror of https://github.com/python/cpython
gh-76785: Avoid Pickled TracebackException for Propagated Subinterpreter Exceptions (gh-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.
This commit is contained in:
parent
7e2d93f30b
commit
c6e614fd81
|
@ -188,8 +188,7 @@ typedef struct _excinfo {
|
||||||
const char *module;
|
const char *module;
|
||||||
} type;
|
} type;
|
||||||
const char *msg;
|
const char *msg;
|
||||||
const char *pickled;
|
const char *errdisplay;
|
||||||
Py_ssize_t pickled_len;
|
|
||||||
} _PyXI_excinfo;
|
} _PyXI_excinfo;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ class ExecFailure(RuntimeError):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
try:
|
try:
|
||||||
formatted = ''.join(self.excinfo.tbexc.format()).rstrip()
|
formatted = self.excinfo.errdisplay
|
||||||
except Exception:
|
except Exception:
|
||||||
return super().__str__()
|
return super().__str__()
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -945,113 +945,105 @@ _xidregistry_fini(struct _xidregistry *registry)
|
||||||
/*************************/
|
/*************************/
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
_copy_raw_string(const char *str, Py_ssize_t len)
|
_copy_string_obj_raw(PyObject *strobj, Py_ssize_t *p_size)
|
||||||
{
|
{
|
||||||
size_t size = len + 1;
|
Py_ssize_t size = -1;
|
||||||
if (len <= 0) {
|
const char *str = PyUnicode_AsUTF8AndSize(strobj, &size);
|
||||||
size = strlen(str) + 1;
|
|
||||||
}
|
|
||||||
char *copied = PyMem_RawMalloc(size);
|
|
||||||
if (copied == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (len <= 0) {
|
|
||||||
strcpy(copied, str);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
memcpy(copied, str, size);
|
|
||||||
}
|
|
||||||
return copied;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *
|
|
||||||
_copy_string_obj_raw(PyObject *strobj)
|
|
||||||
{
|
|
||||||
const char *str = PyUnicode_AsUTF8(strobj);
|
|
||||||
if (str == NULL) {
|
if (str == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *copied = PyMem_RawMalloc(strlen(str)+1);
|
char *copied = PyMem_RawMalloc(size+1);
|
||||||
if (copied == NULL) {
|
if (copied == NULL) {
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
strcpy(copied, str);
|
strcpy(copied, str);
|
||||||
|
if (p_size != NULL) {
|
||||||
|
*p_size = size;
|
||||||
|
}
|
||||||
return copied;
|
return copied;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
_pickle_object(PyObject *obj, const char **p_pickled, Py_ssize_t *p_len)
|
_convert_exc_to_TracebackException(PyObject *exc, PyObject **p_tbexc)
|
||||||
{
|
{
|
||||||
assert(!PyErr_Occurred());
|
PyObject *args = NULL;
|
||||||
PyObject *picklemod = PyImport_ImportModule("_pickle");
|
PyObject *kwargs = NULL;
|
||||||
if (picklemod == NULL) {
|
PyObject *create = NULL;
|
||||||
PyErr_Clear();
|
|
||||||
picklemod = PyImport_ImportModule("pickle");
|
// This is inspired by _PyErr_Display().
|
||||||
if (picklemod == NULL) {
|
PyObject *tbmod = PyImport_ImportModule("traceback");
|
||||||
return -1;
|
if (tbmod == NULL) {
|
||||||
}
|
|
||||||
}
|
|
||||||
PyObject *dumps = PyObject_GetAttrString(picklemod, "dumps");
|
|
||||||
Py_DECREF(picklemod);
|
|
||||||
if (dumps == NULL) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
PyObject *pickledobj = PyObject_CallOneArg(dumps, obj);
|
PyObject *tbexc_type = PyObject_GetAttrString(tbmod, "TracebackException");
|
||||||
Py_DECREF(dumps);
|
Py_DECREF(tbmod);
|
||||||
if (pickledobj == NULL) {
|
if (tbexc_type == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
create = PyObject_GetAttrString(tbexc_type, "from_exception");
|
||||||
|
Py_DECREF(tbexc_type);
|
||||||
|
if (create == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *pickled = NULL;
|
args = PyTuple_Pack(1, exc);
|
||||||
Py_ssize_t len = 0;
|
if (args == NULL) {
|
||||||
if (PyBytes_AsStringAndSize(pickledobj, &pickled, &len) < 0) {
|
goto error;
|
||||||
Py_DECREF(pickledobj);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
const char *copied = _copy_raw_string(pickled, len);
|
|
||||||
Py_DECREF(pickledobj);
|
|
||||||
if (copied == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*p_pickled = copied;
|
kwargs = PyDict_New();
|
||||||
*p_len = len;
|
if (kwargs == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (PyDict_SetItemString(kwargs, "save_exc_type", Py_False) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (PyDict_SetItemString(kwargs, "lookup_lines", Py_False) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *tbexc = PyObject_Call(create, args, kwargs);
|
||||||
|
Py_DECREF(args);
|
||||||
|
Py_DECREF(kwargs);
|
||||||
|
Py_DECREF(create);
|
||||||
|
if (tbexc == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
*p_tbexc = tbexc;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
Py_XDECREF(args);
|
||||||
|
Py_XDECREF(kwargs);
|
||||||
|
Py_XDECREF(create);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
_unpickle_object(const char *pickled, Py_ssize_t size, PyObject **p_obj)
|
static const char *
|
||||||
|
_format_TracebackException(PyObject *tbexc)
|
||||||
{
|
{
|
||||||
assert(!PyErr_Occurred());
|
PyObject *lines = PyObject_CallMethod(tbexc, "format", NULL);
|
||||||
PyObject *picklemod = PyImport_ImportModule("_pickle");
|
if (lines == NULL) {
|
||||||
if (picklemod == NULL) {
|
return NULL;
|
||||||
PyErr_Clear();
|
|
||||||
picklemod = PyImport_ImportModule("pickle");
|
|
||||||
if (picklemod == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PyObject *loads = PyObject_GetAttrString(picklemod, "loads");
|
PyObject *formatted_obj = PyUnicode_Join(&_Py_STR(empty), lines);
|
||||||
Py_DECREF(picklemod);
|
Py_DECREF(lines);
|
||||||
if (loads == NULL) {
|
if (formatted_obj == NULL) {
|
||||||
return -1;
|
return NULL;
|
||||||
}
|
}
|
||||||
PyObject *pickledobj = PyBytes_FromStringAndSize(pickled, size);
|
|
||||||
if (pickledobj == NULL) {
|
Py_ssize_t size = -1;
|
||||||
Py_DECREF(loads);
|
const char *formatted = _copy_string_obj_raw(formatted_obj, &size);
|
||||||
return -1;
|
Py_DECREF(formatted_obj);
|
||||||
}
|
// We remove trailing the newline added by TracebackException.format().
|
||||||
PyObject *obj = PyObject_CallOneArg(loads, pickledobj);
|
assert(formatted[size-1] == '\n');
|
||||||
Py_DECREF(loads);
|
((char *)formatted)[size-1] = '\0';
|
||||||
Py_DECREF(pickledobj);
|
return formatted;
|
||||||
if (obj == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*p_obj = obj;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1101,7 +1093,7 @@ _excinfo_init_type(struct _excinfo_type *info, PyObject *exc)
|
||||||
if (strobj == NULL) {
|
if (strobj == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
info->name = _copy_string_obj_raw(strobj);
|
info->name = _copy_string_obj_raw(strobj, NULL);
|
||||||
Py_DECREF(strobj);
|
Py_DECREF(strobj);
|
||||||
if (info->name == NULL) {
|
if (info->name == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1112,7 +1104,7 @@ _excinfo_init_type(struct _excinfo_type *info, PyObject *exc)
|
||||||
if (strobj == NULL) {
|
if (strobj == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
info->qualname = _copy_string_obj_raw(strobj);
|
info->qualname = _copy_string_obj_raw(strobj, NULL);
|
||||||
Py_DECREF(strobj);
|
Py_DECREF(strobj);
|
||||||
if (info->name == NULL) {
|
if (info->name == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1123,7 +1115,7 @@ _excinfo_init_type(struct _excinfo_type *info, PyObject *exc)
|
||||||
if (strobj == NULL) {
|
if (strobj == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
info->module = _copy_string_obj_raw(strobj);
|
info->module = _copy_string_obj_raw(strobj, NULL);
|
||||||
Py_DECREF(strobj);
|
Py_DECREF(strobj);
|
||||||
if (info->name == NULL) {
|
if (info->name == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1188,8 +1180,8 @@ _PyXI_excinfo_Clear(_PyXI_excinfo *info)
|
||||||
if (info->msg != NULL) {
|
if (info->msg != NULL) {
|
||||||
PyMem_RawFree((void *)info->msg);
|
PyMem_RawFree((void *)info->msg);
|
||||||
}
|
}
|
||||||
if (info->pickled != NULL) {
|
if (info->errdisplay != NULL) {
|
||||||
PyMem_RawFree((void *)info->pickled);
|
PyMem_RawFree((void *)info->errdisplay);
|
||||||
}
|
}
|
||||||
*info = (_PyXI_excinfo){{NULL}};
|
*info = (_PyXI_excinfo){{NULL}};
|
||||||
}
|
}
|
||||||
|
@ -1226,63 +1218,6 @@ _PyXI_excinfo_format(_PyXI_excinfo *info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
_convert_exc_to_TracebackException(PyObject *exc, PyObject **p_tbexc)
|
|
||||||
{
|
|
||||||
PyObject *args = NULL;
|
|
||||||
PyObject *kwargs = NULL;
|
|
||||||
PyObject *create = NULL;
|
|
||||||
|
|
||||||
// This is inspired by _PyErr_Display().
|
|
||||||
PyObject *tbmod = PyImport_ImportModule("traceback");
|
|
||||||
if (tbmod == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
PyObject *tbexc_type = PyObject_GetAttrString(tbmod, "TracebackException");
|
|
||||||
Py_DECREF(tbmod);
|
|
||||||
if (tbexc_type == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
create = PyObject_GetAttrString(tbexc_type, "from_exception");
|
|
||||||
Py_DECREF(tbexc_type);
|
|
||||||
if (create == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
args = PyTuple_Pack(1, exc);
|
|
||||||
if (args == NULL) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
kwargs = PyDict_New();
|
|
||||||
if (kwargs == NULL) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (PyDict_SetItemString(kwargs, "save_exc_type", Py_False) < 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (PyDict_SetItemString(kwargs, "lookup_lines", Py_False) < 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject *tbexc = PyObject_Call(create, args, kwargs);
|
|
||||||
Py_DECREF(args);
|
|
||||||
Py_DECREF(kwargs);
|
|
||||||
Py_DECREF(create);
|
|
||||||
if (tbexc == NULL) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
*p_tbexc = tbexc;
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error:
|
|
||||||
Py_XDECREF(args);
|
|
||||||
Py_XDECREF(kwargs);
|
|
||||||
Py_XDECREF(create);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
_PyXI_excinfo_InitFromException(_PyXI_excinfo *info, PyObject *exc)
|
_PyXI_excinfo_InitFromException(_PyXI_excinfo *info, PyObject *exc)
|
||||||
{
|
{
|
||||||
|
@ -1305,7 +1240,7 @@ _PyXI_excinfo_InitFromException(_PyXI_excinfo *info, PyObject *exc)
|
||||||
failure = "error while formatting exception";
|
failure = "error while formatting exception";
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
info->msg = _copy_string_obj_raw(msgobj);
|
info->msg = _copy_string_obj_raw(msgobj, NULL);
|
||||||
Py_DECREF(msgobj);
|
Py_DECREF(msgobj);
|
||||||
if (info->msg == NULL) {
|
if (info->msg == NULL) {
|
||||||
failure = "error while copying exception message";
|
failure = "error while copying exception message";
|
||||||
|
@ -1321,13 +1256,14 @@ _PyXI_excinfo_InitFromException(_PyXI_excinfo *info, PyObject *exc)
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (_pickle_object(tbexc, &info->pickled, &info->pickled_len) < 0) {
|
info->errdisplay = _format_TracebackException(tbexc);
|
||||||
|
Py_DECREF(tbexc);
|
||||||
|
if (info->errdisplay == NULL) {
|
||||||
#ifdef Py_DEBUG
|
#ifdef Py_DEBUG
|
||||||
PyErr_FormatUnraisable("Exception ignored while pickling TracebackException");
|
PyErr_FormatUnraisable("Exception ignored while formating TracebackException");
|
||||||
#endif
|
#endif
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
}
|
}
|
||||||
Py_DECREF(tbexc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1342,8 +1278,9 @@ static void
|
||||||
_PyXI_excinfo_Apply(_PyXI_excinfo *info, PyObject *exctype)
|
_PyXI_excinfo_Apply(_PyXI_excinfo *info, PyObject *exctype)
|
||||||
{
|
{
|
||||||
PyObject *tbexc = NULL;
|
PyObject *tbexc = NULL;
|
||||||
if (info->pickled != NULL) {
|
if (info->errdisplay != NULL) {
|
||||||
if (_unpickle_object(info->pickled, info->pickled_len, &tbexc) < 0) {
|
tbexc = PyUnicode_FromString(info->errdisplay);
|
||||||
|
if (tbexc == NULL) {
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1354,9 +1291,9 @@ _PyXI_excinfo_Apply(_PyXI_excinfo *info, PyObject *exctype)
|
||||||
|
|
||||||
if (tbexc != NULL) {
|
if (tbexc != NULL) {
|
||||||
PyObject *exc = PyErr_GetRaisedException();
|
PyObject *exc = PyErr_GetRaisedException();
|
||||||
if (PyObject_SetAttrString(exc, "_tbexc", tbexc) < 0) {
|
if (PyObject_SetAttrString(exc, "_errdisplay", tbexc) < 0) {
|
||||||
#ifdef Py_DEBUG
|
#ifdef Py_DEBUG
|
||||||
PyErr_FormatUnraisable("Exception ignored when setting _tbexc");
|
PyErr_FormatUnraisable("Exception ignored when setting _errdisplay");
|
||||||
#endif
|
#endif
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
}
|
}
|
||||||
|
@ -1468,13 +1405,13 @@ _PyXI_excinfo_AsObject(_PyXI_excinfo *info)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info->pickled != NULL) {
|
if (info->errdisplay != NULL) {
|
||||||
PyObject *tbexc = NULL;
|
PyObject *tbexc = PyUnicode_FromString(info->errdisplay);
|
||||||
if (_unpickle_object(info->pickled, info->pickled_len, &tbexc) < 0) {
|
if (tbexc == NULL) {
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res = PyObject_SetAttrString(ns, "tbexc", tbexc);
|
res = PyObject_SetAttrString(ns, "errdisplay", tbexc);
|
||||||
Py_DECREF(tbexc);
|
Py_DECREF(tbexc);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -1646,7 +1583,7 @@ _sharednsitem_is_initialized(_PyXI_namespace_item *item)
|
||||||
static int
|
static int
|
||||||
_sharednsitem_init(_PyXI_namespace_item *item, PyObject *key)
|
_sharednsitem_init(_PyXI_namespace_item *item, PyObject *key)
|
||||||
{
|
{
|
||||||
item->name = _copy_string_obj_raw(key);
|
item->name = _copy_string_obj_raw(key, NULL);
|
||||||
if (item->name == NULL) {
|
if (item->name == NULL) {
|
||||||
assert(!_sharednsitem_is_initialized(item));
|
assert(!_sharednsitem_is_initialized(item));
|
||||||
return -1;
|
return -1;
|
||||||
|
|
Loading…
Reference in New Issue