Skip to content

Commit 3e7b7df

Browse files
authored
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.
1 parent 326119d commit 3e7b7df

File tree

13 files changed

+55
-6
lines changed

13 files changed

+55
-6
lines changed

Doc/library/exceptions.rst

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

418418

419+
.. exception:: PythonFinalizationError
420+
421+
This exception is derived from :exc:`RuntimeError`. It is raised when
422+
an operation is blocked during interpreter shutdown also known as
423+
:term:`Python finalization <interpreter shutdown>`.
424+
425+
Examples of operations which can be blocked with a
426+
:exc:`PythonFinalizationError` during the Python finalization:
427+
428+
* Creating a new Python thread.
429+
* :func:`os.fork`.
430+
431+
See also the :func:`sys.is_finalizing` function.
432+
433+
.. versionadded:: 3.13
434+
Previously, a plain :exc:`RuntimeError` was raised.
435+
436+
419437
.. exception:: RecursionError
420438

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

Doc/library/sys.rst

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

1205+
See also the :exc:`PythonFinalizationError` exception.
1206+
12051207
.. versionadded:: 3.5
12061208

12071209
.. data:: last_exc

Doc/whatsnew/3.13.rst

+15
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,21 @@ Other Language Changes
160160
(Contributed by Levi Sabah, Zackery Spytz and Hugo van Kemenade in
161161
:gh:`73965`.)
162162

163+
* Add :exc:`PythonFinalizationError` exception. This exception derived from
164+
:exc:`RuntimeError` is raised when an operation is blocked during
165+
the :term:`Python finalization <interpreter shutdown>`.
166+
167+
The following functions now raise PythonFinalizationError, instead of
168+
:exc:`RuntimeError`:
169+
170+
* :func:`_thread.start_new_thread`.
171+
* :class:`subprocess.Popen`.
172+
* :func:`os.fork`.
173+
* :func:`os.forkpty`.
174+
175+
(Contributed by Victor Stinner in :gh:`114570`.)
176+
177+
163178
New Modules
164179
===========
165180

Include/cpython/pyerrors.h

+2
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,6 @@ PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalErrorFunc(
122122

123123
PyAPI_FUNC(void) PyErr_FormatUnraisable(const char *, ...);
124124

125+
PyAPI_DATA(PyObject *) PyExc_PythonFinalizationError;
126+
125127
#define Py_FatalError(message) _Py_FatalErrorFunc(__func__, (message))

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

Lib/test/test_pickle.py

+1
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,7 @@ def test_exceptions(self):
564564
if exc in (BlockingIOError,
565565
ResourceWarning,
566566
StopAsyncIteration,
567+
PythonFinalizationError,
567568
RecursionError,
568569
EncodingWarning,
569570
BaseExceptionGroup,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add :exc:`PythonFinalizationError` exception. 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.

Modules/_posixsubprocess.c

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

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

Modules/_threadmodule.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1304,7 +1304,7 @@ do_start_new_thread(thread_module_state* state,
13041304
return -1;
13051305
}
13061306
if (interp->finalizing) {
1307-
PyErr_SetString(PyExc_RuntimeError,
1307+
PyErr_SetString(PyExc_PythonFinalizationError,
13081308
"can't create new thread at interpreter shutdown");
13091309
return -1;
13101310
}

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

Modules/posixmodule.c

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

78427842
PyInterpreterState *interp = _PyInterpreterState_GET();
78437843
if (interp->finalizing) {
7844-
PyErr_SetString(PyExc_RuntimeError,
7844+
PyErr_SetString(PyExc_PythonFinalizationError,
78457845
"can't fork at interpreter shutdown");
78467846
return NULL;
78477847
}
@@ -7885,7 +7885,7 @@ os_fork_impl(PyObject *module)
78857885
pid_t pid;
78867886
PyInterpreterState *interp = _PyInterpreterState_GET();
78877887
if (interp->finalizing) {
7888-
PyErr_SetString(PyExc_RuntimeError,
7888+
PyErr_SetString(PyExc_PythonFinalizationError,
78897889
"can't fork at interpreter shutdown");
78907890
return NULL;
78917891
}
@@ -8718,7 +8718,7 @@ os_forkpty_impl(PyObject *module)
87188718

87198719
PyInterpreterState *interp = _PyInterpreterState_GET();
87208720
if (interp->finalizing) {
8721-
PyErr_SetString(PyExc_RuntimeError,
8721+
PyErr_SetString(PyExc_PythonFinalizationError,
87228722
"can't fork at interpreter shutdown");
87238723
return NULL;
87248724
}

Objects/exceptions.c

+5
Original file line numberDiff line numberDiff line change
@@ -2177,6 +2177,10 @@ SimpleExtendsException(PyExc_Exception, RuntimeError,
21772177
SimpleExtendsException(PyExc_RuntimeError, RecursionError,
21782178
"Recursion limit exceeded.");
21792179

2180+
// PythonFinalizationError extends RuntimeError
2181+
SimpleExtendsException(PyExc_RuntimeError, PythonFinalizationError,
2182+
"Operation blocked during Python finalization.");
2183+
21802184
/*
21812185
* NotImplementedError extends RuntimeError
21822186
*/
@@ -3641,6 +3645,7 @@ static struct static_exception static_exceptions[] = {
36413645
ITEM(KeyError), // base: LookupError(Exception)
36423646
ITEM(ModuleNotFoundError), // base: ImportError(Exception)
36433647
ITEM(NotImplementedError), // base: RuntimeError(Exception)
3648+
ITEM(PythonFinalizationError), // base: RuntimeError(Exception)
36443649
ITEM(RecursionError), // base: RuntimeError(Exception)
36453650
ITEM(UnboundLocalError), // base: NameError(Exception)
36463651
ITEM(UnicodeError), // base: ValueError(Exception)

Tools/c-analyzer/cpython/globals-to-fix.tsv

+2
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ Objects/exceptions.c - _PyExc_ProcessLookupError -
189189
Objects/exceptions.c - _PyExc_TimeoutError -
190190
Objects/exceptions.c - _PyExc_EOFError -
191191
Objects/exceptions.c - _PyExc_RuntimeError -
192+
Objects/exceptions.c - _PyExc_PythonFinalizationError -
192193
Objects/exceptions.c - _PyExc_RecursionError -
193194
Objects/exceptions.c - _PyExc_NotImplementedError -
194195
Objects/exceptions.c - _PyExc_NameError -
@@ -254,6 +255,7 @@ Objects/exceptions.c - PyExc_ProcessLookupError -
254255
Objects/exceptions.c - PyExc_TimeoutError -
255256
Objects/exceptions.c - PyExc_EOFError -
256257
Objects/exceptions.c - PyExc_RuntimeError -
258+
Objects/exceptions.c - PyExc_PythonFinalizationError -
257259
Objects/exceptions.c - PyExc_RecursionError -
258260
Objects/exceptions.c - PyExc_NotImplementedError -
259261
Objects/exceptions.c - PyExc_NameError -

0 commit comments

Comments
 (0)