mirror of
https://github.com/python/cpython.git
synced 2025-11-02 03:01:58 +00:00
gh-108082: C API: Add tests for PyErr_WriteUnraisable() (GH-111455)
Also document the behavior when called with NULL.
This commit is contained in:
parent
8eaa206fec
commit
bca3305429
3 changed files with 70 additions and 0 deletions
|
|
@ -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``.
|
||||
|
||||
.. versionchanged:: 3.8
|
||||
Use :func:`sys.unraisablehook`.
|
||||
|
||||
|
||||
.. c:function:: void PyErr_DisplayException(PyObject *exc)
|
||||
|
||||
Print the standard traceback display of ``exc`` to ``sys.stderr``, including
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ _testcapi = import_helper.import_module('_testcapi')
|
|||
|
||||
NULL = None
|
||||
|
||||
class CustomError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Test_Exceptions(unittest.TestCase):
|
||||
|
||||
def test_exception(self):
|
||||
|
|
@ -270,6 +274,47 @@ class Test_ErrSetAndRestore(unittest.TestCase):
|
|||
(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):
|
||||
|
||||
|
|
|
|||
|
|
@ -303,6 +303,22 @@ _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) {
|
||||
PyErr_SetRaisedException(Py_NewRef(exc));
|
||||
}
|
||||
PyErr_WriteUnraisable(obj);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
_testcapi.unstable_exc_prep_reraise_star
|
||||
orig: object
|
||||
|
|
@ -347,6 +363,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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue