[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