| #include "Python.h" | |
| #include "pycore_fileutils.h" // DECODE_LOCALE_ERR | |
| #include "pycore_getopt.h" // _PyOS_GetOpt() | |
| #include "pycore_initconfig.h" // _PyArgv | |
| #include "pycore_pylifecycle.h" // _Py_LegacyLocaleDetected() | |
| #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); | |
| extern int Py_UTF8Mode; | |
| /* --- File system encoding/errors -------------------------------- */ | |
| // Variables removed from Python limited C API 3.16, but kept in the stable ABI | |
| PyAPI_DATA(const char *) Py_FileSystemDefaultEncoding; | |
| PyAPI_DATA(const char *) Py_FileSystemDefaultEncodeErrors; | |
| PyAPI_DATA(int) Py_HasFileSystemDefaultEncoding; | |
| // The default encoding used by the platform file system APIs. | |
| // If non-NULL, this is different than the default encoding for strings. | |
| 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; | |
| struct _PyOS_GetOpt getopt; | |
| _PyOS_GetOpt_Init(&getopt, argv->length, argv->items); | |
| /* Don't log parsing errors into stderr here: PyConfig_Read() | |
| is responsible for that */ | |
| getopt.error = 0; | |
| do { | |
| int c = _PyOS_GetOpt(&getopt); | |
| if (c == -1 || 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, | |
| getopt.arg); | |
| 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; | |
| /* gh-80624: C locale coercion (PEP 538) is disabled by default using | |
| the Compat configuration. | |
| Py_UTF8Mode=0 disables the UTF-8 mode. PYTHONUTF8 environment variable | |
| is ignored (even if use_environment=1). */ | |
| config->utf8_mode = 1; | |
| 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) 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 = 1; | |
| 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_var(PyPreConfig *config) | |
| { | |
| if (config->_config_init != _PyConfig_INIT_COMPAT) { | |
| /* Python and Isolated configuration ignore global variables */ | |
| return; | |
| } | |
| if (Py_UTF8Mode > 0) { | |
| config->utf8_mode = Py_UTF8Mode; | |
| } | |
| } | |
| static void | |
| preconfig_set_global_var(const PyPreConfig *config) | |
| { | |
| if (config->utf8_mode >= 0) { | |
| Py_UTF8Mode = config->utf8_mode; | |
| } | |
| } | |
| 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; | |
| const 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) { | |
| const 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(); | |
| } | |
| config->utf8_mode = 1; | |
| 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 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_UTF8Mode global configuration variable | |
| - 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_var(config); | |
| if (config->use_environment < 0) { | |
| config->use_environment = 1; | |
| } | |
| if (config->isolated < 0) { | |
| config->isolated = 0; | |
| } | |
| #ifdef MS_WINDOWS | |
| if (config->legacy_windows_fs_encoding < 0) { | |
| config->legacy_windows_fs_encoding = 0; | |
| } | |
| #endif | |
| /* 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); | |
| } | |
| PyPreConfig save_runtime_config; | |
| preconfig_copy(&save_runtime_config, &_PyRuntime.preconfig); | |
| _PyPreCmdline cmdline = _PyPreCmdline_INIT; | |
| 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 the utf8_mode and legacy_windows_fs_encoding members | |
| of _PyRuntime.preconfig. */ | |
| preconfig_copy(&_PyRuntime.preconfig, config); | |
| 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: | |
| // Revert side effects | |
| setlocale(LC_CTYPE, init_ctype_locale); | |
| PyMem_RawFree(init_ctype_locale); | |
| preconfig_copy(&_PyRuntime.preconfig, &save_runtime_config); | |
| _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 (_Py_IsCoreInitialized()) { | |
| /* 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_var(&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(); | |
| } |