Skip to content

Commit f182fcd

Browse files
committed
pythongh-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.
1 parent e81bd3f commit f182fcd

File tree

11 files changed

+43
-6
lines changed

11 files changed

+43
-6
lines changed

Diff for: Doc/library/exceptions.rst

+18
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,24 @@ The following exceptions are the exceptions that are usually raised.
392392
handling in C, most floating point operations are not checked.
393393

394394

395+
.. exception:: PythonFinalizationError
396+
397+
This exception is derived from :exc:`RuntimeError`. It is raised when
398+
an operations is blocked during the :term:`Python finalization <interpreter
399+
shutdown>`.
400+
401+
Examples of operations which can be blocked with a
402+
:exc:`PythonFinalizationError` during the Python finalization:
403+
404+
* create a new Python thread;
405+
* :func:`os.fork`.
406+
407+
See also the :func:`sys.is_finalizing` function.
408+
409+
.. versionadded:: 3.13
410+
Previously, a plain :exc:`RuntimeError` was raised.
411+
412+
395413
.. exception:: RecursionError
396414

397415
This exception is derived from :exc:`RuntimeError`. It is raised when the

Diff for: Doc/library/sys.rst

+2
Original file line numberDiff line numberDiff line change
@@ -1185,6 +1185,8 @@ always available.
11851185
Return :const:`True` if the main Python interpreter is
11861186
:term:`shutting down <interpreter shutdown>`. Return :const:`False` otherwise.
11871187

1188+
See also the :exc:`PythonFinalizationError` exception.
1189+
11881190
.. versionadded:: 3.5
11891191

11901192
.. data:: last_exc

Diff for: Doc/whatsnew/3.13.rst

+6
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ Other Language Changes
9191
of the ``optimize`` argument.
9292
(Contributed by Irit Katriel in :gh:`108113`).
9393

94+
* Add :exc:`PythonFinalizationError`. This exception derived from
95+
:exc:`RuntimeError` is raised when an operation is blocked during
96+
the :term:`Python finalization <interpreter shutdown>`.
97+
(Contributed by Victor Stinner in :gh:`109047`.)
98+
99+
94100
New Modules
95101
===========
96102

Diff for: Include/cpython/pyerrors.h

+2
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,6 @@ PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalErrorFunc(
120120
const char *func,
121121
const char *message);
122122

123+
PyAPI_DATA(PyObject *) PyExc_PythonFinalizationError;
124+
123125
#define Py_FatalError(message) _Py_FatalErrorFunc(__func__, (message))

Diff for: Lib/test/exception_hierarchy.txt

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ BaseException
4040
├── ReferenceError
4141
├── RuntimeError
4242
│ ├── NotImplementedError
43+
│ ├── PythonFinalizationError
4344
│ └── RecursionError
4445
├── StopAsyncIteration
4546
├── StopIteration
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add :exc:`PythonFinalizationError`. This exception derived from
2+
:exc:`RuntimeError` is raised when an operation is blocked during the
3+
:term:`Python finalization <interpreter shutdown>`. Patch by Victor Stinner.

Diff for: Modules/_posixsubprocess.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1030,7 +1030,7 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args,
10301030

10311031
PyInterpreterState *interp = _PyInterpreterState_GET();
10321032
if ((preexec_fn != Py_None) && interp->finalizing) {
1033-
PyErr_SetString(PyExc_RuntimeError,
1033+
PyErr_SetString(PyExc_PythonFinalizationError,
10341034
"preexec_fn not supported at interpreter shutdown");
10351035
return NULL;
10361036
}

Diff for: Modules/_threadmodule.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1179,7 +1179,7 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs)
11791179
return NULL;
11801180
}
11811181
if (interp->finalizing) {
1182-
PyErr_SetString(PyExc_RuntimeError,
1182+
PyErr_SetString(PyExc_PythonFinalizationError,
11831183
"can't create new thread at interpreter shutdown");
11841184
return NULL;
11851185
}

Diff for: Modules/_winapi.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ overlapped_dealloc(OverlappedObject *self)
139139
{
140140
/* The operation is still pending -- give a warning. This
141141
will probably only happen on Windows XP. */
142-
PyErr_SetString(PyExc_RuntimeError,
142+
PyErr_SetString(PyExc_PythonFinalizationError,
143143
"I/O operations still in flight while destroying "
144144
"Overlapped object, the process may crash");
145145
PyErr_WriteUnraisable(NULL);

Diff for: Modules/posixmodule.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -7665,7 +7665,7 @@ os_fork1_impl(PyObject *module)
76657665

76667666
PyInterpreterState *interp = _PyInterpreterState_GET();
76677667
if (interp->finalizing) {
7668-
PyErr_SetString(PyExc_RuntimeError,
7668+
PyErr_SetString(PyExc_PythonFinalizationError,
76697669
"can't fork at interpreter shutdown");
76707670
return NULL;
76717671
}
@@ -7709,7 +7709,7 @@ os_fork_impl(PyObject *module)
77097709
pid_t pid;
77107710
PyInterpreterState *interp = _PyInterpreterState_GET();
77117711
if (interp->finalizing) {
7712-
PyErr_SetString(PyExc_RuntimeError,
7712+
PyErr_SetString(PyExc_PythonFinalizationError,
77137713
"can't fork at interpreter shutdown");
77147714
return NULL;
77157715
}
@@ -8393,7 +8393,7 @@ os_forkpty_impl(PyObject *module)
83938393

83948394
PyInterpreterState *interp = _PyInterpreterState_GET();
83958395
if (interp->finalizing) {
8396-
PyErr_SetString(PyExc_RuntimeError,
8396+
PyErr_SetString(PyExc_PythonFinalizationError,
83978397
"can't fork at interpreter shutdown");
83988398
return NULL;
83998399
}

Diff for: Objects/exceptions.c

+5
Original file line numberDiff line numberDiff line change
@@ -2190,6 +2190,10 @@ SimpleExtendsException(PyExc_Exception, RuntimeError,
21902190
SimpleExtendsException(PyExc_RuntimeError, RecursionError,
21912191
"Recursion limit exceeded.");
21922192

2193+
// PythonFinalizationError extends RuntimeError
2194+
SimpleExtendsException(PyExc_RuntimeError, PythonFinalizationError,
2195+
"Operation blocked during Python finalization.");
2196+
21932197
/*
21942198
* NotImplementedError extends RuntimeError
21952199
*/
@@ -3652,6 +3656,7 @@ static struct static_exception static_exceptions[] = {
36523656
ITEM(KeyError), // base: LookupError(Exception)
36533657
ITEM(ModuleNotFoundError), // base: ImportError(Exception)
36543658
ITEM(NotImplementedError), // base: RuntimeError(Exception)
3659+
ITEM(PythonFinalizationError), // base: RuntimeError(Exception)
36553660
ITEM(RecursionError), // base: RuntimeError(Exception)
36563661
ITEM(UnboundLocalError), // base: NameError(Exception)
36573662
ITEM(UnicodeError), // base: ValueError(Exception)

0 commit comments

Comments
 (0)