[Lldb-commits] [lldb] [lldb/API] Add __getitem__ subscript support to python SBAPI list class (PR #181457)

via lldb-commits lldb-commits at lists.llvm.org
Fri Feb 13 20:33:39 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: Med Ismail Bennani (medismailben)

<details>
<summary>Changes</summary>

This patch adds __getitem__ method to the SBAPI list classes that were missing subscript support, enabling Pythonic index access (e.g., list[0], list[-1]) in Python bindings.

The implementation adds __getitem__ to the following classes:
- SBStringList
- SBFileSpecList
- SBProcessInfoList
- SBMemoryRegionInfoList
- SBThreadCollection
- SBBreakpointList
- SBModuleSpecList
- SBTypeList

Each implementation follows the same pattern:
- Type validation (raises TypeError for non-integer indices)
- Range validation with negative index support (raises IndexError for out-of-range)
- Delegates to the appropriate Get*AtIndex() method

The changes are in SWIG interface extension files (.i), implementing the __getitem__ method in Python bindings while maintaining compatibility with existing Get*AtIndex() API.

---
Full diff: https://github.com/llvm/llvm-project/pull/181457.diff


16 Files Affected:

- (modified) lldb/bindings/interface/SBBreakpointListExtensions.i (+10) 
- (modified) lldb/bindings/interface/SBFileSpecListExtensions.i (+10) 
- (modified) lldb/bindings/interface/SBMemoryRegionInfoListExtensions.i (+13) 
- (modified) lldb/bindings/interface/SBModuleSpecListExtensions.i (+10) 
- (modified) lldb/bindings/interface/SBProcessInfoListExtensions.i (+10) 
- (modified) lldb/bindings/interface/SBStringListExtensions.i (+10) 
- (modified) lldb/bindings/interface/SBThreadCollectionExtensions.i (+10) 
- (modified) lldb/bindings/interface/SBTypeExtensions.i (+10) 
- (modified) lldb/test/API/functionalities/breakpoint/breakpoint_names/TestBreakpointNames.py (+2) 
- (modified) lldb/test/API/functionalities/completion/TestCompletion.py (+6) 
- (modified) lldb/test/API/lang/cpp/namespace/TestNamespace.py (+6) 
- (modified) lldb/test/API/python_api/debugger/TestDebuggerAPI.py (+1) 
- (modified) lldb/test/API/python_api/find_in_memory/TestFindInMemory.py (+16) 
- (modified) lldb/test/API/python_api/sbmodule/TestSBModule.py (+10) 
- (modified) lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py (+18) 
- (modified) lldb/test/API/python_api/type/TestTypeList.py (+10) 


``````````diff
diff --git a/lldb/bindings/interface/SBBreakpointListExtensions.i b/lldb/bindings/interface/SBBreakpointListExtensions.i
index adde0c678f49f..eb7b77af6fb59 100644
--- a/lldb/bindings/interface/SBBreakpointListExtensions.i
+++ b/lldb/bindings/interface/SBBreakpointListExtensions.i
@@ -8,6 +8,16 @@
     def __iter__(self):
         '''Iterate over all breakpoints in a lldb.SBBreakpointList object.'''
         return lldb_iter(self, 'GetSize', 'GetBreakpointAtIndex')
+
+    def __getitem__(self, idx):
+        '''Get the breakpoint at a given index in an lldb.SBBreakpointList object.'''
+        if not isinstance(idx, int):
+            raise TypeError("unsupported index type: %s" % type(idx))
+        count = len(self)
+        if not (-count <= idx < count):
+            raise IndexError("list index out of range")
+        idx %= count
+        return self.GetBreakpointAtIndex(idx)
     %}
 #endif
 }
diff --git a/lldb/bindings/interface/SBFileSpecListExtensions.i b/lldb/bindings/interface/SBFileSpecListExtensions.i
index 1e7b897a08d95..5a2e067de410d 100644
--- a/lldb/bindings/interface/SBFileSpecListExtensions.i
+++ b/lldb/bindings/interface/SBFileSpecListExtensions.i
@@ -10,6 +10,16 @@ STRING_EXTENSION_OUTSIDE(SBFileSpecList)
     def __iter__(self):
       '''Iterate over all FileSpecs in a lldb.SBFileSpecList object.'''
       return lldb_iter(self, 'GetSize', 'GetFileSpecAtIndex')
+
+    def __getitem__(self, idx):
+      '''Get the FileSpec at a given index in an lldb.SBFileSpecList object.'''
+      if not isinstance(idx, int):
+        raise TypeError("unsupported index type: %s" % type(idx))
+      count = len(self)
+      if not (-count <= idx < count):
+        raise IndexError("list index out of range")
+      idx %= count
+      return self.GetFileSpecAtIndex(idx)
     %}
 #endif
 }
