Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
8 changes: 8 additions & 0 deletions Doc/c-api/exceptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,17 @@ Printing and clearing
The function is called with a single argument *obj* that identifies the context
in which the unraisable exception occurred. If possible,
the repr of *obj* will be printed in the warning message.
If *obj* is ``NULL``, only the traceback is printed.

An exception must be set when calling this function.

.. versionchanged:: 3.4
Print a traceback. Print only traceback if *obj* is ``NULL``.
Comment on lines +95 to +96

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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


.. versionchanged:: 3.8
Use :func:`sys.unraisablehook`.
Comment on lines +98 to +99

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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



.. c:function:: void PyErr_DisplayException(PyObject *exc)

Print the standard traceback display of ``exc`` to ``sys.stderr``, including
Expand Down
45 changes: 45 additions & 0 deletions Lib/test/test_capi/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@

NULL = None

class CustomError(Exception):
pass


class Test_Exceptions(unittest.TestCase):

def test_exception(self):
Expand Down Expand Up @@ -270,6 +274,47 @@ def test_setfromerrnowithfilename(self):
(ENOENT, 'No such file or directory', 'file'))
# CRASHES setfromerrnowithfilename(ENOENT, NULL, b'error')

def test_err_writeunraisable(self):
# Test PyErr_WriteUnraisable()
writeunraisable = _testcapi.err_writeunraisable
firstline = self.test_err_writeunraisable.__code__.co_firstlineno

with support.catch_unraisable_exception() as cm:
writeunraisable(CustomError('oops!'), hex)
self.assertEqual(cm.unraisable.exc_type, CustomError)
self.assertEqual(str(cm.unraisable.exc_value), 'oops!')
self.assertEqual(cm.unraisable.exc_traceback.tb_lineno,
firstline + 6)
self.assertIsNone(cm.unraisable.err_msg)
self.assertEqual(cm.unraisable.object, hex)

with support.catch_unraisable_exception() as cm:
writeunraisable(CustomError('oops!'), NULL)
self.assertEqual(cm.unraisable.exc_type, CustomError)
self.assertEqual(str(cm.unraisable.exc_value), 'oops!')
self.assertEqual(cm.unraisable.exc_traceback.tb_lineno,
firstline + 15)
self.assertIsNone(cm.unraisable.err_msg)
self.assertIsNone(cm.unraisable.object)

with (support.swap_attr(sys, 'unraisablehook', None),
support.captured_stderr() as stderr):
writeunraisable(CustomError('oops!'), hex)
lines = stderr.getvalue().splitlines()
self.assertEqual(lines[0], f'Exception ignored in: {hex!r}')
self.assertEqual(lines[1], 'Traceback (most recent call last):')
self.assertEqual(lines[-1], f'{__name__}.CustomError: oops!')

with (support.swap_attr(sys, 'unraisablehook', None),
support.captured_stderr() as stderr):
writeunraisable(CustomError('oops!'), NULL)
lines = stderr.getvalue().splitlines()
self.assertEqual(lines[0], 'Traceback (most recent call last):')
self.assertEqual(lines[-1], f'{__name__}.CustomError: oops!')

# CRASHES writeunraisable(NULL, hex)
# CRASHES writeunraisable(NULL, NULL)


class Test_PyUnstable_Exc_PrepReraiseStar(ExceptionIsLikeMixin, unittest.TestCase):

Expand Down
18 changes: 18 additions & 0 deletions Modules/_testcapi/exceptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,23 @@ _testcapi_traceback_print_impl(PyObject *module, PyObject *traceback,
Py_RETURN_NONE;
}

static PyObject *
err_writeunraisable(PyObject *Py_UNUSED(module), PyObject *args)
{
PyObject *exc, *obj;
if (!PyArg_ParseTuple(args, "OO", &exc, &obj)) {
return NULL;
}
NULLABLE(exc);
NULLABLE(obj);
if (exc) {
Py_INCREF(exc);
PyErr_SetRaisedException(exc);
Comment thread
serhiy-storchaka marked this conversation as resolved.
Outdated
}
PyErr_WriteUnraisable(obj);
Py_RETURN_NONE;
}

/*[clinic input]
_testcapi.unstable_exc_prep_reraise_star
orig: object
Expand Down Expand Up @@ -347,6 +364,7 @@ static PyTypeObject PyRecursingInfinitelyError_Type = {

static PyMethodDef test_methods[] = {
{"err_restore", err_restore, METH_VARARGS},
{"err_writeunraisable", err_writeunraisable, METH_VARARGS},
_TESTCAPI_ERR_SET_RAISED_METHODDEF
_TESTCAPI_EXCEPTION_PRINT_METHODDEF
_TESTCAPI_FATAL_ERROR_METHODDEF
Expand Down