gh-90501: Add PyErr_GetHandledException and PyErr_SetHandledException… · wrongnull/cpython@5d421d7 · GitHub
Skip to content

Commit 5d421d7

Browse files
authored
pythongh-90501: Add PyErr_GetHandledException and PyErr_SetHandledException (pythonGH-30531)
1 parent c06a4ff commit 5d421d7

13 files changed

Lines changed: 141 additions & 24 deletions

File tree

Doc/c-api/exceptions.rst

Lines changed: 40 additions & 4 deletions

Doc/data/stable_abi.dat

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Doc/library/sys.rst

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -381,19 +381,12 @@ always available.
381381

382382
.. function:: exception()
383383

384-
This function returns the exception instance that is currently being
385-
handled. This exception is specific both to the current thread and
386-
to the current stack frame. If the current stack frame is not handling
387-
an exception, the exception is taken from the calling stack frame, or its
388-
caller, and so on until a stack frame is found that is handling an
389-
exception. Here, "handling an exception" is defined as "executing an
390-
except clause." For any stack frame, only the exception being currently
391-
handled is accessible.
384+
This function, when called while an exception handler is executing (such as
385+
an ``except`` or ``except*`` clause), returns the exception instance that
386+
was caught by this handler. When exception handlers are nested within one
387+
another, only the exception handled by the innermost handler is accessible.
392388

393-
.. index:: object: traceback
394-
395-
If no exception is being handled anywhere on the stack, ``None`` is
396-
returned.
389+
If no exception handler is executing, this function returns ``None``.
397390

398391
.. versionadded:: 3.11
399392

Doc/whatsnew/3.11.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,6 +1161,14 @@ New Features
11611161
:c:func:`PyFrame_GetBuiltins`, :c:func:`PyFrame_GetGenerator`,
11621162
:c:func:`PyFrame_GetGlobals`, :c:func:`PyFrame_GetLasti`.
11631163

1164+
* Added two new functions to get and set the active exception instance:
1165+
:c:func:`PyErr_GetHandledException` and :c:func:`PyErr_SetHandledException`.
1166+
These are alternatives to :c:func:`PyErr_SetExcInfo()` and
1167+
:c:func:`PyErr_GetExcInfo()` which work with the legacy 3-tuple
1168+
representation of exceptions.
1169+
(Contributed by Irit Katriel in :issue:`46343`.)
1170+
1171+
11641172
Porting to Python 3.11
11651173
----------------------
11661174

Include/cpython/pyerrors.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ typedef PyOSErrorObject PyWindowsErrorObject;
9191

9292
PyAPI_FUNC(void) _PyErr_SetKeyError(PyObject *);
9393
PyAPI_FUNC(_PyErr_StackItem*) _PyErr_GetTopmostException(PyThreadState *tstate);
94+
PyAPI_FUNC(PyObject*) _PyErr_GetHandledException(PyThreadState *);
95+
PyAPI_FUNC(void) _PyErr_SetHandledException(PyThreadState *, PyObject *);
9496
PyAPI_FUNC(void) _PyErr_GetExcInfo(PyThreadState *, PyObject **, PyObject **, PyObject **);
9597

9698
/* Context manipulation (PEP 3134) */

Include/pyerrors.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ PyAPI_FUNC(PyObject *) PyErr_Occurred(void);
1818
PyAPI_FUNC(void) PyErr_Clear(void);
1919
PyAPI_FUNC(void) PyErr_Fetch(PyObject **, PyObject **, PyObject **);
2020
PyAPI_FUNC(void) PyErr_Restore(PyObject *, PyObject *, PyObject *);
21+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030b0000
22+
PyAPI_FUNC(PyObject*) PyErr_GetHandledException(void);
23+
PyAPI_FUNC(void) PyErr_SetHandledException(PyObject *);
24+
#endif
2125
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000
2226
PyAPI_FUNC(void) PyErr_GetExcInfo(PyObject **, PyObject **, PyObject **);
2327
PyAPI_FUNC(void) PyErr_SetExcInfo(PyObject *, PyObject *, PyObject *);

Lib/test/test_capi.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,28 @@ def test_no_FatalError_infinite_loop(self):
8888
def test_memoryview_from_NULL_pointer(self):
8989
self.assertRaises(ValueError, _testcapi.make_memoryview_from_NULL_pointer)
9090

91+
def test_exception(self):
92+
raised_exception = ValueError("5")
93+
new_exc = TypeError("TEST")
94+
try:
95+
raise raised_exception
96+
except ValueError as e:
97+
orig_sys_exception = sys.exception()
98+
orig_exception = _testcapi.set_exception(new_exc)
99+
new_sys_exception = sys.exception()
100+
new_exception = _testcapi.set_exception(orig_exception)
101+
reset_sys_exception = sys.exception()
102+
103+
self.assertEqual(orig_exception, e)
104+
105+
self.assertEqual(orig_exception, raised_exception)
106+
self.assertEqual(orig_sys_exception, orig_exception)
107+
self.assertEqual(reset_sys_exception, orig_exception)
108+
self.assertEqual(new_exception, new_exc)
109+
self.assertEqual(new_sys_exception, new_exception)
110+
else:
111+
self.fail("Exception not raised")
112+
91113
def test_exc_info(self):
92114
raised_exception = ValueError("5")
93115
new_exc = TypeError("TEST")

Lib/test/test_stable_abi_ctypes.py

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Added :c:func:`PyErr_GetHandledException` and
2+
:c:func:`PyErr_SetHandledException` as simpler alternatives to
3+
:c:func:`PyErr_GetExcInfo` and :c:func:`PyErr_SetExcInfo`.
4+
5+
They are included in the stable ABI.

Misc/stable_abi.txt

Lines changed: 5 additions & 0 deletions

0 commit comments

Comments
 (0)