diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index a5a93d0ebbf284..7bfeca5958cc42 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -460,12 +460,46 @@ Querying the error indicator } -.. c:function:: void PyErr_GetExcInfo(PyObject **ptype, PyObject **pvalue, PyObject **ptraceback) +.. 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 - Retrieve the exception info, as known from ``sys.exc_info()``. This refers +.. 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. 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_GetHandledException` 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. 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:: diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 849a2cfd51f24a..5387d0bf983fa2 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -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,, diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 126da31b5bd32d..2a8b532b592e87 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -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 diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index d803801f273c55..93b24e4eadb583 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -1145,6 +1145,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 ---------------------- diff --git a/Include/cpython/pyerrors.h b/Include/cpython/pyerrors.h index 5281fde1f1a54c..08630cce8ac90a 100644 --- a/Include/cpython/pyerrors.h +++ b/Include/cpython/pyerrors.h @@ -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) */ diff --git a/Include/pyerrors.h b/Include/pyerrors.h index 77d791427d4928..34e3de3328f410 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -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 *); diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 40e4774c6b8ed2..eb0edbf5a3a123 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -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") diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index efd3b1b7cd2d24..0656ff5581be5d 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -152,6 +152,7 @@ def test_available_symbols(self): "PyErr_Format", "PyErr_FormatV", "PyErr_GetExcInfo", + "PyErr_GetHandledException", "PyErr_GivenExceptionMatches", "PyErr_NewException", "PyErr_NewExceptionWithDoc", @@ -168,6 +169,7 @@ def test_available_symbols(self): "PyErr_SetFromErrnoWithFilename", "PyErr_SetFromErrnoWithFilenameObject", "PyErr_SetFromErrnoWithFilenameObjects", + "PyErr_SetHandledException", "PyErr_SetImportError", "PyErr_SetImportErrorSubclass", "PyErr_SetInterrupt", diff --git a/Misc/NEWS.d/next/C API/2022-01-11-12-52-37.bpo-46343.JQJWhZ.rst b/Misc/NEWS.d/next/C API/2022-01-11-12-52-37.bpo-46343.JQJWhZ.rst new file mode 100644 index 00000000000000..1ac8da853c8796 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2022-01-11-12-52-37.bpo-46343.JQJWhZ.rst @@ -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. diff --git a/Misc/stable_abi.txt b/Misc/stable_abi.txt index 4864bf319a76f5..66777a62c43018 100644 --- a/Misc/stable_abi.txt +++ b/Misc/stable_abi.txt @@ -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 + diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 13dd29427aa2ca..71683abebb2316 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -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}, diff --git a/PC/python3dll.c b/PC/python3dll.c index 70f11dc1905547..0aee2aec847261 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -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) diff --git a/Python/errors.c b/Python/errors.c index e170c9dff2dbbc..ce7785855b8e54 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -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); } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy