Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
2b4d518
bpo-46343: Add PyErr_GetActiveException and PyErr_SetActiveException
iritkatriel Jan 11, 2022
87e6087
GetActiveException returns exception as its return value
iritkatriel Jan 11, 2022
ee4d98e
fix test
iritkatriel Jan 11, 2022
ebd9278
use sys.exception instead of sys.exc_info
iritkatriel Jan 13, 2022
5631d32
updated whatsnew
iritkatriel Jan 13, 2022
20fa421
regen-limited-abi
iritkatriel Jan 13, 2022
3a7ab17
[1] Do not steal reference. [2] () --> (void)
iritkatriel Jan 13, 2022
dbfd9d4
Use the new Py_(X)NewRef in a couple of places. Use self.fail() in test.
iritkatriel Jan 17, 2022
a412e4b
add :func: markup in doc
iritkatriel Jan 17, 2022
bcdf599
split out _PyErr_SetActiveException(tstate, exc)
iritkatriel Feb 1, 2022
c63eabf
Merge remote-tracking branch 'upstream/main' into bpo-46343-GetSetExc…
iritkatriel Mar 15, 2022
18eb35c
read --> get
iritkatriel Mar 23, 2022
87981ab
fix limited api version
iritkatriel Mar 23, 2022
9555597
PyErr_GetActiveException always returns NULL if no exception. Fix ref…
iritkatriel Mar 23, 2022
e0ee504
doc changes following review by Victor
iritkatriel Mar 23, 2022
e6a3a7e
renamed Get/SetActiveException --> Get/SetHandledException
iritkatriel Apr 13, 2022
261464c
Merge remote-tracking branch 'upstream/main' into bpo-46343-GetSetExc…
iritkatriel Apr 13, 2022
0355cc9
add new functions to Doc/data/stable_abi.dat
iritkatriel Apr 13, 2022
9382215
alphabetical
iritkatriel Apr 13, 2022
1cc3a4b
make regen-limited-abi
iritkatriel Apr 13, 2022
ffa870b
clearer doc
iritkatriel Apr 15, 2022
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
41 changes: 37 additions & 4 deletions Doc/c-api/exceptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -460,12 +460,45 @@ Querying the error indicator
}


.. c:function:: void PyErr_GetExcInfo(PyObject **ptype, PyObject **pvalue, PyObject **ptraceback)
.. c:function:: PyObject* PyErr_GetActiveException(void)

Retrieve the active exception instance, as would be returned by :func:`sys.exception`.
This refers to an exception that was *already caught*, not to an exception that was
freshly raised. Returns a new reference to the exception or to ``Py_None``.
Comment thread
iritkatriel marked this conversation as resolved.
Outdated
Does not modify the exception state.
Comment thread
iritkatriel marked this conversation as resolved.
Outdated

.. note::

This function is not normally used by code that wants to handle exceptions.
Rather, it can be used when code needs to save and restore the exception
state temporarily. Use :c:func:`PyErr_SetActiveException` to restore or
clear the exception state.

.. versionadded:: 3.11

Retrieve the exception info, as known from ``sys.exc_info()``. This refers
.. c:function:: void PyErr_SetActiveException(PyObject *exc)

Set the active exception, as known from ``sys.exception()``. This refers
to an exception that was *already caught*, not to an exception that was
freshly raised. Returns new references for the three objects, any of which
may be ``NULL``. Does not modify the exception info state.
freshly raised.
To clear the exception state, pass ``NULL``.

.. note::

This function is not normally used by code that wants to handle exceptions.
Rather, it can be used when code needs to save and restore the exception
state temporarily. Use :c:func:`PyErr_GetActiveException` to get the exception
state.

.. versionadded:: 3.11

.. c:function:: void PyErr_GetExcInfo(PyObject **ptype, PyObject **pvalue, PyObject **ptraceback)

Retrieve the old-style representation of the exception info, as known from
:func:`sys.exc_info`. This refers to an exception that was *already caught*,
not to an exception that was freshly raised. Returns new references for the
three objects, any of which may be ``NULL``. Does not modify the exception
info state.

.. note::

Expand Down
2 changes: 2 additions & 0 deletions Doc/data/stable_abi.dat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions Doc/whatsnew/3.11.rst
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,13 @@ New Features
:c:func:`PyFloat_Unpack8`.
(Contributed by Victor Stinner in :issue:`46906`.)

* Added two new functions to get and set the active exception instance:
:c:func:`PyErr_GetActiveException` and :c:func:`PyErr_SetActiveException`.
These are alternatives to :c:func:`PyErr_SetExcInfo()` and
:c:func:`PyErr_GetExcInfo()` which work with the legacy 3-tuple
representation of exceptions.
(Contributed by Irit Katriel in :issue:`46343`.)


Porting to Python 3.11
----------------------
Expand Down
2 changes: 2 additions & 0 deletions Include/cpython/pyerrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ typedef PyOSErrorObject PyWindowsErrorObject;

PyAPI_FUNC(void) _PyErr_SetKeyError(PyObject *);
PyAPI_FUNC(_PyErr_StackItem*) _PyErr_GetTopmostException(PyThreadState *tstate);
PyAPI_FUNC(PyObject*) _PyErr_GetActiveException(PyThreadState *);
PyAPI_FUNC(void) _PyErr_SetActiveException(PyThreadState *, PyObject *);
PyAPI_FUNC(void) _PyErr_GetExcInfo(PyThreadState *, PyObject **, PyObject **, PyObject **);

