Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions Lib/test/test_gdb/test_pretty_print.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_gdb/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
13 changes: 8 additions & 5 deletions Tools/gdb/libpython.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@

import gdb
import os
import locale
import sys


Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down
Loading