Skip to content

gh-108082: Use PyErr_FormatUnraisable() #111580

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions Lib/test/test_capi/test_watchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,11 @@ def test_error(self):
with catch_unraisable_exception() as cm:
d["foo"] = "bar"
self.assertIn(
"PyDict_EVENT_ADDED watcher callback for <dict at",
cm.unraisable.object
"Exception ignored in "
"PyDict_EVENT_ADDED watcher callback for <dict at ",
cm.unraisable.err_msg
)
self.assertIsNone(cm.unraisable.object)
self.assertEqual(str(cm.unraisable.exc_value), "boom!")
self.assert_events([])

Expand Down Expand Up @@ -278,7 +280,9 @@ class C: pass
self.watch(wid, C)
with catch_unraisable_exception() as cm:
C.foo = "bar"
self.assertIs(cm.unraisable.object, C)
self.assertEqual(cm.unraisable.err_msg,
f"Exception ignored in type watcher callback #0 for {C!r}")
self.assertIs(cm.unraisable.object, None)
self.assertEqual(str(cm.unraisable.exc_value), "boom!")
self.assert_events([])

Expand Down Expand Up @@ -416,9 +420,11 @@ def test_error(self):
co = _testcapi.code_newempty("test_watchers", "dummy0", 0)

self.assertEqual(
cm.unraisable.object,
cm.unraisable.err_msg,
f"Exception ignored in "
f"PY_CODE_EVENT_CREATE watcher callback for {co!r}"
)
self.assertIsNone(cm.unraisable.object)
self.assertEqual(str(cm.unraisable.exc_value), "boom!")

def test_dealloc_error(self):
Expand Down Expand Up @@ -520,9 +526,11 @@ def myfunc():
pass

self.assertEqual(
cm.unraisable.object,
f"PyFunction_EVENT_CREATE watcher callback for {myfunc!r}"
cm.unraisable.err_msg,
f"Exception ignored in "
f"PyFunction_EVENT_CREATE watcher callback for {repr(myfunc)[1:-1]}"
)
self.assertIsNone(cm.unraisable.object)

def test_dealloc_watcher_raises_error(self):
class MyError(Exception):
Expand Down
5 changes: 3 additions & 2 deletions Lib/test/test_cmd_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,8 +479,9 @@ def test_stdout_flush_at_shutdown(self):
rc, out, err = assert_python_failure('-c', code)
self.assertEqual(b'', out)
self.assertEqual(120, rc)
self.assertRegex(err.decode('ascii', 'ignore'),
'Exception ignored in.*\nOSError: .*')
self.assertIn(b'Exception ignored on flushing sys.stdout:\n'
b'OSError: '.replace(b'\n', os.linesep.encode()),
err)

def test_closed_stdout(self):
# Issue #13444: if stdout has been explicitly closed, we should
Expand Down
3 changes: 1 addition & 2 deletions Modules/_ctypes/_ctypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ bytes(cdata)
#ifdef MS_WIN32
# include "pycore_modsupport.h" // _PyArg_NoKeywords()
#endif
#include "pycore_pyerrors.h" // _PyErr_WriteUnraisableMsg()


#include <ffi.h>
Expand Down Expand Up @@ -185,7 +184,7 @@ _DictRemover_call(PyObject *myself, PyObject *args, PyObject *kw)
DictRemoverObject *self = (DictRemoverObject *)myself;
if (self->key && self->dict) {
if (-1 == PyDict_DelItem(self->dict, self->key)) {
_PyErr_WriteUnraisableMsg("on calling _ctypes.DictRemover", NULL);
PyErr_FormatUnraisable("Exception ignored on calling _ctypes.DictRemover");
}
Py_CLEAR(self->key);
Py_CLEAR(self->dict);
Expand Down
3 changes: 1 addition & 2 deletions Modules/_lsprof.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#include "Python.h"
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_ceval.h" // _PyEval_SetProfile()
#include "pycore_pyerrors.h" // _PyErr_WriteUnraisableMsg()
#include "pycore_pystate.h" // _PyThreadState_GET()

#include "rotatingtree.h"
Expand Down Expand Up @@ -847,7 +846,7 @@ profiler_dealloc(ProfilerObject *op)
if (op->flags & POF_ENABLED) {
PyThreadState *tstate = _PyThreadState_GET();
if (_PyEval_SetProfile(tstate, NULL, NULL) < 0) {
_PyErr_WriteUnraisableMsg("When destroying _lsprof profiler", NULL);
PyErr_FormatUnraisable("Exception ignored when destroying _lsprof profiler");
}
}

Expand Down
10 changes: 5 additions & 5 deletions Modules/gcmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1032,8 +1032,8 @@ delete_garbage(PyThreadState *tstate, GCState *gcstate,
Py_INCREF(op);
(void) clear(op);
if (_PyErr_Occurred(tstate)) {
_PyErr_WriteUnraisableMsg("in tp_clear of",
(PyObject*)Py_TYPE(op));
PyErr_FormatUnraisable("Exception ignored in tp_clear of %s",
Py_TYPE(op)->tp_name);
}
Py_DECREF(op);
}
Expand Down Expand Up @@ -1344,7 +1344,7 @@ gc_collect_main(PyThreadState *tstate, int generation,
_PyErr_Clear(tstate);
}
else {
_PyErr_WriteUnraisableMsg("in garbage collection", NULL);
PyErr_FormatUnraisable("Exception ignored in garbage collection");
}
}

