mirror of https://github.com/python/cpython
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:
parent
326119d373
commit
3e7b7df5cb
|
@ -416,6 +416,24 @@ The following exceptions are the exceptions that are usually raised.
|
||||||
handling in C, most floating point operations are not checked.
|
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
|
.. exception:: RecursionError
|
||||||
|
|
||||||
This exception is derived from :exc:`RuntimeError`. It is raised when the
|
This exception is derived from :exc:`RuntimeError`. It is raised when the
|
||||||
|
|
|
@ -1202,6 +1202,8 @@ always available.
|
||||||
Return :const:`True` if the main Python interpreter is
|
Return :const:`True` if the main Python interpreter is
|
||||||
:term:`shutting down <interpreter shutdown>`. Return :const:`False` otherwise.
|
:term:`shutting down <interpreter shutdown>`. Return :const:`False` otherwise.
|
||||||
|
|
||||||
|
See also the :exc:`PythonFinalizationError` exception.
|
||||||
|
|
||||||
.. versionadded:: 3.5
|
.. versionadded:: 3.5
|
||||||
|
|
||||||
.. data:: last_exc
|
.. data:: last_exc
|
||||||
|
|
|
@ -160,6 +160,21 @@ Other Language Changes
|
||||||
(Contributed by Levi Sabah, Zackery Spytz and Hugo van Kemenade in
|
(Contributed by Levi Sabah, Zackery Spytz and Hugo van Kemenade in
|
||||||
:gh:`73965`.)
|
: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
|
New Modules
|
||||||
===========
|
===========
|
||||||
|
|
||||||
|
|
|
@ -122,4 +122,6 @@ PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalErrorFunc(
|
||||||
|
|
||||||
PyAPI_FUNC(void) PyErr_FormatUnraisable(const char *, ...);
|
PyAPI_FUNC(void) PyErr_FormatUnraisable(const char *, ...);
|
||||||
|
|
||||||
|
PyAPI_DATA(PyObject *) PyExc_PythonFinalizationError;
|
||||||
|
|
||||||
#define Py_FatalError(message) _Py_FatalErrorFunc(__func__, (message))
|
#define Py_FatalError(message) _Py_FatalErrorFunc(__func__, (message))
|
||||||
|
|
|
@ -40,6 +40,7 @@ BaseException
|
||||||
├── ReferenceError
|
├── ReferenceError
|
||||||
├── RuntimeError
|
├── RuntimeError
|
||||||
│ ├── NotImplementedError
|
│ ├── NotImplementedError
|
||||||
|
│ ├── PythonFinalizationError
|
||||||
│ └── RecursionError
|
│ └── RecursionError
|
||||||
├── StopAsyncIteration
|
├── StopAsyncIteration
|
||||||
├── StopIteration
|
├── StopIteration
|
||||||
|
|
|
@ -564,6 +564,7 @@ class CompatPickleTests(unittest.TestCase):
|
||||||
if exc in (BlockingIOError,
|
if exc in (BlockingIOError,
|
||||||
ResourceWarning,
|
ResourceWarning,
|
||||||
StopAsyncIteration,
|
StopAsyncIteration,
|
||||||
|
PythonFinalizationError,
|
||||||
RecursionError,
|
RecursionError,
|
||||||
EncodingWarning,
|
EncodingWarning,
|
||||||
BaseExceptionGroup,
|
BaseExceptionGroup,
|
||||||
|
|
|
@ -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.
|
|
@ -1032,7 +1032,7 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args,
|
||||||
|
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
if ((preexec_fn != Py_None) && interp->finalizing) {
|
if ((preexec_fn != Py_None) && interp->finalizing) {
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
PyErr_SetString(PyExc_PythonFinalizationError,
|
||||||
"preexec_fn not supported at interpreter shutdown");
|
"preexec_fn not supported at interpreter shutdown");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1304,7 +1304,7 @@ do_start_new_thread(thread_module_state* state,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (interp->finalizing) {
|
if (interp->finalizing) {
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
PyErr_SetString(PyExc_PythonFinalizationError,
|
||||||
"can't create new thread at interpreter shutdown");
|
"can't create new thread at interpreter shutdown");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,7 +139,7 @@ overlapped_dealloc(OverlappedObject *self)
|
||||||
{
|
{
|
||||||
/* The operation is still pending -- give a warning. This
|
/* The operation is still pending -- give a warning. This
|
||||||
will probably only happen on Windows XP. */
|
will probably only happen on Windows XP. */
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
PyErr_SetString(PyExc_PythonFinalizationError,
|
||||||
"I/O operations still in flight while destroying "
|
"I/O operations still in flight while destroying "
|
||||||
"Overlapped object, the process may crash");
|
"Overlapped object, the process may crash");
|
||||||
PyErr_WriteUnraisable(NULL);
|
PyErr_WriteUnraisable(NULL);
|
||||||
|
|
|
@ -7841,7 +7841,7 @@ os_fork1_impl(PyObject *module)
|
||||||
|
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
if (interp->finalizing) {
|
if (interp->finalizing) {
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
PyErr_SetString(PyExc_PythonFinalizationError,
|
||||||
"can't fork at interpreter shutdown");
|
"can't fork at interpreter shutdown");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -7885,7 +7885,7 @@ os_fork_impl(PyObject *module)
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
if (interp->finalizing) {
|
if (interp->finalizing) {
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
PyErr_SetString(PyExc_PythonFinalizationError,
|
||||||
"can't fork at interpreter shutdown");
|
"can't fork at interpreter shutdown");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -8718,7 +8718,7 @@ os_forkpty_impl(PyObject *module)
|
||||||
|
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
if (interp->finalizing) {
|
if (interp->finalizing) {
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
PyErr_SetString(PyExc_PythonFinalizationError,
|
||||||
"can't fork at interpreter shutdown");
|
"can't fork at interpreter shutdown");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2177,6 +2177,10 @@ SimpleExtendsException(PyExc_Exception, RuntimeError,
|
||||||
SimpleExtendsException(PyExc_RuntimeError, RecursionError,
|
SimpleExtendsException(PyExc_RuntimeError, RecursionError,
|
||||||
"Recursion limit exceeded.");
|
"Recursion limit exceeded.");
|
||||||
|
|
||||||
|
// PythonFinalizationError extends RuntimeError
|
||||||
|
SimpleExtendsException(PyExc_RuntimeError, PythonFinalizationError,
|
||||||
|
"Operation blocked during Python finalization.");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NotImplementedError extends RuntimeError
|
* NotImplementedError extends RuntimeError
|
||||||
*/
|
*/
|
||||||
|
@ -3641,6 +3645,7 @@ static struct static_exception static_exceptions[] = {
|
||||||
ITEM(KeyError), // base: LookupError(Exception)
|
ITEM(KeyError), // base: LookupError(Exception)
|
||||||
ITEM(ModuleNotFoundError), // base: ImportError(Exception)
|
ITEM(ModuleNotFoundError), // base: ImportError(Exception)
|
||||||
ITEM(NotImplementedError), // base: RuntimeError(Exception)
|
ITEM(NotImplementedError), // base: RuntimeError(Exception)
|
||||||
|
ITEM(PythonFinalizationError), // base: RuntimeError(Exception)
|
||||||
ITEM(RecursionError), // base: RuntimeError(Exception)
|
ITEM(RecursionError), // base: RuntimeError(Exception)
|
||||||
ITEM(UnboundLocalError), // base: NameError(Exception)
|
ITEM(UnboundLocalError), // base: NameError(Exception)
|
||||||
ITEM(UnicodeError), // base: ValueError(Exception)
|
ITEM(UnicodeError), // base: ValueError(Exception)
|
||||||
|
|
|
@ -189,6 +189,7 @@ Objects/exceptions.c - _PyExc_ProcessLookupError -
|
||||||
Objects/exceptions.c - _PyExc_TimeoutError -
|
Objects/exceptions.c - _PyExc_TimeoutError -
|
||||||
Objects/exceptions.c - _PyExc_EOFError -
|
Objects/exceptions.c - _PyExc_EOFError -
|
||||||
Objects/exceptions.c - _PyExc_RuntimeError -
|
Objects/exceptions.c - _PyExc_RuntimeError -
|
||||||
|
Objects/exceptions.c - _PyExc_PythonFinalizationError -
|
||||||
Objects/exceptions.c - _PyExc_RecursionError -
|
Objects/exceptions.c - _PyExc_RecursionError -
|
||||||
Objects/exceptions.c - _PyExc_NotImplementedError -
|
Objects/exceptions.c - _PyExc_NotImplementedError -
|
||||||
Objects/exceptions.c - _PyExc_NameError -
|
Objects/exceptions.c - _PyExc_NameError -
|
||||||
|
@ -254,6 +255,7 @@ Objects/exceptions.c - PyExc_ProcessLookupError -
|
||||||
Objects/exceptions.c - PyExc_TimeoutError -
|
Objects/exceptions.c - PyExc_TimeoutError -
|
||||||
Objects/exceptions.c - PyExc_EOFError -
|
Objects/exceptions.c - PyExc_EOFError -
|
||||||
Objects/exceptions.c - PyExc_RuntimeError -
|
Objects/exceptions.c - PyExc_RuntimeError -
|
||||||
|
Objects/exceptions.c - PyExc_PythonFinalizationError -
|
||||||
Objects/exceptions.c - PyExc_RecursionError -
|
Objects/exceptions.c - PyExc_RecursionError -
|
||||||
Objects/exceptions.c - PyExc_NotImplementedError -
|
Objects/exceptions.c - PyExc_NotImplementedError -
|
||||||
Objects/exceptions.c - PyExc_NameError -
|
Objects/exceptions.c - PyExc_NameError -
|
||||||
|
|
Can't render this file because it has a wrong number of fields in line 4.
|
Loading…
Reference in New Issue