Skip to content

Commit aba6bb9

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

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
@@ -107,29 +107,28 @@ def test_bytes(self):
107107
@support.requires_resource('cpu')
108108
def test_strings(self):
109109
'Verify the pretty-printing of unicode strings'
110-
# We cannot simply call locale.getpreferredencoding() here,
111-
# as GDB might have been linked against a different version
112-
# of Python with a different encoding and coercion policy
113-
# with respect to PEP 538 and PEP 540.
110+
# gdb emits its output in the host charset, which is not necessarily the
111+
# getpreferredencoding() of the (possibly differently coerced) embedded
112+
# Python.
114113
stdout, stderr = run_gdb(
115114
'--eval-command',
116-
'python import locale; print(locale.getpreferredencoding())')
115+
'python import gdb; print(gdb.host_charset())')
117116

118-
encoding = stdout
117+
encoding = stdout.strip()
119118
if stderr or not encoding:
120119
raise RuntimeError(
121-
f'unable to determine the Python locale preferred encoding '
122-
f'of embedded Python in GDB\n'
120+
f'unable to determine the host charset of gdb\n'
123121
f'stdout={stdout!r}\n'
124122
f'stderr={stderr!r}')
125123

126124
def check_repr(text):
127125
try:
128126
text.encode(encoding)
129-
except UnicodeEncodeError:
127+
# LookupError or ValueError if the host charset is unknown or invalid.
128+
except (UnicodeEncodeError, LookupError, ValueError):
130129
self.assertGdbRepr(text, ascii(text))
131130
else:
132-
self.assertGdbRepr(text)
131+
self.assertGdbRepr(text, repr(text).encode(encoding).decode('ascii', 'surrogateescape'))
133132

134133
self.assertGdbRepr('')
135134
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
@@ -57,7 +57,7 @@ def run_gdb(*args, exitcode=0, check=True, **env_vars):
5757
stdin=subprocess.PIPE,
5858
stdout=subprocess.PIPE,
5959
stderr=subprocess.PIPE,
60-
encoding="utf8", errors="backslashreplace",
60+
encoding="ascii", errors="surrogateescape",
6161
env=env)
6262

6363
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'
@@ -1493,6 +1490,10 @@ def proxyval(self, visited):
14931490
def write_repr(self, out, visited):
14941491
# Write this out as a Python str literal
14951492

1493+
# gdb writes its output in the host charset, so a character is escaped
1494+
# unless it is printable and encodable in that charset.
1495+
encoding = gdb.host_charset()
1496+
14961497
# Get a PyUnicodeObject* within the Python gdb process:
14971498
proxy = self.proxyval(visited)
14981499

@@ -1540,8 +1541,10 @@ def write_repr(self, out, visited):
15401541
printable = ucs.isprintable()
15411542
if printable:
15421543
try:
1543-
ucs.encode(ENCODING)
1544-
except UnicodeEncodeError:
1544+
ucs.encode(encoding)
1545+
# LookupError or ValueError if the host charset is unknown
1546+
# or invalid.
1547+
except (UnicodeEncodeError, LookupError, ValueError):
15451548
printable = False
15461549

15471550
# Map Unicode whitespace and control characters

0 commit comments

Comments
 (0)