https://github.com/python/cpython/commit/efbe54a2c6f0fa95d81c1f0736abbd698e922ff0
commit: efbe54a2c6f0fa95d81c1f0736abbd698e922ff0
branch: 3.15
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-07-24T17:55:12Z
summary:

[3.15] gh-154580: Fix python-gdb.py pretty-printing non-ASCII strings in 
non-UTF-8 locales (GH-154581) (GH-154635)

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 <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>

files:
A Misc/NEWS.d/next/Tools-Demos/2026-07-24-09-30-00.gh-issue-154580.Kp3nQ2.rst
M Lib/test/test_gdb/test_pretty_print.py
M Lib/test/test_gdb/util.py
M Tools/gdb/libpython.py

diff --git a/Lib/test/test_gdb/test_pretty_print.py 
b/Lib/test/test_gdb/test_pretty_print.py
index db3064e3df54c24..edb79ef86d31db5 100644
--- a/Lib/test/test_gdb/test_pretty_print.py
+++ b/Lib/test/test_gdb/test_pretty_print.py
@@ -114,29 +114,28 @@ def test_bytes(self):
     @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 d903adcf2903f34..29c7e9ee8f05498 100644
--- a/Lib/test/test_gdb/util.py
+++ b/Lib/test/test_gdb/util.py
@@ -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
index 000000000000000..f6a039245b015ed
--- /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 ba52ea2a30e0be1..422e4f605920a0c 100755
--- a/Tools/gdb/libpython.py
+++ b/Tools/gdb/libpython.py
@@ -43,7 +43,6 @@
 
 import gdb
 import os
-import locale
 import sys
 
 
@@ -107,8 +106,6 @@ def interp_frame_has_tlbc_index():
 
 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 @@ def proxyval(self, visited):
     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 @@ def write_repr(self, out, visited):
                 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

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to