[Lldb-commits] [lldb] c7a56af - [lldb][bindings] Implement __repr__ instead of __str__

Dave Lee via lldb-commits lldb-commits at lists.llvm.org
Sat Jun 11 10:20:05 PDT 2022


Author: Dave Lee
Date: 2022-06-11T10:19:51-07:00
New Revision: c7a56af3072c2fa89f0968d7f00b22f7bff0812b

URL: https://github.com/llvm/llvm-project/commit/c7a56af3072c2fa89f0968d7f00b22f7bff0812b
DIFF: https://github.com/llvm/llvm-project/commit/c7a56af3072c2fa89f0968d7f00b22f7bff0812b.diff

LOG: [lldb][bindings] Implement __repr__ instead of __str__

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 `print`ing 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.

Differential Revision: https://reviews.llvm.org/D127458

Added: 
    lldb/test/API/sanity/TestReprStrEquality.py

Modified: 
    lldb/bindings/macros.swig
    lldb/test/Shell/Driver/Inputs/convenience.in
    lldb/test/Shell/Driver/TestConvenienceVariables.test

Removed: 
    


################################################################################
diff  --git a/lldb/bindings/macros.swig b/lldb/bindings/macros.swig
index 6793a11c5bd9c..84f2442457380 100644
--- a/lldb/bindings/macros.swig
+++ b/lldb/bindings/macros.swig
@@ -1,6 +1,6 @@
 %define STRING_EXTENSION_LEVEL(Class, Level)
 %extend {
-  std::string lldb:: ## Class ## ::__str__(){
+  std::string lldb:: ## Class ## ::__repr__(){
     lldb::SBStream stream;
     $self->GetDescription (stream, Level);
     const char *desc = stream.GetData();
@@ -15,7 +15,7 @@
 
 %define STRING_EXTENSION(Class)
 %extend {
-  std::string lldb:: ## Class ## ::__str__(){
+  std::string lldb:: ## Class ## ::__repr__(){
     lldb::SBStream stream;
     $self->GetDescription (stream);
     const char *desc = stream.GetData();

diff  --git a/lldb/test/API/sanity/TestReprStrEquality.py b/lldb/test/API/sanity/TestReprStrEquality.py
new file mode 100644
index 0000000000000..798c0f6d7eb8a
--- /dev/null
+++ b/lldb/test/API/sanity/TestReprStrEquality.py
@@ -0,0 +1,18 @@
+"""
+This is a sanity check that verifies that `repr(sbobject)` and `str(sbobject)`
+produce the same string.
+"""
+
+
+import lldb
+from lldbsuite.test.lldbtest import *
+
+
+class TestCase(TestBase):
+
+  mydir = TestBase.compute_mydir(__file__)
+
+  NO_DEBUG_INFO_TESTCASE = True
+
+  def test(self):
+    self.assertEqual(repr(self.dbg), str(self.dbg))

diff  --git a/lldb/test/Shell/Driver/Inputs/convenience.in b/lldb/test/Shell/Driver/Inputs/convenience.in
index 6d5603c435135..f68dc7423d8b8 100644
--- a/lldb/test/Shell/Driver/Inputs/convenience.in
+++ b/lldb/test/Shell/Driver/Inputs/convenience.in
@@ -1,8 +1,11 @@
 breakpoint set -f hello.cpp -p Hello
 run
 script print(lldb.debugger)
+script lldb.debugger
 script print(lldb.target)
+script lldb.target
 script print(lldb.process)
+script lldb.process
 script print(lldb.thread.GetStopDescription(100))
 script lldb.frame.GetLineEntry().GetLine()
 script lldb.frame.GetLineEntry().GetFileSpec().GetFilename()

diff  --git a/lldb/test/Shell/Driver/TestConvenienceVariables.test b/lldb/test/Shell/Driver/TestConvenienceVariables.test
index d9c8f7581bea0..63ba15ec7468c 100644
--- a/lldb/test/Shell/Driver/TestConvenienceVariables.test
+++ b/lldb/test/Shell/Driver/TestConvenienceVariables.test
@@ -6,10 +6,16 @@ RUN: %lldb %t/target.out -s %p/Inputs/convenience.in -o quit | FileCheck %s
 CHECK: stop reason = breakpoint 1.1
 CHECK: script print(lldb.debugger)
 CHECK-NEXT: Debugger (instance: {{.*}}, id: {{[0-9]+}})
+CHECK: script lldb.debugger
+CHECK-NEXT: Debugger (instance: {{.*}}, id: {{[0-9]+}})
 CHECK: script print(lldb.target)
 CHECK-NEXT: target.out
+CHECK: script lldb.target
+CHECK-NEXT: target.out
 CHECK: script print(lldb.process)
 CHECK-NEXT: SBProcess: pid = {{[0-9]+}}, state = stopped, threads = {{[0-9]+}}, executable = target.out
+CHECK: script lldb.process
+CHECK-NEXT: SBProcess: pid = {{[0-9]+}}, state = stopped, threads = {{[0-9]+}}, executable = target.out
 CHECK: script print(lldb.thread.GetStopDescription(100))
 CHECK-NEXT: breakpoint 1.1
 CHECK: script lldb.frame.GetLineEntry().GetLine()


        


More information about the lldb-commits mailing list