[Python-checkins] bpo-36142: Rework error reporting in pymain_main() (GH-12113)

Victor Stinner webhook-mailer at python.org
Fri Mar 1 06:14:45 EST 2019


https://github.com/python/cpython/commit/dfe884759d1f4441c889695f8985bc9feb9f37eb
commit: dfe884759d1f4441c889695f8985bc9feb9f37eb
branch: master
author: Victor Stinner <vstinner at redhat.com>
committer: GitHub <noreply at github.com>
date: 2019-03-01T12:14:41+01:00
summary:

bpo-36142: Rework error reporting in pymain_main() (GH-12113)

Add a new _Py_INIT_EXIT() macro to be able to exit Python with an
exitcode using _PyInitError API. Rewrite function calls by
pymain_main() to use _PyInitError.

Changes:

* Remove _PyMain.err and _PyMain.status field
* Add _Py_INIT_EXIT() macro and _PyInitError.exitcode field.
* Rename _Py_FatalInitError() to _Py_ExitInitError().

files:
M Include/cpython/coreconfig.h
M Include/cpython/pylifecycle.h
M Modules/main.c
M Programs/_freeze_importlib.c
M Programs/_testembed.c
M Python/frozenmain.c
M Python/pathconfig.c
M Python/pylifecycle.c

diff --git a/Include/cpython/coreconfig.h b/Include/cpython/coreconfig.h
index f7ac3f9cff09..2ce99b2eab64 100644
--- a/Include/cpython/coreconfig.h
+++ b/Include/cpython/coreconfig.h
@@ -11,6 +11,7 @@ typedef struct {
     const char *prefix;
     const char *msg;
     int user_err;
+    int exitcode;
 } _PyInitError;
 
 /* Almost all errors causing Python initialization to fail */
