mirror of
https://github.com/python/cpython.git
synced 2025-08-31 05:58:33 +00:00
bpo-36829: Add _PyErr_WriteUnraisableMsg() (GH-13488)
* sys.unraisablehook: add 'err_msg' field to UnraisableHookArgs. * Use _PyErr_WriteUnraisableMsg() in _ctypes _DictRemover_call() and gc delete_garbage().
This commit is contained in:
parent
2f0bfd27a5
commit
71c52e3048
9 changed files with 133 additions and 45 deletions
|
@ -1566,11 +1566,16 @@ always available.
|
||||||
* *exc_type*: Exception type.
|
* *exc_type*: Exception type.
|
||||||
* *exc_value*: Exception value, can be ``None``.
|
* *exc_value*: Exception value, can be ``None``.
|
||||||
* *exc_traceback*: Exception traceback, can be ``None``.
|
* *exc_traceback*: Exception traceback, can be ``None``.
|
||||||
|
* *err_msg*: Error message, can be ``None``.
|
||||||
* *object*: Object causing the exception, can be ``None``.
|
* *object*: Object causing the exception, can be ``None``.
|
||||||
|
|
||||||
:func:`sys.unraisablehook` can be overridden to control how unraisable
|
:func:`sys.unraisablehook` can be overridden to control how unraisable
|
||||||
exceptions are handled.
|
exceptions are handled.
|
||||||
|
|
||||||
|
The default hook formats *err_msg* and *object* as:
|
||||||
|
``f'{err_msg}: {object!r}'``; use "Exception ignored in" error message
|
||||||
|
if *err_msg* is ``None``.
|
||||||
|
|
||||||
See also :func:`excepthook` which handles uncaught exceptions.
|
See also :func:`excepthook` which handles uncaught exceptions.
|
||||||
|
|
||||||
.. versionadded:: 3.8
|
.. versionadded:: 3.8
|
||||||
|
|
|
@ -171,6 +171,9 @@ PyAPI_FUNC(PyObject *) _PyUnicodeTranslateError_Create(
|
||||||
const char *reason /* UTF-8 encoded string */
|
const char *reason /* UTF-8 encoded string */
|
||||||
);
|
);
|
||||||
|
|
||||||
|
PyAPI_FUNC(void) _PyErr_WriteUnraisableMsg(
|
||||||
|
const char *err_msg,
|
||||||
|
PyObject *obj);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -878,31 +878,38 @@ class SysModuleTest(unittest.TestCase):
|
||||||
|
|
||||||
@test.support.cpython_only
|
@test.support.cpython_only
|
||||||
class UnraisableHookTest(unittest.TestCase):
|
class UnraisableHookTest(unittest.TestCase):
|
||||||
def write_unraisable_exc(self, exc, obj):
|
def write_unraisable_exc(self, exc, err_msg, obj):
|
||||||
import _testcapi
|
import _testcapi
|
||||||
import types
|
import types
|
||||||
|
err_msg2 = f"Exception ignored {err_msg}"
|
||||||
try:
|
try:
|
||||||
_testcapi.write_unraisable_exc(exc, obj)
|
_testcapi.write_unraisable_exc(exc, err_msg, obj)
|
||||||
return types.SimpleNamespace(exc_type=type(exc),
|
return types.SimpleNamespace(exc_type=type(exc),
|
||||||
exc_value=exc,
|
exc_value=exc,
|
||||||
exc_traceback=exc.__traceback__,
|
exc_traceback=exc.__traceback__,
|
||||||
|
err_msg=err_msg2,
|
||||||
object=obj)
|
object=obj)
|
||||||
finally:
|
finally:
|
||||||
# Explicitly break any reference cycle
|
# Explicitly break any reference cycle
|
||||||
exc = None
|
exc = None
|
||||||
|
|
||||||
def test_original_unraisablehook(self):
|
def test_original_unraisablehook(self):
|
||||||
obj = "an object"
|
for err_msg in (None, "original hook"):
|
||||||
|
with self.subTest(err_msg=err_msg):
|
||||||
|
obj = "an object"
|
||||||
|
|
||||||
with test.support.captured_output("stderr") as stderr:
|
with test.support.captured_output("stderr") as stderr:
|
||||||
with test.support.swap_attr(sys, 'unraisablehook',
|
with test.support.swap_attr(sys, 'unraisablehook',
|
||||||
sys.__unraisablehook__):
|
sys.__unraisablehook__):
|
||||||
self.write_unraisable_exc(ValueError(42), obj)
|
self.write_unraisable_exc(ValueError(42), err_msg, obj)
|
||||||
|
|
||||||
err = stderr.getvalue()
|
err = stderr.getvalue()
|
||||||
self.assertIn(f'Exception ignored in: {obj!r}\n', err)
|
if err_msg is not None:
|
||||||
self.assertIn('Traceback (most recent call last):\n', err)
|
self.assertIn(f'Exception ignored {err_msg}: {obj!r}\n', err)
|
||||||
self.assertIn('ValueError: 42\n', err)
|
else:
|
||||||
|
self.assertIn(f'Exception ignored in: {obj!r}\n', err)
|
||||||
|
self.assertIn('Traceback (most recent call last):\n', err)
|
||||||
|
self.assertIn('ValueError: 42\n', err)
|
||||||
|
|
||||||
def test_original_unraisablehook_err(self):
|
def test_original_unraisablehook_err(self):
|
||||||
# bpo-22836: PyErr_WriteUnraisable() should give sensible reports
|
# bpo-22836: PyErr_WriteUnraisable() should give sensible reports
|
||||||
|
@ -962,8 +969,9 @@ class UnraisableHookTest(unittest.TestCase):
|
||||||
obj = object()
|
obj = object()
|
||||||
try:
|
try:
|
||||||
with test.support.swap_attr(sys, 'unraisablehook', hook_func):
|
with test.support.swap_attr(sys, 'unraisablehook', hook_func):
|
||||||
expected = self.write_unraisable_exc(ValueError(42), obj)
|
expected = self.write_unraisable_exc(ValueError(42),
|
||||||
for attr in "exc_type exc_value exc_traceback object".split():
|
"custom hook", obj)
|
||||||
|
for attr in "exc_type exc_value exc_traceback err_msg object".split():
|
||||||
self.assertEqual(getattr(hook_args, attr),
|
self.assertEqual(getattr(hook_args, attr),
|
||||||
getattr(expected, attr),
|
getattr(expected, attr),
|
||||||
(hook_args, expected))
|
(hook_args, expected))
|
||||||
|
@ -978,10 +986,12 @@ class UnraisableHookTest(unittest.TestCase):
|
||||||
|
|
||||||
with test.support.captured_output("stderr") as stderr:
|
with test.support.captured_output("stderr") as stderr:
|
||||||
with test.support.swap_attr(sys, 'unraisablehook', hook_func):
|
with test.support.swap_attr(sys, 'unraisablehook', hook_func):
|
||||||
self.write_unraisable_exc(ValueError(42), None)
|
self.write_unraisable_exc(ValueError(42),
|
||||||
|
"custom hook fail", None)
|
||||||
|
|
||||||
err = stderr.getvalue()
|
err = stderr.getvalue()
|
||||||
self.assertIn(f'Exception ignored in: {hook_func!r}\n',
|
self.assertIn(f'Exception ignored in sys.unraisablehook: '
|
||||||
|
f'{hook_func!r}\n',
|
||||||
err)
|
err)
|
||||||
self.assertIn('Traceback (most recent call last):\n', err)
|
self.assertIn('Traceback (most recent call last):\n', err)
|
||||||
self.assertIn('Exception: hook_func failed\n', err)
|
self.assertIn('Exception: hook_func failed\n', err)
|
||||||
|
|
|
@ -150,9 +150,9 @@ _DictRemover_call(PyObject *myself, PyObject *args, PyObject *kw)
|
||||||
{
|
{
|
||||||
DictRemoverObject *self = (DictRemoverObject *)myself;
|
DictRemoverObject *self = (DictRemoverObject *)myself;
|
||||||
if (self->key && self->dict) {
|
if (self->key && self->dict) {
|
||||||
if (-1 == PyDict_DelItem(self->dict, self->key))
|
if (-1 == PyDict_DelItem(self->dict, self->key)) {
|
||||||
/* XXX Error context */
|
_PyErr_WriteUnraisableMsg("on calling _ctypes.DictRemover", NULL);
|
||||||
PyErr_WriteUnraisable(Py_None);
|
}
|
||||||
Py_CLEAR(self->key);
|
Py_CLEAR(self->key);
|
||||||
Py_CLEAR(self->dict);
|
Py_CLEAR(self->dict);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4985,13 +4985,24 @@ negative_refcount(PyObject *self, PyObject *Py_UNUSED(args))
|
||||||
static PyObject*
|
static PyObject*
|
||||||
test_write_unraisable_exc(PyObject *self, PyObject *args)
|
test_write_unraisable_exc(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
PyObject *exc, *obj;
|
PyObject *exc, *err_msg, *obj;
|
||||||
if (!PyArg_ParseTuple(args, "OO", &exc, &obj)) {
|
if (!PyArg_ParseTuple(args, "OOO", &exc, &err_msg, &obj)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *err_msg_utf8;
|
||||||
|
if (err_msg != Py_None) {
|
||||||
|
err_msg_utf8 = PyUnicode_AsUTF8(err_msg);
|
||||||
|
if (err_msg_utf8 == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
err_msg_utf8 = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
PyErr_SetObject((PyObject *)Py_TYPE(exc), exc);
|
PyErr_SetObject((PyObject *)Py_TYPE(exc), exc);
|
||||||
PyErr_WriteUnraisable(obj);
|
_PyErr_WriteUnraisableMsg(err_msg_utf8, obj);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -929,9 +929,8 @@ delete_garbage(struct _gc_runtime_state *state,
|
||||||
Py_INCREF(op);
|
Py_INCREF(op);
|
||||||
(void) clear(op);
|
(void) clear(op);
|
||||||
if (PyErr_Occurred()) {
|
if (PyErr_Occurred()) {
|
||||||
PySys_WriteStderr("Exception ignored in tp_clear of "
|
_PyErr_WriteUnraisableMsg("in tp_clear of",
|
||||||
"%.50s\n", Py_TYPE(op)->tp_name);
|
(PyObject*)Py_TYPE(op));
|
||||||
PyErr_WriteUnraisable(NULL);
|
|
||||||
}
|
}
|
||||||
Py_DECREF(op);
|
Py_DECREF(op);
|
||||||
}
|
}
|
||||||
|
|
9
Python/clinic/sysmodule.c.h
generated
9
Python/clinic/sysmodule.c.h
generated
|
@ -106,9 +106,10 @@ PyDoc_STRVAR(sys_unraisablehook__doc__,
|
||||||
"The unraisable argument has the following attributes:\n"
|
"The unraisable argument has the following attributes:\n"
|
||||||
"\n"
|
"\n"
|
||||||
"* exc_type: Exception type.\n"
|
"* exc_type: Exception type.\n"
|
||||||
"* exc_value: Exception value.\n"
|
"* exc_value: Exception value, can be None.\n"
|
||||||
"* exc_tb: Exception traceback, can be None.\n"
|
"* exc_traceback: Exception traceback, can be None.\n"
|
||||||
"* obj: Object causing the exception, can be None.");
|
"* err_msg: Error message, can be None.\n"
|
||||||
|
"* object: Object causing the exception, can be None.");
|
||||||
|
|
||||||
#define SYS_UNRAISABLEHOOK_METHODDEF \
|
#define SYS_UNRAISABLEHOOK_METHODDEF \
|
||||||
{"unraisablehook", (PyCFunction)sys_unraisablehook, METH_O, sys_unraisablehook__doc__},
|
{"unraisablehook", (PyCFunction)sys_unraisablehook, METH_O, sys_unraisablehook__doc__},
|
||||||
|
@ -1108,4 +1109,4 @@ sys_getandroidapilevel(PyObject *module, PyObject *Py_UNUSED(ignored))
|
||||||
#ifndef SYS_GETANDROIDAPILEVEL_METHODDEF
|
#ifndef SYS_GETANDROIDAPILEVEL_METHODDEF
|
||||||
#define SYS_GETANDROIDAPILEVEL_METHODDEF
|
#define SYS_GETANDROIDAPILEVEL_METHODDEF
|
||||||
#endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */
|
#endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */
|
||||||
/*[clinic end generated code: output=3c32bc91ec659509 input=a9049054013a1b77]*/
|
/*[clinic end generated code: output=03da2eb03135d9f2 input=a9049054013a1b77]*/
|
||||||
|
|
|
@ -1077,6 +1077,7 @@ static PyStructSequence_Field UnraisableHookArgs_fields[] = {
|
||||||
{"exc_type", "Exception type"},
|
{"exc_type", "Exception type"},
|
||||||
{"exc_value", "Exception value"},
|
{"exc_value", "Exception value"},
|
||||||
{"exc_traceback", "Exception traceback"},
|
{"exc_traceback", "Exception traceback"},
|
||||||
|
{"err_msg", "Error message"},
|
||||||
{"object", "Object causing the exception"},
|
{"object", "Object causing the exception"},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
@ -1085,7 +1086,7 @@ static PyStructSequence_Desc UnraisableHookArgs_desc = {
|
||||||
.name = "UnraisableHookArgs",
|
.name = "UnraisableHookArgs",
|
||||||
.doc = UnraisableHookArgs__doc__,
|
.doc = UnraisableHookArgs__doc__,
|
||||||
.fields = UnraisableHookArgs_fields,
|
.fields = UnraisableHookArgs_fields,
|
||||||
.n_in_sequence = 4
|
.n_in_sequence = 5
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1104,7 +1105,8 @@ _PyErr_Init(void)
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
make_unraisable_hook_args(PyThreadState *tstate, PyObject *exc_type,
|
make_unraisable_hook_args(PyThreadState *tstate, PyObject *exc_type,
|
||||||
PyObject *exc_value, PyObject *exc_tb, PyObject *obj)
|
PyObject *exc_value, PyObject *exc_tb,
|
||||||
|
PyObject *err_msg, PyObject *obj)
|
||||||
{
|
{
|
||||||
PyObject *args = PyStructSequence_New(&UnraisableHookArgsType);
|
PyObject *args = PyStructSequence_New(&UnraisableHookArgsType);
|
||||||
if (args == NULL) {
|
if (args == NULL) {
|
||||||
|
@ -1125,6 +1127,7 @@ make_unraisable_hook_args(PyThreadState *tstate, PyObject *exc_type,
|
||||||
ADD_ITEM(exc_type);
|
ADD_ITEM(exc_type);
|
||||||
ADD_ITEM(exc_value);
|
ADD_ITEM(exc_value);
|
||||||
ADD_ITEM(exc_tb);
|
ADD_ITEM(exc_tb);
|
||||||
|
ADD_ITEM(err_msg);
|
||||||
ADD_ITEM(obj);
|
ADD_ITEM(obj);
|
||||||
#undef ADD_ITEM
|
#undef ADD_ITEM
|
||||||
|
|
||||||
|
@ -1145,11 +1148,21 @@ make_unraisable_hook_args(PyThreadState *tstate, PyObject *exc_type,
|
||||||
static int
|
static int
|
||||||
write_unraisable_exc_file(PyThreadState *tstate, PyObject *exc_type,
|
write_unraisable_exc_file(PyThreadState *tstate, PyObject *exc_type,
|
||||||
PyObject *exc_value, PyObject *exc_tb,
|
PyObject *exc_value, PyObject *exc_tb,
|
||||||
PyObject *obj, PyObject *file)
|
PyObject *err_msg, PyObject *obj, PyObject *file)
|
||||||
{
|
{
|
||||||
if (obj != NULL && obj != Py_None) {
|
if (obj != NULL && obj != Py_None) {
|
||||||
if (PyFile_WriteString("Exception ignored in: ", file) < 0) {
|
if (err_msg != NULL && err_msg != Py_None) {
|
||||||
return -1;
|
if (PyFile_WriteObject(err_msg, file, Py_PRINT_RAW) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (PyFile_WriteString(": ", file) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (PyFile_WriteString("Exception ignored in: ", file) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PyFile_WriteObject(obj, file, 0) < 0) {
|
if (PyFile_WriteObject(obj, file, 0) < 0) {
|
||||||
|
@ -1162,6 +1175,14 @@ write_unraisable_exc_file(PyThreadState *tstate, PyObject *exc_type,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (err_msg != NULL && err_msg != Py_None) {
|
||||||
|
if (PyFile_WriteObject(err_msg, file, Py_PRINT_RAW) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (PyFile_WriteString(":\n", file) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (exc_tb != NULL && exc_tb != Py_None) {
|
if (exc_tb != NULL && exc_tb != Py_None) {
|
||||||
if (PyTraceBack_Print(exc_tb, file) < 0) {
|
if (PyTraceBack_Print(exc_tb, file) < 0) {
|
||||||
|
@ -1178,8 +1199,9 @@ write_unraisable_exc_file(PyThreadState *tstate, PyObject *exc_type,
|
||||||
const char *className = PyExceptionClass_Name(exc_type);
|
const char *className = PyExceptionClass_Name(exc_type);
|
||||||
if (className != NULL) {
|
if (className != NULL) {
|
||||||
const char *dot = strrchr(className, '.');
|
const char *dot = strrchr(className, '.');
|
||||||
if (dot != NULL)
|
if (dot != NULL) {
|
||||||
className = dot+1;
|
className = dot+1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_Py_IDENTIFIER(__module__);
|
_Py_IDENTIFIER(__module__);
|
||||||
|
@ -1238,7 +1260,8 @@ write_unraisable_exc_file(PyThreadState *tstate, PyObject *exc_type,
|
||||||
|
|
||||||
static int
|
static int
|
||||||
write_unraisable_exc(PyThreadState *tstate, PyObject *exc_type,
|
write_unraisable_exc(PyThreadState *tstate, PyObject *exc_type,
|
||||||
PyObject *exc_value, PyObject *exc_tb, PyObject *obj)
|
PyObject *exc_value, PyObject *exc_tb, PyObject *err_msg,
|
||||||
|
PyObject *obj)
|
||||||
{
|
{
|
||||||
PyObject *file = _PySys_GetObjectId(&PyId_stderr);
|
PyObject *file = _PySys_GetObjectId(&PyId_stderr);
|
||||||
if (file == NULL || file == Py_None) {
|
if (file == NULL || file == Py_None) {
|
||||||
|
@ -1249,7 +1272,7 @@ write_unraisable_exc(PyThreadState *tstate, PyObject *exc_type,
|
||||||
while we use it */
|
while we use it */
|
||||||
Py_INCREF(file);
|
Py_INCREF(file);
|
||||||
int res = write_unraisable_exc_file(tstate, exc_type, exc_value, exc_tb,
|
int res = write_unraisable_exc_file(tstate, exc_type, exc_value, exc_tb,
|
||||||
obj, file);
|
err_msg, obj, file);
|
||||||
Py_DECREF(file);
|
Py_DECREF(file);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
@ -1272,9 +1295,10 @@ _PyErr_WriteUnraisableDefaultHook(PyObject *args)
|
||||||
PyObject *exc_type = PyStructSequence_GET_ITEM(args, 0);
|
PyObject *exc_type = PyStructSequence_GET_ITEM(args, 0);
|
||||||
PyObject *exc_value = PyStructSequence_GET_ITEM(args, 1);
|
PyObject *exc_value = PyStructSequence_GET_ITEM(args, 1);
|
||||||
PyObject *exc_tb = PyStructSequence_GET_ITEM(args, 2);
|
PyObject *exc_tb = PyStructSequence_GET_ITEM(args, 2);
|
||||||
PyObject *obj = PyStructSequence_GET_ITEM(args, 3);
|
PyObject *err_msg = PyStructSequence_GET_ITEM(args, 3);
|
||||||
|
PyObject *obj = PyStructSequence_GET_ITEM(args, 4);
|
||||||
|
|
||||||
if (write_unraisable_exc(tstate, exc_type, exc_value, exc_tb, obj) < 0) {
|
if (write_unraisable_exc(tstate, exc_type, exc_value, exc_tb, err_msg, obj) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
|
@ -1287,13 +1311,18 @@ _PyErr_WriteUnraisableDefaultHook(PyObject *args)
|
||||||
for Python to handle it. For example, when a destructor raises an exception
|
for Python to handle it. For example, when a destructor raises an exception
|
||||||
or during garbage collection (gc.collect()).
|
or during garbage collection (gc.collect()).
|
||||||
|
|
||||||
|
If err_msg_str is non-NULL, the error message is formatted as:
|
||||||
|
"Exception ignored %s" % err_msg_str. Otherwise, use "Exception ignored in"
|
||||||
|
error message.
|
||||||
|
|
||||||
An exception must be set when calling this function. */
|
An exception must be set when calling this function. */
|
||||||
void
|
void
|
||||||
PyErr_WriteUnraisable(PyObject *obj)
|
_PyErr_WriteUnraisableMsg(const char *err_msg_str, PyObject *obj)
|
||||||
{
|
{
|
||||||
PyThreadState *tstate = _PyThreadState_GET();
|
PyThreadState *tstate = _PyThreadState_GET();
|
||||||
assert(tstate != NULL);
|
assert(tstate != NULL);
|
||||||
|
|
||||||
|
PyObject *err_msg = NULL;
|
||||||
PyObject *exc_type, *exc_value, *exc_tb;
|
PyObject *exc_type, *exc_value, *exc_tb;
|
||||||
_PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb);
|
_PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb);
|
||||||
|
|
||||||
|
@ -1322,13 +1351,20 @@ PyErr_WriteUnraisable(PyObject *obj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (err_msg_str != NULL) {
|
||||||
|
err_msg = PyUnicode_FromFormat("Exception ignored %s", err_msg_str);
|
||||||
|
if (err_msg == NULL) {
|
||||||
|
PyErr_Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_Py_IDENTIFIER(unraisablehook);
|
_Py_IDENTIFIER(unraisablehook);
|
||||||
PyObject *hook = _PySys_GetObjectId(&PyId_unraisablehook);
|
PyObject *hook = _PySys_GetObjectId(&PyId_unraisablehook);
|
||||||
if (hook != NULL && hook != Py_None) {
|
if (hook != NULL && hook != Py_None) {
|
||||||
PyObject *hook_args;
|
PyObject *hook_args;
|
||||||
|
|
||||||
hook_args = make_unraisable_hook_args(tstate, exc_type, exc_value,
|
hook_args = make_unraisable_hook_args(tstate, exc_type, exc_value,
|
||||||
exc_tb, obj);
|
exc_tb, err_msg, obj);
|
||||||
if (hook_args != NULL) {
|
if (hook_args != NULL) {
|
||||||
PyObject *args[1] = {hook_args};
|
PyObject *args[1] = {hook_args};
|
||||||
PyObject *res = _PyObject_FastCall(hook, args, 1);
|
PyObject *res = _PyObject_FastCall(hook, args, 1);
|
||||||
|
@ -1337,6 +1373,18 @@ PyErr_WriteUnraisable(PyObject *obj)
|
||||||
Py_DECREF(res);
|
Py_DECREF(res);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err_msg_str = "Exception ignored in sys.unraisablehook";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
err_msg_str = ("Exception ignored on building "
|
||||||
|
"sys.unraisablehook arguments");
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_XDECREF(err_msg);
|
||||||
|
err_msg = PyUnicode_FromString(err_msg_str);
|
||||||
|
if (err_msg == NULL) {
|
||||||
|
PyErr_Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sys.unraisablehook failed: log its error using default hook */
|
/* sys.unraisablehook failed: log its error using default hook */
|
||||||
|
@ -1350,15 +1398,25 @@ PyErr_WriteUnraisable(PyObject *obj)
|
||||||
|
|
||||||
default_hook:
|
default_hook:
|
||||||
/* Call the default unraisable hook (ignore failure) */
|
/* Call the default unraisable hook (ignore failure) */
|
||||||
(void)write_unraisable_exc(tstate, exc_type, exc_value, exc_tb, obj);
|
(void)write_unraisable_exc(tstate, exc_type, exc_value, exc_tb,
|
||||||
|
err_msg, obj);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
Py_XDECREF(exc_type);
|
Py_XDECREF(exc_type);
|
||||||
Py_XDECREF(exc_value);
|
Py_XDECREF(exc_value);
|
||||||
Py_XDECREF(exc_tb);
|
Py_XDECREF(exc_tb);
|
||||||
|
Py_XDECREF(err_msg);
|
||||||
_PyErr_Clear(tstate); /* Just in case */
|
_PyErr_Clear(tstate); /* Just in case */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PyErr_WriteUnraisable(PyObject *obj)
|
||||||
|
{
|
||||||
|
_PyErr_WriteUnraisableMsg(NULL, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
extern PyObject *PyModule_GetWarningsModule(void);
|
extern PyObject *PyModule_GetWarningsModule(void);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -689,14 +689,15 @@ Handle an unraisable exception.
|
||||||
The unraisable argument has the following attributes:
|
The unraisable argument has the following attributes:
|
||||||
|
|
||||||
* exc_type: Exception type.
|
* exc_type: Exception type.
|
||||||
* exc_value: Exception value.
|
* exc_value: Exception value, can be None.
|
||||||
* exc_tb: Exception traceback, can be None.
|
* exc_traceback: Exception traceback, can be None.
|
||||||
* obj: Object causing the exception, can be None.
|
* err_msg: Error message, can be None.
|
||||||
|
* object: Object causing the exception, can be None.
|
||||||
[clinic start generated code]*/
|
[clinic start generated code]*/
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
sys_unraisablehook(PyObject *module, PyObject *unraisable)
|
sys_unraisablehook(PyObject *module, PyObject *unraisable)
|
||||||
/*[clinic end generated code: output=bb92838b32abaa14 input=fdbdb47fdd0bee06]*/
|
/*[clinic end generated code: output=bb92838b32abaa14 input=ec3af148294af8d3]*/
|
||||||
{
|
{
|
||||||
return _PyErr_WriteUnraisableDefaultHook(unraisable);
|
return _PyErr_WriteUnraisableDefaultHook(unraisable);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue