Skip to content

Commit b4ce1fc

Browse files
committed
Issue #5319: New Py_FinalizeEx() API to exit with status 120 on failure
1 parent 92d5fba commit b4ce1fc

File tree

18 files changed

+120
-58
lines changed

18 files changed

+120
-58
lines changed

Doc/c-api/init.rst

+21-12
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Initializing and finalizing the interpreter
2525
triple: module; search; path
2626
single: PySys_SetArgv()
2727
single: PySys_SetArgvEx()
28-
single: Py_Finalize()
28+
single: Py_FinalizeEx()
2929
3030
Initialize the Python interpreter. In an application embedding Python, this
3131
should be called before using any other Python/C API functions; with the
@@ -34,7 +34,7 @@ Initializing and finalizing the interpreter
3434
modules :mod:`builtins`, :mod:`__main__` and :mod:`sys`. It also initializes
3535
the module search path (``sys.path``). It does not set ``sys.argv``; use
3636
:c:func:`PySys_SetArgvEx` for that. This is a no-op when called for a second time
37-
(without calling :c:func:`Py_Finalize` first). There is no return value; it is a
37+
(without calling :c:func:`Py_FinalizeEx` first). There is no return value; it is a
3838
fatal error if the initialization fails.
3939

4040

@@ -48,19 +48,20 @@ Initializing and finalizing the interpreter
4848
.. c:function:: int Py_IsInitialized()
4949
5050
Return true (nonzero) when the Python interpreter has been initialized, false
51-
(zero) if not. After :c:func:`Py_Finalize` is called, this returns false until
51+
(zero) if not. After :c:func:`Py_FinalizeEx` is called, this returns false until
5252
:c:func:`Py_Initialize` is called again.
5353
5454
55-
.. c:function:: void Py_Finalize()
55+
.. c:function:: int Py_FinalizeEx()
5656
5757
Undo all initializations made by :c:func:`Py_Initialize` and subsequent use of
5858
Python/C API functions, and destroy all sub-interpreters (see
5959
:c:func:`Py_NewInterpreter` below) that were created and not yet destroyed since
6060
the last call to :c:func:`Py_Initialize`. Ideally, this frees all memory
6161
allocated by the Python interpreter. This is a no-op when called for a second
62-
time (without calling :c:func:`Py_Initialize` again first). There is no return
63-
value; errors during finalization are ignored.
62+
time (without calling :c:func:`Py_Initialize` again first). Normally the
63+
return value is 0. If there were errors during finalization
64+
(flushing buffered data), -1 is returned.
6465
6566
This function is provided for a number of reasons. An embedding application
6667
might want to restart Python without having to restart the application itself.
@@ -79,7 +80,15 @@ Initializing and finalizing the interpreter
7980
freed. Some memory allocated by extension modules may not be freed. Some
8081
extensions may not work properly if their initialization routine is called more
8182
than once; this can happen if an application calls :c:func:`Py_Initialize` and
82-
:c:func:`Py_Finalize` more than once.
83+
:c:func:`Py_FinalizeEx` more than once.
84+
85+
.. versionadded:: 3.6
86+
87+
88+
.. c:function:: void Py_Finalize()
89+
90+
This is a backwards-compatible version of :c:func:`Py_FinalizeEx` that
91+
disregards the return value.
8392
8493
8594
Process-wide parameters
@@ -107,7 +116,7 @@ Process-wide parameters
107116
Note that :data:`sys.stderr` always uses the "backslashreplace" error
108117
handler, regardless of this (or any other) setting.
109118
110-
If :c:func:`Py_Finalize` is called, this function will need to be called
119+
If :c:func:`Py_FinalizeEx` is called, this function will need to be called
111120
again in order to affect subsequent calls to :c:func:`Py_Initialize`.
112121
113122
Returns 0 if successful, a nonzero value on error (e.g. calling after the
@@ -918,7 +927,7 @@ using the following functions:
918927
entry.)
919928
920929
.. index::
921-
single: Py_Finalize()
930+
single: Py_FinalizeEx()
922931
single: Py_Initialize()
923932
924933
Extension modules are shared between (sub-)interpreters as follows: the first
@@ -928,22 +937,22 @@ using the following functions:
928937
and filled with the contents of this copy; the extension's ``init`` function is
929938
not called. Note that this is different from what happens when an extension is
930939
imported after the interpreter has been completely re-initialized by calling
931-
:c:func:`Py_Finalize` and :c:func:`Py_Initialize`; in that case, the extension's
940+
:c:func:`Py_FinalizeEx` and :c:func:`Py_Initialize`; in that case, the extension's
932941
``initmodule`` function *is* called again.
933942
934943
.. index:: single: close() (in module os)
935944
936945
937946
.. c:function:: void Py_EndInterpreter(PyThreadState *tstate)
938947
939-
.. index:: single: Py_Finalize()
948+
.. index:: single: Py_FinalizeEx()
940949
941950
Destroy the (sub-)interpreter represented by the given thread state. The given
942951
thread state must be the current thread state. See the discussion of thread
943952
states below. When the call returns, the current thread state is *NULL*. All
944953
thread states associated with this interpreter are destroyed. (The global
945954
interpreter lock must be held before calling this function and is still held
946-
when it returns.) :c:func:`Py_Finalize` will destroy all sub-interpreters that
955+
when it returns.) :c:func:`Py_FinalizeEx` will destroy all sub-interpreters that
947956
haven't been explicitly destroyed at that point.
948957
949958

