diff --git a/Lib/test/test_unicodedata.py b/Lib/test/test_unicodedata.py index d7f4e3f60b3d01..3eeb6694da492c 100644 --- a/Lib/test/test_unicodedata.py +++ b/Lib/test/test_unicodedata.py @@ -89,9 +89,9 @@ class UnicodeFunctionsTest(unittest.TestCase): # Update this if the database changes. Make sure to do a full rebuild # (e.g. 'make distclean && make') to get the correct checksum. - expectedchecksum = ('a91d306c268ba7d5cdf14d49e63b3f967058869c' + expectedchecksum = ('a5b8431ae6c0a0a78075c216193b7364a0497075' if quicktest else - '232affd2a50ec4bd69d2482aa0291385cbdefaba') + '72241cd356ce6dad7d0570d206ce869169151850') def test_function_checksum(self): db = self.db @@ -335,6 +335,12 @@ def test_decomposition(self): # New in 15.0.0 self.assertEqual(self.db.decomposition('\U0001e06d'), '' if self.old else ' 04B1') + # Hangul characters + self.assertEqual(self.db.decomposition('\uAC00'), '1100 1161') + self.assertEqual(self.db.decomposition('\uD4DB'), '1111 1171 11B6') + self.assertEqual(self.db.decomposition('\uC2F8'), '110A 1161') + self.assertEqual(self.db.decomposition('\uD7A3'), '1112 1175 11C2') + self.assertRaises(TypeError, self.db.decomposition) self.assertRaises(TypeError, self.db.decomposition, 'xx') @@ -628,9 +634,9 @@ def test_east_asian_width_unassigned(self): class Unicode_3_2_0_FunctionsTest(UnicodeFunctionsTest): db = unicodedata.ucd_3_2_0 old = True - expectedchecksum = ('4154d8d1232837e255edf3cdcbb5ab184d71f4a4' + expectedchecksum = ('883824cb6c0ccf994e4451ebf281e2d6d479af47' if quicktest else - 'b678d38ffbf1f1de092b2af1ed155602909fcd8d') + '44bbc0dfbfd746ba08180183482aa569a3830510') class UnicodeMiscTest(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2026-02-19-10-57-40.gh-issue-88091.N7qGV-.rst b/Misc/NEWS.d/next/Library/2026-02-19-10-57-40.gh-issue-88091.N7qGV-.rst new file mode 100644 index 00000000000000..15cf25052bbb46 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-02-19-10-57-40.gh-issue-88091.N7qGV-.rst @@ -0,0 +1 @@ +Fix :func:`unicodedata.decomposition` for Hangul characters. diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c index ed0dbb022d5bf0..2d317bd6f71351 100644 --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -386,6 +386,17 @@ unicodedata_UCD_east_asian_width_impl(PyObject *self, int chr) return PyUnicode_FromString(_PyUnicode_EastAsianWidthNames[index]); } +// For Hangul decomposition +#define SBase 0xAC00 +#define LBase 0x1100 +#define VBase 0x1161 +#define TBase 0x11A7 +#define LCount 19 +#define VCount 21 +#define TCount 28 +#define NCount (VCount*TCount) +#define SCount (LCount*NCount) + /*[clinic input] unicodedata.UCD.decomposition @@ -416,6 +427,25 @@ unicodedata_UCD_decomposition_impl(PyObject *self, int chr) return PyUnicode_FromString(""); /* unassigned */ } + // Hangul Decomposition. + // See section 3.12.2, "Hangul Syllable Decomposition" + // https://www.unicode.org/versions/latest/core-spec/chapter-3/#G56669 + if (SBase <= code && code < (SBase + SCount)) { + int SIndex = code - SBase; + int L = LBase + SIndex / NCount; + int V = VBase + (SIndex % NCount) / TCount; + int T = TBase + SIndex % TCount; + if (T != TBase) { + PyOS_snprintf(decomp, sizeof(decomp), + "%04X %04X %04X", L, V, T); + } + else { + PyOS_snprintf(decomp, sizeof(decomp), + "%04X %04X", L, V); + } + return PyUnicode_FromString(decomp); + } + if (code < 0 || code >= 0x110000) index = 0; else { @@ -478,16 +508,6 @@ get_decomp_record(PyObject *self, Py_UCS4 code, (*index)++; } -#define SBase 0xAC00 -#define LBase 0x1100 -#define VBase 0x1161 -#define TBase 0x11A7 -#define LCount 19 -#define VCount 21 -#define TCount 28 -#define NCount (VCount*TCount) -#define SCount (LCount*NCount) - static PyObject* nfd_nfkd(PyObject *self, PyObject *input, int k) { @@ -541,7 +561,9 @@ nfd_nfkd(PyObject *self, PyObject *input, int k) } output = new_output; } - /* Hangul Decomposition. */ + // Hangul Decomposition. + // See section 3.12.2, "Hangul Syllable Decomposition" + // https://www.unicode.org/versions/latest/core-spec/chapter-3/#G56669 if (SBase <= code && code < (SBase+SCount)) { int SIndex = code - SBase; int L = LBase + SIndex / NCount;