Staging
v0.5.1
https://github.com/python/cpython
Revision 39afa2d3147e4b05a1161cc90dbf09b95072c2bb authored by Michael Felt on 15 December 2019, 14:17:53 UTC, committed by Nick Coghlan on 15 December 2019, 14:17:53 UTC
Provides a richer platform tag for AIX that we expect to be sufficient for PEP 425
binary distribution identification. Any backports to earlier Python versions will be
handled via setuptools.

Patch by Michael Felt.
1 parent 94d2c8d
Raw File
Tip revision: 39afa2d3147e4b05a1161cc90dbf09b95072c2bb authored by Michael Felt on 15 December 2019, 14:17:53 UTC
bpo-38021: Modify AIX platform_tag so it covers PEP 425 needs (GH-17303)
Tip revision: 39afa2d
preconfig.c
#include "Python.h"
#include "pycore_initconfig.h"
#include "pycore_getopt.h"
#include "pycore_pystate.h"   /* _PyRuntime_Initialize() */
#include <locale.h>       /* setlocale() */


#define DECODE_LOCALE_ERR(NAME, LEN) \
    (((LEN) == -2) \
     ? _PyStatus_ERR("cannot decode " NAME) \
     : _PyStatus_NO_MEMORY())


/* Forward declarations */
static void
preconfig_copy(PyPreConfig *config, const PyPreConfig *config2);


/* --- File system encoding/errors -------------------------------- */

/* The filesystem encoding is chosen by config_init_fs_encoding(),
   see also initfsencoding().

   Py_FileSystemDefaultEncoding and Py_FileSystemDefaultEncodeErrors
   are encoded to UTF-8. */
const char *Py_FileSystemDefaultEncoding = NULL;
int Py_HasFileSystemDefaultEncoding = 0;
const char *Py_FileSystemDefaultEncodeErrors = NULL;
int _Py_HasFileSystemDefaultEncodeErrors = 0;

void
_Py_ClearFileSystemEncoding(void)
{
    if (!Py_HasFileSystemDefaultEncoding && Py_FileSystemDefaultEncoding) {
        PyMem_RawFree((char*)Py_FileSystemDefaultEncoding);
        Py_FileSystemDefaultEncoding = NULL;
    }
    if (!_Py_HasFileSystemDefaultEncodeErrors && Py_FileSystemDefaultEncodeErrors) {
        PyMem_RawFree((char*)Py_FileSystemDefaultEncodeErrors);
        Py_FileSystemDefaultEncodeErrors = NULL;
    }
}


/* Set Py_FileSystemDefaultEncoding and Py_FileSystemDefaultEncodeErrors
   global configuration variables. */
int
_Py_SetFileSystemEncoding(const char *encoding, const char *errors)
{
    char *encoding2 = _PyMem_RawStrdup(encoding);
    if (encoding2 == NULL) {
        return -1;
    }

    char *errors2 = _PyMem_RawStrdup(errors);
    if (errors2 == NULL) {
        PyMem_RawFree(encoding2);
        return -1;
    }

    _Py_ClearFileSystemEncoding();

    Py_FileSystemDefaultEncoding = encoding2;
    Py_HasFileSystemDefaultEncoding = 0;

    Py_FileSystemDefaultEncodeErrors = errors2;
    _Py_HasFileSystemDefaultEncodeErrors = 0;
    return 0;
}


/* --- _PyArgv ---------------------------------------------------- */

/* Decode bytes_argv using Py_DecodeLocale() */
PyStatus
_PyArgv_AsWstrList(const _PyArgv *args, PyWideStringList *list)
{
    PyWideStringList wargv = _PyWideStringList_INIT;
    if (args->use_bytes_argv) {
        size_t size = sizeof(wchar_t*) * args->argc;
        wargv.items = (wchar_t **)PyMem_RawMalloc(size);
        if (wargv.items == NULL) {
            return _PyStatus_NO_MEMORY();
        }

        for (Py_ssize_t i = 0; i < args->argc; i++) {
            size_t len;
            wchar_t *arg = Py_DecodeLocale(args->bytes_argv[i], &len);
            if (arg == NULL) {
                _PyWideStringList_Clear(&wargv);
                return DECODE_LOCALE_ERR("command line arguments",
                                         (Py_ssize_t)len);
            }
            wargv.items[i] = arg;
            wargv.length++;
        }

        _PyWideStringList_Clear(list);
        *list = wargv;
    }
    else {
        wargv.length = args->argc;
        wargv.items = (wchar_t **)args->wchar_argv;
        if (_PyWideStringList_Copy(list, &wargv) < 0) {
            return _PyStatus_NO_MEMORY();
        }
    }
    return _PyStatus_OK();
}