Doc/c-api/intro.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -578,9 +578,9 @@ Sometimes, it is desirable to "uninitialize" Python. For instance, the
578578
application may want to start over (make another call to
579579
:c:func:`Py_Initialize`) or the application is simply done with its use of
580580
Python and wants to free memory allocated by Python. This can be accomplished
581-
by calling :c:func:`Py_Finalize`. The function :c:func:`Py_IsInitialized` returns
581+
by calling :c:func:`Py_FinalizeEx`. The function :c:func:`Py_IsInitialized` returns
582582
true if Python is currently in the initialized state. More information about
583-
these functions is given in a later chapter. Notice that :c:func:`Py_Finalize`
583+
these functions is given in a later chapter. Notice that :c:func:`Py_FinalizeEx`
584584
does *not* free all memory allocated by the Python interpreter, e.g. memory
585585
allocated by extension modules currently cannot be released.
586586

Doc/c-api/sys.rst

+9-5
Original file line numberDiff line numberDiff line change
@@ -212,20 +212,24 @@ Process Control
212212
.. c:function:: void Py_Exit(int status)
213213
214214
.. index::
215-
single: Py_Finalize()
215+
single: Py_FinalizeEx()
216216
single: exit()
217217
218-
Exit the current process. This calls :c:func:`Py_Finalize` and then calls the
219-
standard C library function ``exit(status)``.
218+
Exit the current process. This calls :c:func:`Py_FinalizeEx` and then calls the
219+
standard C library function ``exit(status)``. If :c:func:`Py_FinalizeEx`
220+
indicates an error, the exit status is set to 120.
221+
222+
.. versionchanged:: 3.6
223+
Errors from finalization no longer ignored.
220224
221225
222226
.. c:function:: int Py_AtExit(void (*func) ())
223227
224228
.. index::
225-
single: Py_Finalize()
229+
single: Py_FinalizeEx()
226230
single: cleanup functions
227231
228-
Register a cleanup function to be called by :c:func:`Py_Finalize`. The cleanup
232+
Register a cleanup function to be called by :c:func:`Py_FinalizeEx`. The cleanup
229233
function will be called with no arguments and should return no value. At most
230234
32 cleanup functions can be registered. When the registration is successful,
231235
:c:func:`Py_AtExit` returns ``0``; on failure, it returns ``-1``. The cleanup