@@ -22,16 +23,18 @@ typedef struct {
 #endif
 
 #define _Py_INIT_OK() \
-    (_PyInitError){.prefix = NULL, .msg = NULL, .user_err = 0}
+    (_PyInitError){.prefix = NULL, .msg = NULL, .user_err = 0, .exitcode = -1}
 #define _Py_INIT_ERR(MSG) \
-    (_PyInitError){.prefix = _Py_INIT_GET_FUNC(), .msg = (MSG), .user_err = 0}
+    (_PyInitError){.prefix = _Py_INIT_GET_FUNC(), .msg = (MSG), .user_err = 0, .exitcode = -1}
 /* Error that can be fixed by the user like invalid input parameter.
    Don't abort() the process on such error. */
 #define _Py_INIT_USER_ERR(MSG) \
-    (_PyInitError){.prefix = _Py_INIT_GET_FUNC(), .msg = (MSG), .user_err = 1}
+    (_PyInitError){.prefix = _Py_INIT_GET_FUNC(), .msg = (MSG), .user_err = 1, .exitcode = -1}
 #define _Py_INIT_NO_MEMORY() _Py_INIT_USER_ERR("memory allocation failed")
+#define _Py_INIT_EXIT(EXITCODE) \
+    (_PyInitError){.prefix = NULL, .msg = NULL, .user_err = 0, .exitcode = (EXITCODE)}
 #define _Py_INIT_FAILED(err) \
-    (err.msg != NULL)
+    (err.msg != NULL || err.exitcode != -1)
 
 /* _PyCoreConfig */
 
diff --git a/Include/cpython/pylifecycle.h b/Include/cpython/pylifecycle.h
index a3fdeefde01b..3bffc80c9376 100644
--- a/Include/cpython/pylifecycle.h
+++ b/Include/cpython/pylifecycle.h
@@ -39,7 +39,7 @@ PyAPI_FUNC(_PyInitError) _Py_InitializeMainInterpreter(
 
 PyAPI_FUNC(_PyInitError) _Py_InitializeFromConfig(
     const _PyCoreConfig *config);
-PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalInitError(_PyInitError err);
+PyAPI_FUNC(void) _Py_NO_RETURN _Py_ExitInitError(_PyInitError err);
 
 /* Py_PyAtExit is for the atexit module, Py_AtExit is for low-level
  * exit functions.
diff --git a/Modules/main.c b/Modules/main.c
index f373dab20890..0d76953a5cae 100644
--- a/Modules/main.c
+++ b/Modules/main.c
@@ -329,11 +329,6 @@ typedef struct {
     char **bytes_argv;
     wchar_t **wchar_argv;
 
-    /* Exit status or "exit code": result of pymain_main() */
-    int status;
-    /* Error message if a function failed */
-    _PyInitError err;
-
     /* non-zero is stdin is a TTY or if -i option is used */
     int stdin_is_interactive;
     int skip_first_line;         /* -x option */
@@ -342,9 +337,6 @@ typedef struct {
     wchar_t *module;             /* -m argument */
 } _PyMain;
 
-#define _PyMain_INIT {.err = _Py_INIT_OK()}
-/* Note: _PyMain_INIT sets other fields to 0/NULL */
-
 
 /* Non-zero if filename, command (-c) or module (-m) is set
    on the command line */
@@ -353,19 +345,7 @@ typedef struct {
      || pymain->module != NULL)
 
 
-static wchar_t*
-pymain_wstrdup(_PyMain *pymain, const wchar_t *str)
-{
-    wchar_t *str2 = _PyMem_RawWcsdup(str);
-    if (str2 == NULL) {
-        pymain->err = _Py_INIT_NO_MEMORY();
-        return NULL;
-    }
-    return str2;
-}
-
-
-static int
+static _PyInitError
 pymain_init_cmdline_argv(_PyMain *pymain, _PyCoreConfig *config,
                          _PyCmdline *cmdline)
 {
@@ -376,8 +356,7 @@ pymain_init_cmdline_argv(_PyMain *pymain, _PyCoreConfig *config,
         size_t size = sizeof(wchar_t*) * (pymain->argc + 1);
         wchar_t** argv = (wchar_t **)PyMem_RawMalloc(size);
         if (argv == NULL) {
-            pymain->err = _Py_INIT_NO_MEMORY();
-            return -1;
+            return _Py_INIT_NO_MEMORY();
         }
 
         for (int i = 0; i < pymain->argc; i++) {
@@ -385,9 +364,8 @@ pymain_init_cmdline_argv(_PyMain *pymain, _PyCoreConfig *config,
             wchar_t *arg = Py_DecodeLocale(pymain->bytes_argv[i], &len);
             if (arg == NULL) {
                 _Py_wstrlist_clear(i, argv);
-                pymain->err = DECODE_LOCALE_ERR("command line arguments",
-                                                (Py_ssize_t)len);
-                return -1;
+                return DECODE_LOCALE_ERR("command line arguments",
+                                         (Py_ssize_t)len);
             }
             argv[i] = arg;
         }
@@ -406,12 +384,12 @@ pymain_init_cmdline_argv(_PyMain *pymain, _PyCoreConfig *config,
     else {
         program = L"";
     }
-    config->program = pymain_wstrdup(pymain, program);
+    config->program = _PyMem_RawWcsdup(program);
     if (config->program == NULL) {
-        return -1;
+        return _Py_INIT_NO_MEMORY();
     }
 
-    return 0;
+    return _Py_INIT_OK();
 }
 
 
@@ -562,26 +540,12 @@ _Py_wstrlist_append(int *len, wchar_t ***list, const wchar_t *str)
 }
 
 
-static int
-pymain_wstrlist_append(_PyMain *pymain, int *len, wchar_t ***list, const wchar_t *str)
-{
-    _PyInitError err = _Py_wstrlist_append(len, list, str);
-    if (_Py_INIT_FAILED(err)) {
-        pymain->err = err;
-        return -1;
-    }
-    return 0;
-}
-
-
-/* Parse the command line arguments
-   Return 0 on success.
-   Return 1 if parsing failed.
-   Set pymain->err and return -1 on other errors. */
-static int
+/* Parse the command line arguments */
+static _PyInitError
 pymain_parse_cmdline_impl(_PyMain *pymain, _PyCoreConfig *config,
-                          _PyCmdline *cmdline)
+                          _PyCmdline *cmdline, int *need_usage)
 {
+    _PyInitError err;
     _PyOS_ResetGetOpt();
     do {
         int longindex = -1;
@@ -598,8 +562,7 @@ pymain_parse_cmdline_impl(_PyMain *pymain, _PyCoreConfig *config,
             size_t len = wcslen(_PyOS_optarg) + 1 + 1;
             wchar_t *command = PyMem_RawMalloc(sizeof(wchar_t) * len);
             if (command == NULL) {
-                pymain->err = _Py_INIT_NO_MEMORY();
-                return -1;
+                return _Py_INIT_NO_MEMORY();
             }
             memcpy(command, _PyOS_optarg, (len - 2) * sizeof(wchar_t));
             command[len - 2] = '\n';
@@ -612,9 +575,9 @@ pymain_parse_cmdline_impl(_PyMain *pymain, _PyCoreConfig *config,
             /* -m is the last option; following arguments
                that look like options are left for the
                module to interpret. */
-            pymain->module = pymain_wstrdup(pymain, _PyOS_optarg);
+            pymain->module = _PyMem_RawWcsdup(_PyOS_optarg);
             if (pymain->module == NULL) {
-                return -1;
+                return _Py_INIT_NO_MEMORY();
             }
             break;
         }
@@ -632,7 +595,8 @@ pymain_parse_cmdline_impl(_PyMain *pymain, _PyCoreConfig *config,
             } else {
                 fprintf(stderr, "--check-hash-based-pycs must be one of "
                         "'default', 'always', or 'never'\n");
-                return 1;
+                *need_usage = 1;
+                return _Py_INIT_OK();
             }
             break;
 
@@ -701,20 +665,20 @@ pymain_parse_cmdline_impl(_PyMain *pymain, _PyCoreConfig *config,
             break;
 
         case 'W':
-            if (pymain_wstrlist_append(pymain,
-                                       &cmdline->nwarnoption,
-                                       &cmdline->warnoptions,
-                                       _PyOS_optarg) < 0) {
-                return -1;
+            err = _Py_wstrlist_append(&cmdline->nwarnoption,
+                                      &cmdline->warnoptions,
+                                      _PyOS_optarg);
+            if (_Py_INIT_FAILED(err)) {
+                return err;
             }
             break;
 
         case 'X':
-            if (pymain_wstrlist_append(pymain,
-                                       &config->nxoption,
-                                       &config->xoptions,
-                                       _PyOS_optarg) < 0) {
-                return -1;
+            err = _Py_wstrlist_append(&config->nxoption,
+                                      &config->xoptions,
+                                      _PyOS_optarg);
+            if (_Py_INIT_FAILED(err)) {
+                return err;
             }
             break;
 
@@ -730,7 +694,8 @@ pymain_parse_cmdline_impl(_PyMain *pymain, _PyCoreConfig *config,
 
         default:
             /* unknown argument: parsing failed */
-            return 1;
+            *need_usage = 1;
+            return _Py_INIT_OK();
         }
     } while (1);
 
@@ -738,16 +703,21 @@ pymain_parse_cmdline_impl(_PyMain *pymain, _PyCoreConfig *config,
         && _PyOS_optind < pymain->argc
         && wcscmp(cmdline->argv[_PyOS_optind], L"-") != 0)
     {
-        pymain->filename = pymain_wstrdup(pymain, cmdline->argv[_PyOS_optind]);
+        pymain->filename = _PyMem_RawWcsdup(cmdline->argv[_PyOS_optind]);
         if (pymain->filename == NULL) {
-            return -1;
+            return _Py_INIT_NO_MEMORY();
         }
     }
 
+    if (pymain->command != NULL || pymain->module != NULL) {
+        /* Backup _PyOS_optind */
+        _PyOS_optind--;
+    }
+
     /* -c and -m options are exclusive */
     assert(!(pymain->command != NULL && pymain->module != NULL));
 
-    return 0;
+    return _Py_INIT_OK();
 }
 
 
@@ -888,9 +858,7 @@ config_init_warnoptions(_PyCoreConfig *config, _PyCmdline *cmdline)
 }
 
 
-/* Get warning options from PYTHONWARNINGS environment variable.
-   Return 0 on success.
-   Set pymain->err and return -1 on error. */
+/* Get warning options from PYTHONWARNINGS environment variable. */
 static _PyInitError
 cmdline_init_env_warnoptions(_PyMain *pymain, const _PyCoreConfig *config,
                              _PyCmdline *cmdline)
@@ -983,7 +951,7 @@ pymain_header(_PyMain *pymain, const _PyCoreConfig *config)
 }
 
 
-static int
+static _PyInitError
 pymain_init_core_argv(_PyMain *pymain, _PyCoreConfig *config, _PyCmdline *cmdline)
 {
     /* Copy argv to be able to modify it (to force -c/-m) */
@@ -1001,8 +969,7 @@ pymain_init_core_argv(_PyMain *pymain, _PyCoreConfig *config, _PyCmdline *cmdlin
     }
 
     if (argv == NULL) {
-        pymain->err = _Py_INIT_NO_MEMORY();
-        return -1;
+        return _Py_INIT_NO_MEMORY();
     }
 
     wchar_t *arg0 = NULL;
@@ -1018,8 +985,7 @@ pymain_init_core_argv(_PyMain *pymain, _PyCoreConfig *config, _PyCmdline *cmdlin
         arg0 = _PyMem_RawWcsdup(arg0);
         if (arg0 == NULL) {
             _Py_wstrlist_clear(argc, argv);
-            pymain->err = _Py_INIT_NO_MEMORY();
-            return -1;
+            return _Py_INIT_NO_MEMORY();
         }
 
         assert(argc >= 1);
@@ -1029,7 +995,7 @@ pymain_init_core_argv(_PyMain *pymain, _PyCoreConfig *config, _PyCmdline *cmdlin
 
     config->argc = argc;
     config->argv = argv;
-    return 0;
+    return _Py_INIT_OK();
 }
 
 
@@ -1104,7 +1070,7 @@ pymain_run_startup(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf)
 }
 
 
-static void
+static int
 pymain_run_file(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf)
 {
     const wchar_t *filename = pymain->filename;
@@ -1121,8 +1087,7 @@ pymain_run_file(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf)
         fprintf(stderr, "%ls: can't open file '%s': [Errno %d] %s\n",
                 config->program, cfilename, err, strerror(err));
         PyMem_RawFree(cfilename_buffer);
-        pymain->status = 2;
-        return;
+        return 2;
     }
 
     if (pymain->skip_first_line) {
@@ -1141,17 +1106,15 @@ pymain_run_file(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf)
         fprintf(stderr,
                 "%ls: '%ls' is a directory, cannot continue\n",
                 config->program, filename);
-        pymain->status = 1;
         fclose(fp);
-        return;
+        return 1;
     }
 
     /* call pending calls like signal handlers (SIGINT) */
     if (Py_MakePendingCalls() == -1) {
         PyErr_Print();
-        pymain->status = 1;
         fclose(fp);
-        return;
+        return 1;
     }
 
     PyObject *unicode, *bytes = NULL;
@@ -1173,11 +1136,11 @@ pymain_run_file(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf)
     /* PyRun_AnyFileExFlags(closeit=1) calls fclose(fp) before running code */
     int run = PyRun_AnyFileExFlags(fp, filename_str, 1, cf);
     Py_XDECREF(bytes);
-    pymain->status = (run != 0);
+    return (run != 0);
 }
 
 
-static void
+static int
 pymain_run_stdin(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf)
 {
     if (pymain->stdin_is_interactive) {
@@ -1190,17 +1153,16 @@ pymain_run_stdin(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf)
     /* call pending calls like signal handlers (SIGINT) */
     if (Py_MakePendingCalls() == -1) {
         PyErr_Print();
-        pymain->status = 1;
-        return;
+        return 1;
     }
 
     int run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, cf);
-    pymain->status = (run != 0);
+    return (run != 0);
 }
 
 
 static void
-pymain_repl(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf)
+pymain_repl(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf, int *exitcode)
 {
     /* Check this environment variable at the end, to give programs the
        opportunity to set it from Python. */
@@ -1218,100 +1180,86 @@ pymain_repl(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf)
     pymain_run_interactive_hook();
 
     int res = PyRun_AnyFileFlags(stdin, "<stdin>", cf);
-    pymain->status = (res != 0);
+    *exitcode = (res != 0);
 }
 
 
 /* Parse the command line.
-   Handle --version and --help options directly.
-
-   Return 1 if Python must exit.
-   Return 0 on success.
-   Set pymain->err and return -1 on failure. */
-static int
+   Handle --version and --help options directly. */
+static _PyInitError
 pymain_parse_cmdline(_PyMain *pymain, _PyCoreConfig *config,
                      _PyCmdline *cmdline)
 {
-    int res = pymain_parse_cmdline_impl(pymain, config, cmdline);
-    if (res < 0) {
-        return -1;
-    }
-    if (res) {
-        pymain_usage(1, config->program);
-        pymain->status = 2;
-        return 1;
+    int need_usage = 0;
+    _PyInitError err;
+    err = pymain_parse_cmdline_impl(pymain, config, cmdline, &need_usage);
+    if (_Py_INIT_FAILED(err)) {
+        return err;
     }
 
-    if (pymain->command != NULL || pymain->module != NULL) {
-        /* Backup _PyOS_optind */
-        _PyOS_optind--;
+    if (need_usage) {
+        pymain_usage(1, config->program);
+        return _Py_INIT_EXIT(2);
     }
-
-    return 0;
+    return _Py_INIT_OK();
 }
 
 
 /* Parse command line options and environment variables.
-   This code must not use Python runtime apart PyMem_Raw memory allocator.
-
-   Return 0 on success.
-   Return 1 if Python is done and must exit.
-   Set pymain->err and return -1 on error. */
-static int
+   This code must not use Python runtime apart PyMem_Raw memory allocator. */
+static _PyInitError
 pymain_read_conf_impl(_PyMain *pymain, _PyCoreConfig *config,
                       _PyCmdline *cmdline)
 {
     _PyInitError err;
 
-    int res = pymain_parse_cmdline(pymain, config, cmdline);
-    if (res != 0) {
-        return res;
+    err = pymain_parse_cmdline(pymain, config, cmdline);
+    if (_Py_INIT_FAILED(err)) {
+        return err;
     }
 
-    if (pymain_init_core_argv(pymain, config, cmdline) < 0) {
-        return -1;
+    err = pymain_init_core_argv(pymain, config, cmdline);
+    if (_Py_INIT_FAILED(err)) {
+        return err;
     }
 
     err = _PyCoreConfig_Read(config);
     if (_Py_INIT_FAILED(err)) {
-        pymain->err = err;
-        return -1;
+        return err;
     }
 
     if (config->use_environment) {
         err = cmdline_init_env_warnoptions(pymain, config, cmdline);
         if (_Py_INIT_FAILED(err)) {
-            pymain->err = err;
-            return -1;
+            return err;
         }
     }
 
     err = config_init_warnoptions(config, cmdline);
     if (_Py_INIT_FAILED(err)) {
-        pymain->err = err;
-        return -1;
+        return err;
     }
-    return 0;
+    return _Py_INIT_OK();
 }
 
 
 /* Read the configuration and initialize the LC_CTYPE locale:
    enable UTF-8 mode (PEP 540) and/or coerce the C locale (PEP 538). */
-static int
+static _PyInitError
 pymain_read_conf(_PyMain *pymain, _PyCoreConfig *config,
                  _PyCmdline *cmdline)
 {
+    _PyInitError err;
     int init_utf8_mode = Py_UTF8Mode;
 #ifdef MS_WINDOWS
     int init_legacy_encoding = Py_LegacyWindowsFSEncodingFlag;
 #endif
     _PyCoreConfig save_config = _PyCoreConfig_INIT;
-    int res = -1;
     int locale_coerced = 0;
     int loops = 0;
 
     if (_PyCoreConfig_Copy(&save_config, config) < 0) {
-        pymain->err = _Py_INIT_NO_MEMORY();
+        err = _Py_INIT_NO_MEMORY();
         goto done;
     }
 
@@ -1325,8 +1273,8 @@ pymain_read_conf(_PyMain *pymain, _PyCoreConfig *config,
         /* Watchdog to prevent an infinite loop */
         loops++;
         if (loops == 3) {
-            pymain->err = _Py_INIT_ERR("Encoding changed twice while "
-                                       "reading the configuration");
+            err = _Py_INIT_ERR("Encoding changed twice while "
+                               "reading the configuration");
             goto done;
         }
 
@@ -1337,13 +1285,13 @@ pymain_read_conf(_PyMain *pymain, _PyCoreConfig *config,
         Py_LegacyWindowsFSEncodingFlag = config->legacy_windows_fs_encoding;
 #endif
 
-        if (pymain_init_cmdline_argv(pymain, config, cmdline) < 0) {
+        err = pymain_init_cmdline_argv(pymain, config, cmdline);
+        if (_Py_INIT_FAILED(err)) {
             goto done;
         }
 
-        int conf_res = pymain_read_conf_impl(pymain, config, cmdline);
-        if (conf_res != 0) {
-            res = conf_res;
+        err = pymain_read_conf_impl(pymain, config, cmdline);
+        if (_Py_INIT_FAILED(err)) {
             goto done;
         }
 
@@ -1384,7 +1332,7 @@ pymain_read_conf(_PyMain *pymain, _PyCoreConfig *config,
         int new_utf8_mode = config->utf8_mode;
         int new_coerce_c_locale = config->coerce_c_locale;
         if (_PyCoreConfig_Copy(config, &save_config) < 0) {
-            pymain->err = _Py_INIT_NO_MEMORY();
+            err = _Py_INIT_NO_MEMORY();
             goto done;
         }
         pymain_clear_cmdline(pymain, cmdline);
@@ -1396,7 +1344,7 @@ pymain_read_conf(_PyMain *pymain, _PyCoreConfig *config,
         /* The encoding changed: read again the configuration
            with the new encoding */
     }
-    res = 0;
+    err = _Py_INIT_OK();
 
 done:
     _PyCoreConfig_Clear(&save_config);
@@ -1404,7 +1352,7 @@ pymain_read_conf(_PyMain *pymain, _PyCoreConfig *config,
 #ifdef MS_WINDOWS
     Py_LegacyWindowsFSEncodingFlag = init_legacy_encoding;
 #endif
-    return res;
+    return err;
 }
 
 
@@ -1605,31 +1553,29 @@ _PyMainInterpreterConfig_Read(_PyMainInterpreterConfig *main_config,
 }
 
 
-static int
-pymain_init_python_main(_PyMain *pymain, _PyCoreConfig *config,
-                        PyInterpreterState *interp)
+static _PyInitError
+pymain_init_python_main(_PyMain *pymain, PyInterpreterState *interp)
 {
     _PyInitError err;
 
     _PyMainInterpreterConfig main_config = _PyMainInterpreterConfig_INIT;
-    err = _PyMainInterpreterConfig_Read(&main_config, config);
+    err = _PyMainInterpreterConfig_Read(&main_config, &interp->core_config);
     if (!_Py_INIT_FAILED(err)) {
         err = _Py_InitializeMainInterpreter(interp, &main_config);
     }
     _PyMainInterpreterConfig_Clear(&main_config);
 
     if (_Py_INIT_FAILED(err)) {
-        pymain->err = err;
-        return -1;
+        return err;
     }
-    return 0;
+    return _Py_INIT_OK();
 }
 
 
-static int
-pymain_run_python(_PyMain *pymain, PyInterpreterState *interp)
+static _PyInitError
+pymain_run_python(_PyMain *pymain, PyInterpreterState *interp, int *exitcode)
 {
-    int res = 0;
+    _PyInitError err;
     _PyCoreConfig *config = &interp->core_config;
 
     PyObject *main_importer_path = NULL;
@@ -1644,7 +1590,7 @@ pymain_run_python(_PyMain *pymain, PyInterpreterState *interp)
 
     if (main_importer_path != NULL) {
         if (pymain_sys_path_add_path0(interp, main_importer_path) < 0) {
-            pymain->status = 1;
+            err = _Py_INIT_EXIT(1);
             goto done;
         }
     }
@@ -1652,14 +1598,13 @@ pymain_run_python(_PyMain *pymain, PyInterpreterState *interp)
         PyObject *path0 = _PyPathConfig_ComputeArgv0(config->argc,
                                                      config->argv);
         if (path0 == NULL) {
-            pymain->err = _Py_INIT_NO_MEMORY();
-            res = -1;
+            err = _Py_INIT_NO_MEMORY();
             goto done;
         }
 
         if (pymain_sys_path_add_path0(interp, path0) < 0) {
             Py_DECREF(path0);
-            pymain->status = 1;
+            err = _Py_INIT_EXIT(1);
             goto done;
         }
         Py_DECREF(path0);
@@ -1671,67 +1616,65 @@ pymain_run_python(_PyMain *pymain, PyInterpreterState *interp)
     pymain_import_readline(pymain, config);
 
     if (pymain->command) {
-        pymain->status = pymain_run_command(pymain->command, &cf);
+        *exitcode = pymain_run_command(pymain->command, &cf);
     }
     else if (pymain->module) {
-        pymain->status = (pymain_run_module(pymain->module, 1) != 0);
+        *exitcode = (pymain_run_module(pymain->module, 1) != 0);
     }
     else if (main_importer_path != NULL) {
         int sts = pymain_run_module(L"__main__", 0);
-        pymain->status = (sts != 0);
+        *exitcode = (sts != 0);
     }
     else if (pymain->filename != NULL) {
-        pymain_run_file(pymain, config, &cf);
+        *exitcode = pymain_run_file(pymain, config, &cf);
     }
     else {
-        pymain_run_stdin(pymain, config, &cf);
+        *exitcode = pymain_run_stdin(pymain, config, &cf);
     }
 
-    pymain_repl(pymain, config, &cf);
+    pymain_repl(pymain, config, &cf, exitcode);
+    err = _Py_INIT_OK();
 
 done:
     Py_XDECREF(main_importer_path);
-    return res;
+    return err;
 }
 
 
-static int
+static _PyInitError
 pymain_cmdline_impl(_PyMain *pymain, _PyCoreConfig *config,
                     _PyCmdline *cmdline)
 {
-    pymain->err = _PyRuntime_Initialize();
-    if (_Py_INIT_FAILED(pymain->err)) {
-        return -1;
-    }
+    _PyInitError err;
 
-    int res = pymain_read_conf(pymain, config, cmdline);
-    if (res < 0) {
-        return -1;
+    err = _PyRuntime_Initialize();
+    if (_Py_INIT_FAILED(err)) {
+        return err;
     }
-    if (res > 0) {
-        /* --help or --version command: we are done */
-        return 1;
+
+    err = pymain_read_conf(pymain, config, cmdline);
+    if (_Py_INIT_FAILED(err)) {
+        return err;
     }
 
     if (cmdline->print_help) {
         pymain_usage(0, config->program);
-        return 1;
+        return _Py_INIT_EXIT(0);
     }
 
     if (cmdline->print_version) {
         printf("Python %s\n",
                (cmdline->print_version >= 2) ? Py_GetVersion() : PY_VERSION);
-        return 1;
+        return _Py_INIT_EXIT(0);
     }
 
     /* For Py_GetArgcArgv(). Cleared by pymain_free(). */
     orig_argv = _Py_wstrlist_copy(pymain->argc, cmdline->argv);
     if (orig_argv == NULL) {
-        pymain->err = _Py_INIT_NO_MEMORY();
-        return -1;
+        return _Py_INIT_NO_MEMORY();
     }
     orig_argc = pymain->argc;
-    return 0;
+    return _Py_INIT_OK();
 }
 
 
@@ -1746,7 +1689,7 @@ pymain_cmdline_impl(_PyMain *pymain, _PyCoreConfig *config,
 
    _PyCmdline is a temporary structure used to prioritize these
    variables. */
-static int
+static _PyInitError
 pymain_cmdline(_PyMain *pymain, _PyCoreConfig *config)
 {
     /* Force default allocator, since pymain_free() and pymain_clear_config()
@@ -1761,7 +1704,7 @@ pymain_cmdline(_PyMain *pymain, _PyCoreConfig *config)
     _PyCmdline cmdline;
     memset(&cmdline, 0, sizeof(cmdline));
 
-    int res = pymain_cmdline_impl(pymain, config, &cmdline);
+    _PyInitError err = pymain_cmdline_impl(pymain, config, &cmdline);
 
     pymain_clear_cmdline(pymain, &cmdline);
 
@@ -1772,13 +1715,15 @@ pymain_cmdline(_PyMain *pymain, _PyCoreConfig *config)
     assert(memcmp(&cur_alloc, &default_alloc, sizeof(cur_alloc)) == 0);
 #endif
     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
-    return res;
+    return err;
 }
 
 
-static int
+static _PyInitError
 pymain_init(_PyMain *pymain, PyInterpreterState **interp_p)
 {
+    _PyInitError err;
+
     /* 754 requires that FP exceptions run in "no stop" mode by default,
      * and until C vendors implement C99's ways to control FP exceptions,
      * Python requires non-stop mode.  Alas, some platforms enable FP
@@ -1793,13 +1738,9 @@ pymain_init(_PyMain *pymain, PyInterpreterState **interp_p)
 
     _PyCoreConfig_GetGlobalConfig(config);
 
-    int cmd_res = pymain_cmdline(pymain, config);
-    if (cmd_res < 0) {
-        _Py_FatalInitError(pymain->err);
-    }
-    if (cmd_res == 1) {
-        pymain_clear_config(config);
-        return 1;
+    err = pymain_cmdline(pymain, config);
+    if (_Py_INIT_FAILED(err)) {
+        goto done;
     }
 
     _PyCoreConfig_SetGlobalConfig(config);
@@ -1807,37 +1748,46 @@ pymain_init(_PyMain *pymain, PyInterpreterState **interp_p)
     pymain_init_stdio(pymain, config);
 
     PyInterpreterState *interp;
-    pymain->err = _Py_InitializeCore(&interp, config);
-    if (_Py_INIT_FAILED(pymain->err)) {
-        _Py_FatalInitError(pymain->err);
+    err = _Py_InitializeCore(&interp, config);
+    if (_Py_INIT_FAILED(err)) {
+        goto done;
     }
     *interp_p = interp;
 
-    pymain_clear_config(config);
-    config = &interp->core_config;
-
-    if (pymain_init_python_main(pymain, config, interp) < 0) {
-        _Py_FatalInitError(pymain->err);
+    err = pymain_init_python_main(pymain, interp);
+    if (_Py_INIT_FAILED(err)) {
+        goto done;
     }
-    return 0;
+
+    err = _Py_INIT_OK();
+
+done:
+    pymain_clear_config(config);
+    return err;
 }
 
 
 static int
 pymain_main(_PyMain *pymain)
 {
+    _PyInitError err;
+
     PyInterpreterState *interp;
-    int res = pymain_init(pymain, &interp);
-    if (res != 1) {
-        if (pymain_run_python(pymain, interp) < 0) {
-            _Py_FatalInitError(pymain->err);
-        }
+    err = pymain_init(pymain, &interp);
+    if (_Py_INIT_FAILED(err)) {
+        _Py_ExitInitError(err);
+    }
 
-        if (Py_FinalizeEx() < 0) {
-            /* Value unlikely to be confused with a non-error exit status or
-               other special meaning */
-            pymain->status = 120;
-        }
+    int exitcode = 0;
+    err = pymain_run_python(pymain, interp, &exitcode);
+    if (_Py_INIT_FAILED(err)) {
+        _Py_ExitInitError(err);
+    }
+
+    if (Py_FinalizeEx() < 0) {
+        /* Value unlikely to be confused with a non-error exit status or
+           other special meaning */
+        exitcode = 120;
     }
 
     pymain_free(pymain);
@@ -1859,20 +1809,22 @@ pymain_main(_PyMain *pymain)
 #ifdef MS_WINDOWS
         /* cmd.exe detects this, prints ^C, and offers to terminate. */
         /* https://msdn.microsoft.com/en-us/library/cc704588.aspx */
-        pymain->status = STATUS_CONTROL_C_EXIT;
+        exitcode = STATUS_CONTROL_C_EXIT;
 #else
-        pymain->status = SIGINT + 128;
+        exitcode = SIGINT + 128;
 #endif  /* !MS_WINDOWS */
     }
 
-    return pymain->status;
+    return exitcode;
 }
 
 
 int
 Py_Main(int argc, wchar_t **argv)
 {
-    _PyMain pymain = _PyMain_INIT;
+    _PyMain pymain;
+    memset(&pymain, 0, sizeof(pymain));
+
     pymain.use_bytes_argv = 0;
     pymain.argc = argc;
     pymain.wchar_argv = argv;
@@ -1884,7 +1836,9 @@ Py_Main(int argc, wchar_t **argv)
 int
 _Py_UnixMain(int argc, char **argv)
 {
-    _PyMain pymain = _PyMain_INIT;
+    _PyMain pymain;
+    memset(&pymain, 0, sizeof(pymain));
+
     pymain.use_bytes_argv = 1;
     pymain.argc = argc;
     pymain.bytes_argv = argv;
diff --git a/Programs/_freeze_importlib.c b/Programs/_freeze_importlib.c
index 3f43757ecb1f..d0e40c96a7a8 100644
--- a/Programs/_freeze_importlib.c
+++ b/Programs/_freeze_importlib.c
@@ -89,7 +89,7 @@ main(int argc, char *argv[])
     /* No need to call _PyCoreConfig_Clear() since we didn't allocate any
        memory: program_name is a constant string. */
     if (_Py_INIT_FAILED(err)) {
-        _Py_FatalInitError(err);
+        _Py_ExitInitError(err);
     }
 
     sprintf(buf, "<frozen %s>", name);
diff --git a/Programs/_testembed.c b/Programs/_testembed.c
index e28d94c175e6..6b5311be27ac 100644
--- a/Programs/_testembed.c
+++ b/Programs/_testembed.c
@@ -553,7 +553,7 @@ static int test_init_from_config(void)
     _PyInitError err = _Py_InitializeFromConfig(&config);
     /* Don't call _PyCoreConfig_Clear() since all strings are static */
     if (_Py_INIT_FAILED(err)) {
-        _Py_FatalInitError(err);
+        _Py_ExitInitError(err);
     }
     dump_config();
     Py_Finalize();
@@ -618,7 +618,7 @@ static int test_init_isolated(void)
     test_init_env_putenvs();
     _PyInitError err = _Py_InitializeFromConfig(&config);
     if (_Py_INIT_FAILED(err)) {
-        _Py_FatalInitError(err);
+        _Py_ExitInitError(err);
     }
     dump_config();
     Py_Finalize();
@@ -635,7 +635,7 @@ static int test_init_dev_mode(void)
     config.program_name = L"./_testembed";
     _PyInitError err = _Py_InitializeFromConfig(&config);
     if (_Py_INIT_FAILED(err)) {
-        _Py_FatalInitError(err);
+        _Py_ExitInitError(err);
     }
     dump_config();
     Py_Finalize();
diff --git a/Python/frozenmain.c b/Python/frozenmain.c
index 616090965b13..6554aa75b038 100644
--- a/Python/frozenmain.c
+++ b/Python/frozenmain.c
@@ -86,7 +86,7 @@ Py_FrozenMain(int argc, char **argv)
     /* No need to call _PyCoreConfig_Clear() since we didn't allocate any
        memory: program_name is a constant string. */
     if (_Py_INIT_FAILED(err)) {
-        _Py_FatalInitError(err);
+        _Py_ExitInitError(err);
     }
 
 #ifdef MS_WINDOWS
diff --git a/Python/pathconfig.c b/Python/pathconfig.c
index eadc09baa9f1..f96f0153e210 100644
--- a/Python/pathconfig.c
+++ b/Python/pathconfig.c
@@ -408,7 +408,7 @@ pathconfig_global_init(void)
 
 error:
     _PyCoreConfig_Clear(&config);
-    _Py_FatalInitError(err);
+    _Py_ExitInitError(err);
 }
 
 
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 1d65d3b86f72..088e7aa9313d 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -960,7 +960,7 @@ Py_InitializeEx(int install_sigs)
     _PyCoreConfig_Clear(&config);
 
     if (_Py_INIT_FAILED(err)) {
-        _Py_FatalInitError(err);
+        _Py_ExitInitError(err);
     }
 }
 
@@ -1432,7 +1432,7 @@ Py_NewInterpreter(void)
     PyThreadState *tstate;
     _PyInitError err = new_interpreter(&tstate);
     if (_Py_INIT_FAILED(err)) {
-        _Py_FatalInitError(err);
+        _Py_ExitInitError(err);
     }
     return tstate;
 
@@ -2073,12 +2073,17 @@ Py_FatalError(const char *msg)
 }
 
 void _Py_NO_RETURN
-_Py_FatalInitError(_PyInitError err)
+_Py_ExitInitError(_PyInitError err)
 {
-    /* On "user" error: exit with status 1.
-       For all other errors, call abort(). */
-    int status = err.user_err ? 1 : -1;
-    fatal_error(err.prefix, err.msg, status);
+    if (err.exitcode >= 0) {
+        exit(err.exitcode);
+    }
+    else {
+        /* On "user" error: exit with status 1.
+           For all other errors, call abort(). */
+        int status = err.user_err ? 1 : -1;
+        fatal_error(err.prefix, err.msg, status);
+    }
 }
 
 /* Clean up and exit */



More information about the Python-checkins mailing list