Skip to content

Commit

Permalink
pythongh-108082: Use PyErr_FormatUnraisable() (pythonGH-111580)
Browse files Browse the repository at this point in the history
Replace most of calls of _PyErr_WriteUnraisableMsg() and some
calls of PyErr_WriteUnraisable(NULL) with PyErr_FormatUnraisable().

Co-authored-by: Victor Stinner <[email protected]>
  • Loading branch information
2 people authored and aisk committed Feb 11, 2024
1 parent 4075bab commit daf14e8
Show file tree
Hide file tree
Showing 18 changed files with 83 additions and 114 deletions.
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, "");
}
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

0 comments on commit daf14e8

Please sign in to comment.