[Python-checkins] bpo-36763: Add _PyCoreConfig_InitPythonConfig() (GH-13388)

Victor Stinner webhook-mailer at python.org
Fri May 17 13:01:23 EDT 2019


https://github.com/python/cpython/commit/cab5d0741ee6adf2ae9ff5aaafe06b75b4b5bca3
commit: cab5d0741ee6adf2ae9ff5aaafe06b75b4b5bca3
branch: master
author: Victor Stinner <vstinner at redhat.com>
committer: GitHub <noreply at github.com>
date: 2019-05-17T19:01:14+02:00
summary:

bpo-36763: Add _PyCoreConfig_InitPythonConfig() (GH-13388)

Add new functions to get the Python interpreter behavior:

* _PyPreConfig_InitPythonConfig()
* _PyCoreConfig_InitPythonConfig()

Add new functions to get an isolated configuration:

* _PyPreConfig_InitIsolatedConfig()
* _PyCoreConfig_InitIsolatedConfig()

Replace _PyPreConfig_INIT and _PyCoreConfig_INIT with new functions
_PyPreConfig_Init() and _PyCoreConfig_Init().

_PyCoreConfig: set configure_c_stdio and parse_argv to 0 by default
to behave as Python 3.6 in the default configuration.

_PyCoreConfig_Read() no longer sets coerce_c_locale_warn to 1 if it's
equal to 0. coerce_c_locale_warn must now be set to -1 (ex: using
_PyCoreConfig_InitPythonConfig()) to enable C locale coercion
warning.

Add unit tests for _PyCoreConfig_InitPythonConfig()
and _PyCoreConfig_InitIsolatedConfig().

Changes:

* Rename _PyCoreConfig_GetCoreConfig() to _PyPreConfig_GetCoreConfig()
* Fix core_read_precmdline(): handle parse_argv=0
* Fix _Py_PreInitializeFromCoreConfig(): pass coreconfig.argv
  to _Py_PreInitializeFromPyArgv(), except if parse_argv=0

files:
M Include/cpython/coreconfig.h
M Include/internal/pycore_coreconfig.h
M Lib/test/test_embed.py
M Modules/main.c
M Programs/_freeze_importlib.c
M Programs/_testembed.c
M Python/coreconfig.c
M Python/frozenmain.c
M Python/pathconfig.c
M Python/preconfig.c
M Python/pylifecycle.c
M Python/pystate.c

