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.
This commit is contained in:
Victor Stinner 2019-05-20 17:16:38 +02:00 committed by GitHub
parent 522ccef869
commit 0f72147ce2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 37 additions and 19 deletions

View File

@ -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); PyAPI_FUNC(int) _PyOS_URandomNonblock(void *buffer, Py_ssize_t size);
/* Legacy locale support */ /* Legacy locale support */
PyAPI_FUNC(void) _Py_CoerceLegacyLocale(int warn); PyAPI_FUNC(int) _Py_CoerceLegacyLocale(int warn);
PyAPI_FUNC(int) _Py_LegacyLocaleDetected(void); PyAPI_FUNC(int) _Py_LegacyLocaleDetected(int warn);
PyAPI_FUNC(char *) _Py_SetLocaleFromEnv(int category); PyAPI_FUNC(char *) _Py_SetLocaleFromEnv(int category);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -660,7 +660,7 @@ preconfig_init_coerce_c_locale(_PyPreConfig *config)
It is only coerced if if the LC_CTYPE locale is "C". */ It is only coerced if if the LC_CTYPE locale is "C". */
if (config->coerce_c_locale < 0 || config->coerce_c_locale == 1) { if (config->coerce_c_locale < 0 || config->coerce_c_locale == 1) {
/* The C locale enables the C locale coercion (PEP 538) */ /* The C locale enables the C locale coercion (PEP 538) */
if (_Py_LegacyLocaleDetected()) { if (_Py_LegacyLocaleDetected(0)) {
config->coerce_c_locale = 2; config->coerce_c_locale = 2;
} }
else { else {
@ -888,32 +888,38 @@ done:
- set the LC_CTYPE locale (coerce C locale, PEP 538) and set the UTF-8 mode - set the LC_CTYPE locale (coerce C locale, PEP 538) and set the UTF-8 mode
(PEP 540) (PEP 540)
If the memory allocator is changed, config is re-allocated with new The applied configuration is written into _PyRuntime.preconfig.
allocator. So calling _PyPreConfig_Clear(config) is safe after this call. If the C locale cannot be coerced, set coerce_c_locale to 0.
Do nothing if called after Py_Initialize(): ignore the new Do nothing if called after Py_Initialize(): ignore the new
pre-configuration. */ pre-configuration. */
_PyInitError _PyInitError
_PyPreConfig_Write(const _PyPreConfig *config) _PyPreConfig_Write(const _PyPreConfig *src_config)
{ {
_PyPreConfig config;
_PyPreConfig_InitFromPreConfig(&config, src_config);
if (_PyRuntime.core_initialized) { if (_PyRuntime.core_initialized) {
/* bpo-34008: Calling this functions after Py_Initialize() ignores /* bpo-34008: Calling this functions after Py_Initialize() ignores
the new configuration. */ the new configuration. */
return _Py_INIT_OK(); return _Py_INIT_OK();
} }
PyMemAllocatorName name = (PyMemAllocatorName)config->allocator; PyMemAllocatorName name = (PyMemAllocatorName)config.allocator;
if (name != PYMEM_ALLOCATOR_NOT_SET) { if (name != PYMEM_ALLOCATOR_NOT_SET) {
if (_PyMem_SetupAllocators(name) < 0) { if (_PyMem_SetupAllocators(name) < 0) {
return _Py_INIT_ERR("Unknown PYTHONMALLOC allocator"); return _Py_INIT_ERR("Unknown PYTHONMALLOC allocator");
} }
} }
_PyPreConfig_SetGlobalConfig(config); _PyPreConfig_SetGlobalConfig(&config);
if (config->configure_locale) { if (config.configure_locale) {
if (config->coerce_c_locale) { if (config.coerce_c_locale) {
_Py_CoerceLegacyLocale(config->coerce_c_locale_warn); 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 */ /* Set LC_CTYPE to the user preferred locale */
@ -921,7 +927,7 @@ _PyPreConfig_Write(const _PyPreConfig *config)
} }
/* Write the new pre-configuration into _PyRuntime */ /* Write the new pre-configuration into _PyRuntime */
_PyPreConfig_Copy(&_PyRuntime.preconfig, config); _PyPreConfig_Copy(&_PyRuntime.preconfig, &config);
return _Py_INIT_OK(); return _Py_INIT_OK();
} }

View File

@ -231,9 +231,18 @@ init_importlib_external(PyInterpreterState *interp)
*/ */
int int
_Py_LegacyLocaleDetected(void) _Py_LegacyLocaleDetected(int warn)
{ {
#ifndef MS_WINDOWS #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 */ /* 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 /* 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 * 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) emit_stderr_warning_for_legacy_locale(_PyRuntimeState *runtime)
{ {
const _PyPreConfig *preconfig = &runtime->preconfig; 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); 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 " "Python detected LC_CTYPE=C: LC_CTYPE coerced to %.20s (set another locale "
"or PYTHONCOERCECLOCALE=0 to disable this locale coercion behavior).\n"; "or PYTHONCOERCECLOCALE=0 to disable this locale coercion behavior).\n";
static void static int
_coerce_default_locale_settings(int warn, const _LocaleCoercionTarget *target) _coerce_default_locale_settings(int warn, const _LocaleCoercionTarget *target)
{ {
const char *newloc = target->locale_name; 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)) { if (setenv("LC_CTYPE", newloc, 1)) {
fprintf(stderr, fprintf(stderr,
"Error setting LC_CTYPE, skipping C locale coercion\n"); "Error setting LC_CTYPE, skipping C locale coercion\n");
return; return 0;
} }
if (warn) { if (warn) {
fprintf(stderr, C_LOCALE_COERCION_WARNING, newloc); 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 */ /* Reconfigure with the overridden environment variables */
_Py_SetLocaleFromEnv(LC_ALL); _Py_SetLocaleFromEnv(LC_ALL);
return 1;
} }
#endif #endif
void int
_Py_CoerceLegacyLocale(int warn) _Py_CoerceLegacyLocale(int warn)
{ {
int coerced = 0;
#ifdef PY_COERCE_C_LOCALE #ifdef PY_COERCE_C_LOCALE
char *oldloc = NULL; char *oldloc = NULL;
oldloc = _PyMem_RawStrdup(setlocale(LC_CTYPE, NULL)); oldloc = _PyMem_RawStrdup(setlocale(LC_CTYPE, NULL));
if (oldloc == NULL) { if (oldloc == NULL) {
return; return coerced;
} }
const char *locale_override = getenv("LC_ALL"); const char *locale_override = getenv("LC_ALL");
@ -345,7 +356,7 @@ _Py_CoerceLegacyLocale(int warn)
} }
#endif #endif
/* Successfully configured locale, so make it the default */ /* Successfully configured locale, so make it the default */
_coerce_default_locale_settings(warn, target); coerced = _coerce_default_locale_settings(warn, target);
goto done; goto done;
} }
} }
@ -357,6 +368,7 @@ _Py_CoerceLegacyLocale(int warn)
done: done:
PyMem_RawFree(oldloc); PyMem_RawFree(oldloc);
#endif #endif
return coerced;
} }
/* _Py_SetLocaleFromEnv() is a wrapper around setlocale(category, "") to /* _Py_SetLocaleFromEnv() is a wrapper around setlocale(category, "") to