Skip to content

Commit efbe54a

Browse files
miss-islingtonserhiy-storchakaclaude
authored
[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 6cb93bd) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 370e670 commit efbe54a

4 files changed

Lines changed: 21 additions & 16 deletions

File tree

Lib/test/test_gdb/test_pretty_print.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -114,29 +114,28 @@ def test_bytes(self):
114114
@support.requires_resource('cpu')
115115
def test_strings(self):
116116
'Verify the pretty-printing of unicode strings'
117-
# We cannot simply call locale.getpreferredencoding() here,
118-
# as GDB might have been linked against a different version
119-
# of Python with a different encoding and coercion policy
120-
# with respect to PEP 538 and PEP 540.
117+
# gdb emits its output in the host charset, which is not necessarily the
118+
# getpreferredencoding() of the (possibly differently coerced) embedded
119+
# Python.
121120
stdout, stderr = run_gdb(
122121
'--eval-command',
123-
'python import locale; print(locale.getpreferredencoding())')
122+
'python import gdb; print(gdb.host_charset())')
124123

125-
encoding = stdout
124+
encoding = stdout.strip()
126125
if stderr or not encoding:
127126
raise RuntimeError(
128-
f'unable to determine the Python locale preferred encoding '
129-
f'of embedded Python in GDB\n'
127+
f'unable to determine the host charset of gdb\n'
130128
f'stdout={stdout!r}\n'
131129
f'stderr={stderr!r}')
132130

133131
def check_repr(text):
134132
try:
135133
text.encode(encoding)
136-
except UnicodeEncodeError:
134+
# LookupError or ValueError if the host charset is unknown or invalid.
135+
except (UnicodeEncodeError, LookupError, ValueError):
137136
self.assertGdbRepr(text, ascii(text))
138137
else:
139-
self.assertGdbRepr(text)
138+
self.assertGdbRepr(text, repr(text).encode(encoding).decode('ascii', 'surrogateescape'))
140139

141140
self.assertGdbRepr('')
142141
self.assertGdbRepr('And now for something hopefully the same')

Lib/test/test_gdb/util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def run_gdb(*args, exitcode=0, check=True, **env_vars):
7878
stdin=subprocess.PIPE,
7979
stdout=subprocess.PIPE,
8080
stderr=subprocess.PIPE,
81-
encoding="utf8", errors="backslashreplace",
81+
encoding="ascii", errors="surrogateescape",
8282
env=env)
8383

8484
stdout = proc.stdout
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix ``python-gdb.py`` raising :exc:`UnicodeEncodeError` when pretty-printing a
2+
non-ASCII :class:`str` in a locale whose host charset cannot encode it, such as
3+
any non-ASCII string in the C locale.

Tools/gdb/libpython.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343

4444
import gdb
4545
import os
46-
import locale
4746
import sys
4847

4948

@@ -107,8 +106,6 @@ def interp_frame_has_tlbc_index():
107106

108107
USED_TAGS = 0b11
109108

110-
ENCODING = locale.getpreferredencoding()
111-
112109
FRAME_INFO_OPTIMIZED_OUT = '(frame information optimized out)'
113110
UNABLE_READ_INFO_PYTHON_FRAME = 'Unable to read information on python frame'
114111
EVALFRAME = '_PyEval_EvalFrameDefault'
@@ -1504,6 +1501,10 @@ def proxyval(self, visited):
15041501
def write_repr(self, out, visited):
15051502
# Write this out as a Python str literal
15061503

1504+
# gdb writes its output in the host charset, so a character is escaped
1505+
# unless it is printable and encodable in that charset.
1506+
encoding = gdb.host_charset()
1507+
15071508
# Get a PyUnicodeObject* within the Python gdb process:
15081509
proxy = self.proxyval(visited)
15091510

@@ -1551,8 +1552,10 @@ def write_repr(self, out, visited):
15511552
printable = ucs.isprintable()
15521553
if printable:
15531554
try:
1554-
ucs.encode(ENCODING)
1555-
except UnicodeEncodeError:
1555+
ucs.encode(encoding)
1556+
# LookupError or ValueError if the host charset is unknown
1557+
# or invalid.
1558+
except (UnicodeEncodeError, LookupError, ValueError):
15561559
printable = False
15571560

15581561
# Map Unicode whitespace and control characters

0 commit comments

Comments
 (0)