summaryrefslogtreecommitdiffstats
path: root/Modules/_localemodule.c
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@redhat.com>2018-11-20 15:20:16 (GMT)
committerGitHub <noreply@github.com>2018-11-20 15:20:16 (GMT)
commit02e6bf7f2025cddcbde6432f6b6396198ab313f4 (patch)
treed7c7a206e2f8c891fe4c41da8a3a46d0902bee13 /Modules/_localemodule.c
parentd5d33681c1cd1df7731eb0fb7c0f297bc2f114e6 (diff)
downloadcpython-02e6bf7f2025cddcbde6432f6b6396198ab313f4.zip
cpython-02e6bf7f2025cddcbde6432f6b6396198ab313f4.tar.gz
cpython-02e6bf7f2025cddcbde6432f6b6396198ab313f4.tar.bz2
bpo-28604: Fix localeconv() for different LC_MONETARY (GH-10606)
locale.localeconv() now sets temporarily the LC_CTYPE locale to the LC_MONETARY locale if the two locales are different and monetary strings are non-ASCII. This temporary change affects other threads. Changes: * locale.localeconv() can now set LC_CTYPE to LC_MONETARY to decode monetary fields. * Add LocaleInfo.grouping_buffer: copy localeconv() grouping string since it can be replaced anytime if a different thread calls localeconv(). * _Py_GetLocaleconvNumeric() now requires a "struct lconv *" structure, so locale.localeconv() now longer calls localeconv() twice. Moreover, the function now requires all arguments to be non-NULL. * Rename STATIC_LOCALE_INFO_INIT to LocaleInfo_STATIC_INIT. * Move _Py_GetLocaleconvNumeric() definition from fileutils.h to pycore_fileutils.h. pycore_fileutils.h now includes locale.h. * The _locale module is now built with Py_BUILD_CORE defined.
Diffstat (limited to 'Modules/_localemodule.c')
-rw-r--r--Modules/_localemodule.c109
1 files changed, 93 insertions, 16 deletions
diff --git a/Modules/_localemodule.c b/Modules/_localemodule.c
index 3fdbc5e..4202cc4 100644
--- a/Modules/_localemodule.c
+++ b/Modules/_localemodule.c
@@ -11,6 +11,7 @@ This software comes with no warranty. Use at your own risk.
#define PY_SSIZE_T_CLEAN
#include "Python.h"
+#include "pycore_fileutils.h"
#include <stdio.h>
#include <locale.h>
@@ -128,6 +129,82 @@ PyLocale_setlocale(PyObject* self, PyObject* args)
return result_object;
}
+static int
+locale_is_ascii(const char *str)
+{
+ return (strlen(str) == 1 && ((unsigned char)str[0]) <= 127);
+}
+
+static int
+locale_decode_monetary(PyObject *dict, struct lconv *lc)
+{
+ int change_locale;
+ change_locale = (!locale_is_ascii(lc->int_curr_symbol)
+ || !locale_is_ascii(lc->currency_symbol)
+ || !locale_is_ascii(lc->mon_decimal_point)
+ || !locale_is_ascii(lc->mon_thousands_sep));
+
+ /* Keep a copy of the LC_CTYPE locale */
+ char *oldloc = NULL, *loc = NULL;
+ if (change_locale) {
+ oldloc = setlocale(LC_CTYPE, NULL);
+ if (!oldloc) {
+ PyErr_SetString(PyExc_RuntimeWarning,
+ "failed to get LC_CTYPE locale");
+ return -1;
+ }
+
+ oldloc = _PyMem_Strdup(oldloc);
+ if (!oldloc) {
+ PyErr_NoMemory();
+ return -1;
+ }
+
+ loc = setlocale(LC_MONETARY, NULL);
+ if (loc != NULL && strcmp(loc, oldloc) == 0) {
+ loc = NULL;
+ }
+
+ if (loc != NULL) {
+ /* Only set the locale temporarily the LC_CTYPE locale
+ to the LC_MONETARY locale if the two locales are different and
+ at least one string is non-ASCII. */
+ setlocale(LC_CTYPE, loc);
+ }
+ }
+
+ int res = -1;
+
+#define RESULT_STRING(ATTR) \
+ do { \
+ PyObject *obj; \
+ obj = PyUnicode_DecodeLocale(lc->ATTR, NULL); \
+ if (obj == NULL) { \
+ goto done; \
+ } \
+ if (PyDict_SetItemString(dict, Py_STRINGIFY(ATTR), obj) < 0) { \
+ Py_DECREF(obj); \
+ goto done; \
+ } \
+ Py_DECREF(obj); \
+ } while (0)
+
+ RESULT_STRING(int_curr_symbol);
+ RESULT_STRING(currency_symbol);
+ RESULT_STRING(mon_decimal_point);
+ RESULT_STRING(mon_thousands_sep);
+#undef RESULT_STRING
+
+ res = 0;
+
+done:
+ if (loc != NULL) {
+ setlocale(LC_CTYPE, oldloc);
+ }
+ PyMem_Free(oldloc);
+ return res;
+}
+
PyDoc_STRVAR(localeconv__doc__,
"() -> dict. Returns numeric and monetary locale-specific parameters.");
@@ -135,7 +212,7 @@ static PyObject*
PyLocale_localeconv(PyObject* self, PyObject *Py_UNUSED(ignored))
{
PyObject* result;
- struct lconv *l;
+ struct lconv *lc;
PyObject *x;
result = PyDict_New();
@@ -144,7 +221,7 @@ PyLocale_localeconv(PyObject* self, PyObject *Py_UNUSED(ignored))
}
/* if LC_NUMERIC is different in the C library, use saved value */
- l = localeconv();
+ lc = localeconv();
/* hopefully, the localeconv result survives the C library calls
involved herein */
@@ -162,22 +239,21 @@ PyLocale_localeconv(PyObject* self, PyObject *Py_UNUSED(ignored))
#define RESULT_STRING(s)\
do { \
- x = PyUnicode_DecodeLocale(l->s, NULL); \
+ x = PyUnicode_DecodeLocale(lc->s, NULL); \
RESULT(#s, x); \
} while (0)
#define RESULT_INT(i)\
do { \
- x = PyLong_FromLong(l->i); \
+ x = PyLong_FromLong(lc->i); \
RESULT(#i, x); \
} while (0)
- /* Monetary information */
- RESULT_STRING(int_curr_symbol);
- RESULT_STRING(currency_symbol);
- RESULT_STRING(mon_decimal_point);
- RESULT_STRING(mon_thousands_sep);
- x = copy_grouping(l->mon_grouping);
+ /* Monetary information: LC_MONETARY encoding */
+ if (locale_decode_monetary(result, lc) < 0) {
+ goto failed;
+ }
+ x = copy_grouping(lc->mon_grouping);
RESULT("mon_grouping", x);
RESULT_STRING(positive_sign);
@@ -191,12 +267,9 @@ PyLocale_localeconv(PyObject* self, PyObject *Py_UNUSED(ignored))
RESULT_INT(p_sign_posn);
RESULT_INT(n_sign_posn);
- /* Numeric information */
+ /* Numeric information: LC_NUMERIC encoding */
PyObject *decimal_point, *thousands_sep;
- const char *grouping;
- if (_Py_GetLocaleconvNumeric(&decimal_point,
- &thousands_sep,
- &grouping) < 0) {
+ if (_Py_GetLocaleconvNumeric(lc, &decimal_point, &thousands_sep) < 0) {
goto failed;
}
@@ -213,7 +286,7 @@ PyLocale_localeconv(PyObject* self, PyObject *Py_UNUSED(ignored))
}
Py_DECREF(thousands_sep);
- x = copy_grouping(grouping);
+ x = copy_grouping(lc->grouping);
RESULT("grouping", x);
return result;
@@ -221,6 +294,10 @@ PyLocale_localeconv(PyObject* self, PyObject *Py_UNUSED(ignored))
failed:
Py_DECREF(result);
return NULL;
+
+#undef RESULT
+#undef RESULT_STRING
+#undef RESULT_INT
}
#if defined(HAVE_WCSCOLL)