/* --- _PyPreCmdline ------------------------------------------------- */

void
_PyPreCmdline_Clear(_PyPreCmdline *cmdline)
{
    _PyWideStringList_Clear(&cmdline->argv);
    _PyWideStringList_Clear(&cmdline->xoptions);
}


PyStatus
_PyPreCmdline_SetArgv(_PyPreCmdline *cmdline, const _PyArgv *args)
{
    return _PyArgv_AsWstrList(args, &cmdline->argv);
}


static void
precmdline_get_preconfig(_PyPreCmdline *cmdline, const PyPreConfig *config)
{
#define COPY_ATTR(ATTR) \
    if (config->ATTR != -1) { \
        cmdline->ATTR = config->ATTR; \
    }

    COPY_ATTR(isolated);
    COPY_ATTR(use_environment);
    COPY_ATTR(dev_mode);

#undef COPY_ATTR
}


static void
precmdline_set_preconfig(const _PyPreCmdline *cmdline, PyPreConfig *config)
{
#define COPY_ATTR(ATTR) \
    config->ATTR = cmdline->ATTR

    COPY_ATTR(isolated);
    COPY_ATTR(use_environment);
    COPY_ATTR(dev_mode);

#undef COPY_ATTR
}


PyStatus
_PyPreCmdline_SetConfig(const _PyPreCmdline *cmdline, PyConfig *config)
{
#define COPY_ATTR(ATTR) \
    config->ATTR = cmdline->ATTR

    PyStatus status = _PyWideStringList_Extend(&config->xoptions, &cmdline->xoptions);
    if (_PyStatus_EXCEPTION(status)) {
        return status;
    }

    COPY_ATTR(isolated);
    COPY_ATTR(use_environment);
    COPY_ATTR(dev_mode);
    return _PyStatus_OK();

#undef COPY_ATTR
}


/* Parse the command line arguments */
static PyStatus
precmdline_parse_cmdline(_PyPreCmdline *cmdline)
{
    const PyWideStringList *argv = &cmdline->argv;

    _PyOS_ResetGetOpt();
    /* Don't log parsing errors into stderr here: PyConfig_Read()
       is responsible for that */
    _PyOS_opterr = 0;
    do {
        int longindex = -1;
        int c = _PyOS_GetOpt(argv->length, argv->items, &longindex);

        if (c == EOF || c == 'c' || c == 'm') {
            break;
        }

        switch (c) {
        case 'E':
            cmdline->use_environment = 0;
            break;

        case 'I':
            cmdline->isolated = 1;
            break;

        case 'X':
        {
            PyStatus status = PyWideStringList_Append(&cmdline->xoptions,
                                                      _PyOS_optarg);
            if (_PyStatus_EXCEPTION(status)) {
                return status;
            }
            break;
        }

        default:
            /* ignore other argument:
               handled by PyConfig_Read() */
            break;
        }
    } while (1);

    return _PyStatus_OK();
}


PyStatus
_PyPreCmdline_Read(_PyPreCmdline *cmdline, const PyPreConfig *preconfig)
{
    precmdline_get_preconfig(cmdline, preconfig);

    if (preconfig->parse_argv) {
        PyStatus status = precmdline_parse_cmdline(cmdline);
        if (_PyStatus_EXCEPTION(status)) {
            return status;
        }
    }

    /* isolated, use_environment */
    if (cmdline->isolated < 0) {
        cmdline->isolated = 0;
    }
    if (cmdline->isolated > 0) {
        cmdline->use_environment = 0;
    }
    if (cmdline->use_environment < 0) {
        cmdline->use_environment = 0;
    }

    /* dev_mode */
    if ((cmdline->dev_mode < 0)
        && (_Py_get_xoption(&cmdline->xoptions, L"dev")
            || _Py_GetEnv(cmdline->use_environment, "PYTHONDEVMODE")))
    {
        cmdline->dev_mode = 1;
    }
    if (cmdline->dev_mode < 0) {
        cmdline->dev_mode = 0;
    }

    assert(cmdline->use_environment >= 0);
    assert(cmdline->isolated >= 0);
    assert(cmdline->dev_mode >= 0);

    return _PyStatus_OK();
}


/* --- PyPreConfig ----------------------------------------------- */


void
_PyPreConfig_InitCompatConfig(PyPreConfig *config)
{
    memset(config, 0, sizeof(*config));

    config->_config_init = (int)_PyConfig_INIT_COMPAT;
    config->parse_argv = 0;
    config->isolated = -1;
    config->use_environment = -1;
    config->configure_locale = 1;

    /* bpo-36443: C locale coercion (PEP 538) and UTF-8 Mode (PEP 540)
       are disabled by default using the Compat configuration.

       Py_UTF8Mode=1 enables the UTF-8 mode. PYTHONUTF8 environment variable
       is ignored (even if use_environment=1). */
    config->utf8_mode = 0;
    config->coerce_c_locale = 0;
    config->coerce_c_locale_warn = 0;

    config->dev_mode = -1;
    config->allocator = PYMEM_ALLOCATOR_NOT_SET;
#ifdef MS_WINDOWS
    config->legacy_windows_fs_encoding = -1;
#endif
}


void
PyPreConfig_InitPythonConfig(PyPreConfig *config)
{
    _PyPreConfig_InitCompatConfig(config);

    config->_config_init = (int)_PyConfig_INIT_PYTHON;
    config->isolated = 0;
    config->parse_argv = 1;
    config->use_environment = 1;
    /* Set to -1 to enable C locale coercion (PEP 538) and UTF-8 Mode (PEP 540)
       depending on the LC_CTYPE locale, PYTHONUTF8 and PYTHONCOERCECLOCALE
       environment variables. */
    config->coerce_c_locale = -1;
    config->coerce_c_locale_warn = -1;
    config->utf8_mode = -1;
#ifdef MS_WINDOWS
    config->legacy_windows_fs_encoding = 0;
#endif
}


void
PyPreConfig_InitIsolatedConfig(PyPreConfig *config)
{
    _PyPreConfig_InitCompatConfig(config);

    config->_config_init = (int)_PyConfig_INIT_ISOLATED;
    config->configure_locale = 0;
    config->isolated = 1;
    config->use_environment = 0;
    config->utf8_mode = 0;
    config->dev_mode = 0;
#ifdef MS_WINDOWS
    config->legacy_windows_fs_encoding = 0;
#endif
}


PyStatus
_PyPreConfig_InitFromPreConfig(PyPreConfig *config,
                               const PyPreConfig *config2)
{
    PyPreConfig_InitPythonConfig(config);
    preconfig_copy(config, config2);
    return _PyStatus_OK();
}


void
_PyPreConfig_InitFromConfig(PyPreConfig *preconfig, const PyConfig *config)
{
    _PyConfigInitEnum config_init = (_PyConfigInitEnum)config->_config_init;
    switch (config_init) {
    case _PyConfig_INIT_PYTHON:
        PyPreConfig_InitPythonConfig(preconfig);
        break;
    case _PyConfig_INIT_ISOLATED:
        PyPreConfig_InitIsolatedConfig(preconfig);
        break;
    case _PyConfig_INIT_COMPAT:
    default:
        _PyPreConfig_InitCompatConfig(preconfig);
    }

    _PyPreConfig_GetConfig(preconfig, config);
}


