[Python-checkins] bpo-34170: Add _Py_InitializeFromConfig() (GH-8454)

Victor Stinner webhook-mailer at python.org
Tue Jul 24 20:49:20 EDT 2018


https://github.com/python/cpython/commit/1dc6e3906acb81163725e98378bf4d1bd1ce771a
commit: 1dc6e3906acb81163725e98378bf4d1bd1ce771a
branch: master
author: Victor Stinner <vstinner at redhat.com>
committer: GitHub <noreply at github.com>
date: 2018-07-25T02:49:17+02:00
summary:

bpo-34170: Add _Py_InitializeFromConfig() (GH-8454)

* If _Py_InitializeCore() is called twice, the second call now copies
  and apply (partially) the new configuration.
* Rename _Py_CommandLineDetails to _PyCmdline
* Move more code into pymain_init(). The core configuration created
  by Py_Main() is new destroyed before running Python to reduce the
  memory footprint.
* _Py_InitializeCore() now returns the created interpreter.
  _Py_InitializeMainInterpreter() now expects an interpreter.
* Remove _Py_InitializeEx_Private(): _freeze_importlib now uses
  _Py_InitializeFromConfig()
* _PyCoreConfig_InitPathConfig() now only computes the path
  configuration if needed.

files:
M Include/pylifecycle.h
M Include/pystate.h
M Modules/main.c
M Programs/_freeze_importlib.c
M Python/pathconfig.c
M Python/pylifecycle.c

diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h
index 3e43a980ddb1..6e5d7964fd22 100644
--- a/Include/pylifecycle.h
+++ b/Include/pylifecycle.h
@@ -51,7 +51,9 @@ PyAPI_FUNC(int) Py_SetStandardStreamEncoding(const char *encoding,
                                              const char *errors);
 
 /* PEP 432 Multi-phase initialization API (Private while provisional!) */
-PyAPI_FUNC(_PyInitError) _Py_InitializeCore(const _PyCoreConfig *);
+PyAPI_FUNC(_PyInitError) _Py_InitializeCore(
+    PyInterpreterState **interp,
+    const _PyCoreConfig *);
 PyAPI_FUNC(int) _Py_IsCoreInitialized(void);
 
 PyAPI_FUNC(_PyInitError) _PyCoreConfig_Read(_PyCoreConfig *config);
@@ -73,14 +75,17 @@ PyAPI_FUNC(int) _PyMainInterpreterConfig_Copy(
     _PyMainInterpreterConfig *config,
     const _PyMainInterpreterConfig *config2);
 
-PyAPI_FUNC(_PyInitError) _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *);
+PyAPI_FUNC(_PyInitError) _Py_InitializeMainInterpreter(
+    PyInterpreterState *interp,
+    const _PyMainInterpreterConfig *);
 #endif
 
 /* Initialization and finalization */
 PyAPI_FUNC(void) Py_Initialize(void);
 PyAPI_FUNC(void) Py_InitializeEx(int);
 #ifndef Py_LIMITED_API
-PyAPI_FUNC(_PyInitError) _Py_InitializeEx_Private(int, int);
+PyAPI_FUNC(_PyInitError) _Py_InitializeFromConfig(
+    const _PyCoreConfig *config);
 PyAPI_FUNC(void) _Py_FatalInitError(_PyInitError err) _Py_NO_RETURN;
 #endif
 PyAPI_FUNC(void) Py_Finalize(void);
