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.
This commit is contained in:
Victor Stinner 2024-02-14 23:35:06 +01:00 committed by GitHub
parent 326119d373
commit 3e7b7df5cb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 55 additions and 6 deletions

View File

@ -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

View File

@ -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

View File

@ -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
===========

View File

@ -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))

View File

@ -40,6 +40,7 @@ BaseException
├── ReferenceError
├── RuntimeError
│ ├── NotImplementedError
│ ├── PythonFinalizationError
│ └── RecursionError
├── StopAsyncIteration
├── StopIteration

View File

@ -564,6 +564,7 @@ class CompatPickleTests(unittest.TestCase):
if exc in (BlockingIOError,
ResourceWarning,
StopAsyncIteration,
PythonFinalizationError,
RecursionError,
EncodingWarning,
BaseExceptionGroup,

View File

@ -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.

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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)

View File

@ -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 -

Can't render this file because it has a wrong number of fields in line 4.