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:
Serhiy Storchaka 2024-10-09 11:42:08 +03:00 committed by GitHub
parent 92760bd85b
commit 21c04e1a97
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 74 additions and 6 deletions

View File

@ -158,7 +158,8 @@ The :mod:`locale` module defines the following exception and functions:
.. 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
across platforms. The possible argument values are numbers, for which
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
Get a representation of up to 100 values used to represent the values
0 to 99.
Get a tuple of up to 100 strings used to represent the values 0 to 99.
The function temporarily sets the ``LC_CTYPE`` locale to the locale
of the category that determines the requested value (``LC_TIME``,

View File

@ -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:
from _locale import (RADIXCHAR, THOUSEP, nl_langinfo)
except ImportError:
@ -74,6 +74,17 @@ known_numerics = {
'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':
# ps_AF doesn't work on Windows: see bpo-38324 (msg361830)
del known_numerics['ps_AF']
@ -179,6 +190,34 @@ class _LocaleTests(unittest.TestCase):
if not tested:
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):
# Bug #1391872: Test whether float parsing is okay on European
# locales.

View File

@ -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.

View File

@ -666,9 +666,35 @@ _locale_nl_langinfo_impl(PyObject *module, int item)
{
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);
return unicode;
return pyresult;
}
}
PyErr_SetString(PyExc_ValueError, "unsupported langinfo constant");