mirror of https://github.com/python/cpython
gh-90501: Add PyErr_GetHandledException and PyErr_SetHandledException (GH-30531)
This commit is contained in:
parent
c06a4ffe81
commit
5d421d7342
|
@ -460,12 +460,46 @@ Querying the error indicator
|
|||
}
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyErr_GetHandledException(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 ``NULL``.
|
||||
Does not modify the interpreter's exception state.
|
||||
|
||||
.. 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_SetHandledException` to restore or
|
||||
clear the exception state.
|
||||
|
||||
.. versionadded:: 3.11
|
||||
|
||||
.. c:function:: void PyErr_SetHandledException(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.
|
||||
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_GetHandledException` to get the exception
|
||||
state.
|
||||
|
||||
.. versionadded:: 3.11
|
||||
|
||||
.. c:function:: void PyErr_GetExcInfo(PyObject **ptype, PyObject **pvalue, PyObject **ptraceback)
|
||||
|
||||
Retrieve the exception info, as known from ``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.
|
||||
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. This function is kept for backwards compatibility. Prefer using
|
||||
:c:func:`PyErr_GetHandledException`.
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -483,6 +517,8 @@ Querying the error indicator
|
|||
to an exception that was *already caught*, not to an exception that was
|
||||
freshly raised. This function steals the references of the arguments.
|
||||
To clear the exception state, pass ``NULL`` for all three arguments.
|
||||
This function is kept for backwards compatibility. Prefer using
|
||||
:c:func:`PyErr_SetHandledException`.
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
|
@ -137,6 +137,7 @@ function,PyErr_Fetch,3.2,,
|
|||
function,PyErr_Format,3.2,,
|
||||
function,PyErr_FormatV,3.5,,
|
||||
function,PyErr_GetExcInfo,3.7,,
|
||||
function,PyErr_GetHandledException,3.11,,
|
||||
function,PyErr_GivenExceptionMatches,3.2,,
|
||||
function,PyErr_NewException,3.2,,
|
||||
function,PyErr_NewExceptionWithDoc,3.2,,
|
||||
|
@ -159,6 +160,7 @@ function,PyErr_SetFromErrnoWithFilenameObject,3.2,,
|
|||
function,PyErr_SetFromErrnoWithFilenameObjects,3.7,,
|
||||
function,PyErr_SetFromWindowsErr,3.7,on Windows,
|
||||
function,PyErr_SetFromWindowsErrWithFilename,3.7,on Windows,
|
||||
function,PyErr_SetHandledException,3.11,,
|
||||
function,PyErr_SetImportError,3.7,,
|
||||
function,PyErr_SetImportErrorSubclass,3.6,,
|
||||
function,PyErr_SetInterrupt,3.2,,
|
||||
|
|
|
@ -381,19 +381,12 @@ always available.
|
|||
|
||||
.. function:: exception()
|
||||
|
||||
This function returns the exception instance that is currently being
|
||||
handled. This exception is specific both to the current thread and
|
||||
to the current stack frame. If the current stack frame is not handling
|
||||
an exception, the exception is taken from the calling stack frame, or its
|
||||
caller, and so on until a stack frame is found that is handling an
|
||||
exception. Here, "handling an exception" is defined as "executing an
|
||||
except clause." For any stack frame, only the exception being currently
|
||||
handled is accessible.
|
||||
This function, when called while an exception handler is executing (such as
|
||||
an ``except`` or ``except*`` clause), returns the exception instance that
|
||||
was caught by this handler. When exception handlers are nested within one
|
||||
another, only the exception handled by the innermost handler is accessible.
|
||||
|
||||
.. index:: object: traceback
|
||||
|
||||
If no exception is being handled anywhere on the stack, ``None`` is
|
||||
returned.
|
||||
If no exception handler is executing, this function returns ``None``.
|
||||
|
||||
.. versionadded:: 3.11
|
||||
|
||||
|
|
|
@ -1161,6 +1161,14 @@ New Features
|
|||
:c:func:`PyFrame_GetBuiltins`, :c:func:`PyFrame_GetGenerator`,
|
||||
:c:func:`PyFrame_GetGlobals`, :c:func:`PyFrame_GetLasti`.
|
||||
|
||||
* Added two new functions to get and set the active exception instance:
|
||||
:c:func:`PyErr_GetHandledException` and :c:func:`PyErr_SetHandledException`.
|
||||
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
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -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_GetHandledException(PyThreadState *);
|
||||
PyAPI_FUNC(void) _PyErr_SetHandledException(PyThreadState *, PyObject *);
|
||||
PyAPI_FUNC(void) _PyErr_GetExcInfo(PyThreadState *, PyObject **, PyObject **, PyObject **);
|
||||
|
||||
/* Context manipulation (PEP 3134) */
|
||||
|
|
|
@ -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_GetHandledException(void);
|
||||
PyAPI_FUNC(void) PyErr_SetHandledException(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 *);
|
||||
|
|
|
@ -88,6 +88,28 @@ class CAPITest(unittest.TestCase):
|
|||
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")
|
||||
|
|
|
@ -152,6 +152,7 @@ SYMBOL_NAMES = (
|
|||
"PyErr_Format",
|
||||
"PyErr_FormatV",
|
||||
"PyErr_GetExcInfo",
|
||||
"PyErr_GetHandledException",
|
||||
"PyErr_GivenExceptionMatches",
|
||||
"PyErr_NewException",
|
||||
"PyErr_NewExceptionWithDoc",
|
||||
|
@ -168,6 +169,7 @@ SYMBOL_NAMES = (
|
|||
"PyErr_SetFromErrnoWithFilename",
|
||||
"PyErr_SetFromErrnoWithFilenameObject",
|
||||
"PyErr_SetFromErrnoWithFilenameObjects",
|
||||
"PyErr_SetHandledException",
|
||||
"PyErr_SetImportError",
|
||||
"PyErr_SetImportErrorSubclass",
|
||||
"PyErr_SetInterrupt",
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
Added :c:func:`PyErr_GetHandledException` and
|
||||
:c:func:`PyErr_SetHandledException` as simpler alternatives to
|
||||
:c:func:`PyErr_GetExcInfo` and :c:func:`PyErr_SetExcInfo`.
|
||||
|
||||
They are included in the stable ABI.
|
|
@ -2253,3 +2253,8 @@ function PyMemoryView_FromBuffer
|
|||
|
||||
data Py_Version
|
||||
added 3.11
|
||||
function PyErr_GetHandledException
|
||||
added 3.11
|
||||
function PyErr_SetHandledException
|
||||
added 3.11
|
||||
|
||||
|
|
|
@ -2562,6 +2562,16 @@ set_errno(PyObject *self, PyObject *args)
|
|||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
test_set_exception(PyObject *self, PyObject *new_exc)
|
||||
{
|
||||
PyObject *exc = PyErr_GetHandledException();
|
||||
assert(PyExceptionInstance_Check(exc) || exc == NULL);
|
||||
|
||||
PyErr_SetHandledException(new_exc);
|
||||
return exc;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
test_set_exc_info(PyObject *self, PyObject *args)
|
||||
{
|
||||
|
@ -6068,6 +6078,7 @@ static PyMethodDef TestMethods[] = {
|
|||
#endif
|
||||
{"traceback_print", traceback_print, METH_VARARGS},
|
||||
{"exception_print", exception_print, METH_VARARGS},
|
||||
{"set_exception", test_set_exception, METH_O},
|
||||
{"set_exc_info", test_set_exc_info, METH_VARARGS},
|
||||
{"argparsing", argparsing, METH_VARARGS},
|
||||
{"code_newempty", code_newempty, METH_VARARGS},
|
||||
|
|
|
@ -196,6 +196,7 @@ EXPORT_FUNC(PyErr_Fetch)
|
|||
EXPORT_FUNC(PyErr_Format)
|
||||
EXPORT_FUNC(PyErr_FormatV)
|
||||
EXPORT_FUNC(PyErr_GetExcInfo)
|
||||
EXPORT_FUNC(PyErr_GetHandledException)
|
||||
EXPORT_FUNC(PyErr_GivenExceptionMatches)
|
||||
EXPORT_FUNC(PyErr_NewException)
|
||||
EXPORT_FUNC(PyErr_NewExceptionWithDoc)
|
||||
|
@ -218,6 +219,7 @@ EXPORT_FUNC(PyErr_SetFromErrnoWithFilenameObject)
|
|||
EXPORT_FUNC(PyErr_SetFromErrnoWithFilenameObjects)
|
||||
EXPORT_FUNC(PyErr_SetFromWindowsErr)
|
||||
EXPORT_FUNC(PyErr_SetFromWindowsErrWithFilename)
|
||||
EXPORT_FUNC(PyErr_SetHandledException)
|
||||
EXPORT_FUNC(PyErr_SetImportError)
|
||||
EXPORT_FUNC(PyErr_SetImportErrorSubclass)
|
||||
EXPORT_FUNC(PyErr_SetInterrupt)
|
||||
|
|
|
@ -499,6 +499,38 @@ _PyErr_GetExcInfo(PyThreadState *tstate,
|
|||
Py_XINCREF(*p_traceback);
|
||||
}
|
||||
|
||||
PyObject*
|
||||
_PyErr_GetHandledException(PyThreadState *tstate)
|
||||
{
|
||||
_PyErr_StackItem *exc_info = _PyErr_GetTopmostException(tstate);
|
||||
PyObject *exc = exc_info->exc_value;
|
||||
if (exc == NULL || exc == Py_None) {
|
||||
return NULL;
|
||||
}
|
||||
return Py_NewRef(exc);
|
||||
}
|
||||
|
||||
PyObject*
|
||||
PyErr_GetHandledException(void)
|
||||
{
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
return _PyErr_GetHandledException(tstate);
|
||||
}
|
||||
|
||||
void
|
||||
_PyErr_SetHandledException(PyThreadState *tstate, PyObject *exc)
|
||||
{
|
||||
PyObject *oldexc = tstate->exc_info->exc_value;
|
||||
tstate->exc_info->exc_value = Py_XNewRef(exc);
|
||||
Py_XDECREF(oldexc);
|
||||
}
|
||||
|
||||
void
|
||||
PyErr_SetHandledException(PyObject *exc)
|
||||
{
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
_PyErr_SetHandledException(tstate, exc);
|
||||
}
|
||||
|
||||
void
|
||||
PyErr_GetExcInfo(PyObject **p_type, PyObject **p_value, PyObject **p_traceback)
|
||||
|
@ -510,17 +542,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_SetHandledException(value);
|
||||
/* These args are no longer used, but we still need to steal a ref */
|
||||
Py_XDECREF(type);
|
||||
Py_XDECREF(traceback);
|
||||
|
||||
Py_XDECREF(oldvalue);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue