diff options
author | nkozlovskiy <nmk@ydb.tech> | 2023-09-29 12:24:06 +0300 |
---|---|---|
committer | nkozlovskiy <nmk@ydb.tech> | 2023-09-29 12:41:34 +0300 |
commit | e0e3e1717e3d33762ce61950504f9637a6e669ed (patch) | |
tree | bca3ff6939b10ed60c3d5c12439963a1146b9711 /contrib/tools/python3/src/Python/preconfig.c | |
parent | 38f2c5852db84c7b4d83adfcb009eb61541d1ccd (diff) | |
download | ydb-e0e3e1717e3d33762ce61950504f9637a6e669ed.tar.gz |
add ydb deps
Diffstat (limited to 'contrib/tools/python3/src/Python/preconfig.c')
-rw-r--r-- | contrib/tools/python3/src/Python/preconfig.c | 973 |
1 files changed, 973 insertions, 0 deletions
diff --git a/contrib/tools/python3/src/Python/preconfig.c b/contrib/tools/python3/src/Python/preconfig.c new file mode 100644 index 0000000000..0deb07a893 --- /dev/null +++ b/contrib/tools/python3/src/Python/preconfig.c @@ -0,0 +1,973 @@ +#include "Python.h" +#include "pycore_fileutils.h" // DECODE_LOCALE_ERR +#include "pycore_getopt.h" // _PyOS_GetOpt() +#include "pycore_initconfig.h" // _PyArgv +#include "pycore_pymem.h" // _PyMem_GetAllocatorName() +#include "pycore_runtime.h" // _PyRuntime_Initialize() + +#include <locale.h> // setlocale() +#include <stdlib.h> // getenv() + + +/* Forward declarations */ +static void +preconfig_copy(PyPreConfig *config, const PyPreConfig *config2); + + +/* --- File system encoding/errors -------------------------------- */ + +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 to PyConfig.filesystem_encoding and + PyConfig.filesystem_errors (encoded to UTF-8). + + Function called by _PyUnicode_InitEncodings(). */ +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", 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); + COPY_ATTR(warn_default_encoding); + 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; + } + + // warn_default_encoding + if (_Py_get_xoption(&cmdline->xoptions, L"warn_default_encoding") + || _Py_GetEnv(cmdline->use_environment, "PYTHONWARNDEFAULTENCODING")) + { + cmdline->warn_default_encoding = 1; + } + + assert(cmdline->use_environment >= 0); + assert(cmdline->isolated >= 0); + assert(cmdline->dev_mode >= 0); + assert(cmdline->warn_default_encoding >= 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 + + 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 + + if (args) { + // Set command line arguments at each iteration. If they are bytes + // strings, they are decoded from the new encoding. + status = _PyPreCmdline_SetArgv(&cmdline, args); + if (_PyStatus_EXCEPTION(status)) { + goto done; + } + } + + 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 and coerce C locale 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(); +} |