diff --git a/lldb/bindings/interface/SBMemoryRegionInfoListExtensions.i b/lldb/bindings/interface/SBMemoryRegionInfoListExtensions.i
index f565f45880119..895ce7c00654a 100644
--- a/lldb/bindings/interface/SBMemoryRegionInfoListExtensions.i
+++ b/lldb/bindings/interface/SBMemoryRegionInfoListExtensions.i
@@ -13,6 +13,19 @@
         region = lldb.SBMemoryRegionInfo()
         self.GetMemoryRegionAtIndex(i, region)
         yield region
+
+    def __getitem__(self, idx):
+      '''Get the memory region at a given index in an lldb.SBMemoryRegionInfoList object.'''
+      if not isinstance(idx, int):
+        raise TypeError("unsupported index type: %s" % type(idx))
+      count = len(self)
+      if not (-count <= idx < count):
+        raise IndexError("list index out of range")
+      idx %= count
+      import lldb
+      region = lldb.SBMemoryRegionInfo()
+      self.GetMemoryRegionAtIndex(idx, region)
+      return region
     %}
 #endif
 }
diff --git a/lldb/bindings/interface/SBModuleSpecListExtensions.i b/lldb/bindings/interface/SBModuleSpecListExtensions.i
index ab51dc4498ad8..073e9d06738f5 100644
--- a/lldb/bindings/interface/SBModuleSpecListExtensions.i
+++ b/lldb/bindings/interface/SBModuleSpecListExtensions.i
@@ -10,6 +10,16 @@ STRING_EXTENSION_OUTSIDE(SBModuleSpecList)
     def __iter__(self):
       '''Iterate over all ModuleSpecs in a lldb.SBModuleSpecList object.'''
       return lldb_iter(self, 'GetSize', 'GetSpecAtIndex')
+
+    def __getitem__(self, idx):
+      '''Get the ModuleSpec at a given index in an lldb.SBModuleSpecList object.'''
+      if not isinstance(idx, int):
+        raise TypeError("unsupported index type: %s" % type(idx))
+      count = len(self)
+      if not (-count <= idx < count):
+        raise IndexError("list index out of range")
+      idx %= count
+      return self.GetSpecAtIndex(idx)
     %}
 #endif
 }
diff --git a/lldb/bindings/interface/SBProcessInfoListExtensions.i b/lldb/bindings/interface/SBProcessInfoListExtensions.i
index 42999846ef6a5..899686943dac2 100644
--- a/lldb/bindings/interface/SBProcessInfoListExtensions.i
+++ b/lldb/bindings/interface/SBProcessInfoListExtensions.i
@@ -8,6 +8,16 @@
     def __iter__(self):
       '''Iterate over all the process info in a lldb.SBProcessInfoListExtensions object.'''
       return lldb_iter(self, 'GetSize', 'GetProcessInfoAtIndex')
+
+    def __getitem__(self, idx):
+      '''Get the process info at a given index in an lldb.SBProcessInfoList object.'''
+      if not isinstance(idx, int):
+        raise TypeError("unsupported index type: %s" % type(idx))
+      count = len(self)
+      if not (-count <= idx < count):
+        raise IndexError("list index out of range")
+      idx %= count
+      return self.GetProcessInfoAtIndex(idx)
     %}
 #endif
 }
diff --git a/lldb/bindings/interface/SBStringListExtensions.i b/lldb/bindings/interface/SBStringListExtensions.i
index 04f72668b37fe..ff32a8213c7f5 100644
--- a/lldb/bindings/interface/SBStringListExtensions.i
+++ b/lldb/bindings/interface/SBStringListExtensions.i
@@ -8,6 +8,16 @@
     def __len__(self):
         '''Return the number of strings in a lldb.SBStringList object.'''
         return self.GetSize()
+
+    def __getitem__(self, idx):
+        '''Get the string at a given index in an lldb.SBStringList object.'''
+        if not isinstance(idx, int):
+            raise TypeError("unsupported index type: %s" % type(idx))
+        count = len(self)
+        if not (-count <= idx < count):
+            raise IndexError("list index out of range")
+        idx %= count
+        return self.GetStringAtIndex(idx)
     %}
 #endif
 }
diff --git a/lldb/bindings/interface/SBThreadCollectionExtensions.i b/lldb/bindings/interface/SBThreadCollectionExtensions.i
index 39a45a6f1bb2b..2a5fd098a1f90 100644
--- a/lldb/bindings/interface/SBThreadCollectionExtensions.i
+++ b/lldb/bindings/interface/SBThreadCollectionExtensions.i
@@ -9,6 +9,16 @@
     def __len__(self):
         '''Return the number of threads in a lldb.SBThreadCollection object.'''
         return self.GetSize()
