Skip to content

Commit ffd9b41

Browse files
gh-151316: Prefer UTF-8 for the en_IN locale alias
X11 locale.alias mapped en_IN to ISO8859-1, so getlocale() invented a codeset that does not exist on modern UTF-8-only systems and broke setlocale(getlocale()) round-trips. Prefer en_IN.UTF-8 (mirroring other modernized aliases) and cover the round-trip in tests. Signed-off-by: Sankalp Thakur <sankalphimself@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent a7541c3 commit ffd9b41

4 files changed

Lines changed: 52 additions & 1 deletion

File tree

Lib/locale.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,11 @@ def getpreferredencoding(do_setlocale=True):
921921
#
922922
# removed 'el_gr@euro'
923923
# removed 'uz_uz@cyrillic'
924+
#
925+
# gh-151316:
926+
# Prefer UTF-8 for en_IN. X11 locale.alias maps en_IN to ISO8859-1 and that
927+
# entry overrides glibc's en_IN/UTF-8 during makelocalealias regeneration,
928+
# so hardcode the modern codeset (mirroring the c.utf8 carve-out).
924929

925930
locale_alias = {
926931
'a3': 'az_AZ.KOI8-C',
@@ -1066,7 +1071,7 @@ def getpreferredencoding(do_setlocale=True):
10661071
'en_hk': 'en_HK.ISO8859-1',
10671072
'en_ie': 'en_IE.ISO8859-1',
10681073
'en_il': 'en_IL.ISO8859-1',
1069-
'en_in': 'en_IN.ISO8859-1',
1074+
'en_in': 'en_IN.UTF-8',
10701075
'en_ng': 'en_NG.UTF-8',
10711076
'en_nz': 'en_NZ.ISO8859-1',
10721077
'en_ph': 'en_PH.ISO8859-1',

Lib/test/test_locale.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,13 @@ def test_english(self):
417417
self.check('english', 'en_EN.ISO8859-1')
418418
self.check('english_uk.ascii', 'en_GB.ISO8859-1')
419419

420+
def test_en_in_utf8(self):
421+
# gh-151316: en_IN is UTF-8 on modern glibc; do not invent ISO8859-1.
422+
self.check('en_IN', 'en_IN.UTF-8')
423+
self.check('en_in', 'en_IN.UTF-8')
424+
self.assertEqual(locale._parse_localename('en_IN'), ('en_IN', 'UTF-8'))
425+
self.assertEqual(locale._parse_localename('en_in'), ('en_IN', 'UTF-8'))
426+
420427
def test_hyphenated_encoding(self):
421428
self.check('az_AZ.iso88599e', 'az_AZ.ISO8859-9E')
422429
self.check('az_AZ.ISO8859-9E', 'az_AZ.ISO8859-9E')
@@ -642,6 +649,38 @@ def test_getlocale_with_modifier(self, localename, localetuple):
642649
self.assertEqual(locale.getlocale(locale.LC_CTYPE), localetuple)
643650

644651

652+
class TestEnINLocale(unittest.TestCase):
653+
"""gh-151316: en_IN must round-trip without inventing ISO8859-1."""
654+
655+
def setUp(self):
656+
self.oldlocale = locale.setlocale(locale.LC_CTYPE)
657+
self.addCleanup(locale.setlocale, locale.LC_CTYPE, self.oldlocale)
658+
659+
def test_getlocale_setlocale_roundtrip(self):
660+
try:
661+
locale.setlocale(locale.LC_CTYPE, 'en_IN')
662+
except locale.Error as exc:
663+
self.skipTest(str(exc))
664+
loc = locale.getlocale(locale.LC_CTYPE)
665+
self.assertEqual(loc[0], 'en_IN')
666+
self.assertNotEqual(loc[1], 'ISO8859-1')
667+
locale.setlocale(locale.LC_CTYPE, loc)
668+
self.assertEqual(locale.getlocale(locale.LC_CTYPE), loc)
669+
670+
def test_setlocale_from_getlocale_tuple(self):
671+
# Reproduces the issue report: setlocale(LC_*, getlocale()).
672+
try:
673+
locale.setlocale(locale.LC_CTYPE, 'en_IN.UTF-8')
674+
except locale.Error:
675+
try:
676+
locale.setlocale(locale.LC_CTYPE, 'en_IN')
677+
except locale.Error as exc:
678+
self.skipTest(str(exc))
679+
loc = locale.getlocale(locale.LC_CTYPE)
680+
locale.setlocale(locale.LC_CTYPE, loc)
681+
self.assertEqual(locale.getlocale(locale.LC_CTYPE)[0], 'en_IN')
682+
683+
645684
class TestMiscellaneous(unittest.TestCase):
646685
def test_defaults_UTF8(self):
647686
# Issue #18378: on (at least) macOS setting LC_CTYPE to "UTF-8" is
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:mod:`locale` now maps the ``en_IN`` locale alias to ``en_IN.UTF-8``
2+
instead of the obsolete X11 ``ISO8859-1`` codeset. This restores
3+
``setlocale(getlocale())`` round-trips on modern glibc systems where
4+
``en_IN`` is UTF-8-only.

Tools/i18n/makelocalealias.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ def check(data):
154154
# Hardcode 'c.utf8' -> 'C.UTF-8' because 'en_US.UTF-8' does not exist
155155
# on all platforms.
156156
data['c.utf8'] = 'C.UTF-8'
157+
# Hardcode 'en_in' -> 'en_IN.UTF-8'. X11 locale.alias still maps en_IN to
158+
# ISO8859-1 and would otherwise override glibc's en_IN/UTF-8 (gh-151316).
159+
data['en_in'] = 'en_IN.UTF-8'
157160
while True:
158161
# Repeat optimization while the size is decreased.
159162
n = len(data)

0 commit comments

Comments
 (0)