Skip to content

Commit 6110833

Browse files
committed
python-gdb.py supports method-wrapper
Issue #29367: python-gdb.py now supports also method-wrapper (wrapperobject) objects.
1 parent c9473b8 commit 6110833

File tree

3 files changed

+71
-6
lines changed

3 files changed

+71
-6
lines changed

Lib/test/test_gdb.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
# The code for testing gdb was adapted from similar work in Unladen Swallow's
44
# Lib/test/test_jit_gdb.py
55

6+
import locale
67
import os
78
import re
89
import subprocess
910
import sys
1011
import sysconfig
12+
import textwrap
1113
import unittest
12-
import locale
1314

1415
# Is this Python configured to support threads?
1516
try:
@@ -847,6 +848,24 @@ def test_pycfunction(self):
847848
)
848849
self.assertIn('#1 <built-in method gmtime', gdb_output)
849850

851+
@unittest.skipIf(python_is_optimized(),
852+
"Python was compiled with optimizations")
853+
def test_wrapper_call(self):
854+
cmd = textwrap.dedent('''
855+
class MyList(list):
856+
def __init__(self):
857+
super().__init__() # wrapper_call()
858+
859+
l = MyList()
860+
''')
861+
# Verify with "py-bt":
862+
gdb_output = self.get_stack_trace(cmd,
863+
breakpoint='wrapper_call',
864+
cmds_after_breakpoint=['py-bt'],
865+
)
866+
self.assertIn("<method-wrapper '__init__' of MyList object at ",
867+
gdb_output)
868+
850869

851870
class PyPrintTests(DebuggerTests):
852871
@unittest.skipIf(python_is_optimized(),

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,9 @@ Build
746746
Tools/Demos
747747
-----------
748748

749+
- Issue #29367: python-gdb.py now supports also ``method-wrapper``
750+
(``wrapperobject``) objects.
751+
749752
- Issue #28023: Fix python-gdb.py didn't support new dict implementation.
750753

751754
- Issue #15369: The pybench and pystone microbenchmark have been removed from

Tools/gdb/libpython.py

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ def subclass_from_type(cls, t):
362362
'set' : PySetObjectPtr,
363363
'frozenset' : PySetObjectPtr,
364364
'builtin_function_or_method' : PyCFunctionObjectPtr,
365+
'method-wrapper': wrapperobject,
365366
}
366367
if tp_name in name_map:
367368
return name_map[tp_name]
@@ -1330,6 +1331,39 @@ def write_repr(self, out, visited):
13301331
out.write(quote)
13311332

13321333

1334+
class wrapperobject(PyObjectPtr):
1335+
_typename = 'wrapperobject'
1336+
1337+
def safe_name(self):
1338+
try:
1339+
name = self.field('descr')['d_base']['name'].string()
1340+
return repr(name)
1341+
except (NullPyObjectPtr, RuntimeError):
1342+
return '<unknown name>'
1343+
1344+
def safe_tp_name(self):
1345+
try:
1346+
return self.field('self')['ob_type']['tp_name'].string()
1347+
except (NullPyObjectPtr, RuntimeError):
1348+
return '<unknown tp_name>'
1349+
1350+
def safe_self_addresss(self):
1351+
try:
1352+
address = long(self.field('self'))
1353+
return '%#x' % address
1354+
except (NullPyObjectPtr, RuntimeError):
1355+
return '<failed to get self address>'
1356+
1357+
def proxyval(self, visited):
1358+
name = self.safe_name()
1359+
tp_name = self.safe_tp_name()
1360+
self_address = self.safe_self_addresss()
1361+
return ("<method-wrapper %s of %s object at %s>"
1362+
% (name, tp_name, self_address))
1363+
1364+
def write_repr(self, out, visited):
1365+
proxy = self.proxyval(visited)
1366+
out.write(proxy)
13331367

13341368

13351369
def int_from_int(gdbval):
@@ -1364,11 +1398,13 @@ def to_string (self):
13641398

13651399
def pretty_printer_lookup(gdbval):
13661400
type = gdbval.type.unqualified()
1367-
if type.code == gdb.TYPE_CODE_PTR:
1368-
type = type.target().unqualified()
1369-
t = str(type)
1370-
if t in ("PyObject", "PyFrameObject", "PyUnicodeObject"):
1371-
return PyObjectPtrPrinter(gdbval)
1401+
if type.code != gdb.TYPE_CODE_PTR:
1402+
return None
1403+
1404+
type = type.target().unqualified()
1405+
t = str(type)
1406+
if t in ("PyObject", "PyFrameObject", "PyUnicodeObject", "wrapperobject"):
1407+
return PyObjectPtrPrinter(gdbval)
13721408

13731409
"""
13741410
During development, I've been manually invoking the code in this way:
@@ -1520,6 +1556,13 @@ def is_other_python_frame(self):
15201556
except RuntimeError:
15211557
return 'PyCFunction invocation (unable to read %s)' % arg_name
15221558

1559+
if caller == 'wrapper_call':
1560+
try:
1561+
func = frame.read_var('wp')
1562+
return str(func)
1563+
except RuntimeError:
1564+
return '<wrapper_call invocation>'
1565+
15231566
# This frame isn't worth reporting:
15241567
return False
15251568

0 commit comments

Comments
 (0)