diff --git a/Include/pystate.h b/Include/pystate.h
index 84428bc7931d..fe14832fcff3 100644
--- a/Include/pystate.h
+++ b/Include/pystate.h
@@ -200,8 +200,7 @@ typedef struct {
     /* --- Private fields -------- */
 
     /* Install importlib? If set to 0, importlib is not initialized at all.
-       Needed by freeze_importlib: see install_importlib argument of
-       _Py_InitializeEx_Private(). */
+       Needed by freeze_importlib. */
     int _install_importlib;
 
     /* Value of the --check-hash-based-pycs configure option. Valid values:
diff --git a/Modules/main.c b/Modules/main.c
index 73ffecdb8515..8c29ef8b89fa 100644
--- a/Modules/main.c
+++ b/Modules/main.c
@@ -421,7 +421,7 @@ typedef struct {
     wchar_t **env_warnoptions;   /* PYTHONWARNINGS environment variables */
     int print_help;              /* -h, -? options */
     int print_version;           /* -V option */
-} _Py_CommandLineDetails;
+} _PyCmdline;
 
 /* Structure used by Py_Main() to pass data to subfunctions */
 typedef struct {
@@ -443,14 +443,10 @@ typedef struct {
     wchar_t *command;            /* -c argument */
     wchar_t *module;             /* -m argument */
 
-    _PyCoreConfig config;
-
     PyObject *main_importer_path;
 } _PyMain;
 
-#define _PyMain_INIT \
-    {.config = _PyCoreConfig_INIT, \
-     .err = _Py_INIT_OK()}
+#define _PyMain_INIT {.err = _Py_INIT_OK()}
 /* Note: _PyMain_INIT sets other fields to 0/NULL */
 
 
@@ -502,7 +498,8 @@ pymain_wstrdup(_PyMain *pymain, const wchar_t *str)
 
 
 static int
-pymain_init_cmdline_argv(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
+pymain_init_cmdline_argv(_PyMain *pymain, _PyCoreConfig *config,
+                         _PyCmdline *cmdline)
 {
     assert(cmdline->argv == NULL);
 
@@ -541,8 +538,8 @@ pymain_init_cmdline_argv(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
     else {
         program = L"";
     }
-    pymain->config.program = pymain_wstrdup(pymain, program);
-    if (pymain->config.program == NULL) {
+    config->program = pymain_wstrdup(pymain, program);
+    if (config->program == NULL) {
         return -1;
     }
 
@@ -767,7 +764,7 @@ _PyCoreConfig_Copy(_PyCoreConfig *config, const _PyCoreConfig *config2)
 
 
 static void
-pymain_clear_cmdline(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
+pymain_clear_cmdline(_PyMain *pymain, _PyCmdline *cmdline)
 {
     PyMemAllocatorEx old_alloc;
     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
@@ -805,14 +802,14 @@ pymain_clear_pymain(_PyMain *pymain)
 }
 
 static void
-pymain_clear_config(_PyMain *pymain)
+pymain_clear_config(_PyCoreConfig *config)
 {
     /* Clear core config with the memory allocator
        used by pymain_read_conf() */
     PyMemAllocatorEx old_alloc;
     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
 
-    _PyCoreConfig_Clear(&pymain->config);
+    _PyCoreConfig_Clear(config);
 
     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
 }
@@ -849,8 +846,6 @@ pymain_free_raw(_PyMain *pymain)
        Py_Initialize()-Py_Finalize() can be called multiple times. */
     _PyPathConfig_ClearGlobal();
 
-    pymain_clear_config(pymain);
-
     /* Force the allocator used by pymain_read_conf() */
     PyMemAllocatorEx old_alloc;
     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
@@ -940,10 +935,9 @@ pymain_wstrlist_append(_PyMain *pymain, int *len, wchar_t ***list, const wchar_t
    Return 1 if parsing failed.
    Set pymain->err and return -1 on other errors. */
 static int
-pymain_parse_cmdline_impl(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
+pymain_parse_cmdline_impl(_PyMain *pymain, _PyCoreConfig *config,
+                          _PyCmdline *cmdline)
 {
-    _PyCoreConfig *config = &pymain->config;
-
     _PyOS_ResetGetOpt();
     do {
         int longindex = -1;
@@ -1185,7 +1179,7 @@ config_add_warnings_optlist(_PyCoreConfig *config, int len, wchar_t **options)
 
 
 static _PyInitError
-config_init_warnoptions(_PyCoreConfig *config, _Py_CommandLineDetails *cmdline)
+config_init_warnoptions(_PyCoreConfig *config, _PyCmdline *cmdline)
 {
     _PyInitError err;
 
@@ -1256,14 +1250,14 @@ config_init_warnoptions(_PyCoreConfig *config, _Py_CommandLineDetails *cmdline)
    Return 0 on success.
    Set pymain->err and return -1 on error. */
 static _PyInitError
-cmdline_init_env_warnoptions(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
+cmdline_init_env_warnoptions(_PyMain *pymain, _PyCoreConfig *config, _PyCmdline *cmdline)
 {
-    if (pymain->config.ignore_environment) {
+    if (config->ignore_environment) {
         return _Py_INIT_OK();
     }
 
     wchar_t *env;
-    int res = config_get_env_var_dup(&pymain->config, &env,
+    int res = config_get_env_var_dup(config, &env,
                                      L"PYTHONWARNINGS", "PYTHONWARNINGS");
     if (res < 0) {
         return DECODE_LOCALE_ERR("PYTHONWARNINGS", res);
@@ -1293,10 +1287,8 @@ cmdline_init_env_warnoptions(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
 
 
 static void
-pymain_init_stdio(_PyMain *pymain)
+pymain_init_stdio(_PyMain *pymain, _PyCoreConfig *config)
 {
-    _PyCoreConfig *config = &pymain->config;
-
     pymain->stdin_is_interactive = (isatty(fileno(stdin))
                                     || config->interactive);
 
@@ -1339,8 +1331,6 @@ pymain_init_stdio(_PyMain *pymain)
 static _PyInitError
 config_init_program_name(_PyCoreConfig *config)
 {
-    assert(config->program_name == NULL);
-
     /* If Py_SetProgramName() was called, use its value */
     const wchar_t *program_name = _Py_path_config.program_name;
     if (program_name != NULL) {
@@ -1434,7 +1424,7 @@ pymain_header(_PyMain *pymain)
 
 
 static int
-pymain_init_core_argv(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
+pymain_init_core_argv(_PyMain *pymain, _PyCoreConfig *config, _PyCmdline *cmdline)
 {
     /* Copy argv to be able to modify it (to force -c/-m) */
     int argc = pymain->argc - _PyOS_optind;
@@ -1477,8 +1467,8 @@ pymain_init_core_argv(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
         argv[0] = arg0;
     }
 
-    pymain->config.argc = argc;
-    pymain->config.argv = argv;
+    config->argc = argc;
+    config->argv = argv;
     return 0;
 }
 
@@ -1506,7 +1496,7 @@ wstrlist_as_pylist(int len, wchar_t **list)
 
 
 static int
-pymain_compute_path0(_PyMain *pymain, PyObject **path0)
+pymain_compute_path0(_PyMain *pymain, _PyCoreConfig *config, PyObject **path0)
 {
     if (pymain->main_importer_path != NULL) {
         /* Let pymain_run_main_from_importer() adjust sys.path[0] later */
@@ -1519,8 +1509,8 @@ pymain_compute_path0(_PyMain *pymain, PyObject **path0)
         return 0;
     }
 
-    *path0 = _PyPathConfig_ComputeArgv0(pymain->config.argc,
-                                        pymain->config.argv);
+    *path0 = _PyPathConfig_ComputeArgv0(config->argc,
+                                        config->argv);
     if (*path0 == NULL) {
         pymain->err = _Py_INIT_NO_MEMORY();
         return -1;
@@ -1573,7 +1563,7 @@ pymain_import_readline(_PyMain *pymain)
 
 
 static FILE*
-pymain_open_filename(_PyMain *pymain)
+pymain_open_filename(_PyMain *pymain, _PyCoreConfig *config)
 {
     FILE* fp;
 
@@ -1588,7 +1578,7 @@ pymain_open_filename(_PyMain *pymain)
         else
             cfilename = "<unprintable file name>";
         fprintf(stderr, "%ls: can't open file '%s': [Errno %d] %s\n",
-                pymain->config.program, cfilename, err, strerror(err));
+                config->program, cfilename, err, strerror(err));
         PyMem_RawFree(cfilename_buffer);
         pymain->status = 2;
         return NULL;
@@ -1611,7 +1601,7 @@ pymain_open_filename(_PyMain *pymain)
             S_ISDIR(sb.st_mode)) {
         fprintf(stderr,
                 "%ls: '%ls' is a directory, cannot continue\n",
-                pymain->config.program, pymain->filename);
+                config->program, pymain->filename);
         fclose(fp);
         pymain->status = 1;
         return NULL;
@@ -1622,9 +1612,9 @@ pymain_open_filename(_PyMain *pymain)
 
 
 static void
-pymain_run_startup(_PyMain *pymain, PyCompilerFlags *cf)
+pymain_run_startup(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf)
 {
-    const char *startup = config_get_env_var(&pymain->config, "PYTHONSTARTUP");
+    const char *startup = config_get_env_var(config, "PYTHONSTARTUP");
     if (startup == NULL) {
         return;
     }
@@ -1649,11 +1639,11 @@ pymain_run_startup(_PyMain *pymain, PyCompilerFlags *cf)
 
 
 static void
-pymain_run_filename(_PyMain *pymain, PyCompilerFlags *cf)
+pymain_run_filename(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf)
 {
     if (pymain->filename == NULL && pymain->stdin_is_interactive) {
         Py_InspectFlag = 0; /* do exit on SystemExit */
-        pymain_run_startup(pymain, cf);
+        pymain_run_startup(pymain, config, cf);
         pymain_run_interactive_hook();
     }
 
@@ -1664,7 +1654,7 @@ pymain_run_filename(_PyMain *pymain, PyCompilerFlags *cf)
 
     FILE *fp;
     if (pymain->filename != NULL) {
-        fp = pymain_open_filename(pymain);
+        fp = pymain_open_filename(pymain, config);
         if (fp == NULL) {
             return;
         }
@@ -1678,11 +1668,11 @@ pymain_run_filename(_PyMain *pymain, PyCompilerFlags *cf)
 
 
 static void
-pymain_repl(_PyMain *pymain, PyCompilerFlags *cf)
+pymain_repl(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf)
 {
     /* Check this environment variable at the end, to give programs the
        opportunity to set it from Python. */
-    if (!Py_InspectFlag && config_get_env_var(&pymain->config, "PYTHONINSPECT")) {
+    if (!Py_InspectFlag && config_get_env_var(config, "PYTHONINSPECT")) {
         Py_InspectFlag = 1;
     }
 
@@ -1705,14 +1695,15 @@ pymain_repl(_PyMain *pymain, PyCompilerFlags *cf)
    Return 0 on success.
    Set pymain->err and return -1 on failure. */
 static int
-pymain_parse_cmdline(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
+pymain_parse_cmdline(_PyMain *pymain, _PyCoreConfig *config,
+                     _PyCmdline *cmdline)
 {
-    int res = pymain_parse_cmdline_impl(pymain, cmdline);
+    int res = pymain_parse_cmdline_impl(pymain, config, cmdline);
     if (res < 0) {
         return -1;
     }
     if (res) {
-        pymain_usage(1, pymain->config.program);
+        pymain_usage(1, config->program);
         pymain->status = 2;
         return 1;
     }
@@ -1881,10 +1872,9 @@ get_env_flag(_PyCoreConfig *config, int *flag, const char *name)
 
 
 static void
-cmdline_get_env_flags(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
+cmdline_get_env_flags(_PyMain *pymain, _PyCoreConfig *config,
+                      _PyCmdline *cmdline)
 {
-    _PyCoreConfig *config = &pymain->config;
-
     get_env_flag(config, &config->debug, "PYTHONDEBUG");
     get_env_flag(config, &config->verbose, "PYTHONVERBOSE");
     get_env_flag(config, &config->optimization_level, "PYTHONOPTIMIZE");
@@ -2085,20 +2075,20 @@ config_read_complex_options(_PyCoreConfig *config)
    Return 1 if Python is done and must exit.
    Set pymain->err and return -1 on error. */
 static int
-pymain_read_conf_impl(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
+pymain_read_conf_impl(_PyMain *pymain, _PyCoreConfig *config,
+                      _PyCmdline *cmdline)
 {
-    _PyCoreConfig *config = &pymain->config;
     _PyInitError err;
 
-    int res = pymain_parse_cmdline(pymain, cmdline);
+    int res = pymain_parse_cmdline(pymain, config, cmdline);
     if (res != 0) {
         return res;
     }
 
     /* Get environment variables */
-    cmdline_get_env_flags(pymain, cmdline);
+    cmdline_get_env_flags(pymain, config, cmdline);
 
-    err = cmdline_init_env_warnoptions(pymain, cmdline);
+    err = cmdline_init_env_warnoptions(pymain, config, cmdline);
     if (_Py_INIT_FAILED(err)) {
         pymain->err = err;
         return -1;
@@ -2110,12 +2100,11 @@ pymain_read_conf_impl(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
     }
 #endif
 
-    if (pymain_init_core_argv(pymain, cmdline) < 0) {
+    if (pymain_init_core_argv(pymain, config, cmdline) < 0) {
         return -1;
     }
 
     err = _PyCoreConfig_Read(config);
-
     if (_Py_INIT_FAILED(err)) {
         pymain->err = err;
         return -1;
@@ -2127,9 +2116,9 @@ pymain_read_conf_impl(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
 /* Read the configuration, but initialize also the LC_CTYPE locale:
    enable UTF-8 mode (PEP 540) and/or coerce the C locale (PEP 538) */
 static int
-pymain_read_conf(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
+pymain_read_conf(_PyMain *pymain, _PyCoreConfig *config,
+                 _PyCmdline *cmdline)
 {
-    _PyCoreConfig *config = &pymain->config;
     _PyCoreConfig save_config = _PyCoreConfig_INIT;
     char *oldloc = NULL;
     int res = -1;
@@ -2163,11 +2152,11 @@ pymain_read_conf(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
             goto done;
         }
 
-        if (pymain_init_cmdline_argv(pymain, cmdline) < 0) {
+        if (pymain_init_cmdline_argv(pymain, config, cmdline) < 0) {
             goto done;
         }
 
-        int conf_res = pymain_read_conf_impl(pymain, cmdline);
+        int conf_res = pymain_read_conf_impl(pymain, config, cmdline);
         if (conf_res != 0) {
             res = conf_res;
             goto done;
@@ -2214,7 +2203,7 @@ pymain_read_conf(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
         }
         pymain_clear_cmdline(pymain, cmdline);
         memset(cmdline, 0, sizeof(*cmdline));
-        pymain->config.utf8_mode = new_utf8_mode;
+        config->utf8_mode = new_utf8_mode;
 
         /* The encoding changed: read again the configuration
            with the new encoding */
@@ -2307,9 +2296,11 @@ _PyCoreConfig_Read(_PyCoreConfig *config)
         return err;
     }
 
-    err = config_init_program_name(config);
-    if (_Py_INIT_FAILED(err)) {
-        return err;
+    if (config->program_name == NULL) {
+        err = config_init_program_name(config);
+        if (_Py_INIT_FAILED(err)) {
+            return err;
+        }
     }
 
     config_init_locale(config);
@@ -2419,7 +2410,7 @@ _PyMainInterpreterConfig_Read(_PyMainInterpreterConfig *main_config,
 
 #define COPY_WSTR(ATTR) \
     do { \
-        if (main_config->ATTR == NULL) { \
+        if (main_config->ATTR == NULL && config->ATTR != NULL) { \
             main_config->ATTR = PyUnicode_FromWideChar(config->ATTR, -1); \
             if (main_config->ATTR == NULL) { \
                 return _Py_INIT_NO_MEMORY(); \
@@ -2468,14 +2459,15 @@ _PyMainInterpreterConfig_Read(_PyMainInterpreterConfig *main_config,
 
 
 static int
-pymain_init_python_main(_PyMain *pymain)
+pymain_init_python_main(_PyMain *pymain, _PyCoreConfig *config,
+                        PyInterpreterState *interp)
 {
     _PyInitError err;
 
     _PyMainInterpreterConfig main_config = _PyMainInterpreterConfig_INIT;
-    err = _PyMainInterpreterConfig_Read(&main_config, &pymain->config);
+    err = _PyMainInterpreterConfig_Read(&main_config, config);
     if (!_Py_INIT_FAILED(err)) {
-        err = _Py_InitializeMainInterpreter(&main_config);
+        err = _Py_InitializeMainInterpreter(interp, &main_config);
     }
     _PyMainInterpreterConfig_Clear(&main_config);
 
@@ -2488,7 +2480,7 @@ pymain_init_python_main(_PyMain *pymain)
 
 
 static int
-pymain_init_sys_path(_PyMain *pymain)
+pymain_init_sys_path(_PyMain *pymain, _PyCoreConfig *config)
 {
     if (pymain->filename != NULL) {
         /* If filename is a package (ex: directory or ZIP file) which contains
@@ -2499,12 +2491,10 @@ pymain_init_sys_path(_PyMain *pymain)
     }
 
     PyObject *path0;
-    if (pymain_compute_path0(pymain, &path0) < 0) {
+    if (pymain_compute_path0(pymain, config, &path0) < 0) {
         return -1;
     }
 
-    pymain_clear_config(pymain);
-
     if (path0 != NULL) {
         if (pymain_update_sys_path(pymain, path0) < 0) {
             Py_DECREF(path0);
@@ -2520,6 +2510,7 @@ static void
 pymain_run_python(_PyMain *pymain)
 {
     PyCompilerFlags cf = {.cf_flags = 0};
+    _PyCoreConfig *config = &PyThreadState_GET()->interp->core_config;
 
     pymain_header(pymain);
     pymain_import_readline(pymain);
@@ -2531,38 +2522,23 @@ pymain_run_python(_PyMain *pymain)
         pymain->status = (pymain_run_module(pymain->module, 1) != 0);
     }
     else {
-        pymain_run_filename(pymain, &cf);
+        pymain_run_filename(pymain, config, &cf);
     }
 
-    pymain_repl(pymain, &cf);
-}
-
-
-static void
-pymain_init(_PyMain *pymain)
-{
-    /* 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
-     * exceptions by default.  Here we disable them.
-     */
-#ifdef __FreeBSD__
-    fedisableexcept(FE_OVERFLOW);
-#endif
-
-    pymain->config.install_signal_handlers = 1;
+    pymain_repl(pymain, config, &cf);
 }
 
 
 static int
-pymain_cmdline_impl(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
+pymain_cmdline_impl(_PyMain *pymain, _PyCoreConfig *config,
+                    _PyCmdline *cmdline)
 {
     pymain->err = _PyRuntime_Initialize();
     if (_Py_INIT_FAILED(pymain->err)) {
         return -1;
     }
 
-    int res = pymain_read_conf(pymain, cmdline);
+    int res = pymain_read_conf(pymain, config, cmdline);
     if (res < 0) {
         return -1;
     }
@@ -2572,7 +2548,7 @@ pymain_cmdline_impl(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
     }
 
     if (cmdline->print_help) {
-        pymain_usage(0, pymain->config.program);
+        pymain_usage(0, config->program);
         return 1;
     }
 
@@ -2590,7 +2566,7 @@ pymain_cmdline_impl(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
     }
     orig_argc = pymain->argc;
 
-    _PyInitError err = config_init_warnoptions(&pymain->config, cmdline);
+    _PyInitError err = config_init_warnoptions(config, cmdline);
     if (_Py_INIT_FAILED(err)) {
         pymain->err = err;
         return -1;
@@ -2608,10 +2584,10 @@ pymain_cmdline_impl(_PyMain *pymain, _Py_CommandLineDetails *cmdline)
    * Environment variables
    * Py_xxx global configuration variables
 
-   _Py_CommandLineDetails is a temporary structure used to prioritize these
+   _PyCmdline is a temporary structure used to prioritize these
    variables. */
 static int
-pymain_cmdline(_PyMain *pymain)
+pymain_cmdline(_PyMain *pymain, _PyCoreConfig *config)
 {
     /* Force default allocator, since pymain_free() and pymain_clear_config()
        must use the same allocator than this function. */
@@ -2622,10 +2598,10 @@ pymain_cmdline(_PyMain *pymain)
     PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &default_alloc);
 #endif
 
-    _Py_CommandLineDetails cmdline;
+    _PyCmdline cmdline;
     memset(&cmdline, 0, sizeof(cmdline));
 
-    int res = pymain_cmdline_impl(pymain, &cmdline);
+    int res = pymain_cmdline_impl(pymain, config, &cmdline);
 
     pymain_clear_cmdline(pymain, &cmdline);
 
@@ -2641,50 +2617,70 @@ pymain_cmdline(_PyMain *pymain)
 
 
 static int
-pymain_main(_PyMain *pymain)
+pymain_init(_PyMain *pymain)
 {
-    pymain_init(pymain);
+    /* 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
+     * exceptions by default.  Here we disable them.
+     */
+#ifdef __FreeBSD__
+    fedisableexcept(FE_OVERFLOW);
+#endif
 
-    _PyCoreConfig_GetGlobalConfig(&pymain->config);
+    _PyCoreConfig local_config = _PyCoreConfig_INIT;
+    _PyCoreConfig *config = &local_config;
+    config->install_signal_handlers = 1;
 
-    int res = pymain_cmdline(pymain);
-    if (res < 0) {
+    _PyCoreConfig_GetGlobalConfig(config);
+
+    int cmd_res = pymain_cmdline(pymain, config);
+    if (cmd_res < 0) {
         _Py_FatalInitError(pymain->err);
     }
-    if (res == 1) {
-        goto done;
+    if (cmd_res == 1) {
+        pymain_clear_config(config);
+        return 1;
     }
 
-    _PyCoreConfig_SetGlobalConfig(&pymain->config);
+    _PyCoreConfig_SetGlobalConfig(config);
 
-    pymain_init_stdio(pymain);
+    pymain_init_stdio(pymain, config);
 
-    /* bpo-34008: For backward compatibility reasons, calling Py_Main() after
-       Py_Initialize() ignores the new configuration. */
-    if (!_PyRuntime.initialized) {
-        pymain->err = _Py_InitializeCore(&pymain->config);
-        if (_Py_INIT_FAILED(pymain->err)) {
-            _Py_FatalInitError(pymain->err);
-        }
+    PyInterpreterState *interp;
+    pymain->err = _Py_InitializeCore(&interp, config);
+    if (_Py_INIT_FAILED(pymain->err)) {
+        _Py_FatalInitError(pymain->err);
     }
 
-    if (pymain_init_python_main(pymain) < 0) {
+    pymain_clear_config(config);
+    config = &interp->core_config;
+
+    if (pymain_init_python_main(pymain, config, interp) < 0) {
         _Py_FatalInitError(pymain->err);
     }
 
-    if (pymain_init_sys_path(pymain) < 0) {
+    if (pymain_init_sys_path(pymain, config) < 0) {
         _Py_FatalInitError(pymain->err);
     }
+    return 0;
+}
 
-    pymain_run_python(pymain);
 
-    if (Py_FinalizeEx() < 0) {
-        /* Value unlikely to be confused with a non-error exit status or
-           other special meaning */
-        pymain->status = 120;
+static int
+pymain_main(_PyMain *pymain)
+{
+    int res = pymain_init(pymain);
+    if (res != 1) {
+        pymain_run_python(pymain);
+
+        if (Py_FinalizeEx() < 0) {
+            /* Value unlikely to be confused with a non-error exit status or
+               other special meaning */
+            pymain->status = 120;
+        }
     }
 
-done:
     pymain_free(pymain);
 
     return pymain->status;
diff --git a/Programs/_freeze_importlib.c b/Programs/_freeze_importlib.c
index b8b630cfed36..03e082634995 100644
--- a/Programs/_freeze_importlib.c
+++ b/Programs/_freeze_importlib.c
@@ -74,14 +74,20 @@ main(int argc, char *argv[])
     }
     text[text_size] = '\0';
 
-    Py_NoUserSiteDirectory++;
-    Py_NoSiteFlag++;
-    Py_IgnoreEnvironmentFlag++;
+    _PyCoreConfig config = _PyCoreConfig_INIT;
+    config.user_site_directory = 0;
+    config.site_import = 0;
+    config.ignore_environment = 1;
+    config.program_name = L"./_freeze_importlib";
+    /* Don't install importlib, since it could execute outdated bytecode. */
+    config._install_importlib = 0;
+    config.install_signal_handlers = 1;
+
     Py_FrozenFlag++;
 
-    Py_SetProgramName(L"./_freeze_importlib");
-    /* Don't install importlib, since it could execute outdated bytecode. */
-    _PyInitError err = _Py_InitializeEx_Private(1, 0);
+    _PyInitError err = _Py_InitializeFromConfig(&config);
+    /* 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);
     }
diff --git a/Python/pathconfig.c b/Python/pathconfig.c
index 509ea8eb1899..4e0830f4cf9a 100644
--- a/Python/pathconfig.c
+++ b/Python/pathconfig.c
@@ -282,8 +282,8 @@ core_config_init_module_search_paths(_PyCoreConfig *config,
 }
 
 
-_PyInitError
-_PyCoreConfig_InitPathConfig(_PyCoreConfig *config)
+static _PyInitError
+_PyCoreConfig_CalculatePathConfig(_PyCoreConfig *config)
 {
     _PyPathConfig path_config = _PyPathConfig_INIT;
     _PyInitError err;
@@ -332,18 +332,6 @@ _PyCoreConfig_InitPathConfig(_PyCoreConfig *config)
     }
 #endif
 
-    if (config->base_prefix == NULL) {
-        if (copy_wstr(&config->base_prefix, config->prefix) < 0) {
-            goto no_memory;
-        }
-    }
-
-    if (config->base_exec_prefix == NULL) {
-        if (copy_wstr(&config->base_exec_prefix, config->exec_prefix) < 0) {
-            goto no_memory;
-        }
-    }
-
     if (path_config.isolated != -1) {
         config->isolated = path_config.isolated;
     }
@@ -363,6 +351,39 @@ _PyCoreConfig_InitPathConfig(_PyCoreConfig *config)
 }
 
 
+_PyInitError
+_PyCoreConfig_InitPathConfig(_PyCoreConfig *config)
+{
+    /* Do we need to calculate the path? */
+    if ((config->nmodule_search_path < 0)
+        || (config->executable == NULL)
+        || (config->prefix == NULL)
+#ifdef MS_WINDOWS
+        || (config->dll_path == NULL)
+#endif
+        || (config->exec_prefix == NULL))
+    {
+        _PyInitError err = _PyCoreConfig_CalculatePathConfig(config);
+        if (_Py_INIT_FAILED(err)) {
+            return err;
+        }
+    }
+
+    if (config->base_prefix == NULL) {
+        if (copy_wstr(&config->base_prefix, config->prefix) < 0) {
+            return _Py_INIT_NO_MEMORY();
+        }
+    }
+
+    if (config->base_exec_prefix == NULL) {
+        if (copy_wstr(&config->base_exec_prefix, config->exec_prefix) < 0) {
+            return _Py_INIT_NO_MEMORY();
+        }
+    }
+    return _Py_INIT_OK();
+}
+
+
 static void
 pathconfig_global_init(void)
 {
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index c88e945f8e27..0729a5f83b52 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -567,7 +567,7 @@ _Py_SetLocaleFromEnv(int category)
 /* Global initializations.  Can be undone by Py_Finalize().  Don't
    call this twice without an intervening Py_Finalize() call.
 
-   Every call to Py_InitializeCore, Py_Initialize or Py_InitializeEx
+   Every call to _Py_InitializeCore, Py_Initialize or Py_InitializeEx
    must have a corresponding call to Py_Finalize.
 
    Locking: you must hold the interpreter lock while calling these APIs.
@@ -576,6 +576,35 @@ _Py_SetLocaleFromEnv(int category)
 
 */
 
+static _PyInitError
+_Py_Initialize_ReconfigureCore(PyInterpreterState *interp,
+                               const _PyCoreConfig *core_config)
+{
+    if (core_config->allocator != NULL) {
+        const char *allocator = _PyMem_GetAllocatorsName();
+        if (allocator == NULL || strcmp(core_config->allocator, allocator) != 0) {
+            return _Py_INIT_USER_ERR("cannot modify memory allocator "
+                                     "after first Py_Initialize()");
+        }
+    }
+
+    _PyCoreConfig_SetGlobalConfig(core_config);
+
+    if (_PyCoreConfig_Copy(&interp->core_config, core_config) < 0) {
+        return _Py_INIT_ERR("failed to copy core config");
+    }
+    core_config = &interp->core_config;
+
+    if (core_config->_install_importlib) {
+        _PyInitError err = _PyCoreConfig_SetPathConfig(core_config);
+        if (_Py_INIT_FAILED(err)) {
+            return err;
+        }
+    }
+    return _Py_INIT_OK();
+}
+
+
 /* Begin interpreter initialization
  *
  * On return, the first thread and interpreter state have been created,
@@ -592,16 +621,41 @@ _Py_SetLocaleFromEnv(int category)
  * Any code invoked from this function should *not* assume it has access
  * to the Python C API (unless the API is explicitly listed as being
  * safe to call without calling Py_Initialize first)
+ *
+ * The caller is responsible to call _PyCoreConfig_Read().
  */
 
-_PyInitError
-_Py_InitializeCore(const _PyCoreConfig *core_config)
+static _PyInitError
+_Py_InitializeCore_impl(PyInterpreterState **interp_p,
+                        const _PyCoreConfig *core_config)
 {
-    assert(core_config != NULL);
+    PyInterpreterState *interp;
+    _PyInitError err;
+
+    /* bpo-34008: For backward compatibility reasons, calling Py_Main() after
+       Py_Initialize() ignores the new configuration. */
+    if (_PyRuntime.core_initialized) {
+        PyThreadState *tstate = PyThreadState_GET();
+        if (!tstate) {
+            return _Py_INIT_ERR("failed to read thread state");
+        }
+
+        interp = tstate->interp;
+        if (interp == NULL) {
+            return _Py_INIT_ERR("can't make main interpreter");
+        }
+        *interp_p = interp;
+
+        return _Py_Initialize_ReconfigureCore(interp, core_config);
+    }
+
+    if (_PyRuntime.initialized) {
+        return _Py_INIT_ERR("main interpreter already initialized");
+    }
 
     _PyCoreConfig_SetGlobalConfig(core_config);
 
-    _PyInitError err = _PyRuntime_Initialize();
+    err = _PyRuntime_Initialize();
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
@@ -612,13 +666,6 @@ _Py_InitializeCore(const _PyCoreConfig *core_config)
         }
     }
 
-    if (_PyRuntime.initialized) {
-        return _Py_INIT_ERR("main interpreter already initialized");
-    }
-    if (_PyRuntime.core_initialized) {
-        return _Py_INIT_ERR("runtime core already initialized");
-    }
-
     /* Py_Finalize leaves _Py_Finalizing set in order to help daemon
      * threads behave a little more gracefully at interpreter shutdown.
      * We clobber it here so the new interpreter can start with a clean
@@ -648,10 +695,11 @@ _Py_InitializeCore(const _PyCoreConfig *core_config)
         return err;
     }
 
-    PyInterpreterState *interp = PyInterpreterState_New();
+    interp = PyInterpreterState_New();
     if (interp == NULL) {
         return _Py_INIT_ERR("can't make main interpreter");
     }
+    *interp_p = interp;
 
     if (_PyCoreConfig_Copy(&interp->core_config, core_config) < 0) {
         return _Py_INIT_ERR("failed to copy core config");
@@ -773,6 +821,43 @@ _Py_InitializeCore(const _PyCoreConfig *core_config)
     return _Py_INIT_OK();
 }
 
+_PyInitError
+_Py_InitializeCore(PyInterpreterState **interp_p,
+                   const _PyCoreConfig *src_config)
+{
+    assert(src_config != NULL);
+
+
+    PyMemAllocatorEx old_alloc;
+    _PyInitError err;
+
+    /* Copy the configuration, since _PyCoreConfig_Read() modifies it
+       (and the input configuration is read only). */
+    _PyCoreConfig config = _PyCoreConfig_INIT;
+
+    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+    if (_PyCoreConfig_Copy(&config, src_config) >= 0) {
+        err = _PyCoreConfig_Read(&config);
+    }
+    else {
+        err = _Py_INIT_ERR("failed to copy core config");
+    }
+    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+
+    if (_Py_INIT_FAILED(err)) {
+        goto done;
+    }
+
+    err = _Py_InitializeCore_impl(interp_p, &config);
+
+done:
+    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+    _PyCoreConfig_Clear(&config);
+    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+
+    return err;
+}
+
 /* Py_Initialize() has already been called: update the main interpreter
    configuration. Example of bpo-34008: Py_Main() called after
    Py_Initialize(). */
@@ -801,27 +886,19 @@ _Py_ReconfigureMainInterpreter(PyInterpreterState *interp,
  * non-zero return code.
  */
 _PyInitError
-_Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config)
+_Py_InitializeMainInterpreter(PyInterpreterState *interp,
+                              const _PyMainInterpreterConfig *config)
 {
     if (!_PyRuntime.core_initialized) {
         return _Py_INIT_ERR("runtime core not initialized");
     }
 
-    /* Get current thread state and interpreter pointer */
-    PyThreadState *tstate = PyThreadState_GET();
-    if (!tstate) {
-        return _Py_INIT_ERR("failed to read thread state");
-    }
-    PyInterpreterState *interp = tstate->interp;
-    if (!interp) {
-        return _Py_INIT_ERR("failed to get interpreter");
-    }
-    _PyCoreConfig *core_config = &interp->core_config;
-
-    /* Now finish configuring the main interpreter */
+    /* Configure the main interpreter */
     if (_PyMainInterpreterConfig_Copy(&interp->config, config) < 0) {
         return _Py_INIT_ERR("failed to copy main interpreter config");
     }
+    config = &interp->config;
+    _PyCoreConfig *core_config = &interp->core_config;
 
     if (_PyRuntime.initialized) {
         return _Py_ReconfigureMainInterpreter(interp, config);
@@ -908,51 +985,44 @@ _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config)
 #undef _INIT_DEBUG_PRINT
 
 _PyInitError
-_Py_InitializeEx_Private(int install_sigs, int install_importlib)
+_Py_InitializeFromConfig(const _PyCoreConfig *config)
 {
-    if (_PyRuntime.initialized) {
-        /* bpo-33932: Calling Py_Initialize() twice does nothing. */
-        return _Py_INIT_OK();
-    }
-
-    _PyCoreConfig config = _PyCoreConfig_INIT;
+    PyInterpreterState *interp;
     _PyInitError err;
-
-    config._install_importlib = install_importlib;
-    config.install_signal_handlers = install_sigs;
-
-    err = _PyCoreConfig_Read(&config);
-    if (_Py_INIT_FAILED(err)) {
-        goto done;
-    }
-
-    err = _Py_InitializeCore(&config);
+    err = _Py_InitializeCore(&interp, config);
     if (_Py_INIT_FAILED(err)) {
-        goto done;
+        return err;
     }
+    config = &interp->core_config;
 
     _PyMainInterpreterConfig main_config = _PyMainInterpreterConfig_INIT;
-    err = _PyMainInterpreterConfig_Read(&main_config, &config);
+    err = _PyMainInterpreterConfig_Read(&main_config, config);
     if (!_Py_INIT_FAILED(err)) {
-        err = _Py_InitializeMainInterpreter(&main_config);
+        err = _Py_InitializeMainInterpreter(interp, &main_config);
     }
     _PyMainInterpreterConfig_Clear(&main_config);
     if (_Py_INIT_FAILED(err)) {
-        goto done;
+        return err;
     }
-
-    err = _Py_INIT_OK();
-
-done:
-    _PyCoreConfig_Clear(&config);
-    return err;
+    return _Py_INIT_OK();
 }
 
 
 void
 Py_InitializeEx(int install_sigs)
 {
-    _PyInitError err = _Py_InitializeEx_Private(install_sigs, 1);
+    if (_PyRuntime.initialized) {
+        /* bpo-33932: Calling Py_Initialize() twice does nothing. */
+        return;
+    }
+
+    _PyInitError err;
+    _PyCoreConfig config = _PyCoreConfig_INIT;
+    config.install_signal_handlers = install_sigs;
+
+    err = _Py_InitializeFromConfig(&config);
+    _PyCoreConfig_Clear(&config);
+
     if (_Py_INIT_FAILED(err)) {
         _Py_FatalInitError(err);
     }



More information about the Python-checkins mailing list