From 6d969f3f860dc68adf926362b986fbdddd4139e7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 25 Sep 2023 00:48:18 +0200 Subject: [PATCH] gh-109047: Add PythonFinalizationError exception Add PythonFinalizationError. This exception derived from RuntimeError is raised when an operation is blocked during the Python finalization. The following functions now raise PythonFinalizationError, instead of RuntimeError: * _thread.start_new_thread() * os.fork() * os.fork1() * os.forkpty() Morever, _winapi.Overlapped finalizer now logs an unraisable PythonFinalizationError, instead of an unraisable RuntimeError. --- Doc/library/exceptions.rst | 18 ++++++++++++++++++ Doc/library/sys.rst | 2 ++ Doc/whatsnew/3.13.rst | 6 ++++++ Include/cpython/pyerrors.h | 2 ++ Lib/test/exception_hierarchy.txt | 1 + Lib/test/test_pickle.py | 1 + ...3-09-25-00-48-13.gh-issue-109047.BzwMlJ.rst | 3 +++ Modules/_posixsubprocess.c | 2 +- Modules/_threadmodule.c | 2 +- Modules/_winapi.c | 2 +- Modules/posixmodule.c | 6 +++--- Objects/exceptions.c | 5 +++++ Tools/c-analyzer/cpython/globals-to-fix.tsv | 2 ++ 13 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-09-25-00-48-13.gh-issue-109047.BzwMlJ.rst diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index fae0cf621323c8..6935ab69726abc 100644 --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -392,6 +392,24 @@ The following exceptions are the exceptions that are usually raised. handling in C, most floating point operations are not checked. +.. exception:: PythonFinalizationError + + This exception is derived from :exc:`RuntimeError`. It is raised when + an operations is blocked during the :term:`Python finalization `. + + Examples of operations which can be blocked with a + :exc:`PythonFinalizationError` during the Python finalization: + + * create a new Python thread; + * :func:`os.fork`. + + See also the :func:`sys.is_finalizing` function. + + .. versionadded:: 3.13 + Previously, a plain :exc:`RuntimeError` was raised. + + .. exception:: RecursionError This exception is derived from :exc:`RuntimeError`. It is raised when the diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index ef818a7da016de..cd0e0214d05d5e 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -1185,6 +1185,8 @@ always available. Return :const:`True` if the main Python interpreter is :term:`shutting down `. Return :const:`False` otherwise. + See also the :exc:`PythonFinalizationError` exception. + .. versionadded:: 3.5 .. data:: last_exc diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index c9e6ca8bf88866..1ef12e1dc38877 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -91,6 +91,12 @@ Other Language Changes of the ``optimize`` argument. (Contributed by Irit Katriel in :gh:`108113`). +* Add :exc:`PythonFinalizationError`. This exception derived from + :exc:`RuntimeError` is raised when an operation is blocked during + the :term:`Python finalization `. + (Contributed by Victor Stinner in :gh:`109047`.) + + New Modules =========== diff --git a/Include/cpython/pyerrors.h b/Include/cpython/pyerrors.h index da96eec4b35aab..47262b951dca0a 100644 --- a/Include/cpython/pyerrors.h +++ b/Include/cpython/pyerrors.h @@ -120,4 +120,6 @@ PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalErrorFunc( const char *func, const char *message); +PyAPI_DATA(PyObject *) PyExc_PythonFinalizationError; + #define Py_FatalError(message) _Py_FatalErrorFunc(__func__, (message)) diff --git a/Lib/test/exception_hierarchy.txt b/Lib/test/exception_hierarchy.txt index 1eca123be0fecb..f2649aa2d41fef 100644 --- a/Lib/test/exception_hierarchy.txt +++ b/Lib/test/exception_hierarchy.txt @@ -40,6 +40,7 @@ BaseException ├── ReferenceError ├── RuntimeError │ ├── NotImplementedError + │ ├── PythonFinalizationError │ └── RecursionError ├── StopAsyncIteration ├── StopIteration diff --git a/Lib/test/test_pickle.py b/Lib/test/test_pickle.py index 1a55da39bdc58d..83b4ac19be75c7 100644 --- a/Lib/test/test_pickle.py +++ b/Lib/test/test_pickle.py @@ -539,6 +539,7 @@ def test_exceptions(self): if exc in (BlockingIOError, ResourceWarning, StopAsyncIteration, + PythonFinalizationError, RecursionError, EncodingWarning, BaseExceptionGroup, diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-25-00-48-13.gh-issue-109047.BzwMlJ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-25-00-48-13.gh-issue-109047.BzwMlJ.rst new file mode 100644 index 00000000000000..644617b3a4f068 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-09-25-00-48-13.gh-issue-109047.BzwMlJ.rst @@ -0,0 +1,3 @@ +Add :exc:`PythonFinalizationError`. This exception derived from +:exc:`RuntimeError` is raised when an operation is blocked during the +:term:`Python finalization `. Patch by Victor Stinner. diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index 2898eedc3e3a8f..ef77d5dd544fb1 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -1030,7 +1030,7 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args, PyInterpreterState *interp = _PyInterpreterState_GET(); if ((preexec_fn != Py_None) && interp->finalizing) { - PyErr_SetString(PyExc_RuntimeError, + PyErr_SetString(PyExc_PythonFinalizationError, "preexec_fn not supported at interpreter shutdown"); return NULL; } diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 9c915488f6e0de..955423e2261ad9 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1179,7 +1179,7 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) return NULL; } if (interp->finalizing) { - PyErr_SetString(PyExc_RuntimeError, + PyErr_SetString(PyExc_PythonFinalizationError, "can't create new thread at interpreter shutdown"); return NULL; } diff --git a/Modules/_winapi.c b/Modules/_winapi.c index eec33499b983fe..a1cc54089d2904 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -139,7 +139,7 @@ overlapped_dealloc(OverlappedObject *self) { /* The operation is still pending -- give a warning. This will probably only happen on Windows XP. */ - PyErr_SetString(PyExc_RuntimeError, + PyErr_SetString(PyExc_PythonFinalizationError, "I/O operations still in flight while destroying " "Overlapped object, the process may crash"); PyErr_WriteUnraisable(NULL); diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 096aa043514c85..9728795efb72c4 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7665,7 +7665,7 @@ os_fork1_impl(PyObject *module) PyInterpreterState *interp = _PyInterpreterState_GET(); if (interp->finalizing) { - PyErr_SetString(PyExc_RuntimeError, + PyErr_SetString(PyExc_PythonFinalizationError, "can't fork at interpreter shutdown"); return NULL; } @@ -7709,7 +7709,7 @@ os_fork_impl(PyObject *module) pid_t pid; PyInterpreterState *interp = _PyInterpreterState_GET(); if (interp->finalizing) { - PyErr_SetString(PyExc_RuntimeError, + PyErr_SetString(PyExc_PythonFinalizationError, "can't fork at interpreter shutdown"); return NULL; } @@ -8393,7 +8393,7 @@ os_forkpty_impl(PyObject *module) PyInterpreterState *interp = _PyInterpreterState_GET(); if (interp->finalizing) { - PyErr_SetString(PyExc_RuntimeError, + PyErr_SetString(PyExc_PythonFinalizationError, "can't fork at interpreter shutdown"); return NULL; } diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 62a44234b34047..19f5d28a568217 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -2190,6 +2190,10 @@ SimpleExtendsException(PyExc_Exception, RuntimeError, SimpleExtendsException(PyExc_RuntimeError, RecursionError, "Recursion limit exceeded."); +// PythonFinalizationError extends RuntimeError +SimpleExtendsException(PyExc_RuntimeError, PythonFinalizationError, + "Operation blocked during Python finalization."); + /* * NotImplementedError extends RuntimeError */ @@ -3652,6 +3656,7 @@ static struct static_exception static_exceptions[] = { ITEM(KeyError), // base: LookupError(Exception) ITEM(ModuleNotFoundError), // base: ImportError(Exception) ITEM(NotImplementedError), // base: RuntimeError(Exception) + ITEM(PythonFinalizationError), // base: RuntimeError(Exception) ITEM(RecursionError), // base: RuntimeError(Exception) ITEM(UnboundLocalError), // base: NameError(Exception) ITEM(UnicodeError), // base: ValueError(Exception) diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index bb85fba895bc25..df10954f854fd9 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -189,6 +189,7 @@ Objects/exceptions.c - _PyExc_ProcessLookupError - Objects/exceptions.c - _PyExc_TimeoutError - Objects/exceptions.c - _PyExc_EOFError - Objects/exceptions.c - _PyExc_RuntimeError - +Objects/exceptions.c - _PyExc_PythonFinalizationError - Objects/exceptions.c - _PyExc_RecursionError - Objects/exceptions.c - _PyExc_NotImplementedError - Objects/exceptions.c - _PyExc_NameError - @@ -253,6 +254,7 @@ Objects/exceptions.c - PyExc_ProcessLookupError - Objects/exceptions.c - PyExc_TimeoutError - Objects/exceptions.c - PyExc_EOFError - Objects/exceptions.c - PyExc_RuntimeError - +Objects/exceptions.c - PyExc_PythonFinalizationError - Objects/exceptions.c - PyExc_RecursionError - Objects/exceptions.c - PyExc_NotImplementedError - Objects/exceptions.c - PyExc_NameError -