From 0f72147ce2b3d65235b41eddc6a57be40237b5c7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 20 May 2019 17:16:38 +0200 Subject: [PATCH] bpo-36763: Fix _PyRuntime.preconfig.coerce_c_locale (GH-13444) _PyRuntime.preconfig.coerce_c_locale can now be used to check if the C locale has been coerced. * Fix _Py_LegacyLocaleDetected(): don't attempt to coerce the C locale if LC_ALL environment variable is set. Add 'warn' parameter: emit_stderr_warning_for_legacy_locale() must not the LC_ALL env var. * _PyPreConfig_Write() sets coerce_c_locale to 0 if _Py_CoerceLegacyLocale() fails. --- Include/cpython/pylifecycle.h | 4 ++-- Python/preconfig.c | 26 ++++++++++++++++---------- Python/pylifecycle.c | 26 +++++++++++++++++++------- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/Include/cpython/pylifecycle.h b/Include/cpython/pylifecycle.h index 1e1dabe59ff..ba5666465d7 100644 --- a/Include/cpython/pylifecycle.h +++ b/Include/cpython/pylifecycle.h @@ -69,8 +69,8 @@ PyAPI_FUNC(int) _PyOS_URandom(void *buffer, Py_ssize_t size); PyAPI_FUNC(int) _PyOS_URandomNonblock(void *buffer, Py_ssize_t size); /* Legacy locale support */ -PyAPI_FUNC(void) _Py_CoerceLegacyLocale(int warn); -PyAPI_FUNC(int) _Py_LegacyLocaleDetected(void); +PyAPI_FUNC(int) _Py_CoerceLegacyLocale(int warn); +PyAPI_FUNC(int) _Py_LegacyLocaleDetected(int warn); PyAPI_FUNC(char *) _Py_SetLocaleFromEnv(int category); #ifdef __cplusplus diff --git a/Python/preconfig.c b/Python/preconfig.c index 71a6ee6c072..4df6208cadb 100644 --- a/Python/preconfig.c +++ b/Python/preconfig.c @@ -660,7 +660,7 @@ preconfig_init_coerce_c_locale(_PyPreConfig *config) 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()) { + if (_Py_LegacyLocaleDetected(0)) { config->coerce_c_locale = 2; } else { @@ -888,32 +888,38 @@ done: - set the LC_CTYPE locale (coerce C locale, PEP 538) and set the UTF-8 mode (PEP 540) - If the memory allocator is changed, config is re-allocated with new - allocator. So calling _PyPreConfig_Clear(config) is safe after this call. + 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. */ _PyInitError -_PyPreConfig_Write(const _PyPreConfig *config) +_PyPreConfig_Write(const _PyPreConfig *src_config) { + _PyPreConfig config; + _PyPreConfig_InitFromPreConfig(&config, src_config); + if (_PyRuntime.core_initialized) { /* bpo-34008: Calling this functions after Py_Initialize() ignores the new configuration. */ return _Py_INIT_OK(); } - PyMemAllocatorName name = (PyMemAllocatorName)config->allocator; + PyMemAllocatorName name = (PyMemAllocatorName)config.allocator; if (name != PYMEM_ALLOCATOR_NOT_SET) { if (_PyMem_SetupAllocators(name) < 0) { return _Py_INIT_ERR("Unknown PYTHONMALLOC allocator"); } } - _PyPreConfig_SetGlobalConfig(config); + _PyPreConfig_SetGlobalConfig(&config); - if (config->configure_locale) { - if (config->coerce_c_locale) { - _Py_CoerceLegacyLocale(config->coerce_c_locale_warn); + 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 */ @@ -921,7 +927,7 @@ _PyPreConfig_Write(const _PyPreConfig *config) } /* Write the new pre-configuration into _PyRuntime */ - _PyPreConfig_Copy(&_PyRuntime.preconfig, config); + _PyPreConfig_Copy(&_PyRuntime.preconfig, &config); return _Py_INIT_OK(); } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 01f725f8f45..01344db410a 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -231,9 +231,18 @@ init_importlib_external(PyInterpreterState *interp) */ int -_Py_LegacyLocaleDetected(void) +_Py_LegacyLocaleDetected(int warn) { #ifndef MS_WINDOWS + if (!warn) { + const char *locale_override = getenv("LC_ALL"); + if (locale_override != NULL && *locale_override != '\0') { + /* Don't coerce C locale if the LC_ALL environment variable + is set */ + return 0; + } + } + /* On non-Windows systems, the C locale is considered a legacy locale */ /* XXX (ncoghlan): some platforms (notably Mac OS X) don't appear to treat * the POSIX locale as a simple alias for the C locale, so @@ -257,7 +266,7 @@ static void emit_stderr_warning_for_legacy_locale(_PyRuntimeState *runtime) { const _PyPreConfig *preconfig = &runtime->preconfig; - if (preconfig->coerce_c_locale_warn && _Py_LegacyLocaleDetected()) { + if (preconfig->coerce_c_locale_warn && _Py_LegacyLocaleDetected(1)) { PySys_FormatStderr("%s", _C_LOCALE_WARNING); } } @@ -292,7 +301,7 @@ static const char C_LOCALE_COERCION_WARNING[] = "Python detected LC_CTYPE=C: LC_CTYPE coerced to %.20s (set another locale " "or PYTHONCOERCECLOCALE=0 to disable this locale coercion behavior).\n"; -static void +static int _coerce_default_locale_settings(int warn, const _LocaleCoercionTarget *target) { const char *newloc = target->locale_name; @@ -304,7 +313,7 @@ _coerce_default_locale_settings(int warn, const _LocaleCoercionTarget *target) if (setenv("LC_CTYPE", newloc, 1)) { fprintf(stderr, "Error setting LC_CTYPE, skipping C locale coercion\n"); - return; + return 0; } if (warn) { fprintf(stderr, C_LOCALE_COERCION_WARNING, newloc); @@ -312,18 +321,20 @@ _coerce_default_locale_settings(int warn, const _LocaleCoercionTarget *target) /* Reconfigure with the overridden environment variables */ _Py_SetLocaleFromEnv(LC_ALL); + return 1; } #endif -void +int _Py_CoerceLegacyLocale(int warn) { + int coerced = 0; #ifdef PY_COERCE_C_LOCALE char *oldloc = NULL; oldloc = _PyMem_RawStrdup(setlocale(LC_CTYPE, NULL)); if (oldloc == NULL) { - return; + return coerced; } const char *locale_override = getenv("LC_ALL"); @@ -345,7 +356,7 @@ _Py_CoerceLegacyLocale(int warn) } #endif /* Successfully configured locale, so make it the default */ - _coerce_default_locale_settings(warn, target); + coerced = _coerce_default_locale_settings(warn, target); goto done; } } @@ -357,6 +368,7 @@ _Py_CoerceLegacyLocale(int warn) done: PyMem_RawFree(oldloc); #endif + return coerced; } /* _Py_SetLocaleFromEnv() is a wrapper around setlocale(category, "") to