+
+    def __getitem__(self, idx):
+        '''Get the thread at a given index in an lldb.SBThreadCollection object.'''
+        if not isinstance(idx, int):
+            raise TypeError("unsupported index type: %s" % type(idx))
+        count = len(self)
+        if not (-count <= idx < count):
+            raise IndexError("list index out of range")
+        idx %= count
+        return self.GetThreadAtIndex(idx)
     %}
 #endif
 }
diff --git a/lldb/bindings/interface/SBTypeExtensions.i b/lldb/bindings/interface/SBTypeExtensions.i
index 9c7d61497951d..85c9d5e26bf7e 100644
--- a/lldb/bindings/interface/SBTypeExtensions.i
+++ b/lldb/bindings/interface/SBTypeExtensions.i
@@ -158,6 +158,16 @@ STRING_EXTENSION_LEVEL_OUTSIDE(SBType, lldb::eDescriptionLevelBrief)
     def __len__(self):
         '''Return the number of types in a lldb.SBTypeList object.'''
         return self.GetSize()
+
+    def __getitem__(self, idx):
+        '''Get the type at a given index in an lldb.SBTypeList object.'''
+        if not isinstance(idx, int):
+            raise TypeError("unsupported index type: %s" % type(idx))
+        count = len(self)
+        if not (-count <= idx < count):
+            raise IndexError("list index out of range")
+        idx %= count
+        return self.GetTypeAtIndex(idx)
     %}
 #endif
 }
diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_names/TestBreakpointNames.py b/lldb/test/API/functionalities/breakpoint/breakpoint_names/TestBreakpointNames.py
index 33aa9877c8e59..cd1166c5734bd 100644
--- a/lldb/test/API/functionalities/breakpoint/breakpoint_names/TestBreakpointNames.py
+++ b/lldb/test/API/functionalities/breakpoint/breakpoint_names/TestBreakpointNames.py
@@ -201,6 +201,8 @@ def do_check_using_names(self):
         found_bkpt = bkpts.GetBreakpointAtIndex(0)
         self.assertEqual(bkpt.GetID(), found_bkpt.GetID(), "The right breakpoint.")
         self.assertEqual(bkpt.GetID(), bkpt_id, "With the same ID as before.")
+        self.assertEqual(bkpts[0].GetID(), bkpt.GetID(), "subscript [0] matches")
+        self.assertEqual(bkpts[-1].GetID(), bkpt.GetID(), "subscript [-1] matches")
 
         retval = lldb.SBCommandReturnObject()
         self.dbg.GetCommandInterpreter().HandleCommand(
diff --git a/lldb/test/API/functionalities/completion/TestCompletion.py b/lldb/test/API/functionalities/completion/TestCompletion.py
index 45750c7ac0817..555838775db8a 100644
--- a/lldb/test/API/functionalities/completion/TestCompletion.py
+++ b/lldb/test/API/functionalities/completion/TestCompletion.py
@@ -139,6 +139,12 @@ def completions_contain_str(self, input, needle):
         interp = self.dbg.GetCommandInterpreter()
         match_strings = lldb.SBStringList()
         num_matches = interp.HandleCompletion(input, len(input), 0, -1, match_strings)
+        if match_strings.GetSize() > 0:
+            self.assertEqual(match_strings[0], match_strings.GetStringAtIndex(0))
+            self.assertEqual(
+                match_strings[-1],
+                match_strings.GetStringAtIndex(match_strings.GetSize() - 1),
+            )
         found_needle = False
         for match in match_strings:
             if needle in match:
diff --git a/lldb/test/API/lang/cpp/namespace/TestNamespace.py b/lldb/test/API/lang/cpp/namespace/TestNamespace.py
index d790002dea072..486fad6e0f953 100644
--- a/lldb/test/API/lang/cpp/namespace/TestNamespace.py
+++ b/lldb/test/API/lang/cpp/namespace/TestNamespace.py
@@ -24,6 +24,12 @@ def test_breakpoints_func_auto(self):
         self.assertTrue(target, VALID_TARGET)
         module_list = lldb.SBFileSpecList()
         module_list.Append(lldb.SBFileSpec(exe, False))
+        self.assertEqual(
+            module_list[0].GetFilename(), lldb.SBFileSpec(exe, False).GetFilename()
+        )
+        self.assertEqual(
+            module_list[-1].GetFilename(), lldb.SBFileSpec(exe, False).GetFilename()
+        )
         cu_list = lldb.SBFileSpecList()
         # Set a breakpoint by name "func" which should pick up all functions
         # whose basename is "func"
diff --git a/lldb/test/API/python_api/debugger/TestDebuggerAPI.py b/lldb/test/API/python_api/debugger/TestDebuggerAPI.py
index 44b1183288017..600cde3c6b807 100644
--- a/lldb/test/API/python_api/debugger/TestDebuggerAPI.py
+++ b/lldb/test/API/python_api/debugger/TestDebuggerAPI.py
@@ -59,6 +59,7 @@ def get_cache_line_size():
             )
 
             self.assertEqual(value_list.GetSize(), 1)
