mirror of https://github.com/python/cpython
gh-124969: Fix locale.nl_langinfo(locale.ALT_DIGITS) (GH-124974)
Now it returns a tuple of up to 100 strings (an empty tuple on most locales). Previously it returned the first item of that tuple or an empty string.
This commit is contained in:
parent
92760bd85b
commit
21c04e1a97
|
@ -158,7 +158,8 @@ 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. This function is not
|
Return some locale-specific information as a string (or a tuple for
|
||||||
|
``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.
|
||||||
|
@ -311,8 +312,7 @@ The :mod:`locale` module defines the following exception and functions:
|
||||||
|
|
||||||
.. data:: ALT_DIGITS
|
.. data:: ALT_DIGITS
|
||||||
|
|
||||||
Get a representation of up to 100 values used to represent the values
|
Get a tuple of up to 100 strings used to represent the values 0 to 99.
|
||||||
0 to 99.
|
|
||||||
|
|
||||||
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``,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from _locale import (setlocale, LC_ALL, LC_CTYPE, LC_NUMERIC, localeconv, Error)
|
from _locale import (setlocale, LC_ALL, LC_CTYPE, LC_NUMERIC, LC_TIME, localeconv, Error)
|
||||||
try:
|
try:
|
||||||
from _locale import (RADIXCHAR, THOUSEP, nl_langinfo)
|
from _locale import (RADIXCHAR, THOUSEP, nl_langinfo)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -74,6 +74,17 @@ known_numerics = {
|
||||||
'ps_AF': ('\u066b', '\u066c'),
|
'ps_AF': ('\u066b', '\u066c'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
known_alt_digits = {
|
||||||
|
'C': (0, {}),
|
||||||
|
'en_US': (0, {}),
|
||||||
|
'fa_IR': (100, {0: '\u06f0\u06f0', 10: '\u06f1\u06f0', 99: '\u06f9\u06f9'}),
|
||||||
|
'ja_JP': (100, {0: '\u3007', 10: '\u5341', 99: '\u4e5d\u5341\u4e5d'}),
|
||||||
|
'lzh_TW': (32, {0: '\u3007', 10: '\u5341', 31: '\u5345\u4e00'}),
|
||||||
|
'my_MM': (100, {0: '\u1040\u1040', 10: '\u1041\u1040', 99: '\u1049\u1049'}),
|
||||||
|
'or_IN': (100, {0: '\u0b66', 10: '\u0b67\u0b66', 99: '\u0b6f\u0b6f'}),
|
||||||
|
'shn_MM': (100, {0: '\u1090\u1090', 10: '\u1091\u1090', 99: '\u1099\u1099'}),
|
||||||
|
}
|
||||||
|
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
# ps_AF doesn't work on Windows: see bpo-38324 (msg361830)
|
# ps_AF doesn't work on Windows: see bpo-38324 (msg361830)
|
||||||
del known_numerics['ps_AF']
|
del known_numerics['ps_AF']
|
||||||
|
@ -179,6 +190,34 @@ class _LocaleTests(unittest.TestCase):
|
||||||
if not tested:
|
if not tested:
|
||||||
self.skipTest('no suitable locales')
|
self.skipTest('no suitable locales')
|
||||||
|
|
||||||
|
@unittest.skipUnless(nl_langinfo, "nl_langinfo is not available")
|
||||||
|
@unittest.skipUnless(hasattr(locale, 'ALT_DIGITS'), "requires locale.ALT_DIGITS")
|
||||||
|
@unittest.skipIf(
|
||||||
|
support.is_emscripten or support.is_wasi,
|
||||||
|
"musl libc issue on Emscripten, bpo-46390"
|
||||||
|
)
|
||||||
|
def test_alt_digits_nl_langinfo(self):
|
||||||
|
# Test nl_langinfo(ALT_DIGITS)
|
||||||
|
tested = False
|
||||||
|
for loc, (count, samples) in known_alt_digits.items():
|
||||||
|
with self.subTest(locale=loc):
|
||||||
|
try:
|
||||||
|
setlocale(LC_TIME, loc)
|
||||||
|
except Error:
|
||||||
|
self.skipTest(f'no locale {loc!r}')
|
||||||
|
continue
|
||||||
|
with self.subTest(locale=loc):
|
||||||
|
alt_digits = nl_langinfo(locale.ALT_DIGITS)
|
||||||
|
self.assertIsInstance(alt_digits, tuple)
|
||||||
|
if count and not alt_digits and sys.platform == 'darwin':
|
||||||
|
self.skipTest(f'ALT_DIGITS is not set for locale {loc!r} on macOS')
|
||||||
|
self.assertEqual(len(alt_digits), count)
|
||||||
|
for i in samples:
|
||||||
|
self.assertEqual(alt_digits[i], samples[i])
|
||||||
|
tested = True
|
||||||
|
if not tested:
|
||||||
|
self.skipTest('no suitable locales')
|
||||||
|
|
||||||
def test_float_parsing(self):
|
def test_float_parsing(self):
|
||||||
# Bug #1391872: Test whether float parsing is okay on European
|
# Bug #1391872: Test whether float parsing is okay on European
|
||||||
# locales.
|
# locales.
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Fix ``locale.nl_langinfo(locale.ALT_DIGITS)``. Now it returns a tuple of up
|
||||||
|
to 100 strings (an empty tuple on most locales). Previously it returned the
|
||||||
|
first item of that tuple or an empty string.
|
|
@ -666,9 +666,35 @@ _locale_nl_langinfo_impl(PyObject *module, int item)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
PyObject *unicode = PyUnicode_DecodeLocale(result, NULL);
|
PyObject *pyresult;
|
||||||
|
#ifdef ALT_DIGITS
|
||||||
|
if (item == ALT_DIGITS) {
|
||||||
|
/* The result is a sequence of up to 100 NUL-separated strings. */
|
||||||
|
const char *s = result;
|
||||||
|
int count = 0;
|
||||||
|
for (; count < 100 && *s; count++) {
|
||||||
|
s += strlen(s) + 1;
|
||||||
|
}
|
||||||
|
pyresult = PyTuple_New(count);
|
||||||
|
if (pyresult != NULL) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
PyObject *unicode = PyUnicode_DecodeLocale(result, NULL);
|
||||||
|
if (unicode == NULL) {
|
||||||
|
Py_CLEAR(pyresult);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
PyTuple_SET_ITEM(pyresult, i, unicode);
|
||||||
|
result += strlen(result) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
pyresult = PyUnicode_DecodeLocale(result, NULL);
|
||||||
|
}
|
||||||
restore_locale(oldloc);
|
restore_locale(oldloc);
|
||||||
return unicode;
|
return pyresult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PyErr_SetString(PyExc_ValueError, "unsupported langinfo constant");
|
PyErr_SetString(PyExc_ValueError, "unsupported langinfo constant");
|
||||||
|
|
Loading…
Reference in New Issue