[Python-checkins] bpo-36142: Move command line parsing to coreconfig.c (GH-12123)

Victor Stinner webhook-mailer at python.org
Fri Mar 1 10:25:23 EST 2019


https://github.com/python/cpython/commit/95e2cbf32f8156c239b27dae558ba058d0f2d496
commit: 95e2cbf32f8156c239b27dae558ba058d0f2d496
branch: master
author: Victor Stinner <vstinner at redhat.com>
committer: GitHub <noreply at github.com>
date: 2019-03-01T16:25:19+01:00
summary:

bpo-36142: Move command line parsing to coreconfig.c (GH-12123)

* Add _PyCoreConfig_ReadFromArgv() function which parses command line
  options: move code from main.c to coreconfig.c.
* Add _PyCoreConfig_Write() to write the new configuration: coerce
  the LC_CTYPE locale, set Py_xxx global configuration variables,
  etc.
* _PyCoreConfig_ReadFromArgv() now only changes the LC_CTYPE locale
  temporarily. _PyCoreConfig_Write() becomes responsible to set the
  LC_CTYPE locale.
* Add _Py_SetArgcArgv() and _Py_ClearArgcArgv() functions
* Rename many "pymain_xxx()" functions
* Add "const" to some function parameters
* Reorganize main.c to declare functions in the order in which they
  are called.

files:
M Include/cpython/coreconfig.h
M Include/internal/pycore_coreconfig.h
M Include/internal/pycore_pathconfig.h
M Modules/main.c
M Python/coreconfig.c