/* Context manipulation (PEP 3134) */
Expand Down
4 changes: 4 additions & 0 deletions Include/pyerrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ PyAPI_FUNC(PyObject *) PyErr_Occurred(void);
PyAPI_FUNC(void) PyErr_Clear(void);
PyAPI_FUNC(void) PyErr_Fetch(PyObject **, PyObject **, PyObject **);
PyAPI_FUNC(void) PyErr_Restore(PyObject *, PyObject *, PyObject *);
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030b0000
PyAPI_FUNC(PyObject*) PyErr_GetActiveException(void);
Comment thread
iritkatriel marked this conversation as resolved.
Outdated
PyAPI_FUNC(void) PyErr_SetActiveException(PyObject *);
#endif
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000
PyAPI_FUNC(void) PyErr_GetExcInfo(PyObject **, PyObject **, PyObject **);
PyAPI_FUNC(void) PyErr_SetExcInfo(PyObject *, PyObject *, PyObject *);
Expand Down
22 changes: 22 additions & 0 deletions Lib/test/test_capi.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,28 @@ def test_no_FatalError_infinite_loop(self):
def test_memoryview_from_NULL_pointer(self):
self.assertRaises(ValueError, _testcapi.make_memoryview_from_NULL_pointer)

def test_exception(self):
raised_exception = ValueError("5")
new_exc = TypeError("TEST")
try:
raise raised_exception
except ValueError as e:
orig_sys_exception = sys.exception()
orig_exception = _testcapi.set_exception(new_exc)
new_sys_exception = sys.exception()
new_exception = _testcapi.set_exception(orig_exception)
reset_sys_exception = sys.exception()

self.assertEqual(orig_exception, e)

self.assertEqual(orig_exception, raised_exception)
self.assertEqual(orig_sys_exception, orig_exception)
self.assertEqual(reset_sys_exception, orig_exception)
self.assertEqual(new_exception, new_exc)
self.assertEqual(new_sys_exception, new_exception)
else:
self.fail("Exception not raised")

def test_exc_info(self):
raised_exception = ValueError("5")
new_exc = TypeError("TEST")
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_stable_abi_ctypes.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Added :c:func:`PyErr_GetActiveException` and
:c:func:`PyErr_SetActiveException` as simpler alternatives to
:c:func:`PyErr_GetExcInfo` and :c:func:`PyErr_SetExcInfo`.

They are included in the stable ABI.
5 changes: 5 additions & 0 deletions Misc/stable_abi.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2224,3 +2224,8 @@ function PyMemoryView_FromBuffer

data Py_Version
added 3.11
function PyErr_GetActiveException
added 3.11
function PyErr_SetActiveException
added 3.11

16 changes: 16 additions & 0 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -2562,6 +2562,21 @@ set_errno(PyObject *self, PyObject *args)
Py_RETURN_NONE;
}

static PyObject *
test_set_exception(PyObject *self, PyObject *args)
Comment thread
iritkatriel marked this conversation as resolved.
Outdated
{
PyObject *new_exc;
if (!PyArg_ParseTuple(args, "O:test_set_exception",
&new_exc)) {
return NULL;
}

PyObject *exc = PyErr_GetActiveException();

PyErr_SetActiveException(Py_NewRef(new_exc));
return exc;
}

static PyObject *
test_set_exc_info(PyObject *self, PyObject *args)
{
Expand Down Expand Up @@ -6013,6 +6028,7 @@ static PyMethodDef TestMethods[] = {
#endif
{"traceback_print", traceback_print, METH_VARARGS},
{"exception_print", exception_print, METH_VARARGS},
{"set_exception", test_set_exception, METH_VARARGS},
{"set_exc_info", test_set_exc_info, METH_VARARGS},
{"argparsing", argparsing, METH_VARARGS},
{"code_newempty", code_newempty, METH_VARARGS},
Expand Down
2 changes: 2 additions & 0 deletions PC/python3dll.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 30 additions & 8 deletions Python/errors.c
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,35 @@ _PyErr_GetExcInfo(PyThreadState *tstate,
Py_XINCREF(*p_traceback);
}

PyObject*
_PyErr_GetActiveException(PyThreadState *tstate)
{
_PyErr_StackItem *exc_info = _PyErr_GetTopmostException(tstate);
PyObject *exc = exc_info->exc_value;
return Py_XNewRef(exc);
}

PyObject*
PyErr_GetActiveException(void)
{
PyThreadState *tstate = _PyThreadState_GET();
return _PyErr_GetActiveException(tstate);
}

void
_PyErr_SetActiveException(PyThreadState *tstate, PyObject *exc)
{
PyObject *oldexc = tstate->exc_info->exc_value;
tstate->exc_info->exc_value = Py_XNewRef(exc);
Py_XDECREF(oldexc);
}

void
PyErr_SetActiveException(PyObject *exc)
{
PyThreadState *tstate = _PyThreadState_GET();
_PyErr_SetActiveException(tstate, exc);
}

void
PyErr_GetExcInfo(PyObject **p_type, PyObject **p_value, PyObject **p_traceback)
Expand All @@ -510,17 +539,10 @@ PyErr_GetExcInfo(PyObject **p_type, PyObject **p_value, PyObject **p_traceback)
void
PyErr_SetExcInfo(PyObject *type, PyObject *value, PyObject *traceback)
{
PyThreadState *tstate = _PyThreadState_GET();

PyObject *oldvalue = tstate->exc_info->exc_value;

tstate->exc_info->exc_value = value;

PyErr_SetActiveException(value);
/* These args are no longer used, but we still need to steal a ref */
Py_XDECREF(type);
Py_XDECREF(traceback);

Py_XDECREF(oldvalue);
}


Expand Down