Expand Down Expand Up @@ -1403,15 +1403,15 @@ invoke_gc_callback(PyThreadState *tstate, const char *phase,
"collected", collected,
"uncollectable", uncollectable);
if (info == NULL) {
PyErr_WriteUnraisable(NULL);
PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks");
return;
}
}

PyObject *phase_obj = PyUnicode_FromString(phase);
if (phase_obj == NULL) {
Py_XDECREF(info);
PyErr_WriteUnraisable(NULL);
PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks");
return;
}

Expand Down
7 changes: 3 additions & 4 deletions Modules/getpath.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include "pycore_fileutils.h" // _Py_abspath()
#include "pycore_initconfig.h" // _PyStatus_EXCEPTION()
#include "pycore_pathconfig.h" // _PyPathConfig_ReadGlobal()
#include "pycore_pyerrors.h" // _PyErr_WriteUnraisableMsg()
#include "pycore_pymem.h" // _PyMem_RawWcsdup()
#include "pycore_pystate.h" // _PyThreadState_GET()

Expand Down Expand Up @@ -911,7 +910,7 @@ _PyConfig_InitPathConfig(PyConfig *config, int compute_path_config)
) {
Py_DECREF(co);
Py_DECREF(dict);
_PyErr_WriteUnraisableMsg("error evaluating initial values", NULL);
PyErr_FormatUnraisable("Exception ignored in preparing getpath");
return PyStatus_Error("error evaluating initial values");
}

Expand All @@ -920,13 +919,13 @@ _PyConfig_InitPathConfig(PyConfig *config, int compute_path_config)

if (!r) {
Py_DECREF(dict);
_PyErr_WriteUnraisableMsg("error evaluating path", NULL);
PyErr_FormatUnraisable("Exception ignored in running getpath");
return PyStatus_Error("error evaluating path");
}
Py_DECREF(r);

if (_PyConfig_FromDict(config, configDict) < 0) {
_PyErr_WriteUnraisableMsg("reading getpath results", NULL);
PyErr_FormatUnraisable("Exception ignored in reading getpath results");
Py_DECREF(dict);
return PyStatus_Error("error getting getpath results");
}
Expand Down
5 changes: 2 additions & 3 deletions Modules/signalmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,7 @@ report_wakeup_write_error(void *data)
errno = (int) (intptr_t) data;
PyObject *exc = PyErr_GetRaisedException();
PyErr_SetFromErrno(PyExc_OSError);
_PyErr_WriteUnraisableMsg("when trying to write to the signal wakeup fd",
NULL);
PyErr_FormatUnraisable("Exception ignored when trying to write to the signal wakeup fd");
PyErr_SetRaisedException(exc);
errno = save_errno;
return 0;
Expand All @@ -262,7 +261,7 @@ report_wakeup_send_error(void* data)
recognizes the error codes used by both GetLastError() and
WSAGetLastError */
PyErr_SetExcFromWindowsErr(PyExc_OSError, send_errno);
_PyErr_WriteUnraisableMsg("when trying to send to the signal wakeup fd", NULL);
PyErr_FormatUnraisable("Exception ignored when trying to send to the signal wakeup fd");
PyErr_SetRaisedException(exc);
return 0;
}
Expand Down
20 changes: 3 additions & 17 deletions Objects/codeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
#include "pycore_tuple.h" // _PyTuple_ITEMS()
#include "clinic/codeobject.c.h"

static PyObject* code_repr(PyCodeObject *co);

