bpo-36829: PyErr_WriteUnraisable() normalizes exception (GH-13507) · python/cpython@df22c03

File tree

5 files changed

lines changed

  • Misc/NEWS.d/next/Core and Builtins

5 files changed

lines changed

Original file line numberDiff line numberDiff line change

@@ -86,6 +86,10 @@ PyAPI_FUNC(void) _Py_DumpHexadecimal(

8686

unsigned long value,

8787

Py_ssize_t width);

8888
89+

PyAPI_FUNC(PyObject*) _PyTraceBack_FromFrame(

90+

PyObject *tb_next,

91+

struct _frame *frame);

92+
8993

#ifdef __cplusplus

9094

}

9195

#endif

Original file line numberDiff line numberDiff line change

@@ -882,19 +882,14 @@ def write_unraisable_exc(self, exc, obj):

882882

import _testcapi

883883

import types

884884

try:

885-

# raise the exception to get a traceback in the except block

886-

try:

887-

raise exc

888-

except Exception as exc2:

889-

_testcapi.write_unraisable_exc(exc2, obj)

890-

return types.SimpleNamespace(exc_type=type(exc2),

891-

exc_value=exc2,

892-

exc_traceback=exc2.__traceback__,

893-

object=obj)

885+

_testcapi.write_unraisable_exc(exc, obj)

886+

return types.SimpleNamespace(exc_type=type(exc),

887+

exc_value=exc,

888+

exc_traceback=exc.__traceback__,

889+

object=obj)

894890

finally:

895891

# Explicitly break any reference cycle

896892

exc = None

897-

exc2 = None

898893
899894

def test_original_unraisablehook(self):

900895

obj = "an object"

Original file line numberDiff line numberDiff line change

@@ -0,0 +1,4 @@

1+

:c:func:`PyErr_WriteUnraisable` now creates a traceback object if there is

2+

no current traceback. Moreover, call :c:func:`PyErr_NormalizeException` and

3+

:c:func:`PyException_SetTraceback` to normalize the exception value. Ignore any

4+

error.

Original file line numberDiff line numberDiff line change

@@ -4,6 +4,7 @@

44

#include "Python.h"

55

#include "pycore_coreconfig.h"

66

#include "pycore_pystate.h"

7+

#include "pycore_traceback.h"

78
89

#ifndef __STDC__

910

#ifndef MS_WINDOWS

@@ -1048,7 +1049,7 @@ write_unraisable_exc_file(PyObject *exc_type, PyObject *exc_value,

10481049

}

10491050

}

10501051
1051-

if (!exc_type) {

1052+

if (exc_type == NULL || exc_type == Py_None) {

10521053

return -1;

10531054

}

10541055

@@ -1106,6 +1107,7 @@ write_unraisable_exc_file(PyObject *exc_type, PyObject *exc_value,

11061107

}

11071108

}

11081109

}

1110+
11091111

if (PyFile_WriteString("\n", file) < 0) {

11101112

return -1;

11111113

}

@@ -1177,6 +1179,24 @@ PyErr_WriteUnraisable(PyObject *obj)

11771179

goto default_hook;

11781180

}

11791181
1182+

if (exc_tb == NULL) {

1183+

struct _frame *frame = _PyThreadState_GET()->frame;

1184+

if (frame != NULL) {

1185+

exc_tb = _PyTraceBack_FromFrame(NULL, frame);

1186+

if (exc_tb == NULL) {

1187+

PyErr_Clear();

1188+

}

1189+

}

1190+

}

1191+
1192+

PyErr_NormalizeException(&exc_type, &exc_value, &exc_tb);

1193+
1194+

if (exc_tb != NULL && exc_tb != Py_None && PyTraceBack_Check(exc_tb)) {

1195+

if (PyException_SetTraceback(exc_value, exc_tb) < 0) {

1196+

PyErr_Clear();

1197+

}

1198+

}

1199+
11801200

_Py_IDENTIFIER(unraisablehook);

11811201

PyObject *hook = _PySys_GetObjectId(&PyId_unraisablehook);

11821202

if (hook != NULL && hook != Py_None) {

Original file line numberDiff line numberDiff line change

@@ -227,13 +227,24 @@ PyTypeObject PyTraceBack_Type = {

227227

tb_new, /* tp_new */

228228

};

229229
230+
231+

PyObject*

232+

_PyTraceBack_FromFrame(PyObject *tb_next, PyFrameObject *frame)

233+

{

234+

assert(tb_next == NULL || PyTraceBack_Check(tb_next));

235+

assert(frame != NULL);

236+
237+

return tb_create_raw((PyTracebackObject *)tb_next, frame, frame->f_lasti,

238+

PyFrame_GetLineNumber(frame));

239+

}

240+
241+
230242

int

231243

PyTraceBack_Here(PyFrameObject *frame)

232244

{

233245

PyObject *exc, *val, *tb, *newtb;

234246

PyErr_Fetch(&exc, &val, &tb);

235-

newtb = tb_create_raw((PyTracebackObject *)tb, frame, frame->f_lasti,

236-

PyFrame_GetLineNumber(frame));

247+

newtb = _PyTraceBack_FromFrame(tb, frame);

237248

if (newtb == NULL) {

238249

_PyErr_ChainExceptions(exc, val, tb);

239250

return -1;