Author: Med Ismail Bennani Date: 2026-02-14T04:43:11Z New Revision: cfc311ed789e87b9febb56095d4173a225ce7a64
URL: https://github.com/llvm/llvm-project/commit/cfc311ed789e87b9febb56095d4173a225ce7a64 DIFF: https://github.com/llvm/llvm-project/commit/cfc311ed789e87b9febb56095d4173a225ce7a64.diff LOG: [lldb/API] Add __getitem__ subscript support to python SBAPI list class (#181457) 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. Signed-off-by: Med Ismail Bennani <[email protected]> Added: Modified: lldb/bindings/interface/SBBreakpointListExtensions.i lldb/bindings/interface/SBFileSpecListExtensions.i lldb/bindings/interface/SBMemoryRegionInfoListExtensions.i lldb/bindings/interface/SBModuleSpecListExtensions.i lldb/bindings/interface/SBProcessInfoListExtensions.i lldb/bindings/interface/SBStringListExtensions.i lldb/bindings/interface/SBThreadCollectionExtensions.i lldb/bindings/interface/SBTypeExtensions.i lldb/test/API/functionalities/breakpoint/breakpoint_names/TestBreakpointNames.py lldb/test/API/functionalities/completion/TestCompletion.py lldb/test/API/lang/cpp/namespace/TestNamespace.py lldb/test/API/python_api/debugger/TestDebuggerAPI.py lldb/test/API/python_api/find_in_memory/TestFindInMemory.py lldb/test/API/python_api/sbmodule/TestSBModule.py lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py lldb/test/API/python_api/type/TestTypeList.py Removed: ################################################################################ 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..4a2b312321566 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,18 @@ def test_memory_info_list_iterable(self): collected_info[1].GetRegionBase(), "Different items should have diff erent 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) _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
