[Lldb-commits] [lldb] [lldb][Python] Enable type annotations for most types (PR #213463)
via lldb-commits
lldb-commits at lists.llvm.org
Thu Aug 6 09:57:22 PDT 2026
https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/213463
>From 8501030fbc22a629261a91d91eeba33810bf1c1b Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Sat, 1 Aug 2026 17:49:14 +0200
Subject: [PATCH 1/2] [lldb][Python] Enable type annotations for most types
---
lldb/bindings/python/python-typemaps.swig | 41 +++++++++++++++
lldb/bindings/python/python.swig | 62 +++++++++++++++++++++++
2 files changed, 103 insertions(+)
diff --git a/lldb/bindings/python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig
index 072e688c4bde1..37f44c619883c 100644
--- a/lldb/bindings/python/python-typemaps.swig
+++ b/lldb/bindings/python/python-typemaps.swig
@@ -53,6 +53,8 @@ AND call SWIG_fail at the same time, because it will result in a double free.
free((char *) $1);
}
+%typemap(pytyping, out="typing.List[str]") char** "typing.Optional[typing.List[str]]"
+
%typecheck(SWIG_TYPECHECK_POINTER) lldb::ScriptObjectPtr {
PythonObject obj(PyRefType::Borrowed, $input);
if (!obj.IsValid()) {
@@ -122,6 +124,8 @@ AND call SWIG_fail at the same time, because it will result in a double free.
Py_INCREF($result);
}
+%typemap(pytyping) lldb::ScriptObjectPtr "typing.Any"
+
%typemap(out) lldb::SBScriptObject {
$result = nullptr;
if (const void* impl = $1.GetPointer())
@@ -134,6 +138,8 @@ AND call SWIG_fail at the same time, because it will result in a double free.
}
}
+%typemap(pytyping) lldb::SBScriptObject "typing.Any"
+
%typemap(out) char** {
int len;
int i;
@@ -272,6 +278,10 @@ AND call SWIG_fail at the same time, because it will result in a double free.
SWIG_fail;
}
}
+
+%typemap(pytyping) (const char *cstr, uint32_t cstr_len),
+ (const char *src, size_t src_len) "typing.Union[str, bytes, bytearray]"
+
// For SBProcess::WriteMemory, SBTarget::GetInstructions and SBDebugger::DispatchInput.
%typemap(in) (const void *buf, size_t size),
(const void *data, size_t data_len),
@@ -294,6 +304,10 @@ AND call SWIG_fail at the same time, because it will result in a double free.
}
}
+%typemap(pytyping) (const void *buf, size_t size),
+ (const void *data, size_t data_len),
+ (const void *buf, uint64_t size) "typing.Union[str, bytes, bytearray]"
+
// typemap for an incoming buffer
// See also SBProcess::ReadMemory.
%typemap(in) (void *buf, size_t size) {
@@ -410,6 +424,12 @@ template <> bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
free($1);
}
+%typemap(pytyping) (uint64_t* array, size_t array_len),
+ (uint32_t* array, size_t array_len),
+ (int64_t* array, size_t array_len),
+ (int32_t* array, size_t array_len) "typing.Optional[typing.List[int]]"
+%typemap(pytyping) (double* array, size_t array_len) "typing.Optional[typing.List[float]]"
+
// these typemaps wrap SBModule::GetVersion() from requiring a memory buffer
// to the more Pythonic style where a list is returned and no previous allocation
// is necessary - this will break if more than 50 versions are ever returned
@@ -469,6 +489,8 @@ template <> bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
$1 = $1 || PyCallable_Check(reinterpret_cast<PyObject *>($input));
}
+%typemap(pytyping) (lldb::LogOutputCallback log_callback, void *baton) "typing.Optional[typing.Callable[[str], None]]"
+
// For lldb::SBDebuggerDestroyCallback
%typemap(in) (lldb::SBDebuggerDestroyCallback destroy_callback, void *baton) {
if (!($input == Py_None ||
@@ -493,6 +515,8 @@ template <> bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
$1 = $1 || PyCallable_Check(reinterpret_cast<PyObject *>($input));
}
+%typemap(pytyping) (lldb::SBDebuggerDestroyCallback destroy_callback, void *baton) "typing.Optional[typing.Callable[[int], None]]"
+
// For lldb::SBCommandPrintCallback
%typemap(in) (lldb::SBCommandPrintCallback callback, void *baton) {
if (!($input == Py_None ||
@@ -512,6 +536,8 @@ template <> bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
$1 = $1 || PyCallable_Check(reinterpret_cast<PyObject *>($input));
}
+%typemap(pytyping) (lldb::SBCommandPrintCallback callback, void *baton) "typing.Optional[typing.Callable[[SBCommandReturnObject], int]]"
+
%typemap(in) (lldb::CommandOverrideCallback callback, void *baton) {
if (!($input == Py_None ||
PyCallable_Check(reinterpret_cast<PyObject *>($input)))) {
@@ -529,6 +555,8 @@ template <> bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
$1 = $1 || PyCallable_Check(reinterpret_cast<PyObject *>($input));
}
+%typemap(pytyping) (lldb::CommandOverrideCallback callback, void *baton) "typing.Optional[typing.Callable[[typing.List[str]], bool]]"
+
%typemap(in) lldb::FileSP {
PythonFile py_file(PyRefType::Borrowed, $input);
if (!py_file) {
@@ -604,6 +632,8 @@ template <> bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
}
}
+%typemap(pytyping) lldb::FileSP "typing.IO"
+
%typemap(in) (const char* string, int len) {
if ($input == Py_None) {
$1 = NULL;
@@ -628,6 +658,7 @@ template <> bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
}
}
+%typemap(pytyping) (const char* string, int len) "typing.Optional[typing.List[str]]"
// Typemap for SBFile::Write.
%typemap(in) (const uint8_t *buf, size_t num_bytes) {
@@ -645,6 +676,8 @@ template <> bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
}
}
+%typemap(pytyping) (const uint8_t *buf, size_t num_bytes) "typing.Union[bytes, bytearray]"
+
// Typemap for SBFile::Read.
%typemap(in) (uint8_t *buf, size_t num_bytes) {
if (PythonByteArray::Check($input)) {
@@ -657,6 +690,8 @@ template <> bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
}
}
+%typemap(pytyping) (uint8_t *buf, size_t num_bytes) "bytearray"
+
%typemap(in) (const char **symbol_name, uint32_t num_names) {
using namespace lldb_private;
/* Check if is a list */
@@ -684,6 +719,8 @@ template <> bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
}
}
+%typemap(pytyping) (const char **symbol_name, uint32_t num_names) "typing.Optional[typing.List[str]]"
+
// For lldb::SBPlatformLocateModuleCallback
%typemap(in) (lldb::SBPlatformLocateModuleCallback callback,
void *callback_baton) {
@@ -734,3 +771,7 @@ template <> bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
$1 = $input == Py_None;
$1 = $1 || PyCallable_Check(reinterpret_cast<PyObject *>($input));
}
+
+%typemap(pytyping)
+ (lldb::SBPlatformLocateModuleCallback callback, void *callback_baton)
+ "typing.Optional[typing.Callable[[SBModuleSpec, SBFileSpec, SBFileSpec], SBError]]"
diff --git a/lldb/bindings/python/python.swig b/lldb/bindings/python/python.swig
index 1c9e7a24e5287..6dd4ad388c606 100644
--- a/lldb/bindings/python/python.swig
+++ b/lldb/bindings/python/python.swig
@@ -64,6 +64,68 @@ except ImportError:
// Parameter types will be used in the autodoc string.
%feature("autodoc", "1");
+#if SWIG_VERSION >= 0x040500
+// Swig 4.5.0 added typing annotations. Use them.
+%feature("python:annotations", "typing");
+
+// The default pytyping entry maps to `typing.Any`. Use the proxy names here.
+// They might generate incorrect annotations (`SWIGTYPE_*`) in more complex scenarios.
+%typemap(pytyping) SWIGTYPE "$&pytypename";
+%typemap(pytyping) SWIGTYPE [] "typing.Optional[$pytypename]";
+%typemap(pytyping) SWIGTYPE * "typing.Optional[$pytypename]";
+%typemap(pytyping) SWIGTYPE & "$pytypename";
+%typemap(pytyping) SWIGTYPE && "$pytypename";
+%typemap(pytyping) enum SWIGTYPE "int";
+
+// But disable the annotations for the following functions.
+// When the support matures, we can annotate these.
+
+// Exclude operator==/!=. They generate `__eq__(self, rhs: T) -> bool`.
+// However, `rhs` should be `object`.
+%feature("python:annotations", "0") operator==;
+%feature("python:annotations", "0") operator!=;
+
+// Uses (char *dst, size_t dst_len) argout typemap.
+// Should return `str`, but annotated as `int`.
+%feature("python:annotations", "0") lldb::SBProcess::GetSTDOUT;
+%feature("python:annotations", "0") lldb::SBProcess::GetSTDERR;
+%feature("python:annotations", "0") lldb::SBProcess::GetAsyncProfileData;
+%feature("python:annotations", "0") lldb::SBStructuredData::GetStringValue;
+
+// Uses (char *dst_or_null, size_t dst_len) argout typemap.
+// Should return `str`, but annotated as `int`.
+%feature("python:annotations", "0") lldb::SBThread::GetStopDescription;
+
+// Uses (char *dst_path, size_t dst_len) argout typemap.
+// Should return `str`, but annotated as `int`.
+%feature("python:annotations", "0") lldb::SBFileSpec::GetPath;
+%feature("python:annotations", "0") lldb::SBFileSpec::ResolvePath;
+
+// Uses (void *buf, size_t size) argout typemap.
+// Should return `typing.Optional[bytes]`, but annotated as `int`.
+%feature("python:annotations", "0") lldb::SBProcess::ReadMemory;
+%feature("python:annotations", "0") lldb::SBTarget::ReadMemory;
+
+// Uses (void *char_buf, size_t size) argout typemap.
+// Should return `str`, but annotated as `int`.
+%feature("python:annotations", "0") lldb::SBProcess::ReadCStringFromMemory;
+
+// Uses (uint32_t *versions, uint32_t num_versions) argout typemap.
+// Should return `typing.List[int]`, but annotated as `int`.
+%feature("python:annotations", "0") lldb::SBModule::GetVersion;
+
+// Uses INPUT/OUTPUT/INOUT. Not supported by the typing annotations right now.
+%feature("python:annotations", "0") lldb::SBDebugger::RunCommandInterpreter;
+%feature("python:annotations", "0") lldb::SBDebugger::GetProgressFromEvent;
+%feature("python:annotations", "0") lldb::SBFile::Read;
+%feature("python:annotations", "0") lldb::SBFile::Write;
+#else
+// Make sure `typing` is imported in all cases (Swig < 4.5.0).
+%pythoncode %{
+import typing
+%}
+#endif // SWIG_VERSION >= 0x040500
+
// Include lldb-python first as it sets Py_LIMITED_API.
%begin %{
#include "../source/Plugins/ScriptInterpreter/Python/lldb-python.h"
>From 46f31f103ddfe9b05b8dc639bee6c586e14a3fe1 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Thu, 6 Aug 2026 18:57:05 +0200
Subject: [PATCH 2/2] fix: review comments
---
lldb/bindings/python/python-typemaps.swig | 29 ++++++++++++++++++
lldb/bindings/python/python.swig | 37 ++---------------------
2 files changed, 31 insertions(+), 35 deletions(-)
diff --git a/lldb/bindings/python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig
index 37f44c619883c..d148bc27ad413 100644
--- a/lldb/bindings/python/python-typemaps.swig
+++ b/lldb/bindings/python/python-typemaps.swig
@@ -210,6 +210,16 @@ AND call SWIG_fail at the same time, because it will result in a double free.
// as char data instead of byte data.
%typemap(argout) (void *char_buf, size_t size) = (char *dst, size_t dst_len);
+// Uses (char *dst, size_t dst_len) argout typemap.
+// Should return `str`, but annotated as `typing.List[typing.Union[int, typing.Optional[str]]]`.
+%feature("python:annotations", "0") lldb::SBProcess::GetSTDOUT;
+%feature("python:annotations", "0") lldb::SBProcess::GetSTDERR;
+%feature("python:annotations", "0") lldb::SBProcess::GetAsyncProfileData;
+%feature("python:annotations", "0") lldb::SBStructuredData::GetStringValue;
+
+// Uses (void *char_buf, size_t size) argout typemap.
+// Should return `str`, but annotated as `typing.List[typing.Union[int, typing.Optional[SWIGTYPE_p_void]]]`.
+%feature("python:annotations", "0") lldb::SBProcess::ReadCStringFromMemory;
// typemap for handling an snprintf-like API like SBThread::GetStopDescription.
%typemap(in) (char *dst_or_null, size_t dst_len) {
@@ -250,11 +260,20 @@ AND call SWIG_fail at the same time, because it will result in a double free.
free($1);
}
+// Uses (char *dst_or_null, size_t dst_len) argout typemap.
+// Should return `str`, but annotated as `typing.List[typing.Union[int, typing.Optional[str]]]`.
+%feature("python:annotations", "0") lldb::SBThread::GetStopDescription;
+
// For lldb::SBFileSpec::GetPath
%typemap(in) (char *dst_path, size_t dst_len) = (char *dst_or_null, size_t dst_len);
%typemap(argout) (char *dst_path, size_t dst_len) = (char *dst_or_null, size_t dst_len);
+// Uses (char *dst_path, size_t dst_len) argout typemap.
+// Should return `str`, but annotated as `typing.List[typing.Union[int, typing.Optional[str]]]`.
+%feature("python:annotations", "0") lldb::SBFileSpec::GetPath;
+%feature("python:annotations", "0") lldb::SBFileSpec::ResolvePath;
+
// typemap for an outgoing buffer
// See also SBEvent::SBEvent(uint32_t event, const char *cstr, uint32_t cstr_len).
@@ -338,6 +357,12 @@ AND call SWIG_fail at the same time, because it will result in a double free.
free($1);
}
+// Uses (void *buf, size_t size) argout typemap.
+// Should return `typing.Optional[bytes]`, but annotated as `typing.List[typing.Union[int, typing.Optional[SWIGTYPE_p_void]]]`.
+%feature("python:annotations", "0") lldb::SBProcess::ReadMemory;
+%feature("python:annotations", "0") lldb::SBTarget::ReadMemory;
+%feature("python:annotations", "0") lldb::SBData::ReadRawData;
+
%{
namespace {
template <class T>
@@ -465,6 +490,10 @@ template <> bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
free($1);
}
+// Uses (uint32_t *versions, uint32_t num_versions) argout typemap.
+// Should return `typing.List[int]`, but annotated as `typing.List[typing.Union[int, typing.Optional[SWIGTYPE_p_unsigned_int]]]`.
+%feature("python:annotations", "0") lldb::SBModule::GetVersion;
+
// For Log::LogOutputCallback
%typemap(in) (lldb::LogOutputCallback log_callback, void *baton) {
if (!($input == Py_None ||
diff --git a/lldb/bindings/python/python.swig b/lldb/bindings/python/python.swig
index 6dd4ad388c606..a44f5746e5a54 100644
--- a/lldb/bindings/python/python.swig
+++ b/lldb/bindings/python/python.swig
@@ -65,7 +65,6 @@ except ImportError:
%feature("autodoc", "1");
#if SWIG_VERSION >= 0x040500
-// Swig 4.5.0 added typing annotations. Use them.
%feature("python:annotations", "typing");
// The default pytyping entry maps to `typing.Any`. Use the proxy names here.
@@ -77,46 +76,14 @@ except ImportError:
%typemap(pytyping) SWIGTYPE && "$pytypename";
%typemap(pytyping) enum SWIGTYPE "int";
-// But disable the annotations for the following functions.
-// When the support matures, we can annotate these.
-
// Exclude operator==/!=. They generate `__eq__(self, rhs: T) -> bool`.
// However, `rhs` should be `object`.
%feature("python:annotations", "0") operator==;
%feature("python:annotations", "0") operator!=;
-// Uses (char *dst, size_t dst_len) argout typemap.
-// Should return `str`, but annotated as `int`.
-%feature("python:annotations", "0") lldb::SBProcess::GetSTDOUT;
-%feature("python:annotations", "0") lldb::SBProcess::GetSTDERR;
-%feature("python:annotations", "0") lldb::SBProcess::GetAsyncProfileData;
-%feature("python:annotations", "0") lldb::SBStructuredData::GetStringValue;
-
-// Uses (char *dst_or_null, size_t dst_len) argout typemap.
-// Should return `str`, but annotated as `int`.
-%feature("python:annotations", "0") lldb::SBThread::GetStopDescription;
-
-// Uses (char *dst_path, size_t dst_len) argout typemap.
-// Should return `str`, but annotated as `int`.
-%feature("python:annotations", "0") lldb::SBFileSpec::GetPath;
-%feature("python:annotations", "0") lldb::SBFileSpec::ResolvePath;
-
-// Uses (void *buf, size_t size) argout typemap.
-// Should return `typing.Optional[bytes]`, but annotated as `int`.
-%feature("python:annotations", "0") lldb::SBProcess::ReadMemory;
-%feature("python:annotations", "0") lldb::SBTarget::ReadMemory;
-
-// Uses (void *char_buf, size_t size) argout typemap.
-// Should return `str`, but annotated as `int`.
-%feature("python:annotations", "0") lldb::SBProcess::ReadCStringFromMemory;
-
-// Uses (uint32_t *versions, uint32_t num_versions) argout typemap.
-// Should return `typing.List[int]`, but annotated as `int`.
-%feature("python:annotations", "0") lldb::SBModule::GetVersion;
-
-// Uses INPUT/OUTPUT/INOUT. Not supported by the typing annotations right now.
-%feature("python:annotations", "0") lldb::SBDebugger::RunCommandInterpreter;
+// OUTPUT/INOUT is not supported by the typing annotations right now.
%feature("python:annotations", "0") lldb::SBDebugger::GetProgressFromEvent;
+%feature("python:annotations", "0") lldb::SBDebugger::RunCommandInterpreter;
%feature("python:annotations", "0") lldb::SBFile::Read;
%feature("python:annotations", "0") lldb::SBFile::Write;
#else
More information about the lldb-commits
mailing list