diff --git a/Include/cpython/coreconfig.h b/Include/cpython/coreconfig.h
index 4985bf3a7598..8109d4abc508 100644
--- a/Include/cpython/coreconfig.h
+++ b/Include/cpython/coreconfig.h
@@ -5,6 +5,16 @@
 extern "C" {
 #endif
 
+/* _PyArgv */
+
+typedef struct {
+    int argc;
+    int use_bytes_argv;
+    char **bytes_argv;
+    wchar_t **wchar_argv;
+} _PyArgv;
+
+
 /* _PyInitError */
 
 typedef struct {
diff --git a/Include/internal/pycore_coreconfig.h b/Include/internal/pycore_coreconfig.h
index b906ac4f7f9f..ebb0a9e9f1f7 100644
--- a/Include/internal/pycore_coreconfig.h
+++ b/Include/internal/pycore_coreconfig.h
@@ -8,6 +8,29 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE or Py_BUILD_CORE_BUILTIN defined"
 #endif
 
+/* _Py_wstrlist */
+
+PyAPI_FUNC(void) _Py_wstrlist_clear(
+    int len,
+    wchar_t **list);
+PyAPI_FUNC(wchar_t**) _Py_wstrlist_copy(
+    int len,
+    wchar_t * const *list);
+PyAPI_FUNC(_PyInitError) _Py_wstrlist_append(
+    int *len,
+    wchar_t ***list,
+    const wchar_t *str);
+PyAPI_FUNC(PyObject*) _Py_wstrlist_as_pylist(
+    int len,
+    wchar_t **list);
+
+/* Py_GetArgcArgv() helpers */
+
+PyAPI_FUNC(void) _Py_ClearArgcArgv(void);
+PyAPI_FUNC(int) _Py_SetArgcArgv(int argc, wchar_t * const *argv);
+
+/* _PyCoreConfig */
+
 PyAPI_FUNC(_PyInitError) _PyCoreConfig_Read(_PyCoreConfig *config);
 PyAPI_FUNC(void) _PyCoreConfig_Clear(_PyCoreConfig *);
 PyAPI_FUNC(int) _PyCoreConfig_Copy(
@@ -26,6 +49,9 @@ PyAPI_FUNC(int) _PyCoreConfig_GetEnvDup(
     wchar_t **dest,
     wchar_t *wname,
     char *name);
+PyAPI_FUNC(_PyInitError) _PyCoreConfig_ReadFromArgv(_PyCoreConfig *config,
+    const _PyArgv *args);
+PyAPI_FUNC(void) _PyCoreConfig_Write(const _PyCoreConfig *config);
 
 #ifdef __cplusplus
 }
diff --git a/Include/internal/pycore_pathconfig.h b/Include/internal/pycore_pathconfig.h
index c0731525ca10..fb6b1d78b364 100644
--- a/Include/internal/pycore_pathconfig.h
+++ b/Include/internal/pycore_pathconfig.h
@@ -8,20 +8,6 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE or Py_BUILD_CORE_BUILTIN define"
 #endif
 
-PyAPI_FUNC(void) _Py_wstrlist_clear(
-    int len,
-    wchar_t **list);
-PyAPI_FUNC(wchar_t**) _Py_wstrlist_copy(
-    int len,
-    wchar_t **list);
-PyAPI_FUNC(_PyInitError) _Py_wstrlist_append(
-    int *len,
-    wchar_t ***list,
-    const wchar_t *str);
-PyAPI_FUNC(PyObject*) _Py_wstrlist_as_pylist(
-    int len,
-    wchar_t **list);
-
 typedef struct _PyPathConfig {
     /* Full path to the Python program */
     wchar_t *program_full_path;
diff --git a/Modules/main.c b/Modules/main.c
index e7d75a79e787..fdeaf1d12ad2 100644
--- a/Modules/main.c
+++ b/Modules/main.c
@@ -1,46 +1,27 @@
 /* Python interpreter main program */
 
 #include "Python.h"
-#include "osdefs.h"
 #include "pycore_coreconfig.h"
-#include "pycore_getopt.h"
-#include "pycore_pathconfig.h"
 #include "pycore_pylifecycle.h"
 #include "pycore_pymem.h"
 #include "pycore_pystate.h"
 
-#include <locale.h>
+#ifdef __FreeBSD__
+#  include <fenv.h>     /* fedisableexcept() */
+#endif
+
+/* Includes for exit_sigint() */
+#include <stdio.h>      /* perror() */
 #ifdef HAVE_SIGNAL_H
-#include <signal.h>
+#  include <signal.h>   /* SIGINT */
 #endif
-#include <stdio.h>
 #if defined(HAVE_GETPID) && defined(HAVE_UNISTD_H)
-#include <unistd.h>
-#endif
-
-#if defined(MS_WINDOWS) || defined(__CYGWIN__)
-#  include <windows.h>
-#  ifdef HAVE_IO_H
-#    include <io.h>
-#  endif
-#  ifdef HAVE_FCNTL_H
-#    include <fcntl.h>
-#  endif
+#  include <unistd.h>   /* getpid() */
 #endif
-
 #ifdef _MSC_VER
-#  include <crtdbg.h>
-#endif
-
-#ifdef __FreeBSD__
-#  include <fenv.h>
-#endif
-
-#if defined(MS_WINDOWS)
-#  define PYTHONHOMEHELP "<prefix>\\python{major}{minor}"
-#else
-#  define PYTHONHOMEHELP "<prefix>/lib/pythonX.X"
+#  include <crtdbg.h>   /* STATUS_CONTROL_C_EXIT */
 #endif
+/* End of includes for exit_sigint() */
 
 #define COPYRIGHT \
     "Type \"help\", \"copyright\", \"credits\" or \"license\" " \
@@ -50,825 +31,370 @@
 extern "C" {
 #endif
 
-#define DECODE_LOCALE_ERR(NAME, LEN) \
-    (((LEN) == -2) \
-     ? _Py_INIT_USER_ERR("cannot decode " NAME) \
-     : _Py_INIT_NO_MEMORY())
-
-
-#ifdef MS_WINDOWS
-#define WCSTOK wcstok_s
-#else
-#define WCSTOK wcstok
-#endif
-
-/* For Py_GetArgcArgv(); set by main() */
-static wchar_t **orig_argv = NULL;
-static int orig_argc = 0;
-
-/* command line options */
-#define BASE_OPTS L"bBc:dEhiIJm:OqRsStuvVW:xX:?"
-
-#define PROGRAM_OPTS BASE_OPTS
-
-static const _PyOS_LongOption longoptions[] = {
-    {L"check-hash-based-pycs", 1, 0},
-    {NULL, 0, 0},
-};
-
-/* Short usage message (with %s for argv0) */
-static const char usage_line[] =
-"usage: %ls [option] ... [-c cmd | -m mod | file | -] [arg] ...\n";
-
-/* Long usage message, split into parts < 512 bytes */
-static const char usage_1[] = "\
-Options and arguments (and corresponding environment variables):\n\
--b     : issue warnings about str(bytes_instance), str(bytearray_instance)\n\
-         and comparing bytes/bytearray with str. (-bb: issue errors)\n\
--B     : don't write .pyc files on import; also PYTHONDONTWRITEBYTECODE=x\n\
--c cmd : program passed in as string (terminates option list)\n\
--d     : debug output from parser; also PYTHONDEBUG=x\n\
--E     : ignore PYTHON* environment variables (such as PYTHONPATH)\n\
--h     : print this help message and exit (also --help)\n\
-";
-static const char usage_2[] = "\
--i     : inspect interactively after running script; forces a prompt even\n\
-         if stdin does not appear to be a terminal; also PYTHONINSPECT=x\n\
--I     : isolate Python from the user's environment (implies -E and -s)\n\
--m mod : run library module as a script (terminates option list)\n\
--O     : remove assert and __debug__-dependent statements; add .opt-1 before\n\
-         .pyc extension; also PYTHONOPTIMIZE=x\n\
--OO    : do -O changes and also discard docstrings; add .opt-2 before\n\
-         .pyc extension\n\
--q     : don't print version and copyright messages on interactive startup\n\
--s     : don't add user site directory to sys.path; also PYTHONNOUSERSITE\n\
--S     : don't imply 'import site' on initialization\n\
-";
-static const char usage_3[] = "\
--u     : force the stdout and stderr streams to be unbuffered;\n\
-         this option has no effect on stdin; also PYTHONUNBUFFERED=x\n\
--v     : verbose (trace import statements); also PYTHONVERBOSE=x\n\
-         can be supplied multiple times to increase verbosity\n\
--V     : print the Python version number and exit (also --version)\n\
-         when given twice, print more information about the build\n\
--W arg : warning control; arg is action:message:category:module:lineno\n\
-         also PYTHONWARNINGS=arg\n\
--x     : skip first line of source, allowing use of non-Unix forms of #!cmd\n\
--X opt : set implementation-specific option\n\
---check-hash-based-pycs always|default|never:\n\
-    control how Python invalidates hash-based .pyc files\n\
-";
-static const char usage_4[] = "\
-file   : program read from script file\n\
--      : program read from stdin (default; interactive mode if a tty)\n\
-arg ...: arguments passed to program in sys.argv[1:]\n\n\
-Other environment variables:\n\
-PYTHONSTARTUP: file executed on interactive startup (no default)\n\
-PYTHONPATH   : '%lc'-separated list of directories prefixed to the\n\
-               default module search path.  The result is sys.path.\n\
-";
-static const char usage_5[] =
-"PYTHONHOME   : alternate <prefix> directory (or <prefix>%lc<exec_prefix>).\n"
-"               The default module search path uses %s.\n"
-"PYTHONCASEOK : ignore case in 'import' statements (Windows).\n"
-"PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.\n"
-"PYTHONFAULTHANDLER: dump the Python traceback on fatal errors.\n";
-static const char usage_6[] =
-"PYTHONHASHSEED: if this variable is set to 'random', a random value is used\n"
-"   to seed the hashes of str, bytes and datetime objects.  It can also be\n"
-"   set to an integer in the range [0,4294967295] to get hash values with a\n"
-"   predictable seed.\n"
-"PYTHONMALLOC: set the Python memory allocators and/or install debug hooks\n"
-"   on Python memory allocators. Use PYTHONMALLOC=debug to install debug\n"
-"   hooks.\n"
-"PYTHONCOERCECLOCALE: if this variable is set to 0, it disables the locale\n"
-"   coercion behavior. Use PYTHONCOERCECLOCALE=warn to request display of\n"
-"   locale coercion and locale compatibility warnings on stderr.\n"
-"PYTHONBREAKPOINT: if this variable is set to 0, it disables the default\n"
-"   debugger. It can be set to the callable of your debugger of choice.\n"
-"PYTHONDEVMODE: enable the development mode.\n"
-"PYTHONPYCACHEPREFIX: root directory for bytecode cache (pyc) files.\n";
-
-static void
-pymain_usage(int error, const wchar_t* program)
-{
-    FILE *f = error ? stderr : stdout;
+/* --- PyMainInterpreter ------------------------------------------ */
 
-    fprintf(f, usage_line, program);
-    if (error)
-        fprintf(f, "Try `python -h' for more information.\n");
-    else {
-        fputs(usage_1, f);
-        fputs(usage_2, f);
-        fputs(usage_3, f);
-        fprintf(f, usage_4, (wint_t)DELIM);
-        fprintf(f, usage_5, (wint_t)DELIM, PYTHONHOMEHELP);
-        fputs(usage_6, f);
-    }
-}
-
-
-static void
-pymain_run_interactive_hook(void)
+void
+_PyMainInterpreterConfig_Clear(_PyMainInterpreterConfig *config)
 {
-    PyObject *sys, *hook, *result;
-    sys = PyImport_ImportModule("sys");
-    if (sys == NULL) {
-        goto error;
-    }
-
-    hook = PyObject_GetAttrString(sys, "__interactivehook__");
-    Py_DECREF(sys);
-    if (hook == NULL) {
-        PyErr_Clear();
-        return;
-    }
-
-    result = _PyObject_CallNoArg(hook);
-    Py_DECREF(hook);
-    if (result == NULL) {
-        goto error;
-    }
-    Py_DECREF(result);
-
-    return;
-
-error:
-    PySys_WriteStderr("Failed calling sys.__interactivehook__\n");
-    PyErr_Print();
+    Py_CLEAR(config->argv);
+    Py_CLEAR(config->executable);
+    Py_CLEAR(config->prefix);
+    Py_CLEAR(config->base_prefix);
+    Py_CLEAR(config->exec_prefix);
+    Py_CLEAR(config->base_exec_prefix);
+    Py_CLEAR(config->warnoptions);
+    Py_CLEAR(config->xoptions);
+    Py_CLEAR(config->module_search_path);
+    Py_CLEAR(config->pycache_prefix);
 }
 
 
 static int
-pymain_run_module(const wchar_t *modname, int set_argv0)
+mainconfig_add_xoption(PyObject *opts, const wchar_t *s)
 {
-    PyObject *module, *runpy, *runmodule, *runargs, *result;
-    runpy = PyImport_ImportModule("runpy");
-    if (runpy == NULL) {
-        fprintf(stderr, "Could not import runpy module\n");
-        PyErr_Print();
-        return -1;
-    }
-    runmodule = PyObject_GetAttrString(runpy, "_run_module_as_main");
-    if (runmodule == NULL) {
-        fprintf(stderr, "Could not access runpy._run_module_as_main\n");
-        PyErr_Print();
-        Py_DECREF(runpy);
-        return -1;
-    }
-    module = PyUnicode_FromWideChar(modname, wcslen(modname));
-    if (module == NULL) {
-        fprintf(stderr, "Could not convert module name to unicode\n");
-        PyErr_Print();
-        Py_DECREF(runpy);
-        Py_DECREF(runmodule);
-        return -1;
-    }
-    runargs = Py_BuildValue("(Oi)", module, set_argv0);
-    if (runargs == NULL) {
-        fprintf(stderr,
-            "Could not create arguments for runpy._run_module_as_main\n");
-        PyErr_Print();
-        Py_DECREF(runpy);
-        Py_DECREF(runmodule);
-        Py_DECREF(module);
-        return -1;
-    }
-    result = PyObject_Call(runmodule, runargs, NULL);
-    if (result == NULL) {
-        PyErr_Print();
+    PyObject *name, *value;
+
+    const wchar_t *name_end = wcschr(s, L'=');
+    if (!name_end) {
+        name = PyUnicode_FromWideChar(s, -1);
+        value = Py_True;
+        Py_INCREF(value);
     }
-    Py_DECREF(runpy);
-    Py_DECREF(runmodule);
-    Py_DECREF(module);
-    Py_DECREF(runargs);
-    if (result == NULL) {
-        return -1;
+    else {
+        name = PyUnicode_FromWideChar(s, name_end - s);
+        value = PyUnicode_FromWideChar(name_end + 1, -1);
     }
-    Py_DECREF(result);
-    return 0;
-}
-
-static PyObject *
-pymain_get_importer(const wchar_t *filename)
-{
-    PyObject *sys_path0 = NULL, *importer;
-
-    sys_path0 = PyUnicode_FromWideChar(filename, wcslen(filename));
-    if (sys_path0 == NULL) {
+    if (name == NULL || value == NULL) {
         goto error;
     }
-
-    importer = PyImport_GetImporter(sys_path0);
-    if (importer == NULL) {
+    if (PyDict_SetItem(opts, name, value) < 0) {
         goto error;
     }
-
-    if (importer == Py_None) {
-        Py_DECREF(sys_path0);
-        Py_DECREF(importer);
-        return NULL;
-    }
-
-    Py_DECREF(importer);
-    return sys_path0;
+    Py_DECREF(name);
+    Py_DECREF(value);
+    return 0;
 
 error:
-    Py_XDECREF(sys_path0);
-    PySys_WriteStderr("Failed checking if argv[0] is an import path entry\n");
-    PyErr_Print();
-    return NULL;
+    Py_XDECREF(name);
+    Py_XDECREF(value);
+    return -1;
 }
 
 
-static int
-pymain_run_command(wchar_t *command, PyCompilerFlags *cf)
+static PyObject*
+mainconfig_create_xoptions_dict(const _PyCoreConfig *config)
 {
-    PyObject *unicode, *bytes;
-    int ret;
-
-    unicode = PyUnicode_FromWideChar(command, -1);
-    if (unicode == NULL) {
-        goto error;
+    int nxoption = config->nxoption;
+    wchar_t **xoptions = config->xoptions;
+    PyObject *dict = PyDict_New();
+    if (dict == NULL) {
+        return NULL;
     }
 
-    bytes = PyUnicode_AsUTF8String(unicode);
-    Py_DECREF(unicode);
-    if (bytes == NULL) {
-        goto error;
+    for (int i=0; i < nxoption; i++) {
+        wchar_t *option = xoptions[i];
+        if (mainconfig_add_xoption(dict, option) < 0) {
+            Py_DECREF(dict);
+            return NULL;
+        }
     }
 
-    ret = PyRun_SimpleStringFlags(PyBytes_AsString(bytes), cf);
-    Py_DECREF(bytes);
-    return (ret != 0);
-
-error:
-    PySys_WriteStderr("Unable to decode the command from the command line:\n");
-    PyErr_Print();
-    return 1;
+    return dict;
 }
 
 
-/* Main program */
-
-typedef struct {
-    int argc;
-    int use_bytes_argv;
-    char **bytes_argv;
-    wchar_t **wchar_argv;
-} _PyArgv;
-
-typedef struct {
-    const _PyArgv *args;
-    wchar_t **argv;
-    int nwarnoption;             /* Number of -W command line options */
-    wchar_t **warnoptions;       /* Command line -W options */
-    int nenv_warnoption;         /* Number of PYTHONWARNINGS environment variables */
-    wchar_t **env_warnoptions;   /* PYTHONWARNINGS environment variables */
-    int print_help;              /* -h, -? options */
-    int print_version;           /* -V option */
-} _PyCmdline;
-
-
-
-/* Non-zero if filename, command (-c) or module (-m) is set
-   on the command line */
-#define RUN_CODE(config) \
-    (config->run_command != NULL || config->run_filename != NULL \
-     || config->run_module != NULL)
-
-
-static _PyInitError
-pymain_init_cmdline_argv(_PyCoreConfig *config, _PyCmdline *cmdline)
+static PyObject*
+mainconfig_copy_attr(PyObject *obj)
 {
-    assert(cmdline->argv == NULL);
-
-    const _PyArgv *args = cmdline->args;
-
-    if (args->use_bytes_argv) {
-        /* +1 for a the NULL terminator */
-        size_t size = sizeof(wchar_t*) * (args->argc + 1);
-        wchar_t** argv = (wchar_t **)PyMem_RawMalloc(size);
-        if (argv == NULL) {
-            return _Py_INIT_NO_MEMORY();
-        }
-
-        for (int i = 0; i < args->argc; i++) {
-            size_t len;
-            wchar_t *arg = Py_DecodeLocale(args->bytes_argv[i], &len);
-            if (arg == NULL) {
-                _Py_wstrlist_clear(i, argv);
-                return DECODE_LOCALE_ERR("command line arguments",
-                                         (Py_ssize_t)len);
-            }
-            argv[i] = arg;
-        }
-        argv[args->argc] = NULL;
-
-        cmdline->argv = argv;
+    if (PyUnicode_Check(obj)) {
+        Py_INCREF(obj);
+        return obj;
     }
-    else {
-        cmdline->argv = args->wchar_argv;
+    else if (PyList_Check(obj)) {
+        return PyList_GetSlice(obj, 0, Py_SIZE(obj));
     }
-
-    wchar_t *program;
-    if (args->argc >= 1 && cmdline->argv != NULL) {
-        program = cmdline->argv[0];
+    else if (PyDict_Check(obj)) {
+        /* The dict type is used for xoptions. Make the assumption that keys
+           and values are immutables */
+        return PyDict_Copy(obj);
     }
     else {
-        program = L"";
-    }
-    config->program = _PyMem_RawWcsdup(program);
-    if (config->program == NULL) {
-        return _Py_INIT_NO_MEMORY();
-    }
-
-    return _Py_INIT_OK();
-}
-
-
-static void
-pymain_clear_cmdline(_PyCmdline *cmdline)
-{
-    PyMemAllocatorEx old_alloc;
-    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
-
-    _Py_wstrlist_clear(cmdline->nwarnoption, cmdline->warnoptions);
-    cmdline->nwarnoption = 0;
-    cmdline->warnoptions = NULL;
-
-    _Py_wstrlist_clear(cmdline->nenv_warnoption, cmdline->env_warnoptions);
-    cmdline->nenv_warnoption = 0;
-    cmdline->env_warnoptions = NULL;
-
-    if (cmdline->args->use_bytes_argv && cmdline->argv != NULL) {
-        _Py_wstrlist_clear(cmdline->args->argc, cmdline->argv);
+        PyErr_Format(PyExc_TypeError,
+                     "cannot copy config attribute of type %.200s",
+                     Py_TYPE(obj)->tp_name);
+        return NULL;
     }
-    cmdline->argv = NULL;
-
-    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
 }
 
 
-static void
-pymain_clear_config(_PyCoreConfig *config)
+int
+_PyMainInterpreterConfig_Copy(_PyMainInterpreterConfig *config,
+                              const _PyMainInterpreterConfig *config2)
 {
-    /* Clear core config with the memory allocator
-       used by pymain_read_conf() */
-    PyMemAllocatorEx old_alloc;
-    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+    _PyMainInterpreterConfig_Clear(config);
 
-    _PyCoreConfig_Clear(config);
+#define COPY_ATTR(ATTR) config->ATTR = config2->ATTR
+#define COPY_OBJ_ATTR(ATTR) \
+    do { \
+        if (config2->ATTR != NULL) { \
+            config->ATTR = mainconfig_copy_attr(config2->ATTR); \
+            if (config->ATTR == NULL) { \
+                return -1; \
+            } \
+        } \
+    } while (0)
 
-    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+    COPY_ATTR(install_signal_handlers);
+    COPY_OBJ_ATTR(argv);
+    COPY_OBJ_ATTR(executable);
+    COPY_OBJ_ATTR(prefix);
+    COPY_OBJ_ATTR(base_prefix);
+    COPY_OBJ_ATTR(exec_prefix);
+    COPY_OBJ_ATTR(base_exec_prefix);
+    COPY_OBJ_ATTR(warnoptions);
+    COPY_OBJ_ATTR(xoptions);
+    COPY_OBJ_ATTR(module_search_path);
+    COPY_OBJ_ATTR(pycache_prefix);
+#undef COPY_ATTR
+#undef COPY_OBJ_ATTR
+    return 0;
 }
 
 
-static void
-pymain_free(void)
+PyObject*
+_PyMainInterpreterConfig_AsDict(const _PyMainInterpreterConfig *config)
 {
-    _PyImport_Fini2();
-
-    /* Free global variables which cannot be freed in Py_Finalize():
-       configuration options set before Py_Initialize() which should
-       remain valid after Py_Finalize(), since
-       Py_Initialize()-Py_Finalize() can be called multiple times. */
-    _PyPathConfig_ClearGlobal();
-    _Py_ClearStandardStreamEncoding();
-
-    /* Force the allocator used by pymain_read_conf() */
-    PyMemAllocatorEx old_alloc;
-    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+    PyObject *dict, *obj;
+    int res;
 
-    _Py_wstrlist_clear(orig_argc, orig_argv);
-    orig_argc = 0;
-    orig_argv = NULL;
+    dict = PyDict_New();
+    if (dict == NULL) {
+        return NULL;
+    }
 
-    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+#define SET_ITEM_INT(ATTR) \
+    do { \
+        obj = PyLong_FromLong(config->ATTR); \
+        if (obj == NULL) { \
+            goto fail; \
+        } \
+        res = PyDict_SetItemString(dict, #ATTR, obj); \
+        Py_DECREF(obj); \
+        if (res < 0) { \
+            goto fail; \
+        } \
+    } while (0)
 
-#ifdef __INSURE__
-    /* Insure++ is a memory analysis tool that aids in discovering
-     * memory leaks and other memory problems.  On Python exit, the
-     * interned string dictionaries are flagged as being in use at exit
-     * (which it is).  Under normal circumstances, this is fine because
-     * the memory will be automatically reclaimed by the system.  Under
-     * memory debugging, it's a huge source of useless noise, so we
-     * trade off slower shutdown for less distraction in the memory
-     * reports.  -baw
-     */
-    _Py_ReleaseInternedUnicodeStrings();
-#endif /* __INSURE__ */
-}
+#define SET_ITEM_OBJ(ATTR) \
+    do { \
+        obj = config->ATTR; \
+        if (obj == NULL) { \
+            obj = Py_None; \
+        } \
+        res = PyDict_SetItemString(dict, #ATTR, obj); \
+        if (res < 0) { \
+            goto fail; \
+        } \
+    } while (0)
 
+    SET_ITEM_INT(install_signal_handlers);
+    SET_ITEM_OBJ(argv);
+    SET_ITEM_OBJ(executable);
+    SET_ITEM_OBJ(prefix);
+    SET_ITEM_OBJ(base_prefix);
+    SET_ITEM_OBJ(exec_prefix);
+    SET_ITEM_OBJ(base_exec_prefix);
+    SET_ITEM_OBJ(warnoptions);
+    SET_ITEM_OBJ(xoptions);
+    SET_ITEM_OBJ(module_search_path);
+    SET_ITEM_OBJ(pycache_prefix);
 
-static int
-pymain_sys_path_add_path0(PyInterpreterState *interp, PyObject *path0)
-{
-    _Py_IDENTIFIER(path);
-    PyObject *sys_path;
-    PyObject *sysdict = interp->sysdict;
-    if (sysdict != NULL) {
-        sys_path = _PyDict_GetItemIdWithError(sysdict, &PyId_path);
-        if (sys_path == NULL && PyErr_Occurred()) {
-            goto error;
-        }
-    }
-    else {
-        sys_path = NULL;
-    }
-    if (sys_path == NULL) {
-        PyErr_SetString(PyExc_RuntimeError, "unable to get sys.path");
-        goto error;
-    }
+    return dict;
 
-    if (PyList_Insert(sys_path, 0, path0)) {
-        goto error;
-    }
-    return 0;
+fail:
+    Py_DECREF(dict);
+    return NULL;
 
-error:
-    PyErr_Print();
-    return -1;
+#undef SET_ITEM_OBJ
 }
 
 
 _PyInitError
-_Py_wstrlist_append(int *len, wchar_t ***list, const wchar_t *str)
+_PyMainInterpreterConfig_Read(_PyMainInterpreterConfig *main_config,
+                              const _PyCoreConfig *config)
 {
-    if (*len == INT_MAX) {
-        /* len+1 would overflow */
-        return _Py_INIT_NO_MEMORY();
-    }
-    wchar_t *str2 = _PyMem_RawWcsdup(str);
-    if (str2 == NULL) {
-        return _Py_INIT_NO_MEMORY();
-    }
-
-    size_t size = (*len + 1) * sizeof(list[0]);
-    wchar_t **list2 = (wchar_t **)PyMem_RawRealloc(*list, size);
-    if (list2 == NULL) {
-        PyMem_RawFree(str2);
-        return _Py_INIT_NO_MEMORY();
+    if (main_config->install_signal_handlers < 0) {
+        main_config->install_signal_handlers = config->install_signal_handlers;
     }
-    list2[*len] = str2;
-    *list = list2;
-    (*len)++;
-    return _Py_INIT_OK();
-}
-
-
-/* Parse the command line arguments */
-static _PyInitError
-pymain_parse_cmdline_impl(_PyCoreConfig *config, _PyCmdline *cmdline,
-                          int *need_usage)
-{
-    _PyInitError err;
-    _PyOS_ResetGetOpt();
-    do {
-        int longindex = -1;
-        int c = _PyOS_GetOpt(cmdline->args->argc, cmdline->argv, PROGRAM_OPTS,
-                             longoptions, &longindex);
-        if (c == EOF) {
-            break;
-        }
-
-        if (c == 'c') {
-            /* -c is the last option; following arguments
-               that look like options are left for the
-               command to interpret. */
-            size_t len = wcslen(_PyOS_optarg) + 1 + 1;
-            wchar_t *command = PyMem_RawMalloc(sizeof(wchar_t) * len);
-            if (command == NULL) {
-                return _Py_INIT_NO_MEMORY();
-            }
-            memcpy(command, _PyOS_optarg, (len - 2) * sizeof(wchar_t));
-            command[len - 2] = '\n';
-            command[len - 1] = 0;
-            config->run_command = command;
-            break;
-        }
-
-        if (c == 'm') {
-            /* -m is the last option; following arguments
-               that look like options are left for the
-               module to interpret. */
-            config->run_module = _PyMem_RawWcsdup(_PyOS_optarg);
-            if (config->run_module == NULL) {
-                return _Py_INIT_NO_MEMORY();
-            }
-            break;
-        }
-
-        switch (c) {
-        case 0:
-            // Handle long option.
-            assert(longindex == 0); // Only one long option now.
-            if (!wcscmp(_PyOS_optarg, L"always")) {
-                config->_check_hash_pycs_mode = "always";
-            } else if (!wcscmp(_PyOS_optarg, L"never")) {
-                config->_check_hash_pycs_mode = "never";
-            } else if (!wcscmp(_PyOS_optarg, L"default")) {
-                config->_check_hash_pycs_mode = "default";
-            } else {
-                fprintf(stderr, "--check-hash-based-pycs must be one of "
-                        "'default', 'always', or 'never'\n");
-                *need_usage = 1;
-                return _Py_INIT_OK();
-            }
-            break;
-
-        case 'b':
-            config->bytes_warning++;
-            break;
-
-        case 'd':
-            config->parser_debug++;
-            break;
-
-        case 'i':
-            config->inspect++;
-            config->interactive++;
-            break;
-
-        case 'I':
-            config->isolated++;
-            break;
-
-        /* case 'J': reserved for Jython */
-
-        case 'O':
-            config->optimization_level++;
-            break;
-
-        case 'B':
-            config->write_bytecode = 0;
-            break;
-
-        case 's':
-            config->user_site_directory = 0;
-            break;
-
-        case 'S':
-            config->site_import = 0;
-            break;
-
-        case 'E':
-            config->use_environment = 0;
-            break;
-
-        case 't':
-            /* ignored for backwards compatibility */
-            break;
-
-        case 'u':
-            config->buffered_stdio = 0;
-            break;
-
-        case 'v':
-            config->verbose++;
-            break;
-
-        case 'x':
-            config->skip_source_first_line = 1;
-            break;
-
-        case 'h':
-        case '?':
-            cmdline->print_help++;
-            break;
-
-        case 'V':
-            cmdline->print_version++;
-            break;
-
-        case 'W':
-            err = _Py_wstrlist_append(&cmdline->nwarnoption,
-                                      &cmdline->warnoptions,
-                                      _PyOS_optarg);
-            if (_Py_INIT_FAILED(err)) {
-                return err;
-            }
-            break;
-
-        case 'X':
-            err = _Py_wstrlist_append(&config->nxoption,
-                                      &config->xoptions,
-                                      _PyOS_optarg);
-            if (_Py_INIT_FAILED(err)) {
-                return err;
-            }
-            break;
-
-        case 'q':
-            config->quiet++;
-            break;
 
-        case 'R':
-            config->use_hash_seed = 0;
-            break;
-
-        /* This space reserved for other options */
-
-        default:
-            /* unknown argument: parsing failed */
-            *need_usage = 1;
-            return _Py_INIT_OK();
-        }
-    } while (1);
-
-    if (config->run_command == NULL && config->run_module == NULL
-        && _PyOS_optind < cmdline->args->argc
-        && wcscmp(cmdline->argv[_PyOS_optind], L"-") != 0)
-    {
-        config->run_filename = _PyMem_RawWcsdup(cmdline->argv[_PyOS_optind]);
-        if (config->run_filename == NULL) {
+    if (main_config->xoptions == NULL) {
+        main_config->xoptions = mainconfig_create_xoptions_dict(config);
+        if (main_config->xoptions == NULL) {
             return _Py_INIT_NO_MEMORY();
         }
     }
 
-    if (config->run_command != NULL || config->run_module != NULL) {
-        /* Backup _PyOS_optind */
-        _PyOS_optind--;
-    }
+#define COPY_WSTR(ATTR) \
+    do { \
+        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(); \
+            } \
+        } \
+    } while (0)
+#define COPY_WSTRLIST(ATTR, LEN, LIST) \
+    do { \
+        if (ATTR == NULL) { \
+            ATTR = _Py_wstrlist_as_pylist(LEN, LIST); \
+            if (ATTR == NULL) { \
+                return _Py_INIT_NO_MEMORY(); \
+            } \
+        } \
+    } while (0)
 
-    /* -c and -m options are exclusive */
-    assert(!(config->run_command != NULL && config->run_module != NULL));
+    COPY_WSTRLIST(main_config->warnoptions,
+                  config->nwarnoption, config->warnoptions);
+    if (config->argc >= 0) {
+        COPY_WSTRLIST(main_config->argv,
+                      config->argc, config->argv);
+    }
 
-    return _Py_INIT_OK();
-}
+    if (config->_install_importlib) {
+        COPY_WSTR(executable);
+        COPY_WSTR(prefix);
+        COPY_WSTR(base_prefix);
+        COPY_WSTR(exec_prefix);
+        COPY_WSTR(base_exec_prefix);
 
+        COPY_WSTRLIST(main_config->module_search_path,
+                      config->nmodule_search_path, config->module_search_paths);
 
-static int
-add_xoption(PyObject *opts, const wchar_t *s)
-{
-    PyObject *name, *value;
+        if (config->pycache_prefix != NULL) {
+            COPY_WSTR(pycache_prefix);
+        } else {
+            main_config->pycache_prefix = NULL;
+        }
 
-    const wchar_t *name_end = wcschr(s, L'=');
-    if (!name_end) {
-        name = PyUnicode_FromWideChar(s, -1);
-        value = Py_True;
-        Py_INCREF(value);
     }
-    else {
-        name = PyUnicode_FromWideChar(s, name_end - s);
-        value = PyUnicode_FromWideChar(name_end + 1, -1);
-    }
-    if (name == NULL || value == NULL) {
-        goto error;
-    }
-    if (PyDict_SetItem(opts, name, value) < 0) {
-        goto error;
-    }
-    Py_DECREF(name);
-    Py_DECREF(value);
-    return 0;
 
-error:
-    Py_XDECREF(name);
-    Py_XDECREF(value);
-    return -1;
+    return _Py_INIT_OK();
+#undef COPY_WSTR
+#undef COPY_WSTRLIST
 }
 
 
-static PyObject*
-config_create_xoptions_dict(const _PyCoreConfig *config)
+/* --- pymain_init() ---------------------------------------------- */
+
+static void
+config_clear(_PyCoreConfig *config)
 {
-    int nxoption = config->nxoption;
-    wchar_t **xoptions = config->xoptions;
-    PyObject *dict = PyDict_New();
-    if (dict == NULL) {
-        return NULL;
-    }
+    PyMemAllocatorEx old_alloc;
+    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
 
-    for (int i=0; i < nxoption; i++) {
-        wchar_t *option = xoptions[i];
-        if (add_xoption(dict, option) < 0) {
-            Py_DECREF(dict);
-            return NULL;
-        }
-    }
+    _PyCoreConfig_Clear(config);
 
-    return dict;
+    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
 }
 
 
 static _PyInitError
-config_add_warnings_optlist(_PyCoreConfig *config, int len, wchar_t **options)
+config_read_write(_PyCoreConfig *config, const _PyArgv *args)
 {
-    for (int i = 0; i < len; i++) {
-        _PyInitError err = _Py_wstrlist_append(&config->nwarnoption,
-                                               &config->warnoptions,
-                                               options[i]);
-        if (_Py_INIT_FAILED(err)) {
-            return err;
-        }
+    _PyInitError err;
+
+    PyMemAllocatorEx old_alloc;
+    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+
+    _PyCoreConfig_GetGlobalConfig(config);
+
+    err = _PyCoreConfig_ReadFromArgv(config, args);
+
+    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+
+    if (_Py_INIT_FAILED(err)) {
+        return err;
     }
+
+    _PyCoreConfig_Write(config);
     return _Py_INIT_OK();
 }
 
 
 static _PyInitError
-config_init_warnoptions(_PyCoreConfig *config, _PyCmdline *cmdline)
+pymain_init_python_main(PyInterpreterState *interp)
 {
     _PyInitError err;
 
-    assert(config->nwarnoption == 0);
-
-    /* The priority order for warnings configuration is (highest precedence
-     * first):
-     *
-     * - the BytesWarning filter, if needed ('-b', '-bb')
-     * - any '-W' command line options; then
-     * - the 'PYTHONWARNINGS' environment variable; then
-     * - the dev mode filter ('-X dev', 'PYTHONDEVMODE'); then
-     * - any implicit filters added by _warnings.c/warnings.py
-     *
-     * All settings except the last are passed to the warnings module via
-     * the `sys.warnoptions` list. Since the warnings module works on the basis
-     * of "the most recently added filter will be checked first", we add
-     * the lowest precedence entries first so that later entries override them.
-     */
-
-    if (config->dev_mode) {
-        err = _Py_wstrlist_append(&config->nwarnoption,
-                                  &config->warnoptions,
-                                  L"default");
-        if (_Py_INIT_FAILED(err)) {
-            return err;
-        }
+    _PyMainInterpreterConfig main_config = _PyMainInterpreterConfig_INIT;
+    err = _PyMainInterpreterConfig_Read(&main_config, &interp->core_config);
+    if (!_Py_INIT_FAILED(err)) {
+        err = _Py_InitializeMainInterpreter(interp, &main_config);
     }
+    _PyMainInterpreterConfig_Clear(&main_config);
 
-    err = config_add_warnings_optlist(config,
-                                      cmdline->nenv_warnoption,
-                                      cmdline->env_warnoptions);
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
+    return _Py_INIT_OK();
+}
+
+
+static _PyInitError
+pymain_init(const _PyArgv *args, PyInterpreterState **interp_p)
+{
+    _PyInitError err;
 
-    err = config_add_warnings_optlist(config,
-                                      cmdline->nwarnoption,
-                                      cmdline->warnoptions);
+    err = _PyRuntime_Initialize();
     if (_Py_INIT_FAILED(err)) {
         return err;
     }
 
-    /* If the bytes_warning_flag isn't set, bytesobject.c and bytearrayobject.c
-     * don't even try to emit a warning, so we skip setting the filter in that
-     * case.
+    /* 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.
      */
-    if (config->bytes_warning) {
-        wchar_t *filter;
-        if (config->bytes_warning> 1) {
-            filter = L"error::BytesWarning";
-        }
-        else {
-            filter = L"default::BytesWarning";
-        }
-        err = _Py_wstrlist_append(&config->nwarnoption,
-                                  &config->warnoptions,
-                                  filter);
-        if (_Py_INIT_FAILED(err)) {
-            return err;
-        }
-    }
-    return _Py_INIT_OK();
-}
+#ifdef __FreeBSD__
+    fedisableexcept(FE_OVERFLOW);
+#endif
 
+    _PyCoreConfig local_config = _PyCoreConfig_INIT;
+    _PyCoreConfig *config = &local_config;
 
-/* Get warning options from PYTHONWARNINGS environment variable. */
-static _PyInitError
-cmdline_init_env_warnoptions(const _PyCoreConfig *config, _PyCmdline *cmdline)
-{
-    wchar_t *env;
-    int res = _PyCoreConfig_GetEnvDup(config, &env,
-                                      L"PYTHONWARNINGS", "PYTHONWARNINGS");
-    if (res < 0) {
-        return DECODE_LOCALE_ERR("PYTHONWARNINGS", res);
+    err = config_read_write(config, args);
+    if (_Py_INIT_FAILED(err)) {
+        goto done;
     }
 
-    if (env == NULL) {
-        return _Py_INIT_OK();
+    PyInterpreterState *interp;
+    err = _Py_InitializeCore(&interp, config);
+    if (_Py_INIT_FAILED(err)) {
+        goto done;
     }
+    *interp_p = interp;
 
-
-    wchar_t *warning, *context = NULL;
-    for (warning = WCSTOK(env, L",", &context);
-         warning != NULL;
-         warning = WCSTOK(NULL, L",", &context))
-    {
-        _PyInitError err = _Py_wstrlist_append(&cmdline->nenv_warnoption,
-                                               &cmdline->env_warnoptions,
-                                               warning);
-        if (_Py_INIT_FAILED(err)) {
-            PyMem_RawFree(env);
-            return err;
-        }
+    err = pymain_init_python_main(interp);
+    if (_Py_INIT_FAILED(err)) {
+        goto done;
     }
-    PyMem_RawFree(env);
-    return _Py_INIT_OK();
+
+    err = _Py_INIT_OK();
+
+done:
+    config_clear(config);
+    return err;
 }
 
 
+/* --- pymain_run_python() ---------------------------------------- */
+
+/* Non-zero if filename, command (-c) or module (-m) is set
+   on the command line */
+#define RUN_CODE(config) \
+    (config->run_command != NULL || config->run_filename != NULL \
+     || config->run_module != NULL)
+
 /* Return non-zero is stdin is a TTY or if -i command line option is used */
 static int
 stdin_is_interactive(const _PyCoreConfig *config)
@@ -877,128 +403,84 @@ stdin_is_interactive(const _PyCoreConfig *config)
 }
 
 
-static void
-pymain_init_stdio(_PyCoreConfig *config)
+static PyObject *
+pymain_get_importer(const wchar_t *filename)
 {
-#if defined(MS_WINDOWS) || defined(__CYGWIN__)
-    /* don't translate newlines (\r\n <=> \n) */
-    _setmode(fileno(stdin), O_BINARY);
-    _setmode(fileno(stdout), O_BINARY);
-    _setmode(fileno(stderr), O_BINARY);
-#endif
+    PyObject *sys_path0 = NULL, *importer;
 
-    if (!config->buffered_stdio) {
-#ifdef HAVE_SETVBUF
-        setvbuf(stdin,  (char *)NULL, _IONBF, BUFSIZ);
-        setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ);
-        setvbuf(stderr, (char *)NULL, _IONBF, BUFSIZ);
-#else /* !HAVE_SETVBUF */
-        setbuf(stdin,  (char *)NULL);
-        setbuf(stdout, (char *)NULL);
-        setbuf(stderr, (char *)NULL);
-#endif /* !HAVE_SETVBUF */
-    }
-    else if (config->interactive) {
-#ifdef MS_WINDOWS
-        /* Doesn't have to have line-buffered -- use unbuffered */
-        /* Any set[v]buf(stdin, ...) screws up Tkinter :-( */
-        setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ);
-#else /* !MS_WINDOWS */
-#ifdef HAVE_SETVBUF
-        setvbuf(stdin,  (char *)NULL, _IOLBF, BUFSIZ);
-        setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ);
-#endif /* HAVE_SETVBUF */
-#endif /* !MS_WINDOWS */
-        /* Leave stderr alone - it should be unbuffered anyway. */
+    sys_path0 = PyUnicode_FromWideChar(filename, wcslen(filename));
+    if (sys_path0 == NULL) {
+        goto error;
     }
-}
 
-
-static void
-pymain_header(const _PyCoreConfig *config)
-{
-    if (config->quiet) {
-        return;
+    importer = PyImport_GetImporter(sys_path0);
+    if (importer == NULL) {
+        goto error;
     }
 
-    if (!config->verbose && (RUN_CODE(config) || !stdin_is_interactive(config))) {
-        return;
+    if (importer == Py_None) {
+        Py_DECREF(sys_path0);
+        Py_DECREF(importer);
+        return NULL;
     }
 
-    fprintf(stderr, "Python %s on %s\n", Py_GetVersion(), Py_GetPlatform());
-    if (config->site_import) {
-        fprintf(stderr, "%s\n", COPYRIGHT);
-    }
+    Py_DECREF(importer);
+    return sys_path0;
+
+error:
+    Py_XDECREF(sys_path0);
+    PySys_WriteStderr("Failed checking if argv[0] is an import path entry\n");
+    PyErr_Print();
+    return NULL;
 }
 
 
-static _PyInitError
-pymain_init_core_argv(_PyCoreConfig *config, _PyCmdline *cmdline)
+static int
+pymain_sys_path_add_path0(PyInterpreterState *interp, PyObject *path0)
 {
-    /* Copy argv to be able to modify it (to force -c/-m) */
-    int argc = cmdline->args->argc - _PyOS_optind;
-    wchar_t **argv;
-
-    if (argc <= 0 || cmdline->argv == NULL) {
-        /* Ensure at least one (empty) argument is seen */
-        static wchar_t *empty_argv[1] = {L""};
-        argc = 1;
-        argv = _Py_wstrlist_copy(1, empty_argv);
+    _Py_IDENTIFIER(path);
+    PyObject *sys_path;
+    PyObject *sysdict = interp->sysdict;
+    if (sysdict != NULL) {
+        sys_path = _PyDict_GetItemIdWithError(sysdict, &PyId_path);
+        if (sys_path == NULL && PyErr_Occurred()) {
+            goto error;
+        }
     }
     else {
-        argv = _Py_wstrlist_copy(argc, &cmdline->argv[_PyOS_optind]);
-    }
-
-    if (argv == NULL) {
-        return _Py_INIT_NO_MEMORY();
-    }
-
-    wchar_t *arg0 = NULL;
-    if (config->run_command != NULL) {
-        /* Force sys.argv[0] = '-c' */
-        arg0 = L"-c";
+        sys_path = NULL;
     }
-    else if (config->run_module != NULL) {
-        /* Force sys.argv[0] = '-m'*/
-        arg0 = L"-m";
+    if (sys_path == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "unable to get sys.path");
+        goto error;
     }
-    if (arg0 != NULL) {
-        arg0 = _PyMem_RawWcsdup(arg0);
-        if (arg0 == NULL) {
-            _Py_wstrlist_clear(argc, argv);
-            return _Py_INIT_NO_MEMORY();
-        }
 
-        assert(argc >= 1);
-        PyMem_RawFree(argv[0]);
-        argv[0] = arg0;
+    if (PyList_Insert(sys_path, 0, path0)) {
+        goto error;
     }
+    return 0;
 
-    config->argc = argc;
-    config->argv = argv;
-    return _Py_INIT_OK();
+error:
+    PyErr_Print();
+    return -1;
 }
 
 
-PyObject*
-_Py_wstrlist_as_pylist(int len, wchar_t **list)
+static void
+pymain_header(const _PyCoreConfig *config)
 {
-    assert(list != NULL || len < 1);
+    if (config->quiet) {
+        return;
+    }
 
-    PyObject *pylist = PyList_New(len);
-    if (pylist == NULL) {
-        return NULL;
+    if (!config->verbose && (RUN_CODE(config) || !stdin_is_interactive(config))) {
+        return;
     }
 
-    for (int i = 0; i < len; i++) {
-        PyObject *v = PyUnicode_FromWideChar(list[i], -1);
-        if (v == NULL) {
-            Py_DECREF(pylist);
-            return NULL;
-        }
-        PyList_SET_ITEM(pylist, i, v);
+    fprintf(stderr, "Python %s on %s\n", Py_GetVersion(), Py_GetPlatform());
+    if (config->site_import) {
+        fprintf(stderr, "%s\n", COPYRIGHT);
     }
-    return pylist;
 }
 
 
@@ -1025,29 +507,82 @@ pymain_import_readline(const _PyCoreConfig *config)
 }
 
 
-static void
-pymain_run_startup(_PyCoreConfig *config, PyCompilerFlags *cf)
+static int
+pymain_run_command(wchar_t *command, PyCompilerFlags *cf)
 {
-    const char *startup = _PyCoreConfig_GetEnv(config, "PYTHONSTARTUP");
-    if (startup == NULL) {
-        return;
+    PyObject *unicode, *bytes;
+    int ret;
+
+    unicode = PyUnicode_FromWideChar(command, -1);
+    if (unicode == NULL) {
+        goto error;
     }
 
-    FILE *fp = _Py_fopen(startup, "r");
-    if (fp == NULL) {
-        int save_errno = errno;
-        PySys_WriteStderr("Could not open PYTHONSTARTUP\n");
-        errno = save_errno;
+    bytes = PyUnicode_AsUTF8String(unicode);
+    Py_DECREF(unicode);
+    if (bytes == NULL) {
+        goto error;
+    }
 
-        PyErr_SetFromErrnoWithFilename(PyExc_OSError,
-                        startup);
+    ret = PyRun_SimpleStringFlags(PyBytes_AsString(bytes), cf);
+    Py_DECREF(bytes);
+    return (ret != 0);
+
+error:
+    PySys_WriteStderr("Unable to decode the command from the command line:\n");
+    PyErr_Print();
+    return 1;
+}
+
+
+static int
+pymain_run_module(const wchar_t *modname, int set_argv0)
+{
+    PyObject *module, *runpy, *runmodule, *runargs, *result;
+    runpy = PyImport_ImportModule("runpy");
+    if (runpy == NULL) {
+        fprintf(stderr, "Could not import runpy module\n");
         PyErr_Print();
-        return;
+        return -1;
     }
-
-    (void) PyRun_SimpleFileExFlags(fp, startup, 0, cf);
-    PyErr_Clear();
-    fclose(fp);
+    runmodule = PyObject_GetAttrString(runpy, "_run_module_as_main");
+    if (runmodule == NULL) {
+        fprintf(stderr, "Could not access runpy._run_module_as_main\n");
+        PyErr_Print();
+        Py_DECREF(runpy);
+        return -1;
+    }
+    module = PyUnicode_FromWideChar(modname, wcslen(modname));
+    if (module == NULL) {
+        fprintf(stderr, "Could not convert module name to unicode\n");
+        PyErr_Print();
+        Py_DECREF(runpy);
+        Py_DECREF(runmodule);
+        return -1;
+    }
+    runargs = Py_BuildValue("(Oi)", module, set_argv0);
+    if (runargs == NULL) {
+        fprintf(stderr,
+            "Could not create arguments for runpy._run_module_as_main\n");
+        PyErr_Print();
+        Py_DECREF(runpy);
+        Py_DECREF(runmodule);
+        Py_DECREF(module);
+        return -1;
+    }
+    result = PyObject_Call(runmodule, runargs, NULL);
+    if (result == NULL) {
+        PyErr_Print();
+    }
+    Py_DECREF(runpy);
+    Py_DECREF(runmodule);
+    Py_DECREF(module);
+    Py_DECREF(runargs);
+    if (result == NULL) {
+        return -1;
+    }
+    Py_DECREF(result);
+    return 0;
 }
 
 
@@ -1117,437 +652,108 @@ pymain_run_file(_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);
-    return (run != 0);
-}
-
-
-static int
-pymain_run_stdin(_PyCoreConfig *config, PyCompilerFlags *cf)
-{
-    if (stdin_is_interactive(config)) {
-        Py_InspectFlag = 0; /* do exit on SystemExit */
-        config->inspect = 0;
-        pymain_run_startup(config, cf);
-        pymain_run_interactive_hook();
-    }
-
-    /* call pending calls like signal handlers (SIGINT) */
-    if (Py_MakePendingCalls() == -1) {
-        PyErr_Print();
-        return 1;
-    }
-
-    int run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, cf);
-    return (run != 0);
-}
-
-
-static void
-pymain_repl(_PyCoreConfig *config, PyCompilerFlags *cf, int *exitcode)
-{
-    /* Check this environment variable at the end, to give programs the
-       opportunity to set it from Python. */
-    if (!Py_InspectFlag && _PyCoreConfig_GetEnv(config, "PYTHONINSPECT")) {
-        Py_InspectFlag = 1;
-        config->inspect = 1;
-    }
-
-    if (!(Py_InspectFlag && stdin_is_interactive(config) && RUN_CODE(config))) {
-        return;
-    }
-
-    Py_InspectFlag = 0;
-    config->inspect = 0;
-    pymain_run_interactive_hook();
-
-    int res = PyRun_AnyFileFlags(stdin, "<stdin>", cf);
-    *exitcode = (res != 0);
-}
-
-
-/* Parse the command line.
-   Handle --version and --help options directly. */
-static _PyInitError
-pymain_parse_cmdline(_PyCoreConfig *config, _PyCmdline *cmdline)
-{
-    int need_usage = 0;
-    _PyInitError err;
-    err = pymain_parse_cmdline_impl(config, cmdline, &need_usage);
-    if (_Py_INIT_FAILED(err)) {
-        return err;
-    }
-
-    if (need_usage) {
-        pymain_usage(1, config->program);
-        return _Py_INIT_EXIT(2);
-    }
-    return _Py_INIT_OK();
-}
-
-
-/* Parse command line options and environment variables.
-   This code must not use Python runtime apart PyMem_Raw memory allocator. */
-static _PyInitError
-pymain_read_conf_impl(_PyCoreConfig *config, _PyCmdline *cmdline)
-{
-    _PyInitError err;
-
-    err = pymain_parse_cmdline(config, cmdline);
-    if (_Py_INIT_FAILED(err)) {
-        return err;
-    }
-
-    err = pymain_init_core_argv(config, cmdline);
-    if (_Py_INIT_FAILED(err)) {
-        return err;
-    }
-
-    err = _PyCoreConfig_Read(config);
-    if (_Py_INIT_FAILED(err)) {
-        return err;
-    }
-
-    if (config->use_environment) {
-        err = cmdline_init_env_warnoptions(config, cmdline);
-        if (_Py_INIT_FAILED(err)) {
-            return err;
-        }
-    }
-
-    err = config_init_warnoptions(config, cmdline);
-    if (_Py_INIT_FAILED(err)) {
-        return err;
-    }
-    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 _PyInitError
-pymain_read_conf(_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 locale_coerced = 0;
-    int loops = 0;
-
-    if (_PyCoreConfig_Copy(&save_config, config) < 0) {
-        err = _Py_INIT_NO_MEMORY();
-        goto done;
-    }
-
-    /* Set LC_CTYPE to the user preferred locale */
-    _Py_SetLocaleFromEnv(LC_CTYPE);
-
-    while (1) {
-        int utf8_mode = config->utf8_mode;
-        int encoding_changed = 0;
-
-        /* Watchdog to prevent an infinite loop */
-        loops++;
-        if (loops == 3) {
-            err = _Py_INIT_ERR("Encoding changed twice while "
-                               "reading the configuration");
-            goto done;
-        }
-
-        /* bpo-34207: Py_DecodeLocale() and Py_EncodeLocale() depend
-           on Py_UTF8Mode and Py_LegacyWindowsFSEncodingFlag. */
-        Py_UTF8Mode = config->utf8_mode;
-#ifdef MS_WINDOWS
-        Py_LegacyWindowsFSEncodingFlag = config->legacy_windows_fs_encoding;
-#endif
-
-        err = pymain_init_cmdline_argv(config, cmdline);
-        if (_Py_INIT_FAILED(err)) {
-            goto done;
-        }
-
-        err = pymain_read_conf_impl(config, cmdline);
-        if (_Py_INIT_FAILED(err)) {
-            goto done;
-        }
-
-        /* The legacy C locale assumes ASCII as the default text encoding, which
-         * causes problems not only for the CPython runtime, but also other
-         * components like GNU readline.
-         *
-         * Accordingly, when the CLI detects it, it attempts to coerce it to a
-         * more capable UTF-8 based alternative.
-         *
-         * See the documentation of the PYTHONCOERCECLOCALE setting for more
-         * details.
-         */
-        if (config->coerce_c_locale && !locale_coerced) {
-            locale_coerced = 1;
-            _Py_CoerceLegacyLocale(config->coerce_c_locale_warn);
-            encoding_changed = 1;
-        }
-
-        if (utf8_mode == -1) {
-            if (config->utf8_mode == 1) {
-                /* UTF-8 Mode enabled */
-                encoding_changed = 1;
-            }
-        }
-        else {
-            if (config->utf8_mode != utf8_mode) {
-                encoding_changed = 1;
-            }
-        }
-
-        if (!encoding_changed) {
-            break;
-        }
-
-        /* Reset the configuration before reading again the configuration,
-           just keep UTF-8 Mode value. */
-        int new_utf8_mode = config->utf8_mode;
-        int new_coerce_c_locale = config->coerce_c_locale;
-        if (_PyCoreConfig_Copy(config, &save_config) < 0) {
-            err = _Py_INIT_NO_MEMORY();
-            goto done;
-        }
-        pymain_clear_cmdline(cmdline);
-        const _PyArgv *args = cmdline->args;
-        memset(cmdline, 0, sizeof(*cmdline));
-        cmdline->args = args;
-        config->utf8_mode = new_utf8_mode;
-        config->coerce_c_locale = new_coerce_c_locale;
-
-        /* The encoding changed: read again the configuration
-           with the new encoding */
-    }
-    err = _Py_INIT_OK();
-
-done:
-    _PyCoreConfig_Clear(&save_config);
-    Py_UTF8Mode = init_utf8_mode ;
-#ifdef MS_WINDOWS
-    Py_LegacyWindowsFSEncodingFlag = init_legacy_encoding;
-#endif
-    return err;
-}
-
-
-void
-_PyMainInterpreterConfig_Clear(_PyMainInterpreterConfig *config)
-{
-    Py_CLEAR(config->argv);
-    Py_CLEAR(config->executable);
-    Py_CLEAR(config->prefix);
-    Py_CLEAR(config->base_prefix);
-    Py_CLEAR(config->exec_prefix);
-    Py_CLEAR(config->base_exec_prefix);
-    Py_CLEAR(config->warnoptions);
-    Py_CLEAR(config->xoptions);
-    Py_CLEAR(config->module_search_path);
-    Py_CLEAR(config->pycache_prefix);
-}
-
-
-static PyObject*
-config_copy_attr(PyObject *obj)
-{
-    if (PyUnicode_Check(obj)) {
-        Py_INCREF(obj);
-        return obj;
-    }
-    else if (PyList_Check(obj)) {
-        return PyList_GetSlice(obj, 0, Py_SIZE(obj));
-    }
-    else if (PyDict_Check(obj)) {
-        /* The dict type is used for xoptions. Make the assumption that keys
-           and values are immutables */
-        return PyDict_Copy(obj);
-    }
-    else {
-        PyErr_Format(PyExc_TypeError,
-                     "cannot copy config attribute of type %.200s",
-                     Py_TYPE(obj)->tp_name);
-        return NULL;
-    }
-}
-
-
-int
-_PyMainInterpreterConfig_Copy(_PyMainInterpreterConfig *config,
-                              const _PyMainInterpreterConfig *config2)
-{
-    _PyMainInterpreterConfig_Clear(config);
-
-#define COPY_ATTR(ATTR) config->ATTR = config2->ATTR
-#define COPY_OBJ_ATTR(ATTR) \
-    do { \
-        if (config2->ATTR != NULL) { \
-            config->ATTR = config_copy_attr(config2->ATTR); \
-            if (config->ATTR == NULL) { \
-                return -1; \
-            } \
-        } \
-    } while (0)
-
-    COPY_ATTR(install_signal_handlers);
-    COPY_OBJ_ATTR(argv);
-    COPY_OBJ_ATTR(executable);
-    COPY_OBJ_ATTR(prefix);
-    COPY_OBJ_ATTR(base_prefix);
-    COPY_OBJ_ATTR(exec_prefix);
-    COPY_OBJ_ATTR(base_exec_prefix);
-    COPY_OBJ_ATTR(warnoptions);
-    COPY_OBJ_ATTR(xoptions);
-    COPY_OBJ_ATTR(module_search_path);
-    COPY_OBJ_ATTR(pycache_prefix);
-#undef COPY_ATTR
-#undef COPY_OBJ_ATTR
-    return 0;
-}
-
-
-PyObject*
-_PyMainInterpreterConfig_AsDict(const _PyMainInterpreterConfig *config)
-{
-    PyObject *dict, *obj;
-    int res;
-
-    dict = PyDict_New();
-    if (dict == NULL) {
-        return NULL;
-    }
-
-#define SET_ITEM_INT(ATTR) \
-    do { \
-        obj = PyLong_FromLong(config->ATTR); \
-        if (obj == NULL) { \
-            goto fail; \
-        } \
-        res = PyDict_SetItemString(dict, #ATTR, obj); \
-        Py_DECREF(obj); \
-        if (res < 0) { \
-            goto fail; \
-        } \
-    } while (0)
-
-#define SET_ITEM_OBJ(ATTR) \
-    do { \
-        obj = config->ATTR; \
-        if (obj == NULL) { \
-            obj = Py_None; \
-        } \
-        res = PyDict_SetItemString(dict, #ATTR, obj); \
-        if (res < 0) { \
-            goto fail; \
-        } \
-    } while (0)
-
-    SET_ITEM_INT(install_signal_handlers);
-    SET_ITEM_OBJ(argv);
-    SET_ITEM_OBJ(executable);
-    SET_ITEM_OBJ(prefix);
-    SET_ITEM_OBJ(base_prefix);
-    SET_ITEM_OBJ(exec_prefix);
-    SET_ITEM_OBJ(base_exec_prefix);
-    SET_ITEM_OBJ(warnoptions);
-    SET_ITEM_OBJ(xoptions);
-    SET_ITEM_OBJ(module_search_path);
-    SET_ITEM_OBJ(pycache_prefix);
+    return (run != 0);
+}
 
-    return dict;
 
-fail:
-    Py_DECREF(dict);
-    return NULL;
+static void
+pymain_run_startup(_PyCoreConfig *config, PyCompilerFlags *cf)
+{
+    const char *startup = _PyCoreConfig_GetEnv(config, "PYTHONSTARTUP");
+    if (startup == NULL) {
+        return;
+    }
 
-#undef SET_ITEM_OBJ
+    FILE *fp = _Py_fopen(startup, "r");
+    if (fp == NULL) {
+        int save_errno = errno;
+        PySys_WriteStderr("Could not open PYTHONSTARTUP\n");
+        errno = save_errno;
+
+        PyErr_SetFromErrnoWithFilename(PyExc_OSError,
+                        startup);
+        PyErr_Print();
+        return;
+    }
+
+    (void) PyRun_SimpleFileExFlags(fp, startup, 0, cf);
+    PyErr_Clear();
+    fclose(fp);
 }
 
 
-_PyInitError
-_PyMainInterpreterConfig_Read(_PyMainInterpreterConfig *main_config,
-                              const _PyCoreConfig *config)
+static void
+pymain_run_interactive_hook(void)
 {
-    if (main_config->install_signal_handlers < 0) {
-        main_config->install_signal_handlers = config->install_signal_handlers;
+    PyObject *sys, *hook, *result;
+    sys = PyImport_ImportModule("sys");
+    if (sys == NULL) {
+        goto error;
     }
 
-    if (main_config->xoptions == NULL) {
-        main_config->xoptions = config_create_xoptions_dict(config);
-        if (main_config->xoptions == NULL) {
-            return _Py_INIT_NO_MEMORY();
-        }
+    hook = PyObject_GetAttrString(sys, "__interactivehook__");
+    Py_DECREF(sys);
+    if (hook == NULL) {
+        PyErr_Clear();
+        return;
     }
 
-#define COPY_WSTR(ATTR) \
-    do { \
-        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(); \
-            } \
-        } \
-    } while (0)
-#define COPY_WSTRLIST(ATTR, LEN, LIST) \
-    do { \
-        if (ATTR == NULL) { \
-            ATTR = _Py_wstrlist_as_pylist(LEN, LIST); \
-            if (ATTR == NULL) { \
-                return _Py_INIT_NO_MEMORY(); \
-            } \
-        } \
-    } while (0)
-
-    COPY_WSTRLIST(main_config->warnoptions,
-                  config->nwarnoption, config->warnoptions);
-    if (config->argc >= 0) {
-        COPY_WSTRLIST(main_config->argv,
-                      config->argc, config->argv);
+    result = _PyObject_CallNoArg(hook);
+    Py_DECREF(hook);
+    if (result == NULL) {
+        goto error;
     }
+    Py_DECREF(result);
 
-    if (config->_install_importlib) {
-        COPY_WSTR(executable);
-        COPY_WSTR(prefix);
-        COPY_WSTR(base_prefix);
-        COPY_WSTR(exec_prefix);
-        COPY_WSTR(base_exec_prefix);
+    return;
 
-        COPY_WSTRLIST(main_config->module_search_path,
-                      config->nmodule_search_path, config->module_search_paths);
+error:
+    PySys_WriteStderr("Failed calling sys.__interactivehook__\n");
+    PyErr_Print();
+}
 
-        if (config->pycache_prefix != NULL) {
-            COPY_WSTR(pycache_prefix);
-        } else {
-            main_config->pycache_prefix = NULL;
-        }
 
+static int
+pymain_run_stdin(_PyCoreConfig *config, PyCompilerFlags *cf)
+{
+    if (stdin_is_interactive(config)) {
+        Py_InspectFlag = 0; /* do exit on SystemExit */
+        config->inspect = 0;
+        pymain_run_startup(config, cf);
+        pymain_run_interactive_hook();
     }
 
-    return _Py_INIT_OK();
-#undef COPY_WSTR
-#undef COPY_WSTRLIST
+    /* call pending calls like signal handlers (SIGINT) */
+    if (Py_MakePendingCalls() == -1) {
+        PyErr_Print();
+        return 1;
+    }
+
+    int run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, cf);
+    return (run != 0);
 }
 
 
-static _PyInitError
-pymain_init_python_main(PyInterpreterState *interp)
+static void
+pymain_repl(_PyCoreConfig *config, PyCompilerFlags *cf, int *exitcode)
 {
-    _PyInitError err;
-
-    _PyMainInterpreterConfig main_config = _PyMainInterpreterConfig_INIT;
-    err = _PyMainInterpreterConfig_Read(&main_config, &interp->core_config);
-    if (!_Py_INIT_FAILED(err)) {
-        err = _Py_InitializeMainInterpreter(interp, &main_config);
+    /* Check this environment variable at the end, to give programs the
+       opportunity to set it from Python. */
+    if (!Py_InspectFlag && _PyCoreConfig_GetEnv(config, "PYTHONINSPECT")) {
+        Py_InspectFlag = 1;
+        config->inspect = 1;
     }
-    _PyMainInterpreterConfig_Clear(&main_config);
 
-    if (_Py_INIT_FAILED(err)) {
-        return err;
+    if (!(Py_InspectFlag && stdin_is_interactive(config) && RUN_CODE(config))) {
+        return;
     }
-    return _Py_INIT_OK();
+
+    Py_InspectFlag = 0;
+    config->inspect = 0;
+    pymain_run_interactive_hook();
+
+    int res = PyRun_AnyFileFlags(stdin, "<stdin>", cf);
+    *exitcode = (res != 0);
 }
 
 
@@ -1620,129 +826,58 @@ pymain_run_python(PyInterpreterState *interp, int *exitcode)
 }
 
 
-static _PyInitError
-pymain_cmdline_impl(_PyCoreConfig *config, _PyCmdline *cmdline)
-{
-    _PyInitError err;
-
-    err = _PyRuntime_Initialize();
-    if (_Py_INIT_FAILED(err)) {
-        return err;
-    }
-
-    err = pymain_read_conf(config, cmdline);
-    if (_Py_INIT_FAILED(err)) {
-        return err;
-    }
-
-    if (cmdline->print_help) {
-        pymain_usage(0, config->program);
-        return _Py_INIT_EXIT(0);
-    }
-
-    if (cmdline->print_version) {
-        printf("Python %s\n",
-               (cmdline->print_version >= 2) ? Py_GetVersion() : PY_VERSION);
-        return _Py_INIT_EXIT(0);
-    }
-
-    /* For Py_GetArgcArgv(). Cleared by pymain_free(). */
-    orig_argv = _Py_wstrlist_copy(cmdline->args->argc, cmdline->argv);
-    if (orig_argv == NULL) {
-        return _Py_INIT_NO_MEMORY();
-    }
-    orig_argc = cmdline->args->argc;
-    return _Py_INIT_OK();
-}
-
-
-/* Read the configuration into _PyCoreConfig and _PyMain, initialize the
-   LC_CTYPE locale and Py_DecodeLocale().
-
-   Configuration:
+/* --- pymain_main() ---------------------------------------------- */
 
-   * Command line arguments
-   * Environment variables
-   * Py_xxx global configuration variables
-
-   _PyCmdline is a temporary structure used to prioritize these
-   variables. */
-static _PyInitError
-pymain_cmdline(_PyArgv *args, _PyCoreConfig *config)
+static void
+pymain_free(void)
 {
-    /* Force default allocator, since pymain_free() and pymain_clear_config()
-       must use the same allocator than this function. */
-    PyMemAllocatorEx old_alloc;
-    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
-#ifdef Py_DEBUG
-    PyMemAllocatorEx default_alloc;
-    PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &default_alloc);
-#endif
-
-    _PyCmdline cmdline;
-    memset(&cmdline, 0, sizeof(cmdline));
-    cmdline.args = args;
-
-    _PyInitError err = pymain_cmdline_impl(config, &cmdline);
-
-    pymain_clear_cmdline(&cmdline);
+    _PyImport_Fini2();
 
-#ifdef Py_DEBUG
-    /* Make sure that PYMEM_DOMAIN_RAW has not been modified */
-    PyMemAllocatorEx cur_alloc;
-    PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &cur_alloc);
-    assert(memcmp(&cur_alloc, &default_alloc, sizeof(cur_alloc)) == 0);
-#endif
-    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
-    return err;
+    /* Free global variables which cannot be freed in Py_Finalize():
+       configuration options set before Py_Initialize() which should
+       remain valid after Py_Finalize(), since
+       Py_Initialize()-Py_Finalize() can be called multiple times. */
+    _PyPathConfig_ClearGlobal();
+    _Py_ClearStandardStreamEncoding();
+    _Py_ClearArgcArgv();
+#ifdef __INSURE__
+    /* Insure++ is a memory analysis tool that aids in discovering
+     * memory leaks and other memory problems.  On Python exit, the
+     * interned string dictionaries are flagged as being in use at exit
+     * (which it is).  Under normal circumstances, this is fine because
+     * the memory will be automatically reclaimed by the system.  Under
+     * memory debugging, it's a huge source of useless noise, so we
+     * trade off slower shutdown for less distraction in the memory
+     * reports.  -baw
+     */
+    _Py_ReleaseInternedUnicodeStrings();
+#endif /* __INSURE__ */
 }
 
 
-static _PyInitError
-pymain_init(_PyArgv *args, PyInterpreterState **interp_p)
+static int
+exit_sigint(void)
 {
-    _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
-     * exceptions by default.  Here we disable them.
-     */
-#ifdef __FreeBSD__
-    fedisableexcept(FE_OVERFLOW);
-#endif
-
-    _PyCoreConfig local_config = _PyCoreConfig_INIT;
-    _PyCoreConfig *config = &local_config;
-
-    _PyCoreConfig_GetGlobalConfig(config);
-
-    err = pymain_cmdline(args, config);
-    if (_Py_INIT_FAILED(err)) {
-        goto done;
-    }
-
-    _PyCoreConfig_SetGlobalConfig(config);
-
-    pymain_init_stdio(config);
-
-    PyInterpreterState *interp;
-    err = _Py_InitializeCore(&interp, config);
-    if (_Py_INIT_FAILED(err)) {
-        goto done;
-    }
-    *interp_p = interp;
-
-    err = pymain_init_python_main(interp);
-    if (_Py_INIT_FAILED(err)) {
-        goto done;
+    /* bpo-1054041: We need to exit via the
+     * SIG_DFL handler for SIGINT if KeyboardInterrupt went unhandled.
+     * If we don't, a calling process such as a shell may not know
+     * about the user's ^C.  https://www.cons.org/cracauer/sigint.html */
+#if defined(HAVE_GETPID) && !defined(MS_WINDOWS)
+    if (PyOS_setsig(SIGINT, SIG_DFL) == SIG_ERR) {
+        perror("signal");  /* Impossible in normal environments. */
+    } else {
+        kill(getpid(), SIGINT);
     }
-
-    err = _Py_INIT_OK();
-
-done:
-    pymain_clear_config(config);
-    return err;
+    /* If setting SIG_DFL failed, or kill failed to terminate us,
+     * there isn't much else we can do aside from an error code. */
+#endif  /* HAVE_GETPID && !MS_WINDOWS */
+#ifdef MS_WINDOWS
+    /* cmd.exe detects this, prints ^C, and offers to terminate. */
+    /* https://msdn.microsoft.com/en-us/library/cc704588.aspx */
+    return STATUS_CONTROL_C_EXIT;
+#else
+    return SIGINT + 128;
+#endif  /* !MS_WINDOWS */
 }
 
 
@@ -1772,26 +907,7 @@ pymain_main(_PyArgv *args)
     pymain_free();
 
     if (_Py_UnhandledKeyboardInterrupt) {
-        /* https://bugs.python.org/issue1054041 - We need to exit via the
-         * SIG_DFL handler for SIGINT if KeyboardInterrupt went unhandled.
-         * If we don't, a calling process such as a shell may not know
-         * about the user's ^C.  https://www.cons.org/cracauer/sigint.html */
-#if defined(HAVE_GETPID) && !defined(MS_WINDOWS)
-        if (PyOS_setsig(SIGINT, SIG_DFL) == SIG_ERR) {
-            perror("signal");  /* Impossible in normal environments. */
-        } else {
-            kill(getpid(), SIGINT);
-        }
-        /* If setting SIG_DFL failed, or kill failed to terminate us,
-         * there isn't much else we can do aside from an error code. */
-#endif  /* HAVE_GETPID && !MS_WINDOWS */
-#ifdef MS_WINDOWS
-        /* cmd.exe detects this, prints ^C, and offers to terminate. */
-        /* https://msdn.microsoft.com/en-us/library/cc704588.aspx */
-        exitcode = STATUS_CONTROL_C_EXIT;
-#else
-        exitcode = SIGINT + 128;
-#endif  /* !MS_WINDOWS */
+        exitcode = exit_sigint();
     }
 
     return exitcode;
@@ -1821,21 +937,6 @@ _Py_UnixMain(int argc, char **argv)
     return pymain_main(&args);
 }
 
-
-/* this is gonna seem *real weird*, but if you put some other code between
-   Py_Main() and Py_GetArgcArgv() you will need to adjust the test in the
-   while statement in Misc/gdbinit:ppystack */
-
-/* Make the *original* argc/argv available to other modules.
-   This is rare, but it is needed by the secureware extension. */
-
-void
-Py_GetArgcArgv(int *argc, wchar_t ***argv)
-{
-    *argc = orig_argc;
-    *argv = orig_argv;
-}
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/Python/coreconfig.c b/Python/coreconfig.c
index d9b30130c931..47dbe66d3eba 100644
--- a/Python/coreconfig.c
+++ b/Python/coreconfig.c
@@ -1,35 +1,116 @@
 #include "Python.h"
+#include "osdefs.h"       /* DELIM */
 #include "pycore_coreconfig.h"
 #include "pycore_fileutils.h"
+#include "pycore_getopt.h"
 #include "pycore_pylifecycle.h"
 #include "pycore_pymem.h"
 #include "pycore_pathconfig.h"
-#include "pycore_pystate.h"
-#include <locale.h>
+#include <locale.h>       /* setlocale() */
 #ifdef HAVE_LANGINFO_H
-#  include <langinfo.h>
+#  include <langinfo.h>   /* nl_langinfo(CODESET) */
 #endif
-
-#include <locale.h>     /* setlocale() */
-#ifdef HAVE_LANGINFO_H
-#include <langinfo.h>   /* nl_langinfo(CODESET) */
+#if defined(MS_WINDOWS) || defined(__CYGWIN__)
+#  include <windows.h>    /* GetACP() */
+#  ifdef HAVE_IO_H
+#    include <io.h>
+#  endif
+#  ifdef HAVE_FCNTL_H
+#    include <fcntl.h>    /* O_BINARY */
+#  endif
 #endif
 
 
-#define DECODE_LOCALE_ERR(NAME, LEN) \
-    (((LEN) == -2) \
-     ? _Py_INIT_USER_ERR("cannot decode " NAME) \
-     : _Py_INIT_NO_MEMORY())
+/* --- Command line options --------------------------------------- */
+
+#define PROGRAM_OPTS L"bBc:dEhiIJm:OqRsStuvVW:xX:?"
+
+static const _PyOS_LongOption longoptions[] = {
+    {L"check-hash-based-pycs", 1, 0},
+    {NULL, 0, 0},
+};
+
+/* Short usage message (with %s for argv0) */
+static const char usage_line[] =
+"usage: %ls [option] ... [-c cmd | -m mod | file | -] [arg] ...\n";
+
+/* Long usage message, split into parts < 512 bytes */
+static const char usage_1[] = "\
+Options and arguments (and corresponding environment variables):\n\
+-b     : issue warnings about str(bytes_instance), str(bytearray_instance)\n\
+         and comparing bytes/bytearray with str. (-bb: issue errors)\n\
+-B     : don't write .pyc files on import; also PYTHONDONTWRITEBYTECODE=x\n\
+-c cmd : program passed in as string (terminates option list)\n\
+-d     : debug output from parser; also PYTHONDEBUG=x\n\
+-E     : ignore PYTHON* environment variables (such as PYTHONPATH)\n\
+-h     : print this help message and exit (also --help)\n\
+";
+static const char usage_2[] = "\
+-i     : inspect interactively after running script; forces a prompt even\n\
+         if stdin does not appear to be a terminal; also PYTHONINSPECT=x\n\
+-I     : isolate Python from the user's environment (implies -E and -s)\n\
+-m mod : run library module as a script (terminates option list)\n\
+-O     : remove assert and __debug__-dependent statements; add .opt-1 before\n\
+         .pyc extension; also PYTHONOPTIMIZE=x\n\
+-OO    : do -O changes and also discard docstrings; add .opt-2 before\n\
+         .pyc extension\n\
+-q     : don't print version and copyright messages on interactive startup\n\
+-s     : don't add user site directory to sys.path; also PYTHONNOUSERSITE\n\
+-S     : don't imply 'import site' on initialization\n\
+";
+static const char usage_3[] = "\
+-u     : force the stdout and stderr streams to be unbuffered;\n\
+         this option has no effect on stdin; also PYTHONUNBUFFERED=x\n\
+-v     : verbose (trace import statements); also PYTHONVERBOSE=x\n\
+         can be supplied multiple times to increase verbosity\n\
+-V     : print the Python version number and exit (also --version)\n\
+         when given twice, print more information about the build\n\
+-W arg : warning control; arg is action:message:category:module:lineno\n\
+         also PYTHONWARNINGS=arg\n\
+-x     : skip first line of source, allowing use of non-Unix forms of #!cmd\n\
+-X opt : set implementation-specific option\n\
+--check-hash-based-pycs always|default|never:\n\
+    control how Python invalidates hash-based .pyc files\n\
+";
+static const char usage_4[] = "\
+file   : program read from script file\n\
+-      : program read from stdin (default; interactive mode if a tty)\n\
+arg ...: arguments passed to program in sys.argv[1:]\n\n\
+Other environment variables:\n\
+PYTHONSTARTUP: file executed on interactive startup (no default)\n\
+PYTHONPATH   : '%lc'-separated list of directories prefixed to the\n\
+               default module search path.  The result is sys.path.\n\
+";
+static const char usage_5[] =
+"PYTHONHOME   : alternate <prefix> directory (or <prefix>%lc<exec_prefix>).\n"
+"               The default module search path uses %s.\n"
+"PYTHONCASEOK : ignore case in 'import' statements (Windows).\n"
+"PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.\n"
+"PYTHONFAULTHANDLER: dump the Python traceback on fatal errors.\n";
+static const char usage_6[] =
+"PYTHONHASHSEED: if this variable is set to 'random', a random value is used\n"
+"   to seed the hashes of str, bytes and datetime objects.  It can also be\n"
+"   set to an integer in the range [0,4294967295] to get hash values with a\n"
+"   predictable seed.\n"
+"PYTHONMALLOC: set the Python memory allocators and/or install debug hooks\n"
+"   on Python memory allocators. Use PYTHONMALLOC=debug to install debug\n"
+"   hooks.\n"
+"PYTHONCOERCECLOCALE: if this variable is set to 0, it disables the locale\n"
+"   coercion behavior. Use PYTHONCOERCECLOCALE=warn to request display of\n"
+"   locale coercion and locale compatibility warnings on stderr.\n"
+"PYTHONBREAKPOINT: if this variable is set to 0, it disables the default\n"
+"   debugger. It can be set to the callable of your debugger of choice.\n"
+"PYTHONDEVMODE: enable the development mode.\n"
+"PYTHONPYCACHEPREFIX: root directory for bytecode cache (pyc) files.\n";
+
+#if defined(MS_WINDOWS)
+#  define PYTHONHOMEHELP "<prefix>\\python{major}{minor}"
+#else
+#  define PYTHONHOMEHELP "<prefix>/lib/pythonX.X"
+#endif
 
 
-/* Global configuration variables */
-
-/* The filesystem encoding is chosen by config_init_fs_encoding(),
-   see also initfsencoding(). */
-const char *Py_FileSystemDefaultEncoding = NULL;
-int Py_HasFileSystemDefaultEncoding = 0;
-const char *Py_FileSystemDefaultEncodeErrors = NULL;
-static int _Py_HasFileSystemDefaultEncodeErrors = 0;
+/* --- Global configuration variables ----------------------------- */
 
 /* 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
@@ -55,6 +136,13 @@ int Py_LegacyWindowsFSEncodingFlag = 0; /* Uses mbcs instead of utf-8 */
 int Py_LegacyWindowsStdioFlag = 0; /* Uses FileIO instead of WindowsConsoleIO */
 #endif
 
+/* The filesystem encoding is chosen by config_init_fs_encoding(),
+   see also initfsencoding(). */
+const char *Py_FileSystemDefaultEncoding = NULL;
+int Py_HasFileSystemDefaultEncoding = 0;
+const char *Py_FileSystemDefaultEncodeErrors = NULL;
+static int _Py_HasFileSystemDefaultEncodeErrors = 0;
+
 
 PyObject *
 _Py_GetGlobalVariablesAsDict(void)
@@ -128,6 +216,8 @@ _Py_GetGlobalVariablesAsDict(void)
 }
 
 
+/* --- _Py_wstrlist ----------------------------------------------- */
+
 void
 _Py_wstrlist_clear(int len, wchar_t **list)
 {
@@ -139,7 +229,7 @@ _Py_wstrlist_clear(int len, wchar_t **list)
 
 
 wchar_t**
-_Py_wstrlist_copy(int len, wchar_t **list)
+_Py_wstrlist_copy(int len, wchar_t * const *list)
 {
     assert((len > 0 && list != NULL) || len == 0);
     size_t size = len * sizeof(list[0]);
@@ -159,6 +249,53 @@ _Py_wstrlist_copy(int len, wchar_t **list)
 }
 
 
+_PyInitError
+_Py_wstrlist_append(int *len, wchar_t ***list, const wchar_t *str)
+{
+    if (*len == INT_MAX) {
+        /* len+1 would overflow */
+        return _Py_INIT_NO_MEMORY();
+    }
+    wchar_t *str2 = _PyMem_RawWcsdup(str);
+    if (str2 == NULL) {
+        return _Py_INIT_NO_MEMORY();
+    }
+
+    size_t size = (*len + 1) * sizeof(list[0]);
+    wchar_t **list2 = (wchar_t **)PyMem_RawRealloc(*list, size);
+    if (list2 == NULL) {
+        PyMem_RawFree(str2);
+        return _Py_INIT_NO_MEMORY();
+    }
+    list2[*len] = str2;
+    *list = list2;
+    (*len)++;
+    return _Py_INIT_OK();
+}
+
+
+PyObject*
+_Py_wstrlist_as_pylist(int len, wchar_t **list)
+{
+    assert(list != NULL || len < 1);
+
+    PyObject *pylist = PyList_New(len);
+    if (pylist == NULL) {
+        return NULL;
+    }
+
+    for (int i = 0; i < len; i++) {
+        PyObject *v = PyUnicode_FromWideChar(list[i], -1);
+        if (v == NULL) {
+            Py_DECREF(pylist);
+            return NULL;
+        }
+        PyList_SET_ITEM(pylist, i, v);
+    }
+    return pylist;
+}
+
+
 void
 _Py_ClearFileSystemEncoding(void)
 {
@@ -173,6 +310,8 @@ _Py_ClearFileSystemEncoding(void)
 }
 
 
+/* --- File system encoding/errors -------------------------------- */
+
 /* Set Py_FileSystemDefaultEncoding and Py_FileSystemDefaultEncodeErrors
    global configuration variables. */
 int
@@ -200,6 +339,8 @@ _Py_SetFileSystemEncoding(const char *encoding, const char *errors)
 }
 
 
+/* --- Py_SetStandardStreamEncoding() ----------------------------- */
+
 /* Helper to allow an embedding application to override the normal
  * mechanism that attempts to figure out an appropriate IO encoding
  */
@@ -282,6 +423,68 @@ _Py_ClearStandardStreamEncoding(void)
 }
 
 
+/* --- Py_GetArgcArgv() ------------------------------------------- */
+
+/* For Py_GetArgcArgv(); set by _Py_SetArgcArgv() */
+static int orig_argc = 0;
+static wchar_t **orig_argv = NULL;
+
+
+void
+_Py_ClearArgcArgv(void)
+{
+    PyMemAllocatorEx old_alloc;
+    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+
+    _Py_wstrlist_clear(orig_argc, orig_argv);
+    orig_argc = 0;
+    orig_argv = NULL;
+
+    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+}
+
+
+int
+_Py_SetArgcArgv(int argc, wchar_t * const *argv)
+{
+    int res;
+
+    PyMemAllocatorEx old_alloc;
+    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+
+    wchar_t **argv_copy = _Py_wstrlist_copy(argc, argv);
+    if (argv_copy != NULL) {
+        _Py_ClearArgcArgv();
+        orig_argc = argc;
+        orig_argv = argv_copy;
+        res = 0;
+    }
+    else {
+        res = -1;
+    }
+
+    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+    return res;
+}
+
+
+/* Make the *original* argc/argv available to other modules.
+   This is rare, but it is needed by the secureware extension. */
+void
+Py_GetArgcArgv(int *argc, wchar_t ***argv)
+{
+    *argc = orig_argc;
+    *argv = orig_argv;
+}
+
+
+/* --- _PyCoreConfig ---------------------------------------------- */
+
+#define DECODE_LOCALE_ERR(NAME, LEN) \
+    (((LEN) == -2) \
+     ? _Py_INIT_USER_ERR("cannot decode " NAME) \
+     : _Py_INIT_NO_MEMORY())
+
 /* Free memory allocated in config, but don't clear all attributes */
 void
 _PyCoreConfig_Clear(_PyCoreConfig *config)
@@ -1469,6 +1672,60 @@ _PyCoreConfig_Read(_PyCoreConfig *config)
 }
 
 
+static void
+config_init_stdio(const _PyCoreConfig *config)
+{
+#if defined(MS_WINDOWS) || defined(__CYGWIN__)
+    /* don't translate newlines (\r\n <=> \n) */
+    _setmode(fileno(stdin), O_BINARY);
+    _setmode(fileno(stdout), O_BINARY);
+    _setmode(fileno(stderr), O_BINARY);
+#endif
+
+    if (!config->buffered_stdio) {
+#ifdef HAVE_SETVBUF
+        setvbuf(stdin,  (char *)NULL, _IONBF, BUFSIZ);
+        setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ);
+        setvbuf(stderr, (char *)NULL, _IONBF, BUFSIZ);
+#else /* !HAVE_SETVBUF */
+        setbuf(stdin,  (char *)NULL);
+        setbuf(stdout, (char *)NULL);
+        setbuf(stderr, (char *)NULL);
+#endif /* !HAVE_SETVBUF */
+    }
+    else if (config->interactive) {
+#ifdef MS_WINDOWS
+        /* Doesn't have to have line-buffered -- use unbuffered */
+        /* Any set[v]buf(stdin, ...) screws up Tkinter :-( */
+        setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ);
+#else /* !MS_WINDOWS */
+#ifdef HAVE_SETVBUF
+        setvbuf(stdin,  (char *)NULL, _IOLBF, BUFSIZ);
+        setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ);
+#endif /* HAVE_SETVBUF */
+#endif /* !MS_WINDOWS */
+        /* Leave stderr alone - it should be unbuffered anyway. */
+    }
+}
+
+
+/* Write the configuration:
+
+   - coerce the LC_CTYPE locale (PEP 538)
+   - UTF-8 mode (PEP 540)
+   - set Py_xxx global configuration variables
+   - initialize C standard streams (stdin, stdout, stderr) */
+void
+_PyCoreConfig_Write(const _PyCoreConfig *config)
+{
+    if (config->coerce_c_locale) {
+        _Py_CoerceLegacyLocale(config->coerce_c_locale_warn);
+    }
+    _PyCoreConfig_SetGlobalConfig(config);
+    config_init_stdio(config);
+}
+
+
 PyObject *
 _PyCoreConfig_AsDict(const _PyCoreConfig *config)
 {
@@ -1586,3 +1843,681 @@ _PyCoreConfig_AsDict(const _PyCoreConfig *config)
 #undef SET_ITEM_WSTR
 #undef SET_ITEM_WSTRLIST
 }
+
+
+/* --- _PyCmdline ------------------------------------------------- */
+
+typedef struct {
+    const _PyArgv *args;
+    wchar_t **argv;
+    int nwarnoption;             /* Number of -W command line options */
+    wchar_t **warnoptions;       /* Command line -W options */
+    int nenv_warnoption;         /* Number of PYTHONWARNINGS environment variables */
+    wchar_t **env_warnoptions;   /* PYTHONWARNINGS environment variables */
+    int print_help;              /* -h, -? options */
+    int print_version;           /* -V option */
+} _PyCmdline;
+
+
+static void
+cmdline_clear(_PyCmdline *cmdline)
+{
+    _Py_wstrlist_clear(cmdline->nwarnoption, cmdline->warnoptions);
+    cmdline->nwarnoption = 0;
+    cmdline->warnoptions = NULL;
+
+    _Py_wstrlist_clear(cmdline->nenv_warnoption, cmdline->env_warnoptions);
+    cmdline->nenv_warnoption = 0;
+    cmdline->env_warnoptions = NULL;
+
+    if (cmdline->args->use_bytes_argv && cmdline->argv != NULL) {
+        _Py_wstrlist_clear(cmdline->args->argc, cmdline->argv);
+    }
+    cmdline->argv = NULL;
+}
+
+
+static _PyInitError
+cmdline_decode_argv(_PyCmdline *cmdline)
+{
+    assert(cmdline->argv == NULL);
+
+    const _PyArgv *args = cmdline->args;
+
+    if (args->use_bytes_argv) {
+        /* +1 for a the NULL terminator */
+        size_t size = sizeof(wchar_t*) * (args->argc + 1);
+        wchar_t** argv = (wchar_t **)PyMem_RawMalloc(size);
+        if (argv == NULL) {
+            return _Py_INIT_NO_MEMORY();
+        }
+
+        for (int i = 0; i < args->argc; i++) {
+            size_t len;
+            wchar_t *arg = Py_DecodeLocale(args->bytes_argv[i], &len);
+            if (arg == NULL) {
+                _Py_wstrlist_clear(i, argv);
+                return DECODE_LOCALE_ERR("command line arguments",
+                                         (Py_ssize_t)len);
+            }
+            argv[i] = arg;
+        }
+        argv[args->argc] = NULL;
+
+        cmdline->argv = argv;
+    }
+    else {
+        cmdline->argv = args->wchar_argv;
+    }
+    return _Py_INIT_OK();
+}
+
+
+/* --- _PyCoreConfig command line parser -------------------------- */
+
+/* Parse the command line arguments */
+static _PyInitError
+config_parse_cmdline(_PyCoreConfig *config, _PyCmdline *cmdline,
+                     int *need_usage)
+{
+    _PyInitError err;
+    _PyOS_ResetGetOpt();
+    do {
+        int longindex = -1;
+        int c = _PyOS_GetOpt(cmdline->args->argc, cmdline->argv, PROGRAM_OPTS,
+                             longoptions, &longindex);
+        if (c == EOF) {
+            break;
+        }
+
+        if (c == 'c') {
+            /* -c is the last option; following arguments
+               that look like options are left for the
+               command to interpret. */
+            size_t len = wcslen(_PyOS_optarg) + 1 + 1;
+            wchar_t *command = PyMem_RawMalloc(sizeof(wchar_t) * len);
+            if (command == NULL) {
+                return _Py_INIT_NO_MEMORY();
+            }
+            memcpy(command, _PyOS_optarg, (len - 2) * sizeof(wchar_t));
+            command[len - 2] = '\n';
+            command[len - 1] = 0;
+            config->run_command = command;
+            break;
+        }
+
+        if (c == 'm') {
+            /* -m is the last option; following arguments
+               that look like options are left for the
+               module to interpret. */
+            config->run_module = _PyMem_RawWcsdup(_PyOS_optarg);
+            if (config->run_module == NULL) {
+                return _Py_INIT_NO_MEMORY();
+            }
+            break;
+        }
+
+        switch (c) {
+        case 0:
+            // Handle long option.
+            assert(longindex == 0); // Only one long option now.
+            if (!wcscmp(_PyOS_optarg, L"always")) {
+                config->_check_hash_pycs_mode = "always";
+            } else if (!wcscmp(_PyOS_optarg, L"never")) {
+                config->_check_hash_pycs_mode = "never";
+            } else if (!wcscmp(_PyOS_optarg, L"default")) {
+                config->_check_hash_pycs_mode = "default";
+            } else {
+                fprintf(stderr, "--check-hash-based-pycs must be one of "
+                        "'default', 'always', or 'never'\n");
+                *need_usage = 1;
+                return _Py_INIT_OK();
+            }
+            break;
+
+        case 'b':
+            config->bytes_warning++;
+            break;
+
+        case 'd':
+            config->parser_debug++;
+            break;
+
+        case 'i':
+            config->inspect++;
+            config->interactive++;
+            break;
+
+        case 'I':
+            config->isolated++;
+            break;
+
+        /* case 'J': reserved for Jython */
+
+        case 'O':
+            config->optimization_level++;
+            break;
+
+        case 'B':
+            config->write_bytecode = 0;
+            break;
+
+        case 's':
+            config->user_site_directory = 0;
+            break;
+
+        case 'S':
+            config->site_import = 0;
+            break;
+
+        case 'E':
+            config->use_environment = 0;
+            break;
+
+        case 't':
+            /* ignored for backwards compatibility */
+            break;
+
+        case 'u':
+            config->buffered_stdio = 0;
+            break;
+
+        case 'v':
+            config->verbose++;
+            break;
+
+        case 'x':
+            config->skip_source_first_line = 1;
+            break;
+
+        case 'h':
+        case '?':
+            cmdline->print_help++;
+            break;
+
+        case 'V':
+            cmdline->print_version++;
+            break;
+
+        case 'W':
+            err = _Py_wstrlist_append(&cmdline->nwarnoption,
+                                      &cmdline->warnoptions,
+                                      _PyOS_optarg);
+            if (_Py_INIT_FAILED(err)) {
+                return err;
+            }
+            break;
+
+        case 'X':
+            err = _Py_wstrlist_append(&config->nxoption,
+                                      &config->xoptions,
+                                      _PyOS_optarg);
+            if (_Py_INIT_FAILED(err)) {
+                return err;
+            }
+            break;
+
+        case 'q':
+            config->quiet++;
+            break;
+
+        case 'R':
+            config->use_hash_seed = 0;
+            break;
+
+        /* This space reserved for other options */
+
+        default:
+            /* unknown argument: parsing failed */
+            *need_usage = 1;
+            return _Py_INIT_OK();
+        }
+    } while (1);
+
+    if (config->run_command == NULL && config->run_module == NULL
+        && _PyOS_optind < cmdline->args->argc
+        && wcscmp(cmdline->argv[_PyOS_optind], L"-") != 0)
+    {
+        config->run_filename = _PyMem_RawWcsdup(cmdline->argv[_PyOS_optind]);
+        if (config->run_filename == NULL) {
+            return _Py_INIT_NO_MEMORY();
+        }
+    }
+
+    if (config->run_command != NULL || config->run_module != NULL) {
+        /* Backup _PyOS_optind */
+        _PyOS_optind--;
+    }
+
+    /* -c and -m options are exclusive */
+    assert(!(config->run_command != NULL && config->run_module != NULL));
+
+    return _Py_INIT_OK();
+}
+
+
+#ifdef MS_WINDOWS
+#  define WCSTOK wcstok_s
+#else
+#  define WCSTOK wcstok
+#endif
+
+/* Get warning options from PYTHONWARNINGS environment variable. */
+static _PyInitError
+cmdline_init_env_warnoptions(_PyCmdline *cmdline, const _PyCoreConfig *config)
+{
+    wchar_t *env;
+    int res = _PyCoreConfig_GetEnvDup(config, &env,
+                                      L"PYTHONWARNINGS", "PYTHONWARNINGS");
+    if (res < 0) {
+        return DECODE_LOCALE_ERR("PYTHONWARNINGS", res);
+    }
+
+    if (env == NULL) {
+        return _Py_INIT_OK();
+    }
+
+
+    wchar_t *warning, *context = NULL;
+    for (warning = WCSTOK(env, L",", &context);
+         warning != NULL;
+         warning = WCSTOK(NULL, L",", &context))
+    {
+        _PyInitError err = _Py_wstrlist_append(&cmdline->nenv_warnoption,
+                                               &cmdline->env_warnoptions,
+                                               warning);
+        if (_Py_INIT_FAILED(err)) {
+            PyMem_RawFree(env);
+            return err;
+        }
+    }
+    PyMem_RawFree(env);
+    return _Py_INIT_OK();
+}
+
+
+static _PyInitError
+config_init_program(_PyCoreConfig *config, const _PyCmdline *cmdline)
+{
+    wchar_t *program;
+    if (cmdline->args->argc >= 1 && cmdline->argv != NULL) {
+        program = cmdline->argv[0];
+    }
+    else {
+        program = L"";
+    }
+    config->program = _PyMem_RawWcsdup(program);
+    if (config->program == NULL) {
+        return _Py_INIT_NO_MEMORY();
+    }
+
+    return _Py_INIT_OK();
+}
+
+
+static _PyInitError
+config_add_warnings_optlist(_PyCoreConfig *config,
+                            int len, wchar_t * const *options)
+{
+    for (int i = 0; i < len; i++) {
+        _PyInitError err = _Py_wstrlist_append(&config->nwarnoption,
+                                               &config->warnoptions,
+                                               options[i]);
+        if (_Py_INIT_FAILED(err)) {
+            return err;
+        }
+    }
+    return _Py_INIT_OK();
+}
+
+
+static _PyInitError
+config_init_warnoptions(_PyCoreConfig *config, const _PyCmdline *cmdline)
+{
+    _PyInitError err;
+
+    assert(config->nwarnoption == 0);
+
+    /* The priority order for warnings configuration is (highest precedence
+     * first):
+     *
+     * - the BytesWarning filter, if needed ('-b', '-bb')
+     * - any '-W' command line options; then
+     * - the 'PYTHONWARNINGS' environment variable; then
+     * - the dev mode filter ('-X dev', 'PYTHONDEVMODE'); then
+     * - any implicit filters added by _warnings.c/warnings.py
+     *
+     * All settings except the last are passed to the warnings module via
+     * the `sys.warnoptions` list. Since the warnings module works on the basis
+     * of "the most recently added filter will be checked first", we add
+     * the lowest precedence entries first so that later entries override them.
+     */
+
+    if (config->dev_mode) {
+        err = _Py_wstrlist_append(&config->nwarnoption,
+                                  &config->warnoptions,
+                                  L"default");
+        if (_Py_INIT_FAILED(err)) {
+            return err;
+        }
+    }
+
+    err = config_add_warnings_optlist(config,
+                                      cmdline->nenv_warnoption,
+                                      cmdline->env_warnoptions);
+    if (_Py_INIT_FAILED(err)) {
+        return err;
+    }
+
+    err = config_add_warnings_optlist(config,
+                                      cmdline->nwarnoption,
+                                      cmdline->warnoptions);
+    if (_Py_INIT_FAILED(err)) {
+        return err;
+    }
+
+    /* If the bytes_warning_flag isn't set, bytesobject.c and bytearrayobject.c
+     * don't even try to emit a warning, so we skip setting the filter in that
+     * case.
+     */
+    if (config->bytes_warning) {
+        wchar_t *filter;
+        if (config->bytes_warning> 1) {
+            filter = L"error::BytesWarning";
+        }
+        else {
+            filter = L"default::BytesWarning";
+        }
+        err = _Py_wstrlist_append(&config->nwarnoption,
+                                  &config->warnoptions,
+                                  filter);
+        if (_Py_INIT_FAILED(err)) {
+            return err;
+        }
+    }
+    return _Py_INIT_OK();
+}
+
+
+static _PyInitError
+config_init_argv(_PyCoreConfig *config, const _PyCmdline *cmdline)
+{
+    /* Copy argv to be able to modify it (to force -c/-m) */
+    int argc = cmdline->args->argc - _PyOS_optind;
+    wchar_t **argv;
+
+    if (argc <= 0 || cmdline->argv == NULL) {
+        /* Ensure at least one (empty) argument is seen */
+        static wchar_t *empty_argv[1] = {L""};
+        argc = 1;
+        argv = _Py_wstrlist_copy(1, empty_argv);
+    }
+    else {
+        argv = _Py_wstrlist_copy(argc, &cmdline->argv[_PyOS_optind]);
+    }
+
+    if (argv == NULL) {
+        return _Py_INIT_NO_MEMORY();
+    }
+
+    wchar_t *arg0 = NULL;
+    if (config->run_command != NULL) {
+        /* Force sys.argv[0] = '-c' */
+        arg0 = L"-c";
+    }
+    else if (config->run_module != NULL) {
+        /* Force sys.argv[0] = '-m'*/
+        arg0 = L"-m";
+    }
+    if (arg0 != NULL) {
+        arg0 = _PyMem_RawWcsdup(arg0);
+        if (arg0 == NULL) {
+            _Py_wstrlist_clear(argc, argv);
+            return _Py_INIT_NO_MEMORY();
+        }
+
+        assert(argc >= 1);
+        PyMem_RawFree(argv[0]);
+        argv[0] = arg0;
+    }
+
+    config->argc = argc;
+    config->argv = argv;
+    return _Py_INIT_OK();
+}
+
+
+static void
+config_usage(int error, const wchar_t* program)
+{
+    FILE *f = error ? stderr : stdout;
+
+    fprintf(f, usage_line, program);
+    if (error)
+        fprintf(f, "Try `python -h' for more information.\n");
+    else {
+        fputs(usage_1, f);
+        fputs(usage_2, f);
+        fputs(usage_3, f);
+        fprintf(f, usage_4, (wint_t)DELIM);
+        fprintf(f, usage_5, (wint_t)DELIM, PYTHONHOMEHELP);
+        fputs(usage_6, f);
+    }
+}
+
+
+/* Parse command line options and environment variables. */
+static _PyInitError
+config_from_cmdline(_PyCoreConfig *config, _PyCmdline *cmdline)
+{
+    int need_usage = 0;
+    _PyInitError err;
+
+    err = config_init_program(config, cmdline);
+    if (_Py_INIT_FAILED(err)) {
+        return err;
+    }
+
+    err = config_parse_cmdline(config, cmdline, &need_usage);
+    if (_Py_INIT_FAILED(err)) {
+        return err;
+    }
+
+    if (need_usage) {
+        config_usage(1, config->program);
+        return _Py_INIT_EXIT(2);
+    }
+
+    if (cmdline->print_help) {
+        config_usage(0, config->program);
+        return _Py_INIT_EXIT(0);
+    }
+
+    if (cmdline->print_version) {
+        printf("Python %s\n",
+               (cmdline->print_version >= 2) ? Py_GetVersion() : PY_VERSION);
+        return _Py_INIT_EXIT(0);
+    }
+
+    err = config_init_argv(config, cmdline);
+    if (_Py_INIT_FAILED(err)) {
+        return err;
+    }
+
+    err = _PyCoreConfig_Read(config);
+    if (_Py_INIT_FAILED(err)) {
+        return err;
+    }
+
+    if (config->use_environment) {
+        err = cmdline_init_env_warnoptions(cmdline, config);
+        if (_Py_INIT_FAILED(err)) {
+            return err;
+        }
+    }
+
+    err = config_init_warnoptions(config, cmdline);
+    if (_Py_INIT_FAILED(err)) {
+        return err;
+    }
+
+    if (_Py_SetArgcArgv(cmdline->args->argc, cmdline->argv) < 0) {
+        return _Py_INIT_NO_MEMORY();
+    }
+    return _Py_INIT_OK();
+}
+
+
+static _PyInitError
+config_read_from_argv_impl(_PyCoreConfig *config, const _PyArgv *args)
+{
+    _PyInitError err;
+
+    _PyCmdline cmdline;
+    memset(&cmdline, 0, sizeof(cmdline));
+    cmdline.args = args;
+
+    err = cmdline_decode_argv(&cmdline);
+    if (_Py_INIT_FAILED(err)) {
+        goto done;
+    }
+
+    err = config_from_cmdline(config, &cmdline);
+    if (_Py_INIT_FAILED(err)) {
+        goto done;
+    }
+    err = _Py_INIT_OK();
+
+done:
+    cmdline_clear(&cmdline);
+    return err;
+}
+
+
+/* Read the configuration into _PyCoreConfig and initialize the LC_CTYPE
+   locale: enable UTF-8 mode (PEP 540) and/or coerce the C locale (PEP 538).
+
+   Read the configuration from:
+
+   * Command line arguments
+   * Environment variables
+   * Py_xxx global configuration variables */
+_PyInitError
+_PyCoreConfig_ReadFromArgv(_PyCoreConfig *config, const _PyArgv *args)
+{
+    _PyInitError err;
+    int init_utf8_mode = Py_UTF8Mode;
+#ifdef MS_WINDOWS
+    int init_legacy_encoding = Py_LegacyWindowsFSEncodingFlag;
+#endif
+    _PyCoreConfig save_config = _PyCoreConfig_INIT;
+    int locale_coerced = 0;
+    int loops = 0;
+    char *init_ctype_locale = NULL;
+
+    /* copy LC_CTYPE locale */
+    const char *loc = setlocale(LC_CTYPE, NULL);
+    if (loc == NULL) {
+        err = _Py_INIT_ERR("failed to LC_CTYPE locale");
+        goto done;
+    }
+    init_ctype_locale = _PyMem_RawStrdup(loc);
+    if (init_ctype_locale == NULL) {
+        err = _Py_INIT_NO_MEMORY();
+        goto done;
+    }
+
+    if (_PyCoreConfig_Copy(&save_config, config) < 0) {
+        err = _Py_INIT_NO_MEMORY();
+        goto done;
+    }
+
+    /* Set LC_CTYPE to the user preferred locale */
+    _Py_SetLocaleFromEnv(LC_CTYPE);
+
+    while (1) {
+        int utf8_mode = config->utf8_mode;
+        int encoding_changed = 0;
+
+        /* Watchdog to prevent an infinite loop */
+        loops++;
+        if (loops == 3) {
+            err = _Py_INIT_ERR("Encoding changed twice while "
+                               "reading the configuration");
+            goto done;
+        }
+
+        /* bpo-34207: Py_DecodeLocale() and Py_EncodeLocale() depend
+           on Py_UTF8Mode and Py_LegacyWindowsFSEncodingFlag. */
+        Py_UTF8Mode = config->utf8_mode;
+#ifdef MS_WINDOWS
+        Py_LegacyWindowsFSEncodingFlag = config->legacy_windows_fs_encoding;
+#endif
+
+        err = config_read_from_argv_impl(config, args);
+        if (_Py_INIT_FAILED(err)) {
+            goto done;
+        }
+        if (locale_coerced) {
+            config->coerce_c_locale = 1;
+        }
+
+        /* The legacy C locale assumes ASCII as the default text encoding, which
+         * causes problems not only for the CPython runtime, but also other
+         * components like GNU readline.
+         *
+         * Accordingly, when the CLI detects it, it attempts to coerce it to a
+         * more capable UTF-8 based alternative.
+         *
+         * See the documentation of the PYTHONCOERCECLOCALE setting for more
+         * details.
+         */
+        if (config->coerce_c_locale && !locale_coerced) {
+            locale_coerced = 1;
+            _Py_CoerceLegacyLocale(0);
+            encoding_changed = 1;
+        }
+
+        if (utf8_mode == -1) {
+            if (config->utf8_mode == 1) {
+                /* UTF-8 Mode enabled */
+                encoding_changed = 1;
+            }
+        }
+        else {
+            if (config->utf8_mode != utf8_mode) {
+                encoding_changed = 1;
+            }
+        }
+
+        if (!encoding_changed) {
+            break;
+        }
+
+        /* Reset the configuration before reading again the configuration,
+           just keep UTF-8 Mode value. */
+        int new_utf8_mode = config->utf8_mode;
+        int new_coerce_c_locale = config->coerce_c_locale;
+        if (_PyCoreConfig_Copy(config, &save_config) < 0) {
+            err = _Py_INIT_NO_MEMORY();
+            goto done;
+        }
+        config->utf8_mode = new_utf8_mode;
+        config->coerce_c_locale = new_coerce_c_locale;
+
+        /* The encoding changed: read again the configuration
+           with the new encoding */
+    }
+    err = _Py_INIT_OK();
+
+done:
+    if (init_ctype_locale != NULL) {
+        setlocale(LC_CTYPE, init_ctype_locale);
+    }
+    _PyCoreConfig_Clear(&save_config);
+    Py_UTF8Mode = init_utf8_mode ;
+#ifdef MS_WINDOWS
+    Py_LegacyWindowsFSEncodingFlag = init_legacy_encoding;
+#endif
+    return err;
+}



More information about the Python-checkins mailing list