static void
preconfig_copy(PyPreConfig *config, const PyPreConfig *config2)
{
#define COPY_ATTR(ATTR) config->ATTR = config2->ATTR

    COPY_ATTR(_config_init);
    COPY_ATTR(parse_argv);
    COPY_ATTR(isolated);
    COPY_ATTR(use_environment);
    COPY_ATTR(configure_locale);
    COPY_ATTR(dev_mode);
    COPY_ATTR(coerce_c_locale);
    COPY_ATTR(coerce_c_locale_warn);
    COPY_ATTR(utf8_mode);
    COPY_ATTR(allocator);
#ifdef MS_WINDOWS
    COPY_ATTR(legacy_windows_fs_encoding);
#endif

#undef COPY_ATTR
}


PyObject*
_PyPreConfig_AsDict(const PyPreConfig *config)
{
    PyObject *dict;

    dict = PyDict_New();
    if (dict == NULL) {
        return NULL;
    }

#define SET_ITEM_INT(ATTR) \
        do { \
            PyObject *obj = PyLong_FromLong(config->ATTR); \
            if (obj == NULL) { \
                goto fail; \
            } \
            int res = PyDict_SetItemString(dict, #ATTR, obj); \
            Py_DECREF(obj); \
            if (res < 0) { \
                goto fail; \
            } \
        } while (0)

    SET_ITEM_INT(_config_init);
    SET_ITEM_INT(parse_argv);
    SET_ITEM_INT(isolated);
    SET_ITEM_INT(use_environment);
    SET_ITEM_INT(configure_locale);
    SET_ITEM_INT(coerce_c_locale);
    SET_ITEM_INT(coerce_c_locale_warn);
    SET_ITEM_INT(utf8_mode);
#ifdef MS_WINDOWS
    SET_ITEM_INT(legacy_windows_fs_encoding);
#endif
    SET_ITEM_INT(dev_mode);
    SET_ITEM_INT(allocator);
    return dict;

fail:
    Py_DECREF(dict);
    return NULL;

#undef SET_ITEM_INT
}


void
_PyPreConfig_GetConfig(PyPreConfig *preconfig, const PyConfig *config)
{
#define COPY_ATTR(ATTR) \
    if (config->ATTR != -1) { \
        preconfig->ATTR = config->ATTR; \
    }

    COPY_ATTR(parse_argv);
    COPY_ATTR(isolated);
    COPY_ATTR(use_environment);
    COPY_ATTR(dev_mode);

#undef COPY_ATTR
}


static void
preconfig_get_global_vars(PyPreConfig *config)
{
    if (config->_config_init != _PyConfig_INIT_COMPAT) {
        /* Python and Isolated configuration ignore global variables */
        return;
    }

#define COPY_FLAG(ATTR, VALUE) \
    if (config->ATTR < 0) { \
        config->ATTR = VALUE; \
    }
#define COPY_NOT_FLAG(ATTR, VALUE) \
    if (config->ATTR < 0) { \
        config->ATTR = !(VALUE); \
    }

    COPY_FLAG(isolated, Py_IsolatedFlag);
    COPY_NOT_FLAG(use_environment, Py_IgnoreEnvironmentFlag);
    if (Py_UTF8Mode > 0) {
        config->utf8_mode = Py_UTF8Mode;
    }
#ifdef MS_WINDOWS
    COPY_FLAG(legacy_windows_fs_encoding, Py_LegacyWindowsFSEncodingFlag);
#endif

#undef COPY_FLAG
#undef COPY_NOT_FLAG
}


static void
preconfig_set_global_vars(const PyPreConfig *config)
{
#define COPY_FLAG(ATTR, VAR) \
    if (config->ATTR >= 0) { \
        VAR = config->ATTR; \
    }
#define COPY_NOT_FLAG(ATTR, VAR) \
    if (config->ATTR >= 0) { \
        VAR = !config->ATTR; \
    }

    COPY_FLAG(isolated, Py_IsolatedFlag);
    COPY_NOT_FLAG(use_environment, Py_IgnoreEnvironmentFlag);
#ifdef MS_WINDOWS
    COPY_FLAG(legacy_windows_fs_encoding, Py_LegacyWindowsFSEncodingFlag);
#endif
    COPY_FLAG(utf8_mode, Py_UTF8Mode);

#undef COPY_FLAG
#undef COPY_NOT_FLAG
}


const char*
_Py_GetEnv(int use_environment, const char *name)
{
    assert(use_environment >= 0);

    if (!use_environment) {
        return NULL;
    }

    const char *var = getenv(name);
    if (var && var[0] != '\0') {
        return var;
    }
    else {
        return NULL;
    }
}


int
_Py_str_to_int(const char *str, int *result)
{
    const char *endptr = str;
    errno = 0;
    long value = strtol(str, (char **)&endptr, 10);
    if (*endptr != '\0' || errno == ERANGE) {
        return -1;
    }
    if (value < INT_MIN || value > INT_MAX) {
        return -1;
    }

    *result = (int)value;
    return 0;
}


void
_Py_get_env_flag(int use_environment, int *flag, const char *name)
{
    const char *var = _Py_GetEnv(use_environment, name);
    if (!var) {
        return;
    }
    int value;
    if (_Py_str_to_int(var, &value) < 0 || value < 0) {
        /* PYTHONDEBUG=text and PYTHONDEBUG=-2 behave as PYTHONDEBUG=1 */
        value = 1;
    }
    if (*flag < value) {
        *flag = value;
    }
}


const wchar_t*
_Py_get_xoption(const PyWideStringList *xoptions, const wchar_t *name)
{
    for (Py_ssize_t i=0; i < xoptions->length; i++) {
        const wchar_t *option = xoptions->items[i];
        size_t len;
        wchar_t *sep = wcschr(option, L'=');
        if (sep != NULL) {
            len = (sep - option);
        }
        else {
            len = wcslen(option);
        }
        if (wcsncmp(option, name, len) == 0 && name[len] == L'\0') {
            return option;
        }
    }
    return NULL;
}


static PyStatus
preconfig_init_utf8_mode(PyPreConfig *config, const _PyPreCmdline *cmdline)
{
#ifdef MS_WINDOWS
    if (config->legacy_windows_fs_encoding) {
        config->utf8_mode = 0;
    }
#endif

    if (config->utf8_mode >= 0) {
        return _PyStatus_OK();
    }

    const wchar_t *xopt;
    xopt = _Py_get_xoption(&cmdline->xoptions, L"utf8");
    if (xopt) {
        wchar_t *sep = wcschr(xopt, L'=');
        if (sep) {
            xopt = sep + 1;
            if (wcscmp(xopt, L"1") == 0) {
                config->utf8_mode = 1;
            }
            else if (wcscmp(xopt, L"0") == 0) {
                config->utf8_mode = 0;
            }
            else {
                return _PyStatus_ERR("invalid -X utf8 option value");
            }
        }
        else {
            config->utf8_mode = 1;
        }
        return _PyStatus_OK();
    }

    const char *opt = _Py_GetEnv(config->use_environment, "PYTHONUTF8");
    if (opt) {
        if (strcmp(opt, "1") == 0) {
            config->utf8_mode = 1;
        }
        else if (strcmp(opt, "0") == 0) {
            config->utf8_mode = 0;
        }
        else {
            return _PyStatus_ERR("invalid PYTHONUTF8 environment "
                                "variable value");
        }
        return _PyStatus_OK();
    }


#ifndef MS_WINDOWS
    if (config->utf8_mode < 0) {
        /* The C locale and the POSIX locale enable the UTF-8 Mode (PEP 540) */
        const char *ctype_loc = setlocale(LC_CTYPE, NULL);
        if (ctype_loc != NULL
           && (strcmp(ctype_loc, "C") == 0
               || strcmp(ctype_loc, "POSIX") == 0))
        {
            config->utf8_mode = 1;
        }
    }
#endif

    if (config->utf8_mode < 0) {
        config->utf8_mode = 0;
    }
    return _PyStatus_OK();
}


static void
preconfig_init_coerce_c_locale(PyPreConfig *config)
{
    if (!config->configure_locale) {
        config->coerce_c_locale = 0;
        config->coerce_c_locale_warn = 0;
        return;
    }

    const char *env = _Py_GetEnv(config->use_environment, "PYTHONCOERCECLOCALE");
    if (env) {
        if (strcmp(env, "0") == 0) {
            if (config->coerce_c_locale < 0) {
                config->coerce_c_locale = 0;
            }
        }
        else if (strcmp(env, "warn") == 0) {
            if (config->coerce_c_locale_warn < 0) {
                config->coerce_c_locale_warn = 1;
            }
        }
        else {
            if (config->coerce_c_locale < 0) {
                config->coerce_c_locale = 1;
            }
        }
    }

    /* Test if coerce_c_locale equals to -1 or equals to 1:
       PYTHONCOERCECLOCALE=1 doesn't imply that the C locale is always coerced.
       It is only coerced if if the LC_CTYPE locale is "C". */
    if (config->coerce_c_locale < 0 || config->coerce_c_locale == 1) {
        /* The C locale enables the C locale coercion (PEP 538) */
        if (_Py_LegacyLocaleDetected(0)) {
            config->coerce_c_locale = 2;
        }
        else {
            config->coerce_c_locale = 0;
        }
    }

    if (config->coerce_c_locale_warn < 0) {
        config->coerce_c_locale_warn = 0;
    }
}


static PyStatus
preconfig_init_allocator(PyPreConfig *config)
{
    if (config->allocator == PYMEM_ALLOCATOR_NOT_SET) {
        /* bpo-34247. The PYTHONMALLOC environment variable has the priority
           over PYTHONDEV env var and "-X dev" command line option.
           For example, PYTHONMALLOC=malloc PYTHONDEVMODE=1 sets the memory
           allocators to "malloc" (and not to "debug"). */
        const char *envvar = _Py_GetEnv(config->use_environment, "PYTHONMALLOC");
        if (envvar) {
            PyMemAllocatorName name;
            if (_PyMem_GetAllocatorName(envvar, &name) < 0) {
                return _PyStatus_ERR("PYTHONMALLOC: unknown allocator");
            }
            config->allocator = (int)name;
        }
    }

    if (config->dev_mode && config->allocator == PYMEM_ALLOCATOR_NOT_SET) {
        config->allocator = PYMEM_ALLOCATOR_DEBUG;
    }
    return _PyStatus_OK();
}


static PyStatus
preconfig_read(PyPreConfig *config, _PyPreCmdline *cmdline)
{
    PyStatus status;

    status = _PyPreCmdline_Read(cmdline, config);
    if (_PyStatus_EXCEPTION(status)) {
        return status;
    }

    precmdline_set_preconfig(cmdline, config);

    /* legacy_windows_fs_encoding, coerce_c_locale, utf8_mode */
#ifdef MS_WINDOWS
    _Py_get_env_flag(config->use_environment,
                     &config->legacy_windows_fs_encoding,
                     "PYTHONLEGACYWINDOWSFSENCODING");
#endif

    preconfig_init_coerce_c_locale(config);

    status = preconfig_init_utf8_mode(config, cmdline);
    if (_PyStatus_EXCEPTION(status)) {
        return status;
    }

    /* allocator */
    status = preconfig_init_allocator(config);
    if (_PyStatus_EXCEPTION(status)) {
        return status;
    }

    assert(config->coerce_c_locale >= 0);
    assert(config->coerce_c_locale_warn >= 0);
#ifdef MS_WINDOWS
    assert(config->legacy_windows_fs_encoding >= 0);
#endif
    assert(config->utf8_mode >= 0);
    assert(config->isolated >= 0);
    assert(config->use_environment >= 0);
    assert(config->dev_mode >= 0);

    return _PyStatus_OK();
}


/* Read the configuration from:

   - command line arguments
   - environment variables
   - Py_xxx global configuration variables
   - the LC_CTYPE locale */
PyStatus
_PyPreConfig_Read(PyPreConfig *config, const _PyArgv *args)
{
    PyStatus status;

    status = _PyRuntime_Initialize();
    if (_PyStatus_EXCEPTION(status)) {
        return status;
    }

    preconfig_get_global_vars(config);

    /* Copy LC_CTYPE locale, since it's modified later */
    const char *loc = setlocale(LC_CTYPE, NULL);
    if (loc == NULL) {
        return _PyStatus_ERR("failed to LC_CTYPE locale");
    }
    char *init_ctype_locale = _PyMem_RawStrdup(loc);
    if (init_ctype_locale == NULL) {
        return _PyStatus_NO_MEMORY();
    }

    /* Save the config to be able to restore it if encodings change */
    PyPreConfig save_config;

    status = _PyPreConfig_InitFromPreConfig(&save_config, config);
    if (_PyStatus_EXCEPTION(status)) {
        return status;
    }

    /* Set LC_CTYPE to the user preferred locale */
    if (config->configure_locale) {
        _Py_SetLocaleFromEnv(LC_CTYPE);
    }

    _PyPreCmdline cmdline = _PyPreCmdline_INIT;
    int init_utf8_mode = Py_UTF8Mode;
#ifdef MS_WINDOWS
    int init_legacy_encoding = Py_LegacyWindowsFSEncodingFlag;
#endif

    if (args) {
        status = _PyPreCmdline_SetArgv(&cmdline, args);
        if (_PyStatus_EXCEPTION(status)) {
            goto done;
        }
    }

    int locale_coerced = 0;
    int loops = 0;

    while (1) {
        int utf8_mode = config->utf8_mode;

        /* Watchdog to prevent an infinite loop */
        loops++;
        if (loops == 3) {
            status = _PyStatus_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

        status = preconfig_read(config, &cmdline);
        if (_PyStatus_EXCEPTION(status)) {
            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.
         */
        int encoding_changed = 0;
        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;
        preconfig_copy(config, &save_config);
        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 */
    }
    status = _PyStatus_OK();

done:
    if (init_ctype_locale != NULL) {
        setlocale(LC_CTYPE, init_ctype_locale);
        PyMem_RawFree(init_ctype_locale);
    }
    Py_UTF8Mode = init_utf8_mode ;
#ifdef MS_WINDOWS
    Py_LegacyWindowsFSEncodingFlag = init_legacy_encoding;
#endif
    _PyPreCmdline_Clear(&cmdline);
    return status;
}


/* Write the pre-configuration:

   - set the memory allocators
   - set Py_xxx global configuration variables
   - set the LC_CTYPE locale (coerce C locale, PEP 538) and set the UTF-8 mode
     (PEP 540)

   The applied configuration is written into _PyRuntime.preconfig.
   If the C locale cannot be coerced, set coerce_c_locale to 0.

   Do nothing if called after Py_Initialize(): ignore the new
   pre-configuration. */
PyStatus
_PyPreConfig_Write(const PyPreConfig *src_config)
{
    PyPreConfig config;

    PyStatus status = _PyPreConfig_InitFromPreConfig(&config, src_config);
    if (_PyStatus_EXCEPTION(status)) {
        return status;
    }

    if (_PyRuntime.core_initialized) {
        /* bpo-34008: Calling this functions after Py_Initialize() ignores
           the new configuration. */
        return _PyStatus_OK();
    }

    PyMemAllocatorName name = (PyMemAllocatorName)config.allocator;
    if (name != PYMEM_ALLOCATOR_NOT_SET) {
        if (_PyMem_SetupAllocators(name) < 0) {
            return _PyStatus_ERR("Unknown PYTHONMALLOC allocator");
        }
    }

    preconfig_set_global_vars(&config);

    if (config.configure_locale) {
        if (config.coerce_c_locale) {
            if (!_Py_CoerceLegacyLocale(config.coerce_c_locale_warn)) {
                /* C locale not coerced */
                config.coerce_c_locale = 0;
            }
        }

        /* Set LC_CTYPE to the user preferred locale */
        _Py_SetLocaleFromEnv(LC_CTYPE);
    }

    /* Write the new pre-configuration into _PyRuntime */
    preconfig_copy(&_PyRuntime.preconfig, &config);

    return _PyStatus_OK();
}
back to top