From 6329607f518392ab8695d33fde7d5fba4721f4bb Mon Sep 17 00:00:00 2001 From: Zackery Spytz Date: Fri, 14 Jun 2019 08:30:52 -0600 Subject: [PATCH 1/2] bpo-19865: ctypes.create_unicode_buffer() fails on non-BMP strings on Windows --- Lib/ctypes/__init__.py | 4 ++++ Lib/ctypes/test/test_buffers.py | 8 ++++++++ .../next/Library/2019-06-14-08-30-16.bpo-19865.FRGH4I.rst | 2 ++ 3 files changed, 14 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2019-06-14-08-30-16.bpo-19865.FRGH4I.rst diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 4107db3e3972d79..43be113029622d4 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -275,6 +275,10 @@ def create_unicode_buffer(init, size=None): if isinstance(init, str): if size is None: size = len(init)+1 + if sizeof(c_wchar) == 2: + for c in init: + if ord(c) > 0xFFFF: + size += 1 buftype = c_wchar * size buf = buftype() buf.value = init diff --git a/Lib/ctypes/test/test_buffers.py b/Lib/ctypes/test/test_buffers.py index 166faaf4e4b89c9..a6f184e048481c9 100644 --- a/Lib/ctypes/test/test_buffers.py +++ b/Lib/ctypes/test/test_buffers.py @@ -60,5 +60,13 @@ def test_unicode_conversion(self): self.assertEqual(b[::2], "ac") self.assertEqual(b[::5], "a") + @need_symbol('c_wchar') + def test_create_unicode_buffer_non_bmp(self): + b = create_unicode_buffer('\U00010000\U00100000') + expected = 5 if sizeof(c_wchar) == 2 else 3 + self.assertEqual(len(b), expected) + self.assertEqual(b[-1], '\0') + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2019-06-14-08-30-16.bpo-19865.FRGH4I.rst b/Misc/NEWS.d/next/Library/2019-06-14-08-30-16.bpo-19865.FRGH4I.rst new file mode 100644 index 000000000000000..b7f58f0d1800b39 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-14-08-30-16.bpo-19865.FRGH4I.rst @@ -0,0 +1,2 @@ +:func:`ctypes.create_unicode_buffer()` no longer fails on non-BMP strings on +Windows. From 7818fb5850b9892e679a07ce7e65d3a4fa0a18c1 Mon Sep 17 00:00:00 2001 From: Zackery Spytz Date: Fri, 14 Jun 2019 09:07:06 -0600 Subject: [PATCH 2/2] Make the requested changes. --- Lib/ctypes/__init__.py | 12 ++++++++---- Lib/ctypes/test/test_buffers.py | 7 ++++--- .../Library/2019-06-14-08-30-16.bpo-19865.FRGH4I.rst | 4 ++-- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 43be113029622d4..128155dbf4f2d9e 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -274,11 +274,15 @@ def create_unicode_buffer(init, size=None): """ if isinstance(init, str): if size is None: - size = len(init)+1 if sizeof(c_wchar) == 2: - for c in init: - if ord(c) > 0xFFFF: - size += 1 + # UTF-16 requires a surrogate pair (2 wchar_t) for non-BMP + # characters (outside [U+0000; U+FFFF] range). +1 for trailing + # NUL character. + size = sum(2 if ord(c) > 0xFFFF else 1 for c in init) + 1 + else: + # 32-bit wchar_t (1 wchar_t per Unicode character). +1 for + # trailing NUL character. + size = len(init) + 1 buftype = c_wchar * size buf = buftype() buf.value = init diff --git a/Lib/ctypes/test/test_buffers.py b/Lib/ctypes/test/test_buffers.py index a6f184e048481c9..15782be757c8535 100644 --- a/Lib/ctypes/test/test_buffers.py +++ b/Lib/ctypes/test/test_buffers.py @@ -62,10 +62,11 @@ def test_unicode_conversion(self): @need_symbol('c_wchar') def test_create_unicode_buffer_non_bmp(self): - b = create_unicode_buffer('\U00010000\U00100000') expected = 5 if sizeof(c_wchar) == 2 else 3 - self.assertEqual(len(b), expected) - self.assertEqual(b[-1], '\0') + for s in '\U00010000\U00100000', '\U00010000\U0010ffff': + b = create_unicode_buffer(s) + self.assertEqual(len(b), expected) + self.assertEqual(b[-1], '\0') if __name__ == "__main__": diff --git a/Misc/NEWS.d/next/Library/2019-06-14-08-30-16.bpo-19865.FRGH4I.rst b/Misc/NEWS.d/next/Library/2019-06-14-08-30-16.bpo-19865.FRGH4I.rst index b7f58f0d1800b39..efd1f55c0135b0b 100644 --- a/Misc/NEWS.d/next/Library/2019-06-14-08-30-16.bpo-19865.FRGH4I.rst +++ b/Misc/NEWS.d/next/Library/2019-06-14-08-30-16.bpo-19865.FRGH4I.rst @@ -1,2 +1,2 @@ -:func:`ctypes.create_unicode_buffer()` no longer fails on non-BMP strings on -Windows. +:func:`ctypes.create_unicode_buffer()` now also supports non-BMP characters +on platforms with 16-bit :c:type:`wchar_t` (for example, Windows and AIX).