From aba6bb9e44769122ef4f96ddfc65e726c2dc3b2c Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 24 Jul 2026 19:46:28 +0200 Subject: [PATCH] [3.14] gh-154580: Fix python-gdb.py pretty-printing non-ASCII strings in non-UTF-8 locales (GH-154581) (GH-154636) The gdb pretty-printer used locale.getpreferredencoding() to decide whether to escape a character, but gdb writes its output in its host charset. Use gdb.host_charset() instead. test_strings had the same problem. (cherry picked from commit 6cb93bd039ec1c5e0909d2d75cfc4fb3641c693b) Co-authored-by: Serhiy Storchaka Co-authored-by: Claude Opus 4.8 --- Lib/test/test_gdb/test_pretty_print.py | 19 +++++++++---------- Lib/test/test_gdb/util.py | 2 +- ...-07-24-09-30-00.gh-issue-154580.Kp3nQ2.rst | 3 +++ Tools/gdb/libpython.py | 13 ++++++++----- 4 files changed, 21 insertions(+), 16 deletions(-) create mode 100644 Misc/NEWS.d/next/Tools-Demos/2026-07-24-09-30-00.gh-issue-154580.Kp3nQ2.rst diff --git a/Lib/test/test_gdb/test_pretty_print.py b/Lib/test/test_gdb/test_pretty_print.py index dfc77d65ab16..a258d7c0e60c 100644 --- a/Lib/test/test_gdb/test_pretty_print.py +++ b/Lib/test/test_gdb/test_pretty_print.py @@ -107,29 +107,28 @@ class PrettyPrintTests(DebuggerTests): @support.requires_resource('cpu') def test_strings(self): 'Verify the pretty-printing of unicode strings' - # We cannot simply call locale.getpreferredencoding() here, - # as GDB might have been linked against a different version - # of Python with a different encoding and coercion policy - # with respect to PEP 538 and PEP 540. + # gdb emits its output in the host charset, which is not necessarily the + # getpreferredencoding() of the (possibly differently coerced) embedded + # Python. stdout, stderr = run_gdb( '--eval-command', - 'python import locale; print(locale.getpreferredencoding())') + 'python import gdb; print(gdb.host_charset())') - encoding = stdout + encoding = stdout.strip() if stderr or not encoding: raise RuntimeError( - f'unable to determine the Python locale preferred encoding ' - f'of embedded Python in GDB\n' + f'unable to determine the host charset of gdb\n' f'stdout={stdout!r}\n' f'stderr={stderr!r}') def check_repr(text): try: text.encode(encoding) - except UnicodeEncodeError: + # LookupError or ValueError if the host charset is unknown or invalid. + except (UnicodeEncodeError, LookupError, ValueError): self.assertGdbRepr(text, ascii(text)) else: - self.assertGdbRepr(text) + self.assertGdbRepr(text, repr(text).encode(encoding).decode('ascii', 'surrogateescape')) self.assertGdbRepr('') self.assertGdbRepr('And now for something hopefully the same') diff --git a/Lib/test/test_gdb/util.py b/Lib/test/test_gdb/util.py index 8097fd52abab..9d9e454ec6de 100644 --- a/Lib/test/test_gdb/util.py +++ b/Lib/test/test_gdb/util.py @@ -57,7 +57,7 @@ def run_gdb(*args, exitcode=0, check=True, **env_vars): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - encoding="utf8", errors="backslashreplace", + encoding="ascii", errors="surrogateescape", env=env) stdout = proc.stdout diff --git a/Misc/NEWS.d/next/Tools-Demos/2026-07-24-09-30-00.gh-issue-154580.Kp3nQ2.rst b/Misc/NEWS.d/next/Tools-Demos/2026-07-24-09-30-00.gh-issue-154580.Kp3nQ2.rst new file mode 100644 index 000000000000..f6a039245b01 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2026-07-24-09-30-00.gh-issue-154580.Kp3nQ2.rst @@ -0,0 +1,3 @@ +Fix ``python-gdb.py`` raising :exc:`UnicodeEncodeError` when pretty-printing a +non-ASCII :class:`str` in a locale whose host charset cannot encode it, such as +any non-ASCII string in the C locale. diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index a85195dcd101..f765d96d9cb7 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -43,7 +43,6 @@ The module also extends gdb with some python-specific commands. import gdb import os -import locale import sys @@ -107,8 +106,6 @@ hexdigits = "0123456789abcdef" USED_TAGS = 0b11 -ENCODING = locale.getpreferredencoding() - FRAME_INFO_OPTIMIZED_OUT = '(frame information optimized out)' UNABLE_READ_INFO_PYTHON_FRAME = 'Unable to read information on python frame' EVALFRAME = '_PyEval_EvalFrameDefault' @@ -1493,6 +1490,10 @@ class PyUnicodeObjectPtr(PyObjectPtr): def write_repr(self, out, visited): # Write this out as a Python str literal + # gdb writes its output in the host charset, so a character is escaped + # unless it is printable and encodable in that charset. + encoding = gdb.host_charset() + # Get a PyUnicodeObject* within the Python gdb process: proxy = self.proxyval(visited) @@ -1540,8 +1541,10 @@ class PyUnicodeObjectPtr(PyObjectPtr): printable = ucs.isprintable() if printable: try: - ucs.encode(ENCODING) - except UnicodeEncodeError: + ucs.encode(encoding) + # LookupError or ValueError if the host charset is unknown + # or invalid. + except (UnicodeEncodeError, LookupError, ValueError): printable = False # Map Unicode whitespace and control characters -- 2.47.3