Doc/extending/embedding.rst

+4-2
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ perform some operation on a file. ::
6767
Py_Initialize();
6868
PyRun_SimpleString("from time import time,ctime\n"
6969
"print('Today is', ctime(time()))\n");
70-
Py_Finalize();
70+
if (Py_FinalizeEx() < 0) {
71+
exit(120);
72+
}
7173
PyMem_RawFree(program);
7274
return 0;
7375
}
@@ -76,7 +78,7 @@ The :c:func:`Py_SetProgramName` function should be called before
7678
:c:func:`Py_Initialize` to inform the interpreter about paths to Python run-time
7779
libraries. Next, the Python interpreter is initialized with
7880
:c:func:`Py_Initialize`, followed by the execution of a hard-coded Python script
79-
that prints the date and time. Afterwards, the :c:func:`Py_Finalize` call shuts
81+
that prints the date and time. Afterwards, the :c:func:`Py_FinalizeEx` call shuts
8082
the interpreter down, followed by the end of the program. In a real program,
8183
you may want to get the Python script from another source, perhaps a text-editor
8284
routine, a file, or a database. Getting the Python code from a file can better

Doc/includes/run-func.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ main(int argc, char *argv[])
6363
fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
6464
return 1;
6565
}
66-
Py_Finalize();
66+
if (Py_FinalizeEx() < 0) {
67+
return 120;
68+
}
6769
return 0;
6870
}

Doc/library/sys.rst

+6-1
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ always available.
255255
(defaulting to zero), or another type of object. If it is an integer, zero
256256
is considered "successful termination" and any nonzero value is considered
257257
"abnormal termination" by shells and the like. Most systems require it to be
258-
in the range 0-127, and produce undefined results otherwise. Some systems
258+
in the range 0--127, and produce undefined results otherwise. Some systems
259259
have a convention for assigning specific meanings to specific exit codes, but
260260
these are generally underdeveloped; Unix programs generally use 2 for command
261261
line syntax errors and 1 for all other kind of errors. If another type of
@@ -268,6 +268,11 @@ always available.
268268
the process when called from the main thread, and the exception is not
269269
intercepted.
270270

271+
.. versionchanged:: 3.6
272+
If an error occurs in the cleanup after the Python interpreter
273+
has caught :exc:`SystemExit` (such as an error flushing buffered data
274+
in the standard streams), the exit status is changed to 120.
275+
271276

272277
.. data:: flags
273278

Doc/whatsnew/3.6.rst

+4-2
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ Optimizations
171171
Build and C API Changes
172172
=======================
173173

174-
* None yet.
174+
* New :c:func:`Py_FinalizeEx` API which indicates if flushing buffered data
175+
failed (:issue:`5319`).
175176

176177

177178
Deprecated
@@ -247,4 +248,5 @@ Changes in the Python API
247248
Changes in the C API
248249
--------------------
249250

250-
* None yet.
251+
* :c:func:`Py_Exit` (and the main interpreter) now override the exit status
252+
with 120 if flushing buffered data failed. See :issue:`5319`.

Include/pylifecycle.h

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ PyAPI_FUNC(void) Py_InitializeEx(int);
2727
PyAPI_FUNC(void) _Py_InitializeEx_Private(int, int);
2828
#endif
2929
PyAPI_FUNC(void) Py_Finalize(void);
30+
PyAPI_FUNC(int) Py_FinalizeEx(void);
3031
PyAPI_FUNC(int) Py_IsInitialized(void);
3132
PyAPI_FUNC(PyThreadState *) Py_NewInterpreter(void);
3233
PyAPI_FUNC(void) Py_EndInterpreter(PyThreadState *);

