Update locale from CPython 3.11.2

This commit is contained in:
CPython Devleopers
2023-02-24 01:15:34 +09:00
committed by Jeong YunWon
parent cfa9de4047
commit 7de056437f
2 changed files with 146 additions and 56 deletions

138
Lib/locale.py vendored
View File

@@ -28,7 +28,7 @@ __all__ = ["getlocale", "getdefaultlocale", "getpreferredencoding", "Error",
"setlocale", "resetlocale", "localeconv", "strcoll", "strxfrm",
"str", "atof", "atoi", "format", "format_string", "currency",
"normalize", "LC_CTYPE", "LC_COLLATE", "LC_TIME", "LC_MONETARY",
"LC_NUMERIC", "LC_ALL", "CHAR_MAX"]
"LC_NUMERIC", "LC_ALL", "CHAR_MAX", "getencoding"]
def _strcoll(a,b):
""" strcoll(string,string) -> int.
@@ -185,8 +185,14 @@ def _format(percent, value, grouping=False, monetary=False, *additional):
formatted = percent % ((value,) + additional)
else:
formatted = percent % value
if percent[-1] in 'eEfFgGdiu':
formatted = _localize(formatted, grouping, monetary)
return formatted
# Transform formatted as locale number according to the locale settings
def _localize(formatted, grouping=False, monetary=False):
# floats and decimal ints need special action!
if percent[-1] in 'eEfFgG':
if '.' in formatted:
seps = 0
parts = formatted.split('.')
if grouping:
@@ -196,7 +202,7 @@ def _format(percent, value, grouping=False, monetary=False, *additional):
formatted = decimal_point.join(parts)
if seps:
formatted = _strip_padding(formatted, seps)
elif percent[-1] in 'diu':
else:
seps = 0
if grouping:
formatted, seps = _group(formatted, monetary=monetary)
@@ -267,7 +273,7 @@ def currency(val, symbol=True, grouping=False, international=False):
raise ValueError("Currency formatting is not possible using "
"the 'C' locale.")
s = _format('%%.%if' % digits, abs(val), grouping, monetary=True)
s = _localize(f'{abs(val):.{digits}f}', grouping, monetary=True)
# '<' and '>' are markers if the sign must be inserted between symbol and value
s = '<' + s + '>'
@@ -279,6 +285,8 @@ def currency(val, symbol=True, grouping=False, international=False):
if precedes:
s = smb + (separated and ' ' or '') + s
else:
if international and smb[-1] == ' ':
smb = smb[:-1]
s = s + (separated and ' ' or '') + smb
sign_pos = conv[val<0 and 'n_sign_posn' or 'p_sign_posn']
@@ -321,6 +329,10 @@ def delocalize(string):
string = string.replace(dd, '.')
return string
def localize(string, grouping=False, monetary=False):
"""Parses a string as locale number according to the locale settings."""
return _localize(string, grouping, monetary)
def atof(string, func=float):
"Parses a string as a float according to the locale settings."
return func(delocalize(string))
@@ -492,6 +504,10 @@ def _parse_localename(localename):
return tuple(code.split('.')[:2])
elif code == 'C':
return None, None
elif code == 'UTF-8':
# On macOS "LC_CTYPE=UTF-8" is a valid locale setting
# for getting UTF-8 handling for text.
return None, 'UTF-8'
raise ValueError('unknown locale: %s' % localename)
def _build_localename(localetuple):
@@ -539,6 +555,12 @@ def getdefaultlocale(envvars=('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE')):
"""
import warnings
warnings.warn(
"Use setlocale(), getencoding() and getlocale() instead",
DeprecationWarning, stacklevel=2
)
try:
# check if it's supported by the _locale module
import _locale
@@ -611,55 +633,72 @@ def resetlocale(category=LC_ALL):
getdefaultlocale(). category defaults to LC_ALL.
"""
_setlocale(category, _build_localename(getdefaultlocale()))
import warnings
warnings.warn(
'Use locale.setlocale(locale.LC_ALL, "") instead',
DeprecationWarning, stacklevel=2
)
if sys.platform.startswith("win"):
# On Win32, this will return the ANSI code page
def getpreferredencoding(do_setlocale = True):
"""Return the charset that the user is likely using."""
if sys.flags.utf8_mode:
return 'UTF-8'
import _bootlocale
return _bootlocale.getpreferredencoding(False)
else:
# On Unix, if CODESET is available, use that.
try:
CODESET
except NameError:
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=DeprecationWarning)
loc = getdefaultlocale()
_setlocale(category, _build_localename(loc))
try:
from _locale import getencoding
except ImportError:
def getencoding():
if hasattr(sys, 'getandroidapilevel'):
# On Android langinfo.h and CODESET are missing, and UTF-8 is
# always used in mbstowcs() and wcstombs().
def getpreferredencoding(do_setlocale = True):
return 'UTF-8'
else:
# Fall back to parsing environment variables :-(
def getpreferredencoding(do_setlocale = True):
"""Return the charset that the user is likely using,
by looking at environment variables."""
if sys.flags.utf8_mode:
return 'UTF-8'
res = getdefaultlocale()[1]
if res is None:
# LANG not set, default conservatively to ASCII
res = 'ascii'
return res
else:
def getpreferredencoding(do_setlocale = True):
"""Return the charset that the user is likely using,
according to the system configuration."""
if sys.flags.utf8_mode:
return 'UTF-8'
import _bootlocale
if do_setlocale:
oldloc = setlocale(LC_CTYPE)
try:
setlocale(LC_CTYPE, "")
except Error:
pass
result = _bootlocale.getpreferredencoding(False)
if do_setlocale:
setlocale(LC_CTYPE, oldloc)
return result
return 'utf-8'
encoding = getdefaultlocale()[1]
if encoding is None:
# LANG not set, default to UTF-8
encoding = 'utf-8'
return encoding
try:
CODESET
except NameError:
def getpreferredencoding(do_setlocale=True):
"""Return the charset that the user is likely using."""
if sys.flags.warn_default_encoding:
import warnings
warnings.warn(
"UTF-8 Mode affects locale.getpreferredencoding(). Consider locale.getencoding() instead.",
EncodingWarning, 2)
if sys.flags.utf8_mode:
return 'utf-8'
return getencoding()
else:
# On Unix, if CODESET is available, use that.
def getpreferredencoding(do_setlocale=True):
"""Return the charset that the user is likely using,
according to the system configuration."""
if sys.flags.warn_default_encoding:
import warnings
warnings.warn(
"UTF-8 Mode affects locale.getpreferredencoding(). Consider locale.getencoding() instead.",
EncodingWarning, 2)
if sys.flags.utf8_mode:
return 'utf-8'
if not do_setlocale:
return getencoding()
old_loc = setlocale(LC_CTYPE)
try:
try:
setlocale(LC_CTYPE, "")
except Error:
pass
return getencoding()
finally:
setlocale(LC_CTYPE, old_loc)
### Database
@@ -734,6 +773,7 @@ locale_encoding_alias = {
for k, v in sorted(locale_encoding_alias.items()):
k = k.replace('_', '')
locale_encoding_alias.setdefault(k, v)
del k, v
#
# The locale_alias table maps lowercase alias names to C locale names

View File

@@ -1,4 +1,5 @@
from test.support import verbose, is_android
from decimal import Decimal
from test.support import verbose, is_android, is_emscripten, is_wasi
from test.support.warnings_helper import check_warnings
import unittest
import locale
@@ -335,8 +336,7 @@ class TestFrFRNumberFormatting(FrFRCookedTest, BaseFormattingTest):
euro = '\u20ac'
self._test_currency(50000, "50000,00 " + euro)
self._test_currency(50000, "50 000,00 " + euro, grouping=True)
# XXX is the trailing space a bug?
self._test_currency(50000, "50 000,00 EUR ",
self._test_currency(50000, "50 000,00 EUR",
grouping=True, international=True)
@@ -367,7 +367,7 @@ class TestEnUSCollation(BaseLocalizedTest, TestCollation):
locale_type = locale.LC_ALL
def setUp(self):
enc = codecs.lookup(locale.getpreferredencoding(False) or 'ascii').name
enc = codecs.lookup(locale.getencoding() or 'ascii').name
if enc not in ('utf-8', 'iso8859-1', 'cp1252'):
raise unittest.SkipTest('encoding not suitable')
if enc != 'iso8859-1' and (sys.platform == 'darwin' or is_android or
@@ -377,11 +377,19 @@ class TestEnUSCollation(BaseLocalizedTest, TestCollation):
@unittest.skipIf(sys.platform.startswith('aix'),
'bpo-29972: broken test on AIX')
@unittest.skipIf(
is_emscripten or is_wasi,
"musl libc issue on Emscripten/WASI, bpo-46390"
)
def test_strcoll_with_diacritic(self):
self.assertLess(locale.strcoll('à', 'b'), 0)
@unittest.skipIf(sys.platform.startswith('aix'),
'bpo-29972: broken test on AIX')
@unittest.skipIf(
is_emscripten or is_wasi,
"musl libc issue on Emscripten/WASI, bpo-46390"
)
def test_strxfrm_with_diacritic(self):
self.assertLess(locale.strxfrm('à'), locale.strxfrm('b'))
@@ -502,7 +510,7 @@ class TestMiscellaneous(unittest.TestCase):
@unittest.expectedFailure
def test_defaults_UTF8(self):
# Issue #18378: on (at least) macOS setting LC_CTYPE to "UTF-8" is
# valid. Futhermore LC_CTYPE=UTF is used by the UTF-8 locale coercing
# valid. Furthermore LC_CTYPE=UTF is used by the UTF-8 locale coercing
# during interpreter startup (on macOS).
import _locale
import os
@@ -524,7 +532,8 @@ class TestMiscellaneous(unittest.TestCase):
os.environ['LC_CTYPE'] = 'UTF-8'
self.assertEqual(locale.getdefaultlocale(), (None, 'UTF-8'))
with check_warnings(('', DeprecationWarning)):
self.assertEqual(locale.getdefaultlocale(), (None, 'UTF-8'))
finally:
for k in orig_env:
@@ -536,6 +545,14 @@ class TestMiscellaneous(unittest.TestCase):
if orig_getlocale is not None:
_locale._getdefaultlocale = orig_getlocale
def test_getencoding(self):
# Invoke getencoding to make sure it does not cause exceptions.
enc = locale.getencoding()
self.assertIsInstance(enc, str)
self.assertNotEqual(enc, "")
# make sure it is valid
codecs.lookup(enc)
def test_getpreferredencoding(self):
# Invoke getpreferredencoding to make sure it does not cause exceptions.
enc = locale.getpreferredencoding()
@@ -573,7 +590,13 @@ class TestMiscellaneous(unittest.TestCase):
loc = locale.getlocale(locale.LC_CTYPE)
if verbose:
print('testing with %a' % (loc,), end=' ', flush=True)
locale.setlocale(locale.LC_CTYPE, loc)
try:
locale.setlocale(locale.LC_CTYPE, loc)
except locale.Error as exc:
# bpo-37945: setlocale(LC_CTYPE) fails with getlocale(LC_CTYPE)
# and the tr_TR locale on Windows. getlocale() builds a locale
# which is not recognize by setlocale().
self.skipTest(f"setlocale(LC_CTYPE, {loc!r}) failed: {exc!r}")
self.assertEqual(loc, locale.getlocale(locale.LC_CTYPE))
def test_invalid_locale_format_in_localetuple(self):
@@ -639,5 +662,32 @@ class TestfrFRDelocalizeTest(FrFRCookedTest, BaseDelocalizeTest):
self._test_atoi('50 000', 50000)
class BaseLocalizeTest(BaseLocalizedTest):
def _test_localize(self, value, out, grouping=False):
self.assertEqual(locale.localize(value, grouping=grouping), out)
class TestEnUSLocalize(EnUSCookedTest, BaseLocalizeTest):
def test_localize(self):
self._test_localize('50000.00', '50000.00')
self._test_localize(
'{0:.16f}'.format(Decimal('1.15')), '1.1500000000000000')
class TestCLocalize(CCookedTest, BaseLocalizeTest):
def test_localize(self):
self._test_localize('50000.00', '50000.00')
class TestfrFRLocalize(FrFRCookedTest, BaseLocalizeTest):
def test_localize(self):
self._test_localize('50000.00', '50000,00')
self._test_localize('50000.00', '50 000,00', grouping=True)
if __name__ == '__main__':
unittest.main()