static const char *
code_event_name(PyCodeEvent event) {
switch (event) {
Expand Down Expand Up @@ -41,21 +39,9 @@ notify_code_watchers(PyCodeEvent event, PyCodeObject *co)
// callback must be non-null if the watcher bit is set
assert(cb != NULL);
if (cb(event, co) < 0) {
// Don't risk resurrecting the object if an unraisablehook keeps
// a reference; pass a string as context.
PyObject *context = NULL;
PyObject *repr = code_repr(co);
if (repr) {
context = PyUnicode_FromFormat(
"%s watcher callback for %U",
code_event_name(event), repr);
Py_DECREF(repr);
}
if (context == NULL) {
context = Py_NewRef(Py_None);
}
PyErr_WriteUnraisable(context);
Py_DECREF(context);
PyErr_FormatUnraisable(
"Exception ignored in %s watcher callback for %R",
code_event_name(event), co);
}
}
i++;
Expand Down
9 changes: 2 additions & 7 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -5912,14 +5912,9 @@ _PyDict_SendEvent(int watcher_bits,
// unraisablehook keep a reference to it, so we don't pass the
// dict as context, just an informative string message. Dict
// repr can call arbitrary code, so we invent a simpler version.
PyObject *context = PyUnicode_FromFormat(
"%s watcher callback for <dict at %p>",
PyErr_FormatUnraisable(
"Exception ignored in %s watcher callback for <dict at %p>",
dict_event_name(event), mp);
if (context == NULL) {
context = Py_NewRef(Py_None);
}
PyErr_WriteUnraisable(context);
Py_DECREF(context);
}
}
watcher_bits >>= 1;
Expand Down
20 changes: 3 additions & 17 deletions Objects/funcobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
#include "pycore_pyerrors.h" // _PyErr_Occurred()


static PyObject* func_repr(PyFunctionObject *op);

static const char *
func_event_name(PyFunction_WatchEvent event) {
switch (event) {
Expand All @@ -35,21 +33,9 @@ notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event,
// callback must be non-null if the watcher bit is set
assert(cb != NULL);
if (cb(event, func, new_value) < 0) {
// Don't risk resurrecting the func if an unraisablehook keeps a
// reference; pass a string as context.
PyObject *context = NULL;
PyObject *repr = func_repr(func);
if (repr != NULL) {
context = PyUnicode_FromFormat(
"%s watcher callback for %U",
func_event_name(event), repr);
Py_DECREF(repr);
}
if (context == NULL) {
context = Py_NewRef(Py_None);
}
PyErr_WriteUnraisable(context);
Py_DECREF(context);
PyErr_FormatUnraisable(
"Exception ignored in %s watcher callback for function %U at %p",
func_event_name(event), func->func_qualname, func);
}
}
i++;
Expand Down
11 changes: 5 additions & 6 deletions Objects/moduleobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ _PyModule_ClearDict(PyObject *d)
PyErr_Clear();
}
if (PyDict_SetItem(d, key, Py_None) != 0) {
PyErr_WriteUnraisable(NULL);
PyErr_FormatUnraisable("Exception ignored on clearing module dict");
}
}
}
Expand All @@ -668,7 +668,7 @@ _PyModule_ClearDict(PyObject *d)
PyErr_Clear();
}
if (PyDict_SetItem(d, key, Py_None) != 0) {
PyErr_WriteUnraisable(NULL);
PyErr_FormatUnraisable("Exception ignored on clearing module dict");
}
}
}
Expand Down Expand Up @@ -902,10 +902,9 @@ module_clear(PyModuleObject *m)
{
int res = m->md_def->m_clear((PyObject*)m);
if (PyErr_Occurred()) {
PySys_FormatStderr("Exception ignored in m_clear of module%s%V\n",
m->md_name ? " " : "",
m->md_name, "");
PyErr_WriteUnraisable(NULL);
PyErr_FormatUnraisable("Exception ignored in m_clear of module%s%V",
m->md_name ? " " : "",
m->md_name, "");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last "" is not used, it can be removed.

}
if (res)
return res;
Expand Down
10 changes: 6 additions & 4 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,9 @@ PyType_Modified(PyTypeObject *type)
if (bits & 1) {
PyType_WatchCallback cb = interp->type_watchers[i];
if (cb && (cb(type) < 0)) {
PyErr_WriteUnraisable((PyObject *)type);
PyErr_FormatUnraisable(
"Exception ignored in type watcher callback #%d for %R",
i, type);
}
}
i++;
Expand Down Expand Up @@ -9291,7 +9293,7 @@ releasebuffer_call_python(PyObject *self, Py_buffer *buffer)
// from a Python __buffer__ function.
mv = PyMemoryView_FromBuffer(buffer);
if (mv == NULL) {
PyErr_WriteUnraisable(self);
PyErr_FormatUnraisable("Exception ignored in bf_releasebuffer of %s", Py_TYPE(self)->tp_name);
goto end;
}
// Set the memoryview to restricted mode, which forbids
Expand All @@ -9304,15 +9306,15 @@ releasebuffer_call_python(PyObject *self, Py_buffer *buffer)
PyObject *stack[2] = {self, mv};
PyObject *ret = vectorcall_method(&_Py_ID(__release_buffer__), stack, 2);
if (ret == NULL) {
PyErr_WriteUnraisable(self);
PyErr_FormatUnraisable("Exception ignored in __release_buffer__ of %s", Py_TYPE(self)->tp_name);
}
else {
Py_DECREF(ret);
}
if (!is_buffer_wrapper) {
PyObject *res = PyObject_CallMethodNoArgs(mv, &_Py_ID(release));
if (res == NULL) {
PyErr_WriteUnraisable(self);
PyErr_FormatUnraisable("Exception ignored in bf_releasebuffer of %s", Py_TYPE(self)->tp_name);
}
else {
Py_DECREF(res);
Expand Down
8 changes: 4 additions & 4 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -2235,7 +2235,7 @@ PyEval_SetProfile(Py_tracefunc func, PyObject *arg)
PyThreadState *tstate = _PyThreadState_GET();
if (_PyEval_SetProfile(tstate, func, arg) < 0) {
/* Log _PySys_Audit() error */
_PyErr_WriteUnraisableMsg("in PyEval_SetProfile", NULL);
PyErr_FormatUnraisable("Exception ignored in PyEval_SetProfile");
}
}

Expand All @@ -2252,7 +2252,7 @@ PyEval_SetProfileAllThreads(Py_tracefunc func, PyObject *arg)

while (ts) {
if (_PyEval_SetProfile(ts, func, arg) < 0) {
_PyErr_WriteUnraisableMsg("in PyEval_SetProfileAllThreads", NULL);
PyErr_FormatUnraisable("Exception ignored in PyEval_SetProfileAllThreads");
}
HEAD_LOCK(runtime);
ts = PyThreadState_Next(ts);
Expand All @@ -2266,7 +2266,7 @@ PyEval_SetTrace(Py_tracefunc func, PyObject *arg)
PyThreadState *tstate = _PyThreadState_GET();
if (_PyEval_SetTrace(tstate, func, arg) < 0) {
/* Log _PySys_Audit() error */
_PyErr_WriteUnraisableMsg("in PyEval_SetTrace", NULL);
PyErr_FormatUnraisable("Exception ignored in PyEval_SetTrace");
}
}

Expand All @@ -2283,7 +2283,7 @@ PyEval_SetTraceAllThreads(Py_tracefunc func, PyObject *arg)

while (ts) {
if (_PyEval_SetTrace(ts, func, arg) < 0) {
_PyErr_WriteUnraisableMsg("in PyEval_SetTraceAllThreads", NULL);
PyErr_FormatUnraisable("Exception ignored in PyEval_SetTraceAllThreads");
}
HEAD_LOCK(runtime);
ts = PyThreadState_Next(ts);
Expand Down
5 changes: 2 additions & 3 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
#include "pycore_flowgraph.h"
#include "pycore_intrinsics.h"
#include "pycore_long.h" // _PyLong_GetZero()
#include "pycore_pyerrors.h" // _PyErr_WriteUnraisableMsg()
#include "pycore_pystate.h" // _Py_GetConfig()
#include "pycore_setobject.h" // _PySet_NextEntry()
#include "pycore_symtable.h" // PySTEntryObject, _PyFuture_FromAST()
Expand Down Expand Up @@ -1407,8 +1406,8 @@ compiler_exit_scope(struct compiler *c)
assert(c->u);
/* we are deleting from a list so this really shouldn't fail */
if (PySequence_DelItem(c->c_stack, n) < 0) {
_PyErr_WriteUnraisableMsg("on removing the last compiler "
"stack item", NULL);
PyErr_FormatUnraisable("Exception ignored on removing "
"the last compiler stack item");
}
}
else {
Expand Down
Loading