This is an archive of the discontinued LLVM Phabricator instance.

[lldb][bindings] Implement __repr__ instead of __str__
ClosedPublic

Authored by kastiglione on Jun 9 2022, 4:53 PM.

Details

Summary

When using the script Python repl, SB objects are printed in a way that gives
the user no information. The simplest example is:

(lldb) script lldb.debugger
<lldb.SBDebugger; proxy of <Swig Object of type 'lldb::SBDebugger *' at 0x1097a5de0> >

This output comes from the Python repl printing the repr() of an object.

None of the SB classes implement __repr__, and all print like the above.
However, many (most?, all?) SB classes implement __str__. Because they
implement __str__, a more detailed output can be had by printing the
object, for example:

(lldb) script print(lldb.debugger)
Debugger (instance: "debugger_1", id: 1)

For convenience, this change switches all SB classes that implement to
__str__ to instead implement __repr__. The result is that str() and
repr() will produce the same output
. This is because str calls __repr__
for classes that have no __str__ method.

The benefit being that when writing a script invocation, you don't need to
remember to wrap in print(). If that isn't enough motivation, consider the
case where your Python expression results in a list of SB objects, in that case
you'd have to map or use a list comprehension like [str(x) for x in <expr>]
in order to see the details of the objects in the list.

For reference, the docs for repr say:

repr(object)

Return a string containing a printable representation of an object. For
many types, this function makes an attempt to return a string that would
yield an object with the same value when passed to eval(); otherwise, the
representation is a string enclosed in angle brackets that contains the
name of the type of the object together with additional information often
including the name and address of the object. A class can control what this
function returns for its instances by defining a __repr__() method.

and the docs for __repr__ say:

object.repr(self)

Called by the repr() built-in function to compute the “official” string
representation of an object. If at all possible, this should look like a
valid Python expression that could be used to recreate an object with the
same value (given an appropriate environment). If this is not possible, a
string of the form <...some useful description...> should be returned. The
return value must be a string object. If a class defines __repr__() but not
__str__(), then __repr__() is also used when an “informal” string
representation of instances of that class is required.

This is typically used for debugging, so it is important that the
representation is information-rich and unambiguous.

Even if it were convenient to construct Python expressions for SB classes so
that they could be eval'd, however for typical lldb usage, I can't think of a
motivating reason to do so. As it stands, the only action the docs say to do,
that this change doesn't do, is wrap the repr string in <> angle brackets.

An alternative implementation is to change lldb's python repl to apply str()
to the top level result. While this would work well in the case of a single SB
object, it doesn't work for a list of SB objects, since str([x]) uses repr
to convert each list element to a string.

Diff Detail

Event Timeline

kastiglione created this revision.Jun 9 2022, 4:53 PM
Herald added a project: Restricted Project. · View Herald TranscriptJun 9 2022, 4:53 PM
kastiglione requested review of this revision.Jun 9 2022, 4:53 PM
Herald added a project: Restricted Project. · View Herald TranscriptJun 9 2022, 4:53 PM
mib added a comment.Jun 9 2022, 6:20 PM

What happens if you try to call str after your change ? I have no objection with this change, I think rather it will greatly improve manipulating SBTypes in the interpreter ! Just making sure this passes the test suite and doesn't break the previous behavior

kastiglione edited the summary of this revision. (Show Details)EditedJun 10 2022, 9:10 AM

@mib I tried but failed to answer that in the original commit description. I have reworded it to be more clear. Here's the relevant paragraph:

For convenience, this change switches all SB classes that implement to
__str__ to instead implement __repr__. The result is that str() and
repr() will produce the same output
. This is because str calls __repr__
for classes that have no __str__ method.

kastiglione edited the summary of this revision. (Show Details)Jun 10 2022, 9:11 AM
kastiglione edited the summary of this revision. (Show Details)Jun 10 2022, 9:14 AM
JDevlieghere accepted this revision.Jun 10 2022, 9:29 AM

Makes sense to me. Maybe add a small shell test that includes the example from the description?

This revision is now accepted and ready to land.Jun 10 2022, 9:29 AM
mib accepted this revision.Jun 10 2022, 9:37 AM

Thanks for clarifying! LGTM with a test!

Note also that the tests use str() in many places, so we can be sure this hasn't regressed that. I will add a test for repr.

add test/API/sanity/TestReprStrEquality.py

update TestConvenienceVariables.test

jingham accepted this revision.Jun 10 2022, 2:36 PM

This looks good to me as well. The job of "recreating the object from an eval of the repr output" is not even possible for many SB objects, by the time the object was made it's lost the factory invocation that produced it. And anyway, I also can't see a good use case for this. The fact that this gives us good output in lists of objects is also pretty persuasive.