+            self.assertEqual(value_list[0], value_list.GetStringAtIndex(0))
             try:
                 return int(value_list.GetStringAtIndex(0))
             except ValueError as error:
diff --git a/lldb/test/API/python_api/find_in_memory/TestFindInMemory.py b/lldb/test/API/python_api/find_in_memory/TestFindInMemory.py
index 74de46dee98a5..943a27e80be4d 100644
--- a/lldb/test/API/python_api/find_in_memory/TestFindInMemory.py
+++ b/lldb/test/API/python_api/find_in_memory/TestFindInMemory.py
@@ -186,3 +186,19 @@ def test_memory_info_list_iterable(self):
             collected_info[1].GetRegionBase(),
             "Different items should have different base addresses",
         )
+
+        self.assertEqual(
+            info_list[0].GetRegionBase(),
+            collected_info[0].GetRegionBase(),
+            "subscript [0] should match first collected item",
+        )
+        self.assertEqual(
+            info_list[-1].GetRegionBase(),
+            collected_info[-1].GetRegionBase(),
+            "subscript [-1] should match last collected item",
+        )
+        with self.assertRaises(IndexError):
+            info_list[info_list.GetSize()]
+        with self.assertRaises(TypeError):
+            info_list["invalid"]
+
diff --git a/lldb/test/API/python_api/sbmodule/TestSBModule.py b/lldb/test/API/python_api/sbmodule/TestSBModule.py
index 096eadd410cfe..1e6cdefc8d8f0 100644
--- a/lldb/test/API/python_api/sbmodule/TestSBModule.py
+++ b/lldb/test/API/python_api/sbmodule/TestSBModule.py
@@ -39,6 +39,16 @@ def test_GetObjectName(self):
         self.assertGreater(
             module_specs.GetSize(), 0, "Archive should have at least one module spec"
         )
+        self.assertEqual(
+            module_specs[0].GetObjectName(),
+            module_specs.GetSpecAtIndex(0).GetObjectName(),
+            "subscript [0] matches GetSpecAtIndex(0)",
+        )
+        self.assertEqual(
+            module_specs[-1].GetObjectName(),
+            module_specs.GetSpecAtIndex(module_specs.GetSize() - 1).GetObjectName(),
+            "subscript [-1] matches last item",
+        )
         found = set()
         expected = {"a.o", "b.o"}
         for i in range(module_specs.GetSize()):
diff --git a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py
index 92ca44ecbbffc..6593a9dfce6f8 100644
--- a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py
+++ b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py
@@ -104,6 +104,24 @@ def test_removing_and_adding_insertion_order(self):
         thread_collection = options.GetThreadsToSave()
         self.assertEqual(thread_collection.GetSize(), 3)
         self.assertIn(middle_thread, thread_collection)
+        self.assertEqual(
+            thread_collection[0].GetThreadID(),
+            thread_collection.GetThreadAtIndex(0).GetThreadID(),
+        )
+        self.assertEqual(
+            thread_collection[1].GetThreadID(),
+            thread_collection.GetThreadAtIndex(1).GetThreadID(),
+        )
+        self.assertEqual(
+            thread_collection[-1].GetThreadID(),
+            thread_collection.GetThreadAtIndex(
+                thread_collection.GetSize() - 1
+            ).GetThreadID(),
+        )
+        with self.assertRaises(IndexError):
+            thread_collection[thread_collection.GetSize()]
+        with self.assertRaises(TypeError):
+            thread_collection["invalid"]
 
     def test_get_current_size_in_bytes(self):
         """
diff --git a/lldb/test/API/python_api/type/TestTypeList.py b/lldb/test/API/python_api/type/TestTypeList.py
index 09879276b44aa..b49f9027d0dd9 100644
--- a/lldb/test/API/python_api/type/TestTypeList.py
+++ b/lldb/test/API/python_api/type/TestTypeList.py
@@ -110,6 +110,16 @@ def test(self):
             )
         # a second Task make be scared up by the Objective-C runtime
         self.assertGreaterEqual(len(type_list), 1)
+        self.assertEqual(
+            type_list[0].GetName(),
+            type_list.GetTypeAtIndex(0).GetName(),
+            "subscript [0] matches GetTypeAtIndex(0)",
+        )
+        self.assertEqual(
+            type_list[-1].GetName(),
+            type_list.GetTypeAtIndex(type_list.GetSize() - 1).GetName(),
+            "subscript [-1] matches last item",
+        )
         for type in type_list:
             self.assertTrue(type)
             self.DebugSBType(type)

``````````

</details>


https://github.com/llvm/llvm-project/pull/181457


More information about the lldb-commits mailing list