Skip to content

bpo-36763: Fix C locale coercion #13444

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Include/cpython/pylifecycle.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 16 additions & 10 deletions Python/preconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -888,40 +888,46 @@ _PyPreConfig_Read(_PyPreConfig *config, const _PyArgv *args)
- 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 */
_Py_SetLocaleFromEnv(LC_CTYPE);
}

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

return _Py_INIT_OK();
}
26 changes: 19 additions & 7 deletions Python/pylifecycle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
}
}
Expand Down Expand Up @@ -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;
Expand All @@ -304,26 +313,28 @@ _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);
}

/* 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");
Expand 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;
}
}
Expand All @@ -357,6 +368,7 @@ _Py_CoerceLegacyLocale(int warn)
done:
PyMem_RawFree(oldloc);
#endif
return coerced;
}

/* _Py_SetLocaleFromEnv() is a wrapper around setlocale(category, "") to
Expand Down