mirror of https://github.com/python/cpython
gh-124969: Make locale.nl_langinfo(locale.ALT_DIGITS) returning a string again (GH-125774)
This is a follow up of GH-124974. Only Glibc needed a fix. Now the returned value is a string consisting of semicolon-separated symbols on all Posix platforms.
This commit is contained in:
parent
5ca4e34bc1
commit
dcc4fb2c90
|
@ -158,8 +158,7 @@ The :mod:`locale` module defines the following exception and functions:
|
||||||
|
|
||||||
.. function:: nl_langinfo(option)
|
.. function:: nl_langinfo(option)
|
||||||
|
|
||||||
Return some locale-specific information as a string (or a tuple for
|
Return some locale-specific information as a string. This function is not
|
||||||
``ALT_DIGITS``). This function is not
|
|
||||||
available on all systems, and the set of possible options might also vary
|
available on all systems, and the set of possible options might also vary
|
||||||
across platforms. The possible argument values are numbers, for which
|
across platforms. The possible argument values are numbers, for which
|
||||||
symbolic constants are available in the locale module.
|
symbolic constants are available in the locale module.
|
||||||
|
@ -312,7 +311,9 @@ The :mod:`locale` module defines the following exception and functions:
|
||||||
|
|
||||||
.. data:: ALT_DIGITS
|
.. data:: ALT_DIGITS
|
||||||
|
|
||||||
Get a tuple of up to 100 strings used to represent the values 0 to 99.
|
Get a string consisting of up to 100 semicolon-separated symbols used
|
||||||
|
to represent the values 0 to 99 in a locale-specific way.
|
||||||
|
In most locales this is an empty string.
|
||||||
|
|
||||||
The function temporarily sets the ``LC_CTYPE`` locale to the locale
|
The function temporarily sets the ``LC_CTYPE`` locale to the locale
|
||||||
of the category that determines the requested value (``LC_TIME``,
|
of the category that determines the requested value (``LC_TIME``,
|
||||||
|
|
|
@ -26,7 +26,10 @@ candidate_locales = ['es_UY', 'fr_FR', 'fi_FI', 'es_CO', 'pt_PT', 'it_IT',
|
||||||
'bs_BA', 'fr_LU', 'kl_GL', 'fa_IR', 'de_BE', 'sv_SE', 'it_CH', 'uk_UA',
|
'bs_BA', 'fr_LU', 'kl_GL', 'fa_IR', 'de_BE', 'sv_SE', 'it_CH', 'uk_UA',
|
||||||
'eu_ES', 'vi_VN', 'af_ZA', 'nb_NO', 'en_DK', 'tg_TJ', 'ps_AF', 'en_US',
|
'eu_ES', 'vi_VN', 'af_ZA', 'nb_NO', 'en_DK', 'tg_TJ', 'ps_AF', 'en_US',
|
||||||
'fr_FR.ISO8859-1', 'fr_FR.UTF-8', 'fr_FR.ISO8859-15@euro',
|
'fr_FR.ISO8859-1', 'fr_FR.UTF-8', 'fr_FR.ISO8859-15@euro',
|
||||||
'ru_RU.KOI8-R', 'ko_KR.eucKR']
|
'ru_RU.KOI8-R', 'ko_KR.eucKR',
|
||||||
|
'ja_JP.UTF-8', 'lzh_TW.UTF-8', 'my_MM.UTF-8', 'or_IN.UTF-8', 'shn_MM.UTF-8',
|
||||||
|
'ar_AE.UTF-8', 'bn_IN.UTF-8', 'mr_IN.UTF-8', 'th_TH.TIS620',
|
||||||
|
]
|
||||||
|
|
||||||
def setUpModule():
|
def setUpModule():
|
||||||
global candidate_locales
|
global candidate_locales
|
||||||
|
@ -78,11 +81,13 @@ known_alt_digits = {
|
||||||
'C': (0, {}),
|
'C': (0, {}),
|
||||||
'en_US': (0, {}),
|
'en_US': (0, {}),
|
||||||
'fa_IR': (100, {0: '\u06f0\u06f0', 10: '\u06f1\u06f0', 99: '\u06f9\u06f9'}),
|
'fa_IR': (100, {0: '\u06f0\u06f0', 10: '\u06f1\u06f0', 99: '\u06f9\u06f9'}),
|
||||||
'ja_JP': (100, {0: '\u3007', 10: '\u5341', 99: '\u4e5d\u5341\u4e5d'}),
|
'ja_JP': (100, {1: '\u4e00', 10: '\u5341', 99: '\u4e5d\u5341\u4e5d'}),
|
||||||
'lzh_TW': (32, {0: '\u3007', 10: '\u5341', 31: '\u5345\u4e00'}),
|
'lzh_TW': (32, {0: '\u3007', 10: '\u5341', 31: '\u5345\u4e00'}),
|
||||||
'my_MM': (100, {0: '\u1040\u1040', 10: '\u1041\u1040', 99: '\u1049\u1049'}),
|
'my_MM': (100, {0: '\u1040\u1040', 10: '\u1041\u1040', 99: '\u1049\u1049'}),
|
||||||
'or_IN': (100, {0: '\u0b66', 10: '\u0b67\u0b66', 99: '\u0b6f\u0b6f'}),
|
'or_IN': (100, {0: '\u0b66', 10: '\u0b67\u0b66', 99: '\u0b6f\u0b6f'}),
|
||||||
'shn_MM': (100, {0: '\u1090\u1090', 10: '\u1091\u1090', 99: '\u1099\u1099'}),
|
'shn_MM': (100, {0: '\u1090\u1090', 10: '\u1091\u1090', 99: '\u1099\u1099'}),
|
||||||
|
'ar_AE': (100, {0: '\u0660', 10: '\u0661\u0660', 99: '\u0669\u0669'}),
|
||||||
|
'bn_IN': (100, {0: '\u09e6', 10: '\u09e7\u09e6', 99: '\u09ef\u09ef'}),
|
||||||
}
|
}
|
||||||
|
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
|
@ -199,21 +204,28 @@ class _LocaleTests(unittest.TestCase):
|
||||||
def test_alt_digits_nl_langinfo(self):
|
def test_alt_digits_nl_langinfo(self):
|
||||||
# Test nl_langinfo(ALT_DIGITS)
|
# Test nl_langinfo(ALT_DIGITS)
|
||||||
tested = False
|
tested = False
|
||||||
for loc, (count, samples) in known_alt_digits.items():
|
for loc in candidate_locales:
|
||||||
with self.subTest(locale=loc):
|
with self.subTest(locale=loc):
|
||||||
try:
|
try:
|
||||||
setlocale(LC_TIME, loc)
|
setlocale(LC_TIME, loc)
|
||||||
except Error:
|
except Error:
|
||||||
self.skipTest(f'no locale {loc!r}')
|
self.skipTest(f'no locale {loc!r}')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
with self.subTest(locale=loc):
|
with self.subTest(locale=loc):
|
||||||
alt_digits = nl_langinfo(locale.ALT_DIGITS)
|
alt_digits = nl_langinfo(locale.ALT_DIGITS)
|
||||||
self.assertIsInstance(alt_digits, tuple)
|
self.assertIsInstance(alt_digits, str)
|
||||||
if count and not alt_digits and support.is_apple:
|
alt_digits = alt_digits.split(';') if alt_digits else []
|
||||||
self.skipTest(f'ALT_DIGITS is not set for locale {loc!r} on Apple platforms')
|
if alt_digits:
|
||||||
self.assertEqual(len(alt_digits), count)
|
self.assertGreaterEqual(len(alt_digits), 10, alt_digits)
|
||||||
for i in samples:
|
loc1 = loc.split('.', 1)[0]
|
||||||
self.assertEqual(alt_digits[i], samples[i])
|
if loc1 in known_alt_digits:
|
||||||
|
count, samples = known_alt_digits[loc1]
|
||||||
|
if count and not alt_digits:
|
||||||
|
self.skipTest(f'ALT_DIGITS is not set for locale {loc!r} on this platform')
|
||||||
|
self.assertEqual(len(alt_digits), count, alt_digits)
|
||||||
|
for i in samples:
|
||||||
|
self.assertEqual(alt_digits[i], samples[i])
|
||||||
tested = True
|
tested = True
|
||||||
if not tested:
|
if not tested:
|
||||||
self.skipTest('no suitable locales')
|
self.skipTest('no suitable locales')
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
``locale.nl_langinfo(locale.ALT_DIGITS)`` now returns a string again. The
|
||||||
|
returned value consists of up to 100 semicolon-separated symbols.
|
|
@ -667,28 +667,36 @@ _locale_nl_langinfo_impl(PyObject *module, int item)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
PyObject *pyresult;
|
PyObject *pyresult;
|
||||||
|
#ifdef __GLIBC__
|
||||||
#ifdef ALT_DIGITS
|
#ifdef ALT_DIGITS
|
||||||
if (item == ALT_DIGITS) {
|
if (item == ALT_DIGITS && *result) {
|
||||||
/* The result is a sequence of up to 100 NUL-separated strings. */
|
/* According to the POSIX specification the result must be
|
||||||
const char *s = result;
|
* a sequence of up to 100 semicolon-separated strings.
|
||||||
|
* But in Glibc they are NUL-separated. */
|
||||||
|
Py_ssize_t i = 0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (; count < 100 && *s; count++) {
|
for (; count < 100 && result[i]; count++) {
|
||||||
s += strlen(s) + 1;
|
i += strlen(result + i) + 1;
|
||||||
}
|
}
|
||||||
pyresult = PyTuple_New(count);
|
char *buf = PyMem_Malloc(i);
|
||||||
if (pyresult != NULL) {
|
if (buf == NULL) {
|
||||||
for (int i = 0; i < count; i++) {
|
PyErr_NoMemory();
|
||||||
PyObject *unicode = PyUnicode_DecodeLocale(result, NULL);
|
pyresult = NULL;
|
||||||
if (unicode == NULL) {
|
}
|
||||||
Py_CLEAR(pyresult);
|
else {
|
||||||
break;
|
memcpy(buf, result, i);
|
||||||
}
|
/* Replace all NULs with semicolons. */
|
||||||
PyTuple_SET_ITEM(pyresult, i, unicode);
|
i = 0;
|
||||||
result += strlen(result) + 1;
|
while (--count) {
|
||||||
|
i += strlen(buf + i);
|
||||||
|
buf[i++] = ';';
|
||||||
}
|
}
|
||||||
|
pyresult = PyUnicode_DecodeLocale(buf, NULL);
|
||||||
|
PyMem_Free(buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
pyresult = PyUnicode_DecodeLocale(result, NULL);
|
pyresult = PyUnicode_DecodeLocale(result, NULL);
|
||||||
|
|
Loading…
Reference in New Issue