]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-154580: Fix python-gdb.py pretty-printing non-ASCII strings in non-UTF-8 locales...
authorSerhiy Storchaka <storchaka@gmail.com>
Fri, 24 Jul 2026 16:11:15 +0000 (19:11 +0300)
committerGitHub <noreply@github.com>
Fri, 24 Jul 2026 16:11:15 +0000 (19:11 +0300)
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.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Lib/test/test_gdb/test_pretty_print.py
Lib/test/test_gdb/util.py
Misc/NEWS.d/next/Tools-Demos/2026-07-24-09-30-00.gh-issue-154580.Kp3nQ2.rst [new file with mode: 0644]
Tools/gdb/libpython.py

index db3064e3df54c24c7954826bc8e980e98ee47bc4..edb79ef86d31db5519b6de9eebebc295805fc8a3 100644 (file)
@@ -114,29 +114,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')
index d903adcf2903f3471e6ffa54a256d97e5b2cdd9d..29c7e9ee8f054989945864ee724e03aa7264ba22 100644 (file)
@@ -78,7 +78,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 (file)
index 0000000..f6a0392
--- /dev/null
@@ -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.
index ba52ea2a30e0be1914a9847b9c493d635a34fc6d..422e4f605920a0cbd1b57980e474a4174b06b6e6 100755 (executable)
@@ -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'
@@ -1504,6 +1501,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)
 
@@ -1551,8 +1552,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