https://github.com/python/cpython/commit/3e7b7df5cbaad5617cc28f0c005010787c48e6d6
commit: 3e7b7df5cbaad5617cc28f0c005010787c48e6d6
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2024-02-14T23:35:06+01:00
summary:

gh-114570: Add PythonFinalizationError exception (#115352)

Add PythonFinalizationError exception. 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()
* subprocess.Popen
* os.fork()
* os.fork1()
* os.forkpty()

Morever, _winapi.Overlapped finalizer now logs an unraisable
PythonFinalizationError, instead of an unraisable RuntimeError.

files:
A Misc/NEWS.d/next/Core and 
Builtins/2024-02-12-17-18-26.gh-issue-114570.BzwMlJ.rst
M Doc/library/exceptions.rst
M Doc/library/sys.rst
M Doc/whatsnew/3.13.rst
M Include/cpython/pyerrors.h
M Lib/test/exception_hierarchy.txt
M Lib/test/test_pickle.py
M Modules/_posixsubprocess.c
M Modules/_threadmodule.c
M Modules/_winapi.c
M Modules/posixmodule.c
M Objects/exceptions.c
M Tools/c-analyzer/cpython/globals-to-fix.tsv

diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst
index 3191315049ad5a..88417b40e4aa7f 100644
--- a/Doc/library/exceptions.rst
+++ b/Doc/library/exceptions.rst
@@ -416,6 +416,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 operation is blocked during interpreter shutdown also known as
+   :term:`Python finalization <interpreter shutdown>`.
+
+   Examples of operations which can be blocked with a
+   :exc:`PythonFinalizationError` during the Python finalization:
+
+   * Creating 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 ad8857fc2807f7..351c44b1915159 100644
--- a/Doc/library/sys.rst
+++ b/Doc/library/sys.rst
@@ -1202,6 +1202,8 @@ always available.
    Return :const:`True` if the main Python interpreter is
    :term:`shutting down <interpreter shutdown>`. 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 b14fb4e5392a2c..1e0764144a2855 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -160,6 +160,21 @@ Other Language Changes
   (Contributed by Levi Sabah, Zackery Spytz and Hugo van Kemenade in
   :gh:`73965`.)
 
+* Add :exc:`PythonFinalizationError` exception. This exception derived from
+  :exc:`RuntimeError` is raised when an operation is blocked during
+  the :term:`Python finalization <interpreter shutdown>`.
+
+  The following functions now raise PythonFinalizationError, instead of
+  :exc:`RuntimeError`:
+
+  * :func:`_thread.start_new_thread`.
+  * :class:`subprocess.Popen`.
+  * :func:`os.fork`.
+  * :func:`os.forkpty`.
+
+  (Contributed by Victor Stinner in :gh:`114570`.)
+
+
 New Modules
 ===========
 
diff --git a/Include/cpython/pyerrors.h b/Include/cpython/pyerrors.h
index 479b908fb7058a..32c5884cd21341 100644
--- a/Include/cpython/pyerrors.h
+++ b/Include/cpython/pyerrors.h
@@ -122,4 +122,6 @@ PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalErrorFunc(
 
 PyAPI_FUNC(void) PyErr_FormatUnraisable(const char *, ...);
 
+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 217ee15d4c8af5..65f54859e2a21d 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 5e187e5189d117..19f977971570b7 100644
--- a/Lib/test/test_pickle.py
+++ b/Lib/test/test_pickle.py
@@ -564,6 +564,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/2024-02-12-17-18-26.gh-issue-114570.BzwMlJ.rst b/Misc/NEWS.d/next/Core 
and Builtins/2024-02-12-17-18-26.gh-issue-114570.BzwMlJ.rst
new file mode 100644
index 00000000000000..828d47d0e18d6b
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and 
Builtins/2024-02-12-17-18-26.gh-issue-114570.BzwMlJ.rst 
@@ -0,0 +1,3 @@
+Add :exc:`PythonFinalizationError` exception. This exception derived from
+:exc:`RuntimeError` is raised when an operation is blocked during the
+:term:`Python finalization <interpreter shutdown>`. Patch by Victor Stinner.
diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c
index aa1a300e4378dd..bcbbe70680b8e7 100644
--- a/Modules/_posixsubprocess.c
+++ b/Modules/_posixsubprocess.c
@@ -1032,7 +1032,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 d7840eaf45e8d6..da6a8bc7b120fe 100644
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -1304,7 +1304,7 @@ do_start_new_thread(thread_module_state* state,
         return -1;
     }
     if (interp->finalizing) {
-        PyErr_SetString(PyExc_RuntimeError,
+        PyErr_SetString(PyExc_PythonFinalizationError,
                         "can't create new thread at interpreter shutdown");
         return -1;
     }
diff --git a/Modules/_winapi.c b/Modules/_winapi.c
index 83a4ccd4802ae0..8f9b8520bb3f34 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 ef6d65623bf038..958b5a5e6e2406 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -7841,7 +7841,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;
     }
@@ -7885,7 +7885,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;
     }
@@ -8718,7 +8718,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 3df3a9b3b1a253..63c461d34fb4ff 100644
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -2177,6 +2177,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
  */
@@ -3641,6 +3645,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 5c5016f7137164..45119664af4362 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        -
@@ -254,6 +255,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 -

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to