diff --git a/Include/cpython/coreconfig.h b/Include/cpython/coreconfig.h
index dca41341dfe4..7d561ceb3eec 100644
--- a/Include/cpython/coreconfig.h
+++ b/Include/cpython/coreconfig.h
@@ -86,12 +86,18 @@ typedef struct {
 
        If it is equal to 1, LC_CTYPE locale is read to decide it it should be
        coerced or not (ex: PYTHONCOERCECLOCALE=1). Internally, it is set to 2
-       if the LC_CTYPE locale must be coerced. */
+       if the LC_CTYPE locale must be coerced.
+
+       Disable by default (set to 0). Set it to -1 to let Python decides if it
+       should be enabled or not. */
     int coerce_c_locale;
 
     /* Emit a warning if the LC_CTYPE locale is coerced?
 
-       Disabled by default. Set to 1 by PYTHONCOERCECLOCALE=warn. */
+       Set to 1 by PYTHONCOERCECLOCALE=warn.
+
+       Disable by default (set to 0). Set it to -1 to let Python decides if it
+       should be enabled or not. */
     int coerce_c_locale_warn;
 
 #ifdef MS_WINDOWS
@@ -116,7 +122,10 @@ typedef struct {
        Set to 0 by "-X utf8=0" and PYTHONUTF8=0.
 
        If equals to -1, it is set to 1 if the LC_CTYPE locale is "C" or
-       "POSIX", otherwise inherit Py_UTF8Mode value. */
+       "POSIX", otherwise it is set to 0.
+
+       If equals to -2, inherit Py_UTF8Mode value value (which is equal to 0
+       by default). */
     int utf8_mode;
 
     int dev_mode;           /* Development mode. PYTHONDEVMODE, -X dev */
@@ -138,9 +147,14 @@ typedef struct {
         ._config_version = _Py_CONFIG_VERSION, \
         .isolated = -1, \
         .use_environment = -1, \
+        .utf8_mode = -2, \
         .dev_mode = -1, \
         .allocator = PYMEM_ALLOCATOR_NOT_SET}
 
+PyAPI_FUNC(void) _PyPreConfig_Init(_PyPreConfig *config);
+PyAPI_FUNC(void) _PyPreConfig_InitPythonConfig(_PyPreConfig *config);
+PyAPI_FUNC(void) _PyPreConfig_InitIsolateConfig(_PyPreConfig *config);
+
 
 /* --- _PyCoreConfig ---------------------------------------------- */
 
@@ -213,8 +227,8 @@ typedef struct {
 
     /* Command line arguments (sys.argv).
 
-       By default, Python command line arguments are parsed and then stripped
-       from argv. Set parse_argv to 0 to avoid that.
+       Set parse_argv to 1 to parse argv as Python command line arguments
+       and then strip Python arguments from argv.
 
        If argv is empty, an empty string is added to ensure that sys.argv
        always exists and is never empty. */
@@ -442,7 +456,7 @@ typedef struct {
         .faulthandler = -1, \
         .tracemalloc = -1, \
         .use_module_search_paths = 0, \
-        .parse_argv = 1, \
+        .parse_argv = 0, \
         .site_import = -1, \
         .bytes_warning = -1, \
         .inspect = -1, \
@@ -453,7 +467,7 @@ typedef struct {
         .verbose = -1, \
         .quiet = -1, \
         .user_site_directory = -1, \
-        .configure_c_stdio = 1, \
+        .configure_c_stdio = 0, \
         .buffered_stdio = -1, \
         ._install_importlib = 1, \
         .check_hash_pycs_mode = NULL, \
@@ -461,6 +475,10 @@ typedef struct {
         ._init_main = 1}
 /* Note: _PyCoreConfig_INIT sets other fields to 0/NULL */
 
+PyAPI_FUNC(void) _PyCoreConfig_Init(_PyCoreConfig *config);
+PyAPI_FUNC(_PyInitError) _PyCoreConfig_InitPythonConfig(_PyCoreConfig *config);
+PyAPI_FUNC(_PyInitError) _PyCoreConfig_InitIsolateConfig(_PyCoreConfig *config);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/Include/internal/pycore_coreconfig.h b/Include/internal/pycore_coreconfig.h
index ccb7948ef4d4..e9c6d9fee817 100644
--- a/Include/internal/pycore_coreconfig.h
+++ b/Include/internal/pycore_coreconfig.h
@@ -88,10 +88,13 @@ PyAPI_FUNC(_PyInitError) _PyPreCmdline_Read(_PyPreCmdline *cmdline,
 
 /* --- _PyPreConfig ----------------------------------------------- */
 
+PyAPI_FUNC(void) _PyPreConfig_Init(_PyPreConfig *config);
+PyAPI_FUNC(void) _PyPreConfig_InitPythonConfig(_PyPreConfig *config);
+PyAPI_FUNC(void) _PyPreConfig_InitIsolatedConfig(_PyPreConfig *config);
 PyAPI_FUNC(int) _PyPreConfig_Copy(_PyPreConfig *config,
     const _PyPreConfig *config2);
 PyAPI_FUNC(PyObject*) _PyPreConfig_AsDict(const _PyPreConfig *config);
-PyAPI_FUNC(void) _PyCoreConfig_GetCoreConfig(_PyPreConfig *config,
+PyAPI_FUNC(void) _PyPreConfig_GetCoreConfig(_PyPreConfig *config,
     const _PyCoreConfig *core_config);
 PyAPI_FUNC(_PyInitError) _PyPreConfig_Read(_PyPreConfig *config,
     const _PyArgv *args);
@@ -101,6 +104,8 @@ PyAPI_FUNC(_PyInitError) _PyPreConfig_Write(const _PyPreConfig *config);
 /* --- _PyCoreConfig ---------------------------------------------- */
 
 PyAPI_FUNC(void) _PyCoreConfig_Clear(_PyCoreConfig *);
+PyAPI_FUNC(_PyInitError) _PyCoreConfig_InitPythonConfig(_PyCoreConfig *config);
+PyAPI_FUNC(_PyInitError) _PyCoreConfig_InitIsolatedConfig(_PyCoreConfig *config);
 PyAPI_FUNC(_PyInitError) _PyCoreConfig_Copy(
     _PyCoreConfig *config,
     const _PyCoreConfig *config2);
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index 92cc405859c9..50badd8e585c 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -307,7 +307,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
 
         'pycache_prefix': None,
         'program_name': GET_DEFAULT_CONFIG,
-        'parse_argv': 1,
+        'parse_argv': 0,
         'argv': [""],
 
         'xoptions': [],
@@ -333,7 +333,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
         'verbose': 0,
         'quiet': 0,
         'user_site_directory': 1,
-        'configure_c_stdio': 1,
+        'configure_c_stdio': 0,
         'buffered_stdio': 1,
 
         'stdio_encoding': GET_DEFAULT_CONFIG,
@@ -588,6 +588,7 @@ def test_init_from_config(self):
             'pycache_prefix': 'conf_pycache_prefix',
             'program_name': './conf_program_name',
             'argv': ['-c', 'arg2'],
+            'parse_argv': 1,
             'xoptions': ['core_xoption1=3', 'core_xoption2=', 'core_xoption3'],
             'warnoptions': ['error::ResourceWarning', 'default::BytesWarning'],
             'run_command': 'pass\n',
@@ -600,7 +601,7 @@ def test_init_from_config(self):
             'write_bytecode': 0,
             'verbose': 1,
             'quiet': 1,
-            'configure_c_stdio': 0,
+            'configure_c_stdio': 1,
             'buffered_stdio': 0,
             'user_site_directory': 0,
             'faulthandler': 1,
@@ -661,14 +662,14 @@ def test_init_dev_mode(self):
         }
         self.check_config("init_dev_mode", config, preconfig)
 
-    def test_init_isolated(self):
+    def test_init_isolated_flag(self):
         preconfig = {}
         config = {
             'isolated': 1,
             'use_environment': 0,
             'user_site_directory': 0,
         }
-        self.check_config("init_isolated", config, preconfig)
+        self.check_config("init_isolated_flag", config, preconfig)
 
     def test_preinit_isolated1(self):
         # _PyPreConfig.isolated=1, _PyCoreConfig.isolated not set
@@ -690,6 +691,25 @@ def test_preinit_isolated2(self):
         }
         self.check_config("preinit_isolated2", config, preconfig)
 
+    def test_init_isolated_config(self):
+        preconfig = {}
+        config = {
+            'isolated': 1,
+            'use_environment': 0,
+            'user_site_directory': 0,
+            'install_signal_handlers': 0,
+            'pathconfig_warnings': 0,
+        }
+        self.check_config("init_isolated_config", config, preconfig)
+
+    def test_init_python_config(self):
+        preconfig = {}
+        config = {
+            'configure_c_stdio': 1,
+            'parse_argv': 1,
+        }
+        self.check_config("init_python_config", config, preconfig)
+
     def test_init_read_set(self):
         preconfig = {}
         core_config = {
@@ -707,6 +727,7 @@ def test_init_run_main(self):
             'argv': ['-c', 'arg2'],
             'program_name': './python3',
             'run_command': code + '\n',
+            'parse_argv': 1,
         }
         self.check_config("init_run_main", core_config, preconfig)
 
@@ -718,15 +739,26 @@ def test_init_main(self):
             'argv': ['-c', 'arg2'],
             'program_name': './python3',
             'run_command': code + '\n',
+            'parse_argv': 1,
             '_init_main': 0,
         }
         self.check_config("init_main", core_config, preconfig,
                           stderr="Run Python code before _Py_InitializeMain")
 
+    def test_init_parse_argv(self):
+        core_config = {
+            'argv': ['-c', 'arg1', '-v', 'arg3'],
+            'program_name': './argv0',
+            'parse_argv': 1,
+            'run_command': 'pass\n',
+            'use_environment': 0,
+        }
+        self.check_config("init_parse_argv", core_config, {})
+
     def test_init_dont_parse_argv(self):
         core_config = {
-            'argv': ['-v', '-c', 'arg1', '-W', 'arg2'],
-            'parse_argv': 0,
+            'argv': ['./argv0', '-E', '-c', 'pass', 'arg1', '-v', 'arg3'],
+            'program_name': './argv0',
         }
         self.check_config("init_dont_parse_argv", core_config, {})
 
diff --git a/Modules/main.c b/Modules/main.c
index bb103c28a3e2..72546a21cac4 100644
--- a/Modules/main.c
+++ b/Modules/main.c
@@ -52,23 +52,28 @@ pymain_init(const _PyArgv *args)
     fedisableexcept(FE_OVERFLOW);
 #endif
 
-    _PyPreConfig preconfig = _PyPreConfig_INIT;
-    /* Set to -1 to enable them depending on the LC_CTYPE locale and the
-       environment variables (PYTHONUTF8 and PYTHONCOERCECLOCALE)  */
-    preconfig.coerce_c_locale = -1;
-    preconfig.utf8_mode = -1;
+    _PyPreConfig preconfig;
+    _PyPreConfig_InitPythonConfig(&preconfig);
     err = _Py_PreInitializeFromPyArgv(&preconfig, args);
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
 
+    _PyCoreConfig config;
+    err = _PyCoreConfig_InitPythonConfig(&config);
+    if (_Py_INIT_FAILED(err)) {
+        return err;
+    }
+
     /* pass NULL as the config: config is read from command line arguments,
        environment variables, configuration files */
     if (args->use_bytes_argv) {
-        return _Py_InitializeFromArgs(NULL, args->argc, args->bytes_argv);
+        return _Py_InitializeFromArgs(&config,
+                                      args->argc, args->bytes_argv);
     }
     else {
-        return _Py_InitializeFromWideArgs(NULL, args->argc, args->wchar_argv);
+        return _Py_InitializeFromWideArgs(&config,
+                                          args->argc, args->wchar_argv);
     }
 }
 
diff --git a/Programs/_freeze_importlib.c b/Programs/_freeze_importlib.c
index 8cbbe17cfaf1..bc29297a6b9d 100644
--- a/Programs/_freeze_importlib.c
+++ b/Programs/_freeze_importlib.c
@@ -76,7 +76,8 @@ main(int argc, char *argv[])
     }
     text[text_size] = '\0';
 
-    _PyCoreConfig config = _PyCoreConfig_INIT;
+    _PyCoreConfig config;
+    _PyCoreConfig_Init(&config);
     config.use_environment = 0;
     config.user_site_directory = 0;
     config.site_import = 0;
diff --git a/Programs/_testembed.c b/Programs/_testembed.c
index 3327c8ce6639..e6896966f53b 100644
--- a/Programs/_testembed.c
+++ b/Programs/_testembed.c
@@ -291,7 +291,9 @@ static int test_initialize_twice(void)
 static int test_initialize_pymain(void)
 {
     wchar_t *argv[] = {L"PYTHON", L"-c",
-                       L"import sys; print(f'Py_Main() after Py_Initialize: sys.argv={sys.argv}')",
+                       (L"import sys; "
+                        L"print(f'Py_Main() after Py_Initialize: "
+                        L"sys.argv={sys.argv}')"),
                        L"arg2"};
     _testembed_Py_Initialize();
 
@@ -376,7 +378,8 @@ static int test_init_from_config(void)
 {
     _PyInitError err;
 
-    _PyPreConfig preconfig = _PyPreConfig_INIT;
+    _PyPreConfig preconfig;
+    _PyPreConfig_Init(&preconfig);
 
     putenv("PYTHONMALLOC=malloc_debug");
     preconfig.allocator = PYMEM_ALLOCATOR_MALLOC;
@@ -391,7 +394,8 @@ static int test_init_from_config(void)
     }
 
     /* Test _Py_InitializeFromConfig() */
-    _PyCoreConfig config = _PyCoreConfig_INIT;
+    _PyCoreConfig config;
+    _PyCoreConfig_Init(&config);
     config.install_signal_handlers = 0;
 
     /* FIXME: test use_environment */
@@ -400,7 +404,7 @@ static int test_init_from_config(void)
     config.use_hash_seed = 1;
     config.hash_seed = 123;
 
-    /* dev_mode=1 is tested in test_init_dev_mode() */
+    /* dev_mode=1 is tested in init_dev_mode() */
 
     putenv("PYTHONFAULTHANDLER=");
     config.faulthandler = 1;
@@ -432,6 +436,7 @@ static int test_init_from_config(void)
     };
     config.argv.length = Py_ARRAY_LENGTH(argv);
     config.argv.items = argv;
+    config.parse_argv = 1;
 
     static wchar_t* xoptions[3] = {
         L"core_xoption1=3",
@@ -481,7 +486,7 @@ static int test_init_from_config(void)
     Py_QuietFlag = 0;
     config.quiet = 1;
 
-    config.configure_c_stdio = 0;
+    config.configure_c_stdio = 1;
 
     putenv("PYTHONUNBUFFERED=");
     Py_UnbufferedStdioFlag = 0;
@@ -516,25 +521,26 @@ static int test_init_from_config(void)
 }
 
 
-static int test_init_dont_parse_argv(void)
+static int test_init_parse_argv(int parse_argv)
 {
     _PyInitError err;
 
-    _PyCoreConfig config = _PyCoreConfig_INIT;
+    _PyCoreConfig config;
+    _PyCoreConfig_Init(&config);
 
     static wchar_t* argv[] = {
-        L"-v",
+        L"./argv0",
+        L"-E",
         L"-c",
+        L"pass",
         L"arg1",
-        L"-W",
-        L"arg2",
+        L"-v",
+        L"arg3",
     };
 
-    config.program_name = L"./_testembed";
-
     config.argv.length = Py_ARRAY_LENGTH(argv);
     config.argv.items = argv;
-    config.parse_argv = 0;
+    config.parse_argv = parse_argv;
 
     err = _Py_InitializeFromConfig(&config);
     if (_Py_INIT_FAILED(err)) {
@@ -546,6 +552,18 @@ static int test_init_dont_parse_argv(void)
 }
 
 
+static int init_parse_argv(void)
+{
+    return test_init_parse_argv(1);
+}
+
+
+static int init_dont_parse_argv(void)
+{
+    return test_init_parse_argv(0);
+}
+
+
 static void test_init_env_putenvs(void)
 {
     putenv("PYTHONHASHSEED=42");
@@ -619,12 +637,13 @@ static int test_init_env_dev_mode_alloc(void)
 }
 
 
-static int test_init_isolated(void)
+static int init_isolated_flag(void)
 {
     _PyInitError err;
 
     /* Test _PyCoreConfig.isolated=1 */
-    _PyCoreConfig config = _PyCoreConfig_INIT;
+    _PyCoreConfig config;
+    _PyCoreConfig_Init(&config);
 
     Py_IsolatedFlag = 0;
     config.isolated = 1;
@@ -648,7 +667,8 @@ static int test_preinit_isolated1(void)
 {
     _PyInitError err;
 
-    _PyPreConfig preconfig = _PyPreConfig_INIT;
+    _PyPreConfig preconfig;
+    _PyPreConfig_Init(&preconfig);
     preconfig.isolated = 1;
 
     err = _Py_PreInitialize(&preconfig);
@@ -656,7 +676,8 @@ static int test_preinit_isolated1(void)
         _Py_ExitInitError(err);
     }
 
-    _PyCoreConfig config = _PyCoreConfig_INIT;
+    _PyCoreConfig config;
+    _PyCoreConfig_Init(&config);
     config.program_name = L"./_testembed";
 
     test_init_env_dev_mode_putenvs();
@@ -675,7 +696,8 @@ static int test_preinit_isolated2(void)
 {
     _PyInitError err;
 
-    _PyPreConfig preconfig = _PyPreConfig_INIT;
+    _PyPreConfig preconfig;
+    _PyPreConfig_Init(&preconfig);
     preconfig.isolated = 0;
 
     err = _Py_PreInitialize(&preconfig);
@@ -684,7 +706,8 @@ static int test_preinit_isolated2(void)
     }
 
     /* Test _PyCoreConfig.isolated=1 */
-    _PyCoreConfig config = _PyCoreConfig_INIT;
+    _PyCoreConfig config;
+    _PyCoreConfig_Init(&config);
 
     Py_IsolatedFlag = 0;
     config.isolated = 1;
@@ -703,9 +726,72 @@ static int test_preinit_isolated2(void)
 }
 
 
-static int test_init_dev_mode(void)
+static int init_isolated_config(void)
+{
+    _PyInitError err;
+
+    _PyPreConfig preconfig;
+    _PyPreConfig_InitIsolatedConfig(&preconfig);
+
+    err = _Py_PreInitialize(&preconfig);
+    if (_Py_INIT_FAILED(err)) {
+        _Py_ExitInitError(err);
+    }
+
+    _PyPreConfig *rt_preconfig = &_PyRuntime.preconfig;
+    assert(rt_preconfig->isolated == 1);
+    assert(rt_preconfig->use_environment == 0);
+
+    _PyCoreConfig config;
+    err = _PyCoreConfig_InitIsolatedConfig(&config);
+    if (_Py_INIT_FAILED(err)) {
+        _Py_ExitInitError(err);
+    }
+    config.program_name = L"./_testembed";
+
+    err = _Py_InitializeFromConfig(&config);
+    if (_Py_INIT_FAILED(err)) {
+        _Py_ExitInitError(err);
+    }
+    dump_config();
+    Py_Finalize();
+    return 0;
+}
+
+
+static int init_python_config(void)
+{
+    _PyInitError err;
+
+    _PyPreConfig preconfig;
+    _PyPreConfig_InitPythonConfig(&preconfig);
+
+    err = _Py_PreInitialize(&preconfig);
+    if (_Py_INIT_FAILED(err)) {
+        _Py_ExitInitError(err);
+    }
+
+    _PyCoreConfig config;
+    err = _PyCoreConfig_InitPythonConfig(&config);
+    if (_Py_INIT_FAILED(err)) {
+        _Py_ExitInitError(err);
+    }
+    config.program_name = L"./_testembed";
+
+    err = _Py_InitializeFromConfig(&config);
+    if (_Py_INIT_FAILED(err)) {
+        _Py_ExitInitError(err);
+    }
+    dump_config();
+    Py_Finalize();
+    return 0;
+}
+
+
+static int init_dev_mode(void)
 {
-    _PyCoreConfig config = _PyCoreConfig_INIT;
+    _PyCoreConfig config;
+    _PyCoreConfig_Init(&config);
     putenv("PYTHONFAULTHANDLER=");
     putenv("PYTHONMALLOC=");
     config.dev_mode = 1;
@@ -723,7 +809,8 @@ static int test_init_dev_mode(void)
 static int test_init_read_set(void)
 {
     _PyInitError err;
-    _PyCoreConfig config = _PyCoreConfig_INIT;
+    _PyCoreConfig config;
+    _PyCoreConfig_Init(&config);
 
     err = _PyCoreConfig_DecodeLocale(&config.program_name, "./init_read_set");
     if (_Py_INIT_FAILED(err)) {
@@ -772,13 +859,15 @@ static void configure_init_main(_PyCoreConfig *config)
 {
     config->argv.length = Py_ARRAY_LENGTH(init_main_argv);
     config->argv.items = init_main_argv;
+    config->parse_argv = 1;
     config->program_name = L"./python3";
 }
 
 
 static int test_init_run_main(void)
 {
-    _PyCoreConfig config = _PyCoreConfig_INIT;
+    _PyCoreConfig config;
+    _PyCoreConfig_Init(&config);
     configure_init_main(&config);
 
     _PyInitError err = _Py_InitializeFromConfig(&config);
@@ -792,7 +881,8 @@ static int test_init_run_main(void)
 
 static int test_init_main(void)
 {
-    _PyCoreConfig config = _PyCoreConfig_INIT;
+    _PyCoreConfig config;
+    _PyCoreConfig_Init(&config);
     configure_init_main(&config);
     config._init_main = 0;
 
@@ -821,7 +911,8 @@ static int test_init_main(void)
 
 static int test_run_main(void)
 {
-    _PyCoreConfig config = _PyCoreConfig_INIT;
+    _PyCoreConfig config;
+    _PyCoreConfig_Init(&config);
 
     wchar_t *argv[] = {L"python3", L"-c",
                        (L"import sys; "
@@ -829,6 +920,7 @@ static int test_run_main(void)
                        L"arg2"};
     config.argv.length = Py_ARRAY_LENGTH(argv);
     config.argv.items = argv;
+    config.parse_argv = 1;
     config.program_name = L"./python3";
 
     _PyInitError err = _Py_InitializeFromConfig(&config);
@@ -869,12 +961,15 @@ static struct TestCase TestCases[] = {
     { "init_default_config", test_init_default_config },
     { "init_global_config", test_init_global_config },
     { "init_from_config", test_init_from_config },
-    { "init_dont_parse_argv", test_init_dont_parse_argv },
+    { "init_parse_argv", init_parse_argv },
+    { "init_dont_parse_argv", init_dont_parse_argv },
     { "init_env", test_init_env },
     { "init_env_dev_mode", test_init_env_dev_mode },
     { "init_env_dev_mode_alloc", test_init_env_dev_mode_alloc },
-    { "init_dev_mode", test_init_dev_mode },
-    { "init_isolated", test_init_isolated },
+    { "init_dev_mode", init_dev_mode },
+    { "init_isolated_flag", init_isolated_flag },
+    { "init_isolated_config", init_isolated_config },
+    { "init_python_config", init_python_config },
     { "preinit_isolated1", test_preinit_isolated1 },
     { "preinit_isolated2", test_preinit_isolated2 },
     { "init_read_set", test_init_read_set },
diff --git a/Python/coreconfig.c b/Python/coreconfig.c
index 634891ed2146..2e8f4cf6f102 100644
--- a/Python/coreconfig.c
+++ b/Python/coreconfig.c
@@ -109,7 +109,7 @@ static const char usage_6[] =
 /* UTF-8 mode (PEP 540): if equals to 1, use the UTF-8 encoding, and change
    stdin and stdout error handler to "surrogateescape". It is equal to
    -1 by default: unknown, will be set by Py_Main() */
-int Py_UTF8Mode = -1;
+int Py_UTF8Mode = 0;
 int Py_DebugFlag = 0; /* Needed by parser.c */
 int Py_VerboseFlag = 0; /* Needed by import.c */
 int Py_QuietFlag = 0; /* Needed by sysmodule.c */
@@ -520,6 +520,61 @@ _PyCoreConfig_Clear(_PyCoreConfig *config)
 }
 
 
+void
+_PyCoreConfig_Init(_PyCoreConfig *config)
+{
+    *config = _PyCoreConfig_INIT;
+}
+
+
+_PyInitError
+_PyCoreConfig_InitPythonConfig(_PyCoreConfig *config)
+{
+    _PyCoreConfig_Init(config);
+
+    config->configure_c_stdio = 1;
+    config->parse_argv = 1;
+
+    return _Py_INIT_OK();
+}
+
+
+_PyInitError
+_PyCoreConfig_InitIsolatedConfig(_PyCoreConfig *config)
+{
+    _PyCoreConfig_Init(config);
+
+    /* set to 1 */
+    config->isolated = 1;
+    config->site_import = 1;
+    config->write_bytecode = 1;
+    config->buffered_stdio = 1;
+
+    /* set to 0 */
+    config->use_environment = 0;
+    config->dev_mode = 0;
+    config->install_signal_handlers = 0;
+    config->use_hash_seed = 0;
+    config->faulthandler = 0;
+    config->tracemalloc = 0;
+    config->bytes_warning = 0;
+    config->inspect = 0;
+    config->interactive = 0;
+    config->optimization_level = 0;
+    config->parser_debug = 0;
+    config->verbose = 0;
+    config->quiet = 0;
+    config->user_site_directory = 0;
+    config->configure_c_stdio = 0;
+    config->pathconfig_warnings = 0;
+#ifdef MS_WINDOWS
+    config->legacy_windows_stdio = 0;
+#endif
+
+    return _Py_INIT_OK();
+}
+
+
 /* Copy str into *config_str (duplicate the string) */
 _PyInitError
 _PyCoreConfig_SetString(wchar_t **config_str, const wchar_t *str)
@@ -2014,17 +2069,20 @@ core_read_precmdline(_PyCoreConfig *config, _PyPreCmdline *precmdline)
 {
     _PyInitError err;
 
-    if (_PyWstrList_Copy(&precmdline->argv, &config->argv) < 0) {
-        return _Py_INIT_NO_MEMORY();
+    if (config->parse_argv) {
+        if (_PyWstrList_Copy(&precmdline->argv, &config->argv) < 0) {
+            return _Py_INIT_NO_MEMORY();
+        }
     }
 
-    _PyPreConfig preconfig = _PyPreConfig_INIT;
+    _PyPreConfig preconfig;
+    _PyPreConfig_Init(&preconfig);
     if (_PyPreConfig_Copy(&preconfig, &_PyRuntime.preconfig) < 0) {
         err = _Py_INIT_NO_MEMORY();
         return err;
     }
 
-    _PyCoreConfig_GetCoreConfig(&preconfig, config);
+    _PyPreConfig_GetCoreConfig(&preconfig, config);
 
     err = _PyPreCmdline_Read(precmdline, &preconfig);
     if (_Py_INIT_FAILED(err)) {
@@ -2155,6 +2213,7 @@ _PyInitError
 _PyCoreConfig_Read(_PyCoreConfig *config)
 {
     _PyInitError err;
+    _PyWstrList orig_argv = _PyWstrList_INIT;
 
     err = _Py_PreInitializeFromCoreConfig(config, NULL);
     if (_Py_INIT_FAILED(err)) {
@@ -2163,6 +2222,10 @@ _PyCoreConfig_Read(_PyCoreConfig *config)
 
     _PyCoreConfig_GetGlobalConfig(config);
 
+    if (_PyWstrList_Copy(&orig_argv, &config->argv) < 0) {
+        return _Py_INIT_NO_MEMORY();
+    }
+
     _PyPreCmdline precmdline = _PyPreCmdline_INIT;
     err = core_read_precmdline(config, &precmdline);
     if (_Py_INIT_FAILED(err)) {
@@ -2185,10 +2248,7 @@ _PyCoreConfig_Read(_PyCoreConfig *config)
         goto done;
     }
 
-    /* precmdline.argv is a copy of config.argv which is modified
-       by config_read_cmdline() */
-    const _PyWstrList *argv = &precmdline.argv;
-    if (_Py_SetArgcArgv(argv->length, argv->items) < 0) {
+    if (_Py_SetArgcArgv(orig_argv.length, orig_argv.items) < 0) {
         err = _Py_INIT_NO_MEMORY();
         goto done;
     }
@@ -2249,6 +2309,7 @@ _PyCoreConfig_Read(_PyCoreConfig *config)
     err = _Py_INIT_OK();
 
 done:
+    _PyWstrList_Clear(&orig_argv);
     _PyPreCmdline_Clear(&precmdline);
     return err;
 }
diff --git a/Python/frozenmain.c b/Python/frozenmain.c
index f2499ef84cd9..0175e42596b8 100644
--- a/Python/frozenmain.c
+++ b/Python/frozenmain.c
@@ -39,7 +39,8 @@ Py_FrozenMain(int argc, char **argv)
         }
     }
 
-    _PyCoreConfig config = _PyCoreConfig_INIT;
+    _PyCoreConfig config;
+    _PyCoreConfig_Init(&config);
     config.pathconfig_warnings = 0;   /* Suppress errors from getpath.c */
 
     if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\0')
diff --git a/Python/pathconfig.c b/Python/pathconfig.c
index 2fcb81605805..c8c69ebad6a0 100644
--- a/Python/pathconfig.c
+++ b/Python/pathconfig.c
@@ -392,7 +392,8 @@ pathconfig_global_init(void)
     }
 
     _PyInitError err;
-    _PyCoreConfig config = _PyCoreConfig_INIT;
+    _PyCoreConfig config;
+    _PyCoreConfig_Init(&config);
 
     err = _PyCoreConfig_Read(&config);
     if (_Py_INIT_FAILED(err)) {
diff --git a/Python/preconfig.c b/Python/preconfig.c
index 2bbf8e6fb7fb..7814ee08a63f 100644
--- a/Python/preconfig.c
+++ b/Python/preconfig.c
@@ -260,6 +260,42 @@ _PyPreCmdline_Read(_PyPreCmdline *cmdline,
 
 /* --- _PyPreConfig ----------------------------------------------- */
 
+void
+_PyPreConfig_Init(_PyPreConfig *config)
+{
+    *config = _PyPreConfig_INIT;
+}
+
+
+void
+_PyPreConfig_InitPythonConfig(_PyPreConfig *config)
+{
+    _PyPreConfig_Init(config);
+
+    /* Set to -1 to enable C locale coercion (PEP 538) and UTF-8 Mode (PEP 540)
+       depending on the LC_CTYPE locale, PYTHONUTF8 and PYTHONCOERCECLOCALE
+       environment variables. */
+    config->coerce_c_locale = -1;
+    config->coerce_c_locale_warn = -1;
+    config->utf8_mode = -1;
+}
+
+
+void
+_PyPreConfig_InitIsolatedConfig(_PyPreConfig *config)
+{
+    _PyPreConfig_Init(config);
+
+    config->isolated = 1;
+    config->use_environment = 0;
+#ifdef MS_WINDOWS
+    config->legacy_windows_fs_encoding = 0;
+#endif
+    config->utf8_mode = 0;
+    config->dev_mode = 0;
+}
+
+
 int
 _PyPreConfig_Copy(_PyPreConfig *config, const _PyPreConfig *config2)
 {
@@ -346,7 +382,7 @@ _PyPreConfig_AsDict(const _PyPreConfig *config)
 
 
 void
-_PyCoreConfig_GetCoreConfig(_PyPreConfig *config,
+_PyPreConfig_GetCoreConfig(_PyPreConfig *config,
                             const _PyCoreConfig *core_config)
 {
 #define COPY_ATTR(ATTR) \
@@ -366,11 +402,11 @@ static void
 _PyPreConfig_GetGlobalConfig(_PyPreConfig *config)
 {
 #define COPY_FLAG(ATTR, VALUE) \
-    if (config->ATTR == -1) { \
+    if (config->ATTR < 0) { \
         config->ATTR = VALUE; \
     }
 #define COPY_NOT_FLAG(ATTR, VALUE) \
-    if (config->ATTR == -1) { \
+    if (config->ATTR < 0) { \
         config->ATTR = !(VALUE); \
     }
 
@@ -379,8 +415,8 @@ _PyPreConfig_GetGlobalConfig(_PyPreConfig *config)
 #ifdef MS_WINDOWS
     COPY_FLAG(legacy_windows_fs_encoding, Py_LegacyWindowsFSEncodingFlag);
 #endif
-    if (Py_UTF8Mode > 0) {
-        config->utf8_mode = 1;
+    if (config->utf8_mode == -2) {
+        config->utf8_mode = Py_UTF8Mode;
     }
 
 #undef COPY_FLAG
@@ -392,11 +428,11 @@ static void
 _PyPreConfig_SetGlobalConfig(const _PyPreConfig *config)
 {
 #define COPY_FLAG(ATTR, VAR) \
-    if (config->ATTR != -1) { \
+    if (config->ATTR >= 0) { \
         VAR = config->ATTR; \
     }
 #define COPY_NOT_FLAG(ATTR, VAR) \
-    if (config->ATTR != -1) { \
+    if (config->ATTR >= 0) { \
         VAR = !config->ATTR; \
     }
 
@@ -575,7 +611,9 @@ preconfig_init_coerce_c_locale(_PyPreConfig *config)
             }
         }
         else if (strcmp(env, "warn") == 0) {
-            config->coerce_c_locale_warn = 1;
+            if (config->coerce_c_locale_warn < 0) {
+                config->coerce_c_locale_warn = 1;
+            }
         }
         else {
             if (config->coerce_c_locale < 0) {
@@ -587,19 +625,19 @@ preconfig_init_coerce_c_locale(_PyPreConfig *config)
     /* Test if coerce_c_locale equals to -1 or equals to 1:
        PYTHONCOERCECLOCALE=1 doesn't imply that the C locale is always coerced.
        It is only coerced if if the LC_CTYPE locale is "C". */
-    if (config->coerce_c_locale == 0 || config->coerce_c_locale == 2) {
-        return;
+    if (config->coerce_c_locale < 0 || config->coerce_c_locale == 1) {
+        /* The C locale enables the C locale coercion (PEP 538) */
+        if (_Py_LegacyLocaleDetected()) {
+            config->coerce_c_locale = 2;
+        }
+        else {
+            config->coerce_c_locale = 0;
+        }
     }
 
-    /* The C locale enables the C locale coercion (PEP 538) */
-    if (_Py_LegacyLocaleDetected()) {
-        config->coerce_c_locale = 2;
+    if (config->coerce_c_locale_warn < 0) {
+        config->coerce_c_locale_warn = 0;
     }
-    else {
-        config->coerce_c_locale = 0;
-    }
-
-    assert(config->coerce_c_locale >= 0);
 }
 
 
@@ -659,6 +697,7 @@ preconfig_read(_PyPreConfig *config, _PyPreCmdline *cmdline)
     }
 
     assert(config->coerce_c_locale >= 0);
+    assert(config->coerce_c_locale_warn >= 0);
 #ifdef MS_WINDOWS
     assert(config->legacy_windows_fs_encoding >= 0);
 #endif
@@ -700,7 +739,8 @@ _PyPreConfig_Read(_PyPreConfig *config, const _PyArgv *args)
     }
 
     /* Save the config to be able to restore it if encodings change */
-    _PyPreConfig save_config = _PyPreConfig_INIT;
+    _PyPreConfig save_config;
+    _PyPreConfig_Init(&save_config);
     if (_PyPreConfig_Copy(&save_config, config) < 0) {
         return _Py_INIT_NO_MEMORY();
     }
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index eecb439a11d7..231706d2ab60 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -701,7 +701,8 @@ _Py_PreInitializeFromPyArgv(const _PyPreConfig *src_config, const _PyArgv *args)
         return _Py_INIT_OK();
     }
 
-    _PyPreConfig config = _PyPreConfig_INIT;
+    _PyPreConfig config;
+    _PyPreConfig_Init(&config);
 
     if (src_config) {
         if (_PyPreConfig_Copy(&config, src_config) < 0) {
@@ -752,13 +753,22 @@ _PyInitError
 _Py_PreInitializeFromCoreConfig(const _PyCoreConfig *coreconfig,
                                 const _PyArgv *args)
 {
-    _PyPreConfig config = _PyPreConfig_INIT;
+    _PyPreConfig config;
+    _PyPreConfig_Init(&config);
     if (coreconfig != NULL) {
-        _PyCoreConfig_GetCoreConfig(&config, coreconfig);
+        _PyPreConfig_GetCoreConfig(&config, coreconfig);
+    }
+
+    if (args == NULL && coreconfig != NULL && coreconfig->parse_argv) {
+        _PyArgv config_args = {
+            .use_bytes_argv = 0,
+            .argc = coreconfig->argv.length,
+            .wchar_argv = coreconfig->argv.items};
+        return _Py_PreInitializeFromPyArgv(&config, &config_args);
+    }
+    else {
+        return _Py_PreInitializeFromPyArgv(&config, args);
     }
-    return _Py_PreInitializeFromPyArgv(&config, args);
-    /* No need to clear config:
-       _PyCoreConfig_GetCoreConfig() doesn't allocate memory */
 }
 
 
@@ -829,7 +839,8 @@ _Py_InitializeCore(_PyRuntimeState *runtime,
         return err;
     }
 
-    _PyCoreConfig local_config = _PyCoreConfig_INIT;
+    _PyCoreConfig local_config;
+    _PyCoreConfig_Init(&local_config);
     err = pyinit_coreconfig(runtime, &local_config, src_config, args, interp_p);
     _PyCoreConfig_Clear(&local_config);
     return err;
@@ -1051,7 +1062,8 @@ Py_InitializeEx(int install_sigs)
         return;
     }
 
-    _PyCoreConfig config = _PyCoreConfig_INIT;
+    _PyCoreConfig config;
+    _PyCoreConfig_Init(&config);
     config.install_signal_handlers = install_sigs;
 
     err = _Py_InitializeFromConfig(&config);
diff --git a/Python/pystate.c b/Python/pystate.c
index 8c906ce87ad4..2f80aa253b5a 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -49,7 +49,7 @@ _PyRuntimeState_Init_impl(_PyRuntimeState *runtime)
 
     _PyGC_Initialize(&runtime->gc);
     _PyEval_Initialize(&runtime->ceval);
-    runtime->preconfig = _PyPreConfig_INIT;
+    _PyPreConfig_Init(&runtime->preconfig);
 
     runtime->gilstate.check_enabled = 1;
 
@@ -189,7 +189,7 @@ PyInterpreterState_New(void)
     memset(interp, 0, sizeof(*interp));
     interp->id_refcount = -1;
     interp->check_interval = 100;
-    interp->core_config = _PyCoreConfig_INIT;
+    _PyCoreConfig_Init(&interp->core_config);
     interp->eval_frame = _PyEval_EvalFrameDefault;
 #ifdef HAVE_DLOPEN
 #if HAVE_DECL_RTLD_NOW



More information about the Python-checkins mailing list