Skip to content

Commit ca9fe33

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 ca9fe33

File tree

1 file changed

+33
-12
lines changed

1 file changed

+33
-12
lines changed

cpython_lldb.py

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -426,24 +426,45 @@ 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+
return next(
451+
v for k, v in PyDictObject(value).value.items() if k.value == "data"
452+
)
453+
else:
454+
# CPython >= 3.11: &__dict__ is always stored at a fixed offset
455+
# before the start of the common PyObject header.
456+
object_type = self.target.FindFirstType("PyDictValues")
457+
address = lldb.SBAddress(int(self.lldb_value.value, 16) - 32, self.target)
458+
value = self.target.CreateValueFromAddress(
459+
"value", address, object_type.GetPointerType()
460+
)
442461

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-
)
462+
# TODO: This only works because there is only one instance variable
463+
# called "data" right now. We need to solve the generic case and
464+
# implement iteration over PyDictValues entries.
465+
return PyObject.from_value(
466+
value.deref.GetChildMemberWithName("values").GetChildAtIndex(0)
467+
)
447468

448469

449470
class UserDict(_CollectionsUserObject, PyObject):

0 commit comments

Comments
 (0)