Update locale.py from 3.13.6 and made _locale available on android (#6091)

This commit is contained in:
Shahar Naveh authored and GitHub committed 2025-08-20 13:44:57 +09:00
1 parent ddc08498cc
commit 242814fa72
4 files changed
+141 -98

No files matched your search

+61 -72
View File
@@ -25,8 +25,8 @@ import functools
# Yuck: LC_MESSAGES is non-standard: can't tell whether it exists before
# trying the import. So __all__ is also fiddled at the end of the file.
__all__ = ["getlocale", "getdefaultlocale", "getpreferredencoding", "Error",
"setlocale", "resetlocale", "localeconv", "strcoll", "strxfrm",
"str", "atof", "atoi", "format", "format_string", "currency",
"setlocale", "localeconv", "strcoll", "strxfrm",
"str", "atof", "atoi", "format_string", "currency",
"normalize", "LC_CTYPE", "LC_COLLATE", "LC_TIME", "LC_MONETARY",
"LC_NUMERIC", "LC_ALL", "CHAR_MAX", "getencoding"]
@@ -247,21 +247,6 @@ def format_string(f, val, grouping=False, monetary=False):
return new_f % val
def format(percent, value, grouping=False, monetary=False, *additional):
"""Deprecated, use format_string instead."""
import warnings
warnings.warn(
"This method will be removed in a future version of Python. "
"Use 'locale.format_string()' instead.",
DeprecationWarning, stacklevel=2
)
match = _percent_re.match(percent)
if not match or len(match.group())!= len(percent):
raise ValueError(("format() must be given exactly one %%char "
"format specifier, %s not valid") % repr(percent))
return _format(percent, value, grouping, monetary, *additional)
def currency(val, symbol=True, grouping=False, international=False):
"""Formats val according to the currency settings
in the current locale."""
@@ -556,11 +541,15 @@ def getdefaultlocale(envvars=('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE')):
"""
import warnings
warnings.warn(
"Use setlocale(), getencoding() and getlocale() instead",
DeprecationWarning, stacklevel=2
)
warnings._deprecated(
"locale.getdefaultlocale",
"{name!r} is deprecated and slated for removal in Python {remove}. "
"Use setlocale(), getencoding() and getlocale() instead.",
remove=(3, 15))
return _getdefaultlocale(envvars)
def _getdefaultlocale(envvars=('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE')):
try:
# check if it's supported by the _locale module
import _locale
@@ -625,40 +614,15 @@ def setlocale(category, locale=None):
locale = normalize(_build_localename(locale))
return _setlocale(category, locale)
def resetlocale(category=LC_ALL):
""" Sets the locale for category to the default setting.
The default setting is determined by calling
getdefaultlocale(). category defaults to LC_ALL.
"""
import warnings
warnings.warn(
'Use locale.setlocale(locale.LC_ALL, "") instead',
DeprecationWarning, stacklevel=2
)
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=DeprecationWarning)
loc = getdefaultlocale()
_setlocale(category, _build_localename(loc))
try:
from _locale import getencoding
except ImportError:
# When _locale.getencoding() is missing, locale.getencoding() uses the
# Python filesystem encoding.
def getencoding():
if hasattr(sys, 'getandroidapilevel'):
# On Android langinfo.h and CODESET are missing, and UTF-8 is
# always used in mbstowcs() and wcstombs().
return 'utf-8'
encoding = getdefaultlocale()[1]
if encoding is None:
# LANG not set, default to UTF-8
encoding = 'utf-8'
return encoding
return sys.getfilesystemencoding()
try:
CODESET
@@ -896,6 +860,28 @@ del k, v
# updated 'ca_es@valencia' -> 'ca_ES.ISO8859-15@valencia' to 'ca_ES.UTF-8@valencia'
# updated 'kk_kz' -> 'kk_KZ.RK1048' to 'kk_KZ.ptcp154'
# updated 'russian' -> 'ru_RU.ISO8859-5' to 'ru_RU.KOI8-R'
#
# SS 2025-02-04:
# Updated alias mapping with glibc 2.41 supported locales and the latest
# X lib alias mapping.
#
# These are the differences compared to the old mapping (Python 3.13.1
# and older):
#
# updated 'c.utf8' -> 'C.UTF-8' to 'en_US.UTF-8'
# updated 'de_it' -> 'de_IT.ISO8859-1' to 'de_IT.UTF-8'
# removed 'de_li.utf8'
# updated 'en_il' -> 'en_IL.UTF-8' to 'en_IL.ISO8859-1'
# removed 'english.iso88591'
# updated 'es_cu' -> 'es_CU.UTF-8' to 'es_CU.ISO8859-1'
# updated 'russian' -> 'ru_RU.KOI8-R' to 'ru_RU.ISO8859-5'
# updated 'sr@latn' -> 'sr_CS.UTF-8@latin' to 'sr_RS.UTF-8@latin'
# removed 'univ'
# removed 'universal'
#
# SS 2025-06-10:
# Remove 'c.utf8' -> 'en_US.UTF-8' because 'en_US.UTF-8' does not exist
# on all platforms.
locale_alias = {
'a3': 'az_AZ.KOI8-C',
@@ -975,7 +961,6 @@ locale_alias = {
'c.ascii': 'C',
'c.en': 'C',
'c.iso88591': 'en_US.ISO8859-1',
'c.utf8': 'en_US.UTF-8',
'c_c': 'C',
'c_c.c': 'C',
'ca': 'ca_ES.ISO8859-1',
@@ -992,6 +977,7 @@ locale_alias = {
'chr_us': 'chr_US.UTF-8',
'ckb_iq': 'ckb_IQ.UTF-8',
'cmn_tw': 'cmn_TW.UTF-8',
'crh_ru': 'crh_RU.UTF-8',
'crh_ua': 'crh_UA.UTF-8',
'croatian': 'hr_HR.ISO8859-2',
'cs': 'cs_CZ.ISO8859-2',
@@ -1013,11 +999,12 @@ locale_alias = {
'de_be': 'de_BE.ISO8859-1',
'de_ch': 'de_CH.ISO8859-1',
'de_de': 'de_DE.ISO8859-1',
'de_it': 'de_IT.ISO8859-1',
'de_li.utf8': 'de_LI.UTF-8',
'de_it': 'de_IT.UTF-8',
'de_li': 'de_LI.ISO8859-1',
'de_lu': 'de_LU.ISO8859-1',
'deutsch': 'de_DE.ISO8859-1',
'doi_in': 'doi_IN.UTF-8',
'dsb_de': 'dsb_DE.UTF-8',
'dutch': 'nl_NL.ISO8859-1',
'dutch.iso88591': 'nl_BE.ISO8859-1',
'dv_mv': 'dv_MV.UTF-8',
@@ -1040,7 +1027,7 @@ locale_alias = {
'en_gb': 'en_GB.ISO8859-1',
'en_hk': 'en_HK.ISO8859-1',
'en_ie': 'en_IE.ISO8859-1',
'en_il': 'en_IL.UTF-8',
'en_il': 'en_IL.ISO8859-1',
'en_in': 'en_IN.ISO8859-1',
'en_ng': 'en_NG.UTF-8',
'en_nz': 'en_NZ.ISO8859-1',
@@ -1056,7 +1043,6 @@ locale_alias = {
'en_zw.utf8': 'en_ZS.UTF-8',
'eng_gb': 'en_GB.ISO8859-1',
'english': 'en_EN.ISO8859-1',
'english.iso88591': 'en_US.ISO8859-1',
'english_uk': 'en_GB.ISO8859-1',
'english_united-states': 'en_US.ISO8859-1',
'english_united-states.437': 'C',
@@ -1072,7 +1058,7 @@ locale_alias = {
'es_cl': 'es_CL.ISO8859-1',
'es_co': 'es_CO.ISO8859-1',
'es_cr': 'es_CR.ISO8859-1',
'es_cu': 'es_CU.UTF-8',
'es_cu': 'es_CU.ISO8859-1',
'es_do': 'es_DO.ISO8859-1',
'es_ec': 'es_EC.ISO8859-1',
'es_es': 'es_ES.ISO8859-1',
@@ -1122,6 +1108,7 @@ locale_alias = {
'ga_ie': 'ga_IE.ISO8859-1',
'galego': 'gl_ES.ISO8859-1',
'galician': 'gl_ES.ISO8859-1',
'gbm_in': 'gbm_IN.UTF-8',
'gd': 'gd_GB.ISO8859-1',
'gd_gb': 'gd_GB.ISO8859-1',
'ger_de': 'de_DE.ISO8859-1',
@@ -1162,6 +1149,7 @@ locale_alias = {
'icelandic': 'is_IS.ISO8859-1',
'id': 'id_ID.ISO8859-1',
'id_id': 'id_ID.ISO8859-1',
'ie': 'ie.UTF-8',
'ig_ng': 'ig_NG.UTF-8',
'ik_ca': 'ik_CA.UTF-8',
'in': 'id_ID.ISO8859-1',
@@ -1216,6 +1204,7 @@ locale_alias = {
'ks_in': 'ks_IN.UTF-8',
'ks_in@devanagari.utf8': 'ks_IN.UTF-8@devanagari',
'ku_tr': 'ku_TR.ISO8859-9',
'kv_ru': 'kv_RU.UTF-8',
'kw': 'kw_GB.ISO8859-1',
'kw_gb': 'kw_GB.ISO8859-1',
'ky': 'ky_KG.UTF-8',
@@ -1234,6 +1223,7 @@ locale_alias = {
'lo_la.mulelao1': 'lo_LA.MULELAO-1',
'lt': 'lt_LT.ISO8859-13',
'lt_lt': 'lt_LT.ISO8859-13',
'ltg_lv.utf8': 'ltg_LV.UTF-8',
'lv': 'lv_LV.ISO8859-13',
'lv_lv': 'lv_LV.ISO8859-13',
'lzh_tw': 'lzh_TW.UTF-8',
@@ -1241,6 +1231,7 @@ locale_alias = {
'mai': 'mai_IN.UTF-8',
'mai_in': 'mai_IN.UTF-8',
'mai_np': 'mai_NP.UTF-8',
'mdf_ru': 'mdf_RU.UTF-8',
'mfe_mu': 'mfe_MU.UTF-8',
'mg_mg': 'mg_MG.ISO8859-15',
'mhr_ru': 'mhr_RU.UTF-8',
@@ -1254,6 +1245,7 @@ locale_alias = {
'ml_in': 'ml_IN.UTF-8',
'mn_mn': 'mn_MN.UTF-8',
'mni_in': 'mni_IN.UTF-8',
'mnw_mm': 'mnw_MM.UTF-8',
'mr': 'mr_IN.UTF-8',
'mr_in': 'mr_IN.UTF-8',
'ms': 'ms_MY.ISO8859-1',
@@ -1322,6 +1314,7 @@ locale_alias = {
'pt_pt': 'pt_PT.ISO8859-1',
'quz_pe': 'quz_PE.UTF-8',
'raj_in': 'raj_IN.UTF-8',
'rif_ma': 'rif_MA.UTF-8',
'ro': 'ro_RO.ISO8859-2',
'ro_ro': 'ro_RO.ISO8859-2',
'romanian': 'ro_RO.ISO8859-2',
@@ -1329,12 +1322,14 @@ locale_alias = {
'ru_ru': 'ru_RU.UTF-8',
'ru_ua': 'ru_UA.KOI8-U',
'rumanian': 'ro_RO.ISO8859-2',
'russian': 'ru_RU.KOI8-R',
'russian': 'ru_RU.ISO8859-5',
'rw': 'rw_RW.ISO8859-1',
'rw_rw': 'rw_RW.ISO8859-1',
'sa_in': 'sa_IN.UTF-8',
'sah_ru': 'sah_RU.UTF-8',
'sat_in': 'sat_IN.UTF-8',
'sc_it': 'sc_IT.UTF-8',
'scn_it': 'scn_IT.UTF-8',
'sd': 'sd_IN.UTF-8',
'sd_in': 'sd_IN.UTF-8',
'sd_in@devanagari.utf8': 'sd_IN.UTF-8@devanagari',
@@ -1376,7 +1371,7 @@ locale_alias = {
'sq_mk': 'sq_MK.UTF-8',
'sr': 'sr_RS.UTF-8',
'sr@cyrillic': 'sr_RS.UTF-8',
'sr@latn': 'sr_CS.UTF-8@latin',
'sr@latn': 'sr_RS.UTF-8@latin',
'sr_cs': 'sr_CS.UTF-8',
'sr_cs.iso88592@latn': 'sr_CS.ISO8859-2',
'sr_cs@latn': 'sr_CS.UTF-8@latin',
@@ -1395,14 +1390,17 @@ locale_alias = {
'sr_yu@cyrillic': 'sr_RS.UTF-8',
'ss': 'ss_ZA.ISO8859-1',
'ss_za': 'ss_ZA.ISO8859-1',
'ssy_er': 'ssy_ER.UTF-8',
'st': 'st_ZA.ISO8859-1',
'st_za': 'st_ZA.ISO8859-1',
'su_id': 'su_ID.UTF-8',
'sv': 'sv_SE.ISO8859-1',
'sv_fi': 'sv_FI.ISO8859-1',
'sv_se': 'sv_SE.ISO8859-1',
'sw_ke': 'sw_KE.UTF-8',
'sw_tz': 'sw_TZ.UTF-8',
'swedish': 'sv_SE.ISO8859-1',
'syr': 'syr.UTF-8',
'szl_pl': 'szl_PL.UTF-8',
'ta': 'ta_IN.TSCII-0',
'ta_in': 'ta_IN.TSCII-0',
@@ -1429,6 +1427,7 @@ locale_alias = {
'tn': 'tn_ZA.ISO8859-15',
'tn_za': 'tn_ZA.ISO8859-15',
'to_to': 'to_TO.UTF-8',
'tok': 'tok.UTF-8',
'tpi_pg': 'tpi_PG.UTF-8',
'tr': 'tr_TR.ISO8859-9',
'tr_cy': 'tr_CY.ISO8859-9',
@@ -1443,8 +1442,7 @@ locale_alias = {
'ug_cn': 'ug_CN.UTF-8',
'uk': 'uk_UA.KOI8-U',
'uk_ua': 'uk_UA.KOI8-U',
'univ': 'en_US.utf',
'universal': 'en_US.utf',
'univ.utf8': 'en_US.UTF-8',
'universal.utf8@ucs4': 'en_US.UTF-8',
'unm_us': 'unm_US.UTF-8',
'ur': 'ur_PK.CP1256',
@@ -1473,6 +1471,7 @@ locale_alias = {
'yo_ng': 'yo_NG.UTF-8',
'yue_hk': 'yue_HK.UTF-8',
'yuw_pg': 'yuw_PG.UTF-8',
'zgh_ma': 'zgh_MA.UTF-8',
'zh': 'zh_CN.eucCN',
'zh_cn': 'zh_CN.gb2312',
'zh_cn.big5': 'zh_TW.big5',
@@ -1496,7 +1495,8 @@ locale_alias = {
# to include every locale up to Windows Vista.
#
# NOTE: this mapping is incomplete. If your language is missing, please
# submit a bug report to the Python bug tracker at http://bugs.python.org/
# submit a bug report as detailed in the Python devguide at:
# https://devguide.python.org/triage/issue-tracker/
# Make sure you include the missing language identifier and the suggested
# locale code.
#
@@ -1742,17 +1742,6 @@ def _print_locale():
print(' Encoding: ', enc or '(undefined)')
print()
print()
print('Locale settings after calling resetlocale():')
print('-'*72)
resetlocale()
for name,category in categories.items():
print(name, '...')
lang, enc = getlocale(category)
print(' Language: ', lang or '(undefined)')
print(' Encoding: ', enc or '(undefined)')
print()
try:
setlocale(LC_ALL, "")
except:
+69 -21
View File
@@ -1,8 +1,11 @@
from decimal import Decimal
from test.support import verbose, is_android, is_emscripten, is_wasi
from test.support.warnings_helper import check_warnings
from test.support.import_helper import import_fresh_module
from unittest import mock
import unittest
import locale
import os
import sys
import codecs
@@ -353,6 +356,8 @@ class TestEnUSCollation(BaseLocalizedTest, TestCollation):
is_emscripten or is_wasi,
"musl libc issue on Emscripten/WASI, bpo-46390"
)
@unittest.skipIf(sys.platform.startswith("netbsd"),
"gh-124108: NetBSD doesn't support UTF-8 for LC_COLLATE")
def test_strcoll_with_diacritic(self):
self.assertLess(locale.strcoll('à', 'b'), 0)
@@ -362,6 +367,8 @@ class TestEnUSCollation(BaseLocalizedTest, TestCollation):
is_emscripten or is_wasi,
"musl libc issue on Emscripten/WASI, bpo-46390"
)
@unittest.skipIf(sys.platform.startswith("netbsd"),
"gh-124108: NetBSD doesn't support UTF-8 for LC_COLLATE")
def test_strxfrm_with_diacritic(self):
self.assertLess(locale.strxfrm('à'), locale.strxfrm('b'))
@@ -382,6 +389,10 @@ class NormalizeTest(unittest.TestCase):
self.check('c', 'C')
self.check('posix', 'C')
def test_c_utf8(self):
self.check('c.utf8', 'C.UTF-8')
self.check('C.UTF-8', 'C.UTF-8')
def test_english(self):
self.check('en', 'en_US.ISO8859-1')
self.check('EN', 'en_US.ISO8859-1')
@@ -477,6 +488,55 @@ class NormalizeTest(unittest.TestCase):
self.check('jp_jp', 'ja_JP.eucJP')
class TestRealLocales(unittest.TestCase):
def setUp(self):
oldlocale = locale.setlocale(locale.LC_CTYPE)
self.addCleanup(locale.setlocale, locale.LC_CTYPE, oldlocale)
def test_getsetlocale_issue1813(self):
# Issue #1813: setting and getting the locale under a Turkish locale
try:
locale.setlocale(locale.LC_CTYPE, 'tr_TR')
except locale.Error:
# Unsupported locale on this system
self.skipTest('test needs Turkish locale')
loc = locale.getlocale(locale.LC_CTYPE)
if verbose:
print('testing with %a' % (loc,), end=' ', flush=True)
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))
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; Error not raised")
@unittest.skipUnless(os.name == 'nt', 'requires Windows')
def test_setlocale_long_encoding(self):
with self.assertRaises(locale.Error):
locale.setlocale(locale.LC_CTYPE, 'English.%016d' % 1252)
locale.setlocale(locale.LC_CTYPE, 'English.%015d' % 1252)
loc = locale.setlocale(locale.LC_ALL)
self.assertIn('.1252', loc)
loc2 = loc.replace('.1252', '.%016d' % 1252, 1)
with self.assertRaises(locale.Error):
locale.setlocale(locale.LC_ALL, loc2)
loc2 = loc.replace('.1252', '.%015d' % 1252, 1)
locale.setlocale(locale.LC_ALL, loc2)
# gh-137273: Debug assertion failure on Windows for long encoding.
with self.assertRaises(locale.Error):
locale.setlocale(locale.LC_CTYPE, 'en_US.' + 'x'*16)
locale.setlocale(locale.LC_CTYPE, 'en_US.UTF-8')
loc = locale.setlocale(locale.LC_ALL)
self.assertIn('.UTF-8', loc)
loc2 = loc.replace('.UTF-8', '.' + 'x'*16, 1)
with self.assertRaises(locale.Error):
locale.setlocale(locale.LC_ALL, loc2)
class TestMiscellaneous(unittest.TestCase):
def test_defaults_UTF8(self):
# Issue #18378: on (at least) macOS setting LC_CTYPE to "UTF-8" is
@@ -523,6 +583,15 @@ class TestMiscellaneous(unittest.TestCase):
# make sure it is valid
codecs.lookup(enc)
def test_getencoding_fallback(self):
# When _locale.getencoding() is missing, locale.getencoding() uses
# the Python filesystem
encoding = 'FALLBACK_ENCODING'
with mock.patch.object(sys, 'getfilesystemencoding',
return_value=encoding):
locale_fallback = import_fresh_module('locale', blocked=['_locale'])
self.assertEqual(locale_fallback.getencoding(), encoding)
def test_getpreferredencoding(self):
# Invoke getpreferredencoding to make sure it does not cause exceptions.
enc = locale.getpreferredencoding()
@@ -546,27 +615,6 @@ class TestMiscellaneous(unittest.TestCase):
# crasher from bug #7419
self.assertRaises(locale.Error, locale.setlocale, 12345)
def test_getsetlocale_issue1813(self):
# Issue #1813: setting and getting the locale under a Turkish locale
oldlocale = locale.setlocale(locale.LC_CTYPE)
self.addCleanup(locale.setlocale, locale.LC_CTYPE, oldlocale)
try:
locale.setlocale(locale.LC_CTYPE, 'tr_TR')
except locale.Error:
# Unsupported locale on this system
self.skipTest('test needs Turkish locale')
loc = locale.getlocale(locale.LC_CTYPE)
if verbose:
print('testing with %a' % (loc,), end=' ', flush=True)
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):
with self.assertRaises(TypeError):
locale.setlocale(locale.LC_ALL, b'fi_FI')
+4 -2
View File
@@ -31,8 +31,10 @@ mod sha3;
mod sha512;
mod json;
#[cfg(not(any(target_os = "ios", target_os = "android", target_arch = "wasm32")))]
#[cfg(not(any(target_os = "ios", target_arch = "wasm32")))]
mod locale;
mod math;
#[cfg(unix)]
mod mmap;
@@ -198,7 +200,7 @@ pub fn get_module_inits() -> impl Iterator<Item = (Cow<'static, str>, StdlibInit
{
"_uuid" => uuid::make_module,
}
#[cfg(not(any(target_os = "ios", target_os = "android", target_arch = "wasm32")))]
#[cfg(not(any(target_os = "ios", target_arch = "wasm32")))]
{
"_locale" => locale::make_module,
}
+7 -3
View File
@@ -61,11 +61,15 @@ mod _locale {
ABDAY_1, ABDAY_2, ABDAY_3, ABDAY_4, ABDAY_5, ABDAY_6, ABDAY_7, ABMON_1, ABMON_2, ABMON_3,
ABMON_4, ABMON_5, ABMON_6, ABMON_7, ABMON_8, ABMON_9, ABMON_10, ABMON_11, ABMON_12,
ALT_DIGITS, AM_STR, CODESET, CRNCYSTR, D_FMT, D_T_FMT, DAY_1, DAY_2, DAY_3, DAY_4, DAY_5,
DAY_6, DAY_7, ERA, ERA_D_FMT, ERA_D_T_FMT, ERA_T_FMT, LC_MESSAGES, MON_1, MON_2, MON_3,
MON_4, MON_5, MON_6, MON_7, MON_8, MON_9, MON_10, MON_11, MON_12, NOEXPR, PM_STR,
RADIXCHAR, T_FMT, T_FMT_AMPM, THOUSEP, YESEXPR,
DAY_6, DAY_7, ERA, ERA_D_FMT, ERA_D_T_FMT, ERA_T_FMT, MON_1, MON_2, MON_3, MON_4, MON_5,
MON_6, MON_7, MON_8, MON_9, MON_10, MON_11, MON_12, NOEXPR, PM_STR, RADIXCHAR, T_FMT,
T_FMT_AMPM, THOUSEP, YESEXPR,
};
#[cfg(all(unix, not(any(target_os = "ios", target_os = "redox"))))]
#[pyattr]
use libc::LC_MESSAGES;
#[pyattr]
use libc::{LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME};