# HG changeset patch # Parent 421847aa0b2289170bce49bd7e5a24345b05dea7 Issue #5319: New Py_FinalizeEx() API to exit with status 1 on failure diff -r 421847aa0b22 Doc/c-api/init.rst --- a/Doc/c-api/init.rst Mon Jun 01 19:38:50 2015 -0700 +++ b/Doc/c-api/init.rst Mon Aug 31 02:30:26 2015 +0000 @@ -25,7 +25,7 @@ triple: module; search; path single: PySys_SetArgv() single: PySys_SetArgvEx() - single: Py_Finalize() + single: Py_FinalizeEx() Initialize the Python interpreter. In an application embedding Python, this should be called before using any other Python/C API functions; with the @@ -34,7 +34,7 @@ modules :mod:`builtins`, :mod:`__main__` and :mod:`sys`. It also initializes the module search path (``sys.path``). It does not set ``sys.argv``; use :c:func:`PySys_SetArgvEx` for that. This is a no-op when called for a second time - (without calling :c:func:`Py_Finalize` first). There is no return value; it is a + (without calling :c:func:`Py_FinalizeEx` first). There is no return value; it is a fatal error if the initialization fails. @@ -48,19 +48,20 @@ .. c:function:: int Py_IsInitialized() Return true (nonzero) when the Python interpreter has been initialized, false - (zero) if not. After :c:func:`Py_Finalize` is called, this returns false until + (zero) if not. After :c:func:`Py_FinalizeEx` is called, this returns false until :c:func:`Py_Initialize` is called again. -.. c:function:: void Py_Finalize() +.. c:function:: int Py_FinalizeEx() Undo all initializations made by :c:func:`Py_Initialize` and subsequent use of Python/C API functions, and destroy all sub-interpreters (see :c:func:`Py_NewInterpreter` below) that were created and not yet destroyed since the last call to :c:func:`Py_Initialize`. Ideally, this frees all memory allocated by the Python interpreter. This is a no-op when called for a second - time (without calling :c:func:`Py_Initialize` again first). There is no return - value; errors during finalization are ignored. + time (without calling :c:func:`Py_Initialize` again first). Normally the + return value is zero. If there were errors during finalization + (flushing buffered data), -1 is returned. This function is provided for a number of reasons. An embedding application might want to restart Python without having to restart the application itself. @@ -79,7 +80,15 @@ freed. Some memory allocated by extension modules may not be freed. Some extensions may not work properly if their initialization routine is called more than once; this can happen if an application calls :c:func:`Py_Initialize` and - :c:func:`Py_Finalize` more than once. + :c:func:`Py_FinalizeEx` more than once. + + .. versionadded:: 3.6 + + +.. c:function:: void Py_Finalize() + + This is a backwards-compatible version of :c:func:`Py_FinalizeEx` that + disregards the return value. Process-wide parameters @@ -107,7 +116,7 @@ Note that :data:`sys.stderr` always uses the "backslashreplace" error handler, regardless of this (or any other) setting. - If :c:func:`Py_Finalize` is called, this function will need to be called + If :c:func:`Py_FinalizeEx` is called, this function will need to be called again in order to affect subsequent calls to :c:func:`Py_Initialize`. Returns 0 if successful, a nonzero value on error (e.g. calling after the @@ -916,7 +925,7 @@ entry.) .. index:: - single: Py_Finalize() + single: Py_FinalizeEx() single: Py_Initialize() Extension modules are shared between (sub-)interpreters as follows: the first @@ -926,7 +935,7 @@ and filled with the contents of this copy; the extension's ``init`` function is not called. Note that this is different from what happens when an extension is imported after the interpreter has been completely re-initialized by calling - :c:func:`Py_Finalize` and :c:func:`Py_Initialize`; in that case, the extension's + :c:func:`Py_FinalizeEx` and :c:func:`Py_Initialize`; in that case, the extension's ``initmodule`` function *is* called again. .. index:: single: close() (in module os) @@ -934,14 +943,14 @@ .. c:function:: void Py_EndInterpreter(PyThreadState *tstate) - .. index:: single: Py_Finalize() + .. index:: single: Py_FinalizeEx() Destroy the (sub-)interpreter represented by the given thread state. The given thread state must be the current thread state. See the discussion of thread states below. When the call returns, the current thread state is *NULL*. All thread states associated with this interpreter are destroyed. (The global interpreter lock must be held before calling this function and is still held - when it returns.) :c:func:`Py_Finalize` will destroy all sub-interpreters that + when it returns.) :c:func:`Py_FinalizeEx` will destroy all sub-interpreters that haven't been explicitly destroyed at that point. diff -r 421847aa0b22 Doc/c-api/intro.rst --- a/Doc/c-api/intro.rst Mon Jun 01 19:38:50 2015 -0700 +++ b/Doc/c-api/intro.rst Mon Aug 31 02:30:26 2015 +0000 @@ -578,9 +578,9 @@ application may want to start over (make another call to :c:func:`Py_Initialize`) or the application is simply done with its use of Python and wants to free memory allocated by Python. This can be accomplished -by calling :c:func:`Py_Finalize`. The function :c:func:`Py_IsInitialized` returns +by calling :c:func:`Py_FinalizeEx`. The function :c:func:`Py_IsInitialized` returns true if Python is currently in the initialized state. More information about -these functions is given in a later chapter. Notice that :c:func:`Py_Finalize` +these functions is given in a later chapter. Notice that :c:func:`Py_FinalizeEx` does *not* free all memory allocated by the Python interpreter, e.g. memory allocated by extension modules currently cannot be released. diff -r 421847aa0b22 Doc/c-api/sys.rst --- a/Doc/c-api/sys.rst Mon Jun 01 19:38:50 2015 -0700 +++ b/Doc/c-api/sys.rst Mon Aug 31 02:30:26 2015 +0000 @@ -212,20 +212,24 @@ .. c:function:: void Py_Exit(int status) .. index:: - single: Py_Finalize() + single: Py_FinalizeEx() single: exit() - Exit the current process. This calls :c:func:`Py_Finalize` and then calls the - standard C library function ``exit(status)``. + Exit the current process. This calls :c:func:`Py_FinalizeEx` and then calls the + standard C library function ``exit(status)``. If :c:func:`Py_FinalizeEx` + indicates an error, the exit status is set to 120. + + .. versionchanged:: 3.6 + Errors from finalization no longer ignored. .. c:function:: int Py_AtExit(void (*func) ()) .. index:: - single: Py_Finalize() + single: Py_FinalizeEx() single: cleanup functions - Register a cleanup function to be called by :c:func:`Py_Finalize`. The cleanup + Register a cleanup function to be called by :c:func:`Py_FinalizeEx`. The cleanup function will be called with no arguments and should return no value. At most 32 cleanup functions can be registered. When the registration is successful, :c:func:`Py_AtExit` returns ``0``; on failure, it returns ``-1``. The cleanup diff -r 421847aa0b22 Doc/extending/embedding.rst --- a/Doc/extending/embedding.rst Mon Jun 01 19:38:50 2015 -0700 +++ b/Doc/extending/embedding.rst Mon Aug 31 02:30:26 2015 +0000 @@ -67,7 +67,9 @@ Py_Initialize(); PyRun_SimpleString("from time import time,ctime\n" "print('Today is', ctime(time()))\n"); - Py_Finalize(); + if (Py_FinalizeEx() < 0) { + exit(120); + } PyMem_RawFree(program); return 0; } @@ -76,7 +78,7 @@ :c:func:`Py_Initialize` to inform the interpreter about paths to Python run-time libraries. Next, the Python interpreter is initialized with :c:func:`Py_Initialize`, followed by the execution of a hard-coded Python script -that prints the date and time. Afterwards, the :c:func:`Py_Finalize` call shuts +that prints the date and time. Afterwards, the :c:func:`Py_FinalizeEx` call shuts the interpreter down, followed by the end of the program. In a real program, you may want to get the Python script from another source, perhaps a text-editor routine, a file, or a database. Getting the Python code from a file can better diff -r 421847aa0b22 Doc/includes/run-func.c --- a/Doc/includes/run-func.c Mon Jun 01 19:38:50 2015 -0700 +++ b/Doc/includes/run-func.c Mon Aug 31 02:30:26 2015 +0000 @@ -63,6 +63,8 @@ fprintf(stderr, "Failed to load \"%s\"\n", argv[1]); return 1; } - Py_Finalize(); + if (Py_FinalizeEx() < 0) { + return 120; + } return 0; } diff -r 421847aa0b22 Doc/library/sys.rst --- a/Doc/library/sys.rst Mon Jun 01 19:38:50 2015 -0700 +++ b/Doc/library/sys.rst Mon Aug 31 02:30:26 2015 +0000 @@ -255,7 +255,7 @@ (defaulting to zero), or another type of object. If it is an integer, zero is considered "successful termination" and any nonzero value is considered "abnormal termination" by shells and the like. Most systems require it to be - in the range 0-127, and produce undefined results otherwise. Some systems + in the range 0--127, and produce undefined results otherwise. Some systems have a convention for assigning specific meanings to specific exit codes, but these are generally underdeveloped; Unix programs generally use 2 for command line syntax errors and 1 for all other kind of errors. If another type of @@ -268,6 +268,11 @@ the process when called from the main thread, and the exception is not intercepted. + .. versionadded:: 3.6 + If an error occurs in the cleanup after the Python interpreter + has caught :exc:`SystemExit` (such as an error flushing buffered data + in the standard streams), the exit status is changed to the value 120. + .. data:: flags diff -r 421847aa0b22 Doc/whatsnew/3.6.rst --- a/Doc/whatsnew/3.6.rst Mon Jun 01 19:38:50 2015 -0700 +++ b/Doc/whatsnew/3.6.rst Mon Aug 31 02:30:26 2015 +0000 @@ -107,7 +107,8 @@ Build and C API Changes ======================= -* None yet. +* New :c:func:`Py_FinalizeEx` API which has a return value indicating if + flushing buffered data failed (:issue:`5319`). Deprecated diff -r 421847aa0b22 Include/pylifecycle.h --- a/Include/pylifecycle.h Mon Jun 01 19:38:50 2015 -0700 +++ b/Include/pylifecycle.h Mon Aug 31 02:30:26 2015 +0000 @@ -27,6 +27,7 @@ PyAPI_FUNC(void) _Py_InitializeEx_Private(int, int); #endif PyAPI_FUNC(void) Py_Finalize(void); +PyAPI_FUNC(int) Py_FinalizeEx(void); PyAPI_FUNC(int) Py_IsInitialized(void); PyAPI_FUNC(PyThreadState *) Py_NewInterpreter(void); PyAPI_FUNC(void) Py_EndInterpreter(PyThreadState *); diff -r 421847aa0b22 Lib/test/test_cmd_line.py --- a/Lib/test/test_cmd_line.py Mon Jun 01 19:38:50 2015 -0700 +++ b/Lib/test/test_cmd_line.py Mon Aug 31 02:30:26 2015 +0000 @@ -348,8 +348,9 @@ test.support.SuppressCrashReport().__enter__() sys.stdout.write('x') os.close(sys.stdout.fileno())""" - rc, out, err = assert_python_ok('-c', code) + rc, out, err = assert_python_failure('-c', code) self.assertEqual(b'', out) + self.assertEqual(120, rc) self.assertRegex(err.decode('ascii', 'ignore'), 'Exception ignored in.*\nOSError: .*') diff -r 421847aa0b22 Misc/NEWS --- a/Misc/NEWS Mon Jun 01 19:38:50 2015 -0700 +++ b/Misc/NEWS Mon Aug 31 02:30:26 2015 +0000 @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #5319: New Py_FinalizeEx() API allowing Python to set an exit status + on failure to flush buffered streams. + Library ------- diff -r 421847aa0b22 Misc/SpecialBuilds.txt --- a/Misc/SpecialBuilds.txt Mon Jun 01 19:38:50 2015 -0700 +++ b/Misc/SpecialBuilds.txt Mon Aug 31 02:30:26 2015 +0000 @@ -65,9 +65,9 @@ simply by virtue of being in the list. envvar PYTHONDUMPREFS - If this envvar exists, Py_Finalize() arranges to print a list of all + If this envvar exists, Py_FinalizeEx() arranges to print a list of all still-live heap objects. This is printed twice, in different formats, - before and after Py_Finalize has cleaned up everything it can clean up. The + before and after Py_FinalizeEx has cleaned up everything it can clean up. The first output block produces the repr() of each object so is more informative; however, a lot of stuff destined to die is still alive then. The second output block is much harder to work with (repr() can't be invoked @@ -144,7 +144,7 @@ envvar PYTHONMALLOCSTATS If this envvar exists, a report of pymalloc summary statistics is printed to - stderr whenever a new arena is allocated, and also by Py_Finalize(). + stderr whenever a new arena is allocated, and also by Py_FinalizeEx(). Changed in 2.5: The number of extra bytes allocated is 4*sizeof(size_t). Before it was 16 on all boxes, reflecting that Python couldn't make use of @@ -179,7 +179,7 @@ */ int tp_maxalloc; -Allocation and deallocation code keeps these counts up to date. Py_Finalize() +Allocation and deallocation code keeps these counts up to date. Py_FinalizeEx() displays a summary of the info returned by sys.getcounts() (see below), along with assorted other special allocation counts (like the number of tuple allocations satisfied by a tuple free-list, the number of 1-character strings diff -r 421847aa0b22 Modules/main.c --- a/Modules/main.c Mon Jun 01 19:38:50 2015 -0700 +++ b/Modules/main.c Mon Aug 31 02:30:26 2015 +0000 @@ -654,7 +654,7 @@ Py_SetProgramName(wbuf); /* Don't free wbuf, the argument to Py_SetProgramName - * must remain valid until the Py_Finalize is called. + * must remain valid until Py_FinalizeEx is called. */ } else { Py_SetProgramName(argv[0]); @@ -785,7 +785,11 @@ sts = PyRun_AnyFileFlags(stdin, "", &cf) != 0; } - Py_Finalize(); + if (Py_FinalizeEx() < 0) { + /* Value unlikely to be confused with a non-error exit status or + other special meaning */ + sts = 120; + } #ifdef __INSURE__ /* Insure++ is a memory analysis tool that aids in discovering diff -r 421847aa0b22 PC/bdist_wininst/install.c --- a/PC/bdist_wininst/install.c Mon Jun 01 19:38:50 2015 -0700 +++ b/PC/bdist_wininst/install.c Mon Aug 31 02:30:26 2015 +0000 @@ -709,7 +709,7 @@ * 1 if the Python-dll does not export the functions we need * 2 if no install-script is specified in pathname * 3 if the install-script file could not be opened - * the return value of PyRun_SimpleString() otherwise, + * the return value of PyRun_SimpleString() or Py_FinalizeEx() otherwise, * which is 0 if everything is ok, -1 if an exception had occurred * in the install-script. */ @@ -722,7 +722,7 @@ DECLPROC(hPython, void, Py_Initialize, (void)); DECLPROC(hPython, int, PySys_SetArgv, (int, wchar_t **)); DECLPROC(hPython, int, PyRun_SimpleString, (char *)); - DECLPROC(hPython, void, Py_Finalize, (void)); + DECLPROC(hPython, int, Py_FinalizeEx, (void)); DECLPROC(hPython, PyObject *, Py_BuildValue, (char *, ...)); DECLPROC(hPython, PyObject *, PyCFunction_New, (PyMethodDef *, PyObject *)); @@ -730,7 +730,7 @@ DECLPROC(hPython, PyObject *, PyErr_Format, (PyObject *, char *)); if (!Py_Initialize || !PySys_SetArgv - || !PyRun_SimpleString || !Py_Finalize) + || !PyRun_SimpleString || !Py_FinalizeEx) return 1; if (!Py_BuildValue || !PyArg_ParseTuple || !PyErr_Format) @@ -777,7 +777,9 @@ } } } - Py_Finalize(); + if (Py_FinalizeEx() < 0) { + result = -1; + } close(fh); return result; @@ -839,11 +841,11 @@ int rc; DECLPROC(hPython, void, Py_Initialize, (void)); DECLPROC(hPython, void, Py_SetProgramName, (wchar_t *)); - DECLPROC(hPython, void, Py_Finalize, (void)); + DECLPROC(hPython, int, Py_FinalizeEx, (void)); DECLPROC(hPython, int, PyRun_SimpleString, (char *)); DECLPROC(hPython, void, PyErr_Print, (void)); - if (!Py_Initialize || !Py_SetProgramName || !Py_Finalize || + if (!Py_Initialize || !Py_SetProgramName || !Py_FinalizeEx || !PyRun_SimpleString || !PyErr_Print) return -1; @@ -853,7 +855,9 @@ rc = PyRun_SimpleString(script); if (rc) PyErr_Print(); - Py_Finalize(); + if (Py_FinalizeEx() < 0) { + rc = -1; + } return rc; } diff -r 421847aa0b22 PC/python3.def --- a/PC/python3.def Mon Jun 01 19:38:50 2015 -0700 +++ b/PC/python3.def Mon Aug 31 02:30:26 2015 +0000 @@ -648,6 +648,7 @@ Py_FatalError=python36.Py_FatalError Py_FileSystemDefaultEncoding=python36.Py_FileSystemDefaultEncoding DATA Py_Finalize=python36.Py_Finalize + Py_FinalizeEx=python36.Py_FinalizeEx Py_GetBuildInfo=python36.Py_GetBuildInfo Py_GetCompiler=python36.Py_GetCompiler Py_GetCopyright=python36.Py_GetCopyright diff -r 421847aa0b22 Python/frozenmain.c --- a/Python/frozenmain.c Mon Jun 01 19:38:50 2015 -0700 +++ b/Python/frozenmain.c Mon Aug 31 02:30:26 2015 +0000 @@ -99,7 +99,9 @@ #ifdef MS_WINDOWS PyWinFreeze_ExeTerm(); #endif - Py_Finalize(); + if (Py_FinalizeEx() < 0) { + sts = 120; + } error: PyMem_RawFree(argv_copy); diff -r 421847aa0b22 Python/pylifecycle.c --- a/Python/pylifecycle.c Mon Jun 01 19:38:50 2015 -0700 +++ b/Python/pylifecycle.c Mon Aug 31 02:30:26 2015 +0000 @@ -155,8 +155,8 @@ return 0; } -/* Global initializations. Can be undone by Py_Finalize(). Don't - call this twice without an intervening Py_Finalize() call. When +/* Global initializations. Can be undone by Py_FinalizeEx(). Don't + call this twice without an intervening Py_FinalizeEx() call. When initializations fail, a fatal error is issued and the function does not return. On return, the first thread and interpreter state have been created. @@ -327,11 +327,11 @@ (void) PyThreadState_Swap(tstate); #ifdef WITH_THREAD - /* We can't call _PyEval_FiniThreads() in Py_Finalize because + /* We can't call _PyEval_FiniThreads() in Py_FinalizeEx because destroying the GIL might fail when it is being referenced from another running thread (see issue #9901). Instead we destroy the previously created GIL here, which ensures - that we can call Py_Initialize / Py_Finalize multiple times. */ + that we can call Py_Initialize / Py_FinalizeEx multiple times. */ _PyEval_FiniThreads(); /* Auto-thread-state API */ @@ -477,28 +477,35 @@ return r > 0; } -static void +static int flush_std_files(void) { PyObject *fout = _PySys_GetObjectId(&PyId_stdout); PyObject *ferr = _PySys_GetObjectId(&PyId_stderr); PyObject *tmp; + int status = 0; if (fout != NULL && fout != Py_None && !file_is_closed(fout)) { tmp = _PyObject_CallMethodId(fout, &PyId_flush, ""); - if (tmp == NULL) + if (tmp == NULL) { PyErr_WriteUnraisable(fout); + status = -1; + } else Py_DECREF(tmp); } if (ferr != NULL && ferr != Py_None && !file_is_closed(ferr)) { tmp = _PyObject_CallMethodId(ferr, &PyId_flush, ""); - if (tmp == NULL) + if (tmp == NULL) { PyErr_Clear(); + status = -1; + } else Py_DECREF(tmp); } + + return status; } /* Undo the effect of Py_Initialize(). @@ -515,14 +522,15 @@ */ -void -Py_Finalize(void) +int +Py_FinalizeEx(void) { PyInterpreterState *interp; PyThreadState *tstate; + int status = 0; if (!initialized) - return; + return status; wait_for_thread_shutdown(); @@ -547,7 +555,9 @@ initialized = 0; /* Flush sys.stdout and sys.stderr */ - flush_std_files(); + if (flush_std_files() < 0) { + status = -1; + } /* Disable signal handling */ PyOS_FiniInterrupts(); @@ -576,7 +586,9 @@ PyImport_Cleanup(); /* Flush sys.stdout and sys.stderr (again, in case more was printed) */ - flush_std_files(); + if (flush_std_files() < 0) { + status = -1; + } /* Collect final garbage. This disposes of cycles created by * class definitions, for example. @@ -696,6 +708,13 @@ #endif call_ll_exitfuncs(); + return status; +} + +void +Py_Finalize(void) +{ + Py_FinalizeEx(); } /* Create and initialize a new interpreter and thread, and return the @@ -803,7 +822,7 @@ frames, and that it is its interpreter's only remaining thread. It is a fatal error to violate these constraints. - (Py_Finalize() doesn't have these constraints -- it zaps + (Py_FinalizeEx() doesn't have these constraints -- it zaps everything, regardless.) Locking: as above. @@ -993,7 +1012,8 @@ mode = "rb"; buf = _PyObject_CallMethodId(io, &PyId_open, "isiOOOi", fd, mode, buffering, - Py_None, Py_None, Py_None, 0); + Py_None, Py_None, /* encoding, errors */ + Py_None, 0); /* newline, closefd */ if (buf == NULL) goto error; @@ -1451,7 +1471,9 @@ void Py_Exit(int sts) { - Py_Finalize(); + if (Py_FinalizeEx() < 0) { + sts = 120; + } exit(sts); } diff -r 421847aa0b22 Python/pystate.c --- a/Python/pystate.c Mon Jun 01 19:38:50 2015 -0700 +++ b/Python/pystate.c Mon Aug 31 02:30:26 2015 +0000 @@ -685,7 +685,7 @@ } /* Internal initialization/finalization functions called by - Py_Initialize/Py_Finalize + Py_Initialize/Py_FinalizeEx */ void _PyGILState_Init(PyInterpreterState *i, PyThreadState *t) diff -r 421847aa0b22 Tools/scripts/combinerefs.py --- a/Tools/scripts/combinerefs.py Mon Jun 01 19:38:50 2015 -0700 +++ b/Tools/scripts/combinerefs.py Mon Aug 31 02:30:26 2015 +0000 @@ -6,7 +6,7 @@ A helper for analyzing PYTHONDUMPREFS output. When the PYTHONDUMPREFS envar is set in a debug build, at Python shutdown -time Py_Finalize() prints the list of all live objects twice: first it +time Py_FinalizeEx() prints the list of all live objects twice: first it prints the repr() of each object while the interpreter is still fully intact. After cleaning up everything it can, it prints all remaining live objects again, but the second time just prints their addresses, refcounts, and type @@ -41,7 +41,7 @@ objects shown in the repr: the repr was captured from the first output block, and some of the containees may have been released since then. For example, it's common for the line showing the dict of interned strings to display -strings that no longer exist at the end of Py_Finalize; this can be recognized +strings that no longer exist at the end of Py_FinalizeEx; this can be recognized (albeit painfully) because such containees don't have a line of their own. The objects are listed in allocation order, with most-recently allocated