Lib/test/test_cmd_line.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,9 @@ def test_stdout_flush_at_shutdown(self):
348348
test.support.SuppressCrashReport().__enter__()
349349
sys.stdout.write('x')
350350
os.close(sys.stdout.fileno())"""
351-
rc, out, err = assert_python_ok('-c', code)
351+
rc, out, err = assert_python_failure('-c', code)
352352
self.assertEqual(b'', out)
353+
self.assertEqual(120, rc)
353354
self.assertRegex(err.decode('ascii', 'ignore'),
354355
'Exception ignored in.*\nOSError: .*')
355356

Misc/NEWS

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ Release date: XXXX-XX-XX
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #5319: New Py_FinalizeEx() API allowing Python to set an exit status
14+
of 120 on failure to flush buffered streams.
15+
1316
- Issue #25485: telnetlib.Telnet is now a context manager.
1417

1518
- Issue #24097: Fixed crash in object.__reduce__() if slot name is freed inside

Misc/SpecialBuilds.txt

+4-4
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ sys.getobjects(max[, type])
6565
simply by virtue of being in the list.
6666

6767
envvar PYTHONDUMPREFS
68-
If this envvar exists, Py_Finalize() arranges to print a list of all
68+
If this envvar exists, Py_FinalizeEx() arranges to print a list of all
6969
still-live heap objects. This is printed twice, in different formats,
70-
before and after Py_Finalize has cleaned up everything it can clean up. The
70+
before and after Py_FinalizeEx has cleaned up everything it can clean up. The
7171
first output block produces the repr() of each object so is more
7272
informative; however, a lot of stuff destined to die is still alive then.
7373
The second output block is much harder to work with (repr() can't be invoked
@@ -144,7 +144,7 @@ Special gimmicks:
144144

145145
envvar PYTHONMALLOCSTATS
146146
If this envvar exists, a report of pymalloc summary statistics is printed to
147-
stderr whenever a new arena is allocated, and also by Py_Finalize().
147+
stderr whenever a new arena is allocated, and also by Py_FinalizeEx().
148148

149149
Changed in 2.5: The number of extra bytes allocated is 4*sizeof(size_t).
150150
Before it was 16 on all boxes, reflecting that Python couldn't make use of
@@ -179,7 +179,7 @@ Each type object grows three new members:
179179
*/
180180
int tp_maxalloc;
181181

182-
Allocation and deallocation code keeps these counts up to date. Py_Finalize()
182+
Allocation and deallocation code keeps these counts up to date. Py_FinalizeEx()
183183
displays a summary of the info returned by sys.getcounts() (see below), along
184184
with assorted other special allocation counts (like the number of tuple
185185
allocations satisfied by a tuple free-list, the number of 1-character strings

Modules/main.c

+6-2
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,7 @@ Py_Main(int argc, wchar_t **argv)
654654
Py_SetProgramName(wbuf);
655655

656656
/* Don't free wbuf, the argument to Py_SetProgramName
657-
* must remain valid until the Py_Finalize is called.
657+
* must remain valid until Py_FinalizeEx is called.
658658
*/
659659
} else {
660660
Py_SetProgramName(argv[0]);
@@ -785,7 +785,11 @@ Py_Main(int argc, wchar_t **argv)
785785
sts = PyRun_AnyFileFlags(stdin, "<stdin>", &cf) != 0;
786786
}
787787

788-
Py_Finalize();
788+
if (Py_FinalizeEx() < 0) {
789+
/* Value unlikely to be confused with a non-error exit status or
790+
other special meaning */
791+
sts = 120;
792+
}
789793

790794
#ifdef __INSURE__
791795
/* Insure++ is a memory analysis tool that aids in discovering

PC/bdist_wininst/install.c

+11-7
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@ static int prepare_script_environment(HINSTANCE hPython)
709709
* 1 if the Python-dll does not export the functions we need
710710
* 2 if no install-script is specified in pathname
711711
* 3 if the install-script file could not be opened
712-
* the return value of PyRun_SimpleString() otherwise,
712+
* the return value of PyRun_SimpleString() or Py_FinalizeEx() otherwise,
713713
* which is 0 if everything is ok, -1 if an exception had occurred
714714
* in the install-script.
715715
*/
@@ -722,15 +722,15 @@ do_run_installscript(HINSTANCE hPython, char *pathname, int argc, char **argv)
722722
DECLPROC(hPython, void, Py_Initialize, (void));
723723
DECLPROC(hPython, int, PySys_SetArgv, (int, wchar_t **));
724724
DECLPROC(hPython, int, PyRun_SimpleString, (char *));
725-
DECLPROC(hPython, void, Py_Finalize, (void));
725+
DECLPROC(hPython, int, Py_FinalizeEx, (void));
726726
DECLPROC(hPython, PyObject *, Py_BuildValue, (char *, ...));
727727
DECLPROC(hPython, PyObject *, PyCFunction_New,
728728
(PyMethodDef *, PyObject *));
729729
DECLPROC(hPython, int, PyArg_ParseTuple, (PyObject *, char *, ...));
730730
DECLPROC(hPython, PyObject *, PyErr_Format, (PyObject *, char *));
731731

732732
if (!Py_Initialize || !PySys_SetArgv
733-
|| !PyRun_SimpleString || !Py_Finalize)
733+
|| !PyRun_SimpleString || !Py_FinalizeEx)
734734
return 1;
735735

736736
if (!Py_BuildValue || !PyArg_ParseTuple || !PyErr_Format)
@@ -777,7 +777,9 @@ do_run_installscript(HINSTANCE hPython, char *pathname, int argc, char **argv)
777777
}
778778
}
779779
}
780-
Py_Finalize();
780+
if (Py_FinalizeEx() < 0) {
781+
result = -1;
782+
}
781783

782784
close(fh);
783785
return result;
@@ -839,11 +841,11 @@ static int do_run_simple_script(HINSTANCE hPython, char *script)
839841
int rc;
840842
DECLPROC(hPython, void, Py_Initialize, (void));
841843
DECLPROC(hPython, void, Py_SetProgramName, (wchar_t *));
842-
DECLPROC(hPython, void, Py_Finalize, (void));
844+
DECLPROC(hPython, int, Py_FinalizeEx, (void));
843845
DECLPROC(hPython, int, PyRun_SimpleString, (char *));
844846
DECLPROC(hPython, void, PyErr_Print, (void));
845847

846-
if (!Py_Initialize || !Py_SetProgramName || !Py_Finalize ||
848+
if (!Py_Initialize || !Py_SetProgramName || !Py_FinalizeEx ||
847849
!PyRun_SimpleString || !PyErr_Print)
848850
return -1;
849851

@@ -853,7 +855,9 @@ static int do_run_simple_script(HINSTANCE hPython, char *script)
853855
rc = PyRun_SimpleString(script);
854856
if (rc)
855857
PyErr_Print();
856-
Py_Finalize();
858+
if (Py_FinalizeEx() < 0) {
859+
rc = -1;
860+
}
857861
return rc;
858862
}
859863

PC/python3.def

+1
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,7 @@ EXPORTS
648648
Py_FatalError=python36.Py_FatalError
649649
Py_FileSystemDefaultEncoding=python36.Py_FileSystemDefaultEncoding DATA
650650
Py_Finalize=python36.Py_Finalize
651+
Py_FinalizeEx=python36.Py_FinalizeEx
651652
Py_GetBuildInfo=python36.Py_GetBuildInfo
652653
Py_GetCompiler=python36.Py_GetCompiler
653654
Py_GetCopyright=python36.Py_GetCopyright

Python/frozenmain.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,9 @@ Py_FrozenMain(int argc, char **argv)
9999
#ifdef MS_WINDOWS
100100
PyWinFreeze_ExeTerm();
101101
#endif
102-
Py_Finalize();
102+
if (Py_FinalizeEx() < 0) {
103+
sts = 120;
104+
}
103105

104106
error:
105107
PyMem_RawFree(argv_copy);

0 commit comments

Comments
 (0)