Skip to content

Commit 47deb61

Browse files
committed
cpython3.11: fix pretty-printing of UserDict, UserString, UserList
python/cpython#29879 changed the layout of user classes, and the code is no longer able to find the location of __dict__. Do a quick fix for now, as we don't support pretty-printing user defined classes.
1 parent d4b328e commit 47deb61

File tree

1 file changed

+34
-12
lines changed

1 file changed

+34
-12
lines changed

cpython_lldb.py

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -426,24 +426,46 @@ def value(self):
426426
class _CollectionsUserObject(object):
427427
@property
428428
def value(self):
429+
# UserDict, UserString, and UserList all have a single instance variable
430+
# called "data", which is the collection used for storing the elements.
431+
# As usual, that instance variable is stored in __dict__, so we need to
432+
# find the location of that dict object first and look up the key.
433+
429434
dict_offset = (
430435
self.lldb_value.GetChildMemberWithName("ob_type")
431436
.GetChildMemberWithName("tp_dictoffset")
432-
.unsigned
437+
.signed
433438
)
439+
if dict_offset > 0:
440+
# CPython < 3.11: __dict__ is located $tp_dictoffset bytes after the
441+
# start of the common PyObject header.
442+
object_type = self.target.FindFirstType("PyObject")
443+
address = lldb.SBAddress(
444+
int(self.lldb_value.value, 16) + dict_offset, self.target
445+
)
446+
value = self.target.CreateValueFromAddress(
447+
"value", address, object_type.GetPointerType()
448+
)
434449

435-
object_type = self.target.FindFirstType("PyObject")
436-
address = lldb.SBAddress(
437-
int(self.lldb_value.value, 16) + dict_offset, self.target
438-
)
439-
value = self.target.CreateValueFromAddress(
440-
"value", address, object_type.GetPointerType()
441-
)
450+
# "data" is the application level collection that stores the items
451+
return next(
452+
v for k, v in PyDictObject(value).value.items() if k.value == "data"
453+
)
454+
else:
455+
# CPython >= 3.11: &__dict__ is always stored at a fixed offset
456+
# before the start of the common PyObject header.
457+
object_type = self.target.FindFirstType("PyDictValues")
458+
address = lldb.SBAddress(int(self.lldb_value.value, 16) - 32, self.target)
459+
value = self.target.CreateValueFromAddress(
460+
"value", address, object_type.GetPointerType()
461+
)
442462

443-
# "data" is the real object used to store the contents of the class
444-
return next(
445-
v for k, v in PyDictObject(value).value.items() if k.value == "data"
446-
)
463+
# TODO: This only works because there is only instance variable called "data"
464+
# right now. We need to solve the generic case and implement iteration over
465+
# PyDictValues entries.
466+
return PyObject.from_value(
467+
value.deref.GetChildMemberWithName("values").GetChildAtIndex(0)
468+
)
447469

448470

449471
class UserDict(_CollectionsUserObject, PyObject):

0 commit comments

Comments
 (0)