aboutsummaryrefslogtreecommitdiff
path: root/lldb
diff options
context:
space:
mode:
Diffstat (limited to 'lldb')
-rw-r--r--lldb/bindings/python/python-wrapper.swig73
-rw-r--r--lldb/docs/use/python-reference.rst185
-rw-r--r--lldb/examples/python/cmdtemplate.py15
-rw-r--r--lldb/examples/python/templates/parsed_cmd.py97
-rw-r--r--lldb/include/lldb/Interpreter/ScriptInterpreter.h14
-rw-r--r--lldb/include/lldb/Utility/CompletionRequest.h2
-rw-r--r--lldb/source/Commands/CommandObjectCommands.cpp191
-rw-r--r--lldb/source/Interpreter/Options.cpp5
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h9
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp40
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h8
-rw-r--r--lldb/test/API/commands/command/script/add/TestAddParsedCommand.py132
-rw-r--r--lldb/test/API/commands/command/script/add/test_commands.py69
-rw-r--r--lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp13
14 files changed, 771 insertions, 82 deletions
diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index 961fb2d..b72a462 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -667,6 +667,79 @@ lldb_private::python::SWIGBridge::LLDBSwigPythonGetRepeatCommandForScriptedComma
return result.Str().GetString().str();
}
+StructuredData::DictionarySP
+lldb_private::python::SWIGBridge::LLDBSwigPythonHandleArgumentCompletionForScriptedCommand(PyObject *implementor,
+ std::vector<llvm::StringRef> &args_vec, size_t args_pos, size_t pos_in_arg) {
+
+ PyErr_Cleaner py_err_cleaner(true);
+
+ PythonObject self(PyRefType::Borrowed, implementor);
+ auto pfunc = self.ResolveName<PythonCallable>("handle_argument_completion");
+ // If this isn't implemented, return an empty dict to signal falling back to default completion:
+ if (!pfunc.IsAllocated())
+ return {};
+
+ PythonList args_list(PyInitialValue::Empty);
+ for (auto elem : args_vec)
+ args_list.AppendItem(PythonString(elem));
+
+ PythonObject result = pfunc(args_list, PythonInteger(args_pos), PythonInteger(pos_in_arg));
+ // Returning None means do the ordinary completion
+ if (result.IsNone())
+ return {};
+
+ // Convert the return dictionary to a DictionarySP.
+ StructuredData::ObjectSP result_obj_sp = result.CreateStructuredObject();
+ if (!result_obj_sp)
+ return {};
+
+ StructuredData::DictionarySP dict_sp(new StructuredData::Dictionary(result_obj_sp));
+ if (dict_sp->GetType() == lldb::eStructuredDataTypeInvalid)
+ return {};
+ return dict_sp;
+}
+
+StructuredData::DictionarySP
+lldb_private::python::SWIGBridge::LLDBSwigPythonHandleOptionArgumentCompletionForScriptedCommand(PyObject *implementor,
+ llvm::StringRef &long_option, size_t pos_in_arg) {
+
+ PyErr_Cleaner py_err_cleaner(true);
+
+ PythonObject self(PyRefType::Borrowed, implementor);
+ auto pfunc = self.ResolveName<PythonCallable>("handle_option_argument_completion");
+ // If this isn't implemented, return an empty dict to signal falling back to default completion:
+ if (!pfunc.IsAllocated())
+ return {};
+
+ PythonObject result = pfunc(PythonString(long_option), PythonInteger(pos_in_arg));
+ // Returning None means do the ordinary completion
+ if (result.IsNone())
+ return {};
+
+ // Returning a boolean:
+ // True means the completion was handled, but there were no completions
+ // False means that the completion was not handled, again, do the ordinary completion:
+ if (result.GetObjectType() == PyObjectType::Boolean) {
+ if (!result.IsTrue())
+ return {};
+ // Make up a completion dictionary with the right element:
+ StructuredData::DictionarySP dict_sp(new StructuredData::Dictionary());
+ dict_sp->AddBooleanItem("no-completion", true);
+ return dict_sp;
+ }
+
+
+ // Convert the return dictionary to a DictionarySP.
+ StructuredData::ObjectSP result_obj_sp = result.CreateStructuredObject();
+ if (!result_obj_sp)
+ return {};
+
+ StructuredData::DictionarySP dict_sp(new StructuredData::Dictionary(result_obj_sp));
+ if (dict_sp->GetType() == lldb::eStructuredDataTypeInvalid)
+ return {};
+ return dict_sp;
+}
+
#include "lldb/Interpreter/CommandReturnObject.h"
bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
diff --git a/lldb/docs/use/python-reference.rst b/lldb/docs/use/python-reference.rst
index b12048f..95a6020 100644
--- a/lldb/docs/use/python-reference.rst
+++ b/lldb/docs/use/python-reference.rst
@@ -551,7 +551,7 @@ command definition form can't do the right thing.
Since lldb 3.7, Python commands can also be implemented by means of a class
which should implement the following interface:
-::
+.. code-block:: python
class CommandObjectType:
def __init__(self, debugger, internal_dict):
@@ -586,20 +586,193 @@ which should implement the following interface:
As a convenience, you can treat the result object as a Python file object, and
say
-::
+.. code-block:: python
print >>result, "my command does lots of cool stuff"
SBCommandReturnObject and SBStream both support this file-like behavior by
providing write() and flush() calls at the Python layer.
+The commands that are added using this class definition are what lldb calls
+"raw" commands. The command interpreter doesn't attempt to parse the command,
+doesn't handle option values, neither generating help for them, or their
+completion. Raw commands are useful when the arguments passed to the command
+are unstructured, and having to protect them against lldb command parsing would
+be onerous. For instance, "expr" is a raw command.
+
+You can also add scripted commands that implement the "parsed command", where
+the options and their types are specified, as well as the argument and argument
+types. These commands look and act like the majority of lldb commands, and you
+can also add custom completions for the options and/or the arguments if you have
+special needs.
+
+The easiest way to do this is to derive your new command from the lldb.ParsedCommand
+class. That responds in the same way to the help & repeat command interfaces, and
+provides some convenience methods, and most importantly an LLDBOptionValueParser,
+accessed throught lldb.ParsedCommand.get_parser(). The parser is used to set
+your command definitions, and to retrieve option values in the __call__ method.
+
+To set up the command definition, implement the ParsedCommand abstract method:
+
+.. code-block:: python
+
+ def setup_command_definition(self):
+
+This is called when your command is added to lldb. In this method you add the
+options and their types, the option help strings, etc. to the command using the API:
+
+.. code-block:: python
+
+ def add_option(self, short_option, long_option, help, default,
+ dest = None, required=False, groups = None,
+ value_type=lldb.eArgTypeNone, completion_type=None,
+ enum_values=None):
+ """
+ short_option: one character, must be unique, not required
+ long_option: no spaces, must be unique, required
+ help: a usage string for this option, will print in the command help
+ default: the initial value for this option (if it has a value)
+ dest: the name of the property that gives you access to the value for
+ this value. Defaults to the long option if not provided.
+ required: if true, this option must be provided or the command will error out
+ groups: Which "option groups" does this option belong to. This can either be
+ a simple list (e.g. [1, 3, 4, 5]) or you can specify ranges by sublists:
+ so [1, [3,5]] is the same as [1, 3, 4, 5].
+ value_type: one of the lldb.eArgType enum values. Some of the common arg
+ types also have default completers, which will be applied automatically.
+ completion_type: currently these are values form the lldb.CompletionType enum. If
+ you need custom completions, implement handle_option_argument_completion.
+ enum_values: An array of duples: ["element_name", "element_help"]. If provided,
+ only one of the enum elements is allowed. The value will be the
+ element_name for the chosen enum element as a string.
+ """
+
+Similarly, you can add argument types to the command:
+
+.. code-block:: python
+
+ def make_argument_element(self, arg_type, repeat = "optional", groups = None):
+ """
+ arg_type: The argument type, one of the lldb.eArgType enum values.
+ repeat: Choose from the following options:
+ "plain" - one value
+ "optional" - zero or more values
+ "plus" - one or more values
+ groups: As with add_option.
+ """
+
+Then implement the body of the command by defining:
+
+.. code-block:: python
+
+ def __call__(self, debugger, args_array, exe_ctx, result):
+ """This is the command callback. The option values are
+ provided by the 'dest' properties on the parser.
+
+ args_array: This is the list of arguments provided.
+ exe_ctx: Gives the SBExecutionContext on which the
+ command should operate.
+ result: Any results of the command should be
+ written into this SBCommandReturnObject.
+ """
+
+This differs from the "raw" command's __call__ in that the arguments are already
+parsed into the args_array, and the option values are set in the parser, and
+can be accessed using their property name. The LLDBOptionValueParser class has
+a couple of other handy methods:
+
+.. code-block:: python
+ def was_set(self, long_option_name):
+
+returns True if the option was specified on the command line.
+
+.. code-block:: python
+
+ def dest_for_option(self, long_option_name):
+ """
+ This will return the value of the dest variable you defined for opt_name.
+ Mostly useful for handle_completion where you get passed the long option.
+ """
+
+lldb will handle completing your option names, and all your enum values
+automatically. If your option or argument types have associated built-in completers,
+then lldb will also handle that completion for you. But if you have a need for
+custom completions, either in your arguments or option values, you can handle
+completion by hand as well. To handle completion of option value arguments,
+your lldb.ParsedCommand subclass should implement:
+
+.. code-block:: python
+
+ def handle_option_argument_completion(self, long_option, cursor_pos):
+ """
+ long_option: The long option name of the option whose value you are
+ asked to complete.
+ cursor_pos: The cursor position in the value for that option - which
+ you can get from the option parser.
+ """
+
+And to handle the completion of arguments:
+
+.. code-block:: python
+
+ def handle_argument_completion(self, args, arg_pos, cursor_pos):
+ """
+ args: A list of the arguments to the command
+ arg_pos: An index into the args list of the argument with the cursor
+ cursor_pos: The cursor position in the arg specified by arg_pos
+ """
+
+When either of these API's is called, the command line will have been parsed up to
+the word containing the cursor, and any option values set in that part of the command
+string are available from the option value parser. That's useful for instance
+if you have a --shared-library option that would constrain the completions for,
+say, a symbol name option or argument.
+
+The return value specifies what the completion options are. You have four
+choices:
+
+- `True`: the completion was handled with no completions.
+
+- `False`: the completion was not handled, forward it to the regular
+completion machinery.
+
+- A dictionary with the key: "completion": there is one candidate,
+whose value is the value of the "completion" key. Optionally you can pass a
+"mode" key whose value is either "partial" or "complete". Return partial if
+the "completion" string is a prefix for all the completed value.
+
+For instance, if the string you are completing is "Test" and the available completions are:
+"Test1", "Test11" and "Test111", you should return the dictionary:
+
+.. code-block:: python
+
+ return {"completion": "Test1", "mode" : "partial"}
+
+and then lldb will add the "1" at the curson and advance it after the added string,
+waiting for more completions. But if "Test1" is the only completion, return:
+
+.. code-block:: python
+
+ {"completion": "Test1", "mode": "complete"}
+
+and lldb will add "1 " at the cursor, indicating the command string is complete.
+
+The default is "complete", you don't need to specify a "mode" in that case.
+
+- A dictionary with the key: "values" whose value is a list of candidate completion
+strings. The command interpreter will present those strings as the available choices.
+You can optionally include a "descriptions" key, whose value is a parallel array
+of description strings, and the completion will show the description next to
+each completion.
+
+
One other handy convenience when defining lldb command-line commands is the
-command command script import which will import a module specified by file
+command "command script import" which will import a module specified by file
path, so you don't have to change your PYTHONPATH for temporary scripts. It
also has another convenience that if your new script module has a function of
the form:
-::
+.. code-block python
def __lldb_init_module(debugger, internal_dict):
# Command Initialization code goes here
@@ -615,7 +788,7 @@ creating scripts that can be run from the command line. However, for command
line scripts, the debugger instance must be created manually. Sample code would
look like:
-::
+.. code-block:: python
if __name__ == '__main__':
# Initialize the debugger before making any API calls.
@@ -638,7 +811,7 @@ look like:
Now we can create a module called ls.py in the file ~/ls.py that will implement
a function that can be used by LLDB's python command code:
-::
+.. code-block:: python
#!/usr/bin/env python
diff --git a/lldb/examples/python/cmdtemplate.py b/lldb/examples/python/cmdtemplate.py
index b6a21cb..a9fbe0b 100644
--- a/lldb/examples/python/cmdtemplate.py
+++ b/lldb/examples/python/cmdtemplate.py
@@ -29,8 +29,8 @@ class FrameStatCommand(ParsedCommand):
return lldb.eCommandRequiresFrame | lldb.eCommandProcessMustBePaused
def setup_command_definition(self):
-
- self.ov_parser.add_option(
+ ov_parser = self.get_parser()
+ ov_parser.add_option(
"i",
"in-scope",
help = "in_scope_only = True",
@@ -39,7 +39,7 @@ class FrameStatCommand(ParsedCommand):
default = True,
)
- self.ov_parser.add_option(
+ ov_parser.add_option(
"i",
"in-scope",
help = "in_scope_only = True",
@@ -48,7 +48,7 @@ class FrameStatCommand(ParsedCommand):
default=True,
)
- self.ov_parser.add_option(
+ ov_parser.add_option(
"a",
"arguments",
help = "arguments = True",
@@ -57,7 +57,7 @@ class FrameStatCommand(ParsedCommand):
default = True,
)
- self.ov_parser.add_option(
+ ov_parser.add_option(
"l",
"locals",
help = "locals = True",
@@ -66,7 +66,7 @@ class FrameStatCommand(ParsedCommand):
default = True,
)
- self.ov_parser.add_option(
+ ov_parser.add_option(
"s",
"statics",
help = "statics = True",
@@ -103,8 +103,9 @@ class FrameStatCommand(ParsedCommand):
result.SetError("invalid frame")
return
+ ov_parser = self.get_parser()
variables_list = frame.GetVariables(
- self.ov_parser.arguments, self.ov_parser.locals, self.ov_parser.statics, self.ov_parser.inscope
+ ov_parser.arguments, ov_parser.locals, ov_parser.statics, ov_parser.inscope
)
variables_count = variables_list.GetSize()
if variables_count == 0:
diff --git a/lldb/examples/python/templates/parsed_cmd.py b/lldb/examples/python/templates/parsed_cmd.py
index 06124ad..13d6eae 100644
--- a/lldb/examples/python/templates/parsed_cmd.py
+++ b/lldb/examples/python/templates/parsed_cmd.py
@@ -4,7 +4,8 @@ lldb parsed commands more Pythonic.
The way to use it is to make a class for your command that inherits from ParsedCommandBase.
That will make an LLDBOptionValueParser which you will use for your
option definition, and to fetch option values for the current invocation
-of your command. Access to the OV parser is through:
+of your command. For concision, I'll call this the `OVParser`.
+Access to the `OVParser` is through:
ParsedCommandBase.get_parser()
@@ -43,7 +44,65 @@ will fetch the value, and:
will return True if the user set this option, and False if it was left at its default
value.
-There are example commands in the lldb testsuite at:
+Custom Completions:
+
+You can also implement custom completers for your custom command, either for the
+arguments to your command or to the option values in your command. If you use enum
+values or if your option/argument uses is one of the types we have completers for,
+you should not need to do this. But if you have your own completeable types, or if
+you want completion of one option to be conditioned by other options on the command
+line, you can use this interface to take over the completion.
+
+You can choose to add a completion for the option values defined for your command,
+or for the arguments, separately. For the option values, define:
+
+def handle_option_argument_completion(self, long_option, cursor_pos):
+
+The line to be completed will be parsed up to the option containint the cursor position,
+and the values will be set in the OptionValue parser object. long_option will be
+the option name containing the cursor, and cursor_pos will be the position of the cursor
+in that option's value. You can call the `OVParser` method: `dest_for_option(long_option)`
+to get the value for that option. The other options that came before the cursor in the command
+line will also be set in the `OVParser` when the completion handler is called.
+
+For argument values, define:
+
+def handle_argument_completion(self, args, arg_pos, cursor_pos):
+
+Again, the command line will be parsed up to the cursor position, and all the options
+before the cursor pose will be set in the `OVParser`. args is a python list of the
+arguments, arg_pos is the index of the argument with the cursor, and cursor_pos is
+the position of the cursor in the argument.
+
+In both cases, the return value determines the completion.
+
+Return False to mean "Not Handled" - in which case lldb will fall back on the
+standard completion machinery.
+
+Return True to mean "Handled with no completions".
+
+If there is a single unique completion, return a Python dictionary with two elements:
+
+return {"completion" : "completed_value", "mode" : <"partial", "complete">}
+
+If the mode is "partial", then the completion is to a common base, if it is "complete"
+then the argument is considered done - mostly meaning lldb will put a space after the
+completion string. "complete" is the default if no "mode" is specified.
+
+If there are multiple completion options, then return:
+
+return {"values" : ["option1", "option2"]}
+
+Optionally, you can return a parallel array of "descriptions" which the completer will
+print alongside the options:
+
+return {"values" : ["option1", "option2"], "descriptions" : ["the first option", "the second option"]}
+
+The cmdtemplate example currently uses the parsed command infrastructure:
+
+llvm-project/lldb/examples/python/cmdtemplate.py
+
+There are also a few example commands in the lldb testsuite at:
llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
"""
@@ -226,10 +285,14 @@ class LLDBOptionValueParser:
return True
def was_set(self, opt_name):
- """ Call this in the __call__ method of your command to determine
- whether this option was set on the command line. It is sometimes
- useful to know whether an option has the default value because the
- user set it explicitly (was_set -> True) or not. """
+ """Call this in the __call__ method of your command to determine
+ whether this option was set on the command line. It is sometimes
+ useful to know whether an option has the default value because the
+ user set it explicitly (was_set -> True) or not.
+ You can also call this in a handle_completion method, but it will
+ currently only report true values for the options mentioned
+ BEFORE the cursor point in the command line.
+ """
elem = self.get_option_element(opt_name)
if not elem:
@@ -239,6 +302,16 @@ class LLDBOptionValueParser:
except AttributeError:
return False
+ def dest_for_option(self, opt_name):
+ """This will return the value of the dest variable you defined for opt_name.
+ Mostly useful for handle_completion where you get passed the long option.
+ """
+ elem = self.get_option_element(opt_name)
+ if not elem:
+ return None
+ value = self.__dict__[elem["dest"]]
+ return value
+
def add_option(self, short_option, long_option, help, default,
dest = None, required=False, groups = None,
value_type=lldb.eArgTypeNone, completion_type=None,
@@ -251,14 +324,16 @@ class LLDBOptionValueParser:
dest: the name of the property that gives you access to the value for
this value. Defaults to the long option if not provided.
required: if true, this option must be provided or the command will error out
- groups: Which "option groups" does this option belong to
+ groups: Which "option groups" does this option belong to. This can either be
+ a simple list (e.g. [1, 3, 4, 5]) or you can specify ranges by sublists:
+ so [1, [3,5]] is the same as [1, 3, 4, 5].
value_type: one of the lldb.eArgType enum values. Some of the common arg
types also have default completers, which will be applied automatically.
- completion_type: currently these are values form the lldb.CompletionType enum, I
- haven't done custom completions yet.
+ completion_type: currently these are values form the lldb.CompletionType enum. If
+ you need custom completions, implement handle_option_argument_completion.
enum_values: An array of duples: ["element_name", "element_help"]. If provided,
- only one of the enum elements is allowed. The value will be the
- element_name for the chosen enum element as a string.
+ only one of the enum elements is allowed. The value will be the
+ element_name for the chosen enum element as a string.
"""
if not dest:
dest = long_option
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index 901ecf3..2c2bd6f 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -420,6 +420,20 @@ public:
return std::nullopt;
}
+ virtual StructuredData::DictionarySP
+ HandleArgumentCompletionForScriptedCommand(
+ StructuredData::GenericSP impl_obj_sp, std::vector<llvm::StringRef> &args,
+ size_t args_pos, size_t char_in_arg) {
+ return {};
+ }
+
+ virtual StructuredData::DictionarySP
+ HandleOptionArgumentCompletionForScriptedCommand(
+ StructuredData::GenericSP impl_obj_sp, llvm::StringRef &long_name,
+ size_t char_in_arg) {
+ return {};
+ }
+
virtual bool RunScriptFormatKeyword(const char *impl_function,
Process *process, std::string &output,
Status &error) {
diff --git a/lldb/include/lldb/Utility/CompletionRequest.h b/lldb/include/lldb/Utility/CompletionRequest.h
index 1a2b1d6..650158a 100644
--- a/lldb/include/lldb/Utility/CompletionRequest.h
+++ b/lldb/include/lldb/Utility/CompletionRequest.h
@@ -139,6 +139,8 @@ public:
return GetParsedLine()[GetCursorIndex()];
}
+ size_t GetCursorCharPos() const { return m_cursor_char_position; }
+
/// Drops the first argument from the argument list.
void ShiftArguments() {
m_cursor_index--;
diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp
index e3291640..845b89a 100644
--- a/lldb/source/Commands/CommandObjectCommands.cpp
+++ b/lldb/source/Commands/CommandObjectCommands.cpp
@@ -1637,6 +1637,129 @@ private:
size_t GetNumOptions() { return m_num_options; }
+ void PrepareOptionsForCompletion(CompletionRequest &request,
+ OptionElementVector &option_vec,
+ ExecutionContext *exe_ctx) {
+ // I'm not sure if we'll get into trouble doing an option parsing start
+ // and end in this context. If so, then I'll have to directly tell the
+ // scripter to do this.
+ OptionParsingStarting(exe_ctx);
+ auto opt_defs = GetDefinitions();
+
+ // Iterate through the options we found so far, and push them into
+ // the scripted side.
+ for (auto option_elem : option_vec) {
+ int cur_defs_index = option_elem.opt_defs_index;
+ // If we don't recognize this option we can't set it.
+ if (cur_defs_index == OptionArgElement::eUnrecognizedArg ||
+ cur_defs_index == OptionArgElement::eBareDash ||
+ cur_defs_index == OptionArgElement::eBareDoubleDash)
+ continue;
+ bool option_has_arg = opt_defs[cur_defs_index].option_has_arg;
+ llvm::StringRef cur_arg_value;
+ if (option_has_arg) {
+ int cur_arg_pos = option_elem.opt_arg_pos;
+ if (cur_arg_pos != OptionArgElement::eUnrecognizedArg &&
+ cur_arg_pos != OptionArgElement::eBareDash &&
+ cur_arg_pos != OptionArgElement::eBareDoubleDash) {
+ cur_arg_value =
+ request.GetParsedLine().GetArgumentAtIndex(cur_arg_pos);
+ }
+ }
+ SetOptionValue(cur_defs_index, cur_arg_value, exe_ctx);
+ }
+ OptionParsingFinished(exe_ctx);
+ }
+
+ void
+ ProcessCompletionDict(CompletionRequest &request,
+ StructuredData::DictionarySP &completion_dict_sp) {
+ // We don't know how to process an empty completion dict, our callers have
+ // to do that.
+ assert(completion_dict_sp && "Must have valid completion dict");
+ // First handle the case of a single completion:
+ llvm::StringRef completion;
+ // If the dictionary has one element "no-completion" then we return here
+ if (completion_dict_sp->GetValueForKeyAsString("no-completion",
+ completion))
+ return;
+
+ if (completion_dict_sp->GetValueForKeyAsString("completion",
+ completion)) {
+ llvm::StringRef mode_str;
+ CompletionMode mode = CompletionMode::Normal;
+ if (completion_dict_sp->GetValueForKeyAsString("mode", mode_str)) {
+ if (mode_str == "complete")
+ mode = CompletionMode::Normal;
+ else if (mode_str == "partial")
+ mode = CompletionMode::Partial;
+ else {
+ // FIXME - how do I report errors here?
+ return;
+ }
+ }
+ request.AddCompletion(completion, "", mode);
+ return;
+ }
+ // The completions are required, the descriptions are not:
+ StructuredData::Array *completions;
+ StructuredData::Array *descriptions;
+ if (completion_dict_sp->GetValueForKeyAsArray("values", completions)) {
+ completion_dict_sp->GetValueForKeyAsArray("descriptions", descriptions);
+ size_t num_completions = completions->GetSize();
+ for (size_t idx = 0; idx < num_completions; idx++) {
+ auto val = completions->GetItemAtIndexAsString(idx);
+ if (!val)
+ // FIXME: How do I report this error?
+ return;
+
+ if (descriptions) {
+ auto desc = descriptions->GetItemAtIndexAsString(idx);
+ request.AddCompletion(*val, desc ? *desc : "");
+ } else
+ request.AddCompletion(*val);
+ }
+ }
+ }
+
+ void
+ HandleOptionArgumentCompletion(lldb_private::CompletionRequest &request,
+ OptionElementVector &option_vec,
+ int opt_element_index,
+ CommandInterpreter &interpreter) override {
+ ScriptInterpreter *scripter =
+ interpreter.GetDebugger().GetScriptInterpreter();
+
+ if (!scripter)
+ return;
+
+ ExecutionContext exe_ctx = interpreter.GetExecutionContext();
+ PrepareOptionsForCompletion(request, option_vec, &exe_ctx);
+
+ auto defs = GetDefinitions();
+
+ size_t defs_index = option_vec[opt_element_index].opt_defs_index;
+ llvm::StringRef option_name = defs[defs_index].long_option;
+ bool is_enum = defs[defs_index].enum_values.size() != 0;
+ if (option_name.empty())
+ return;
+ // If this is an enum, we don't call the custom completer, just let the
+ // regular option completer handle that:
+ StructuredData::DictionarySP completion_dict_sp;
+ if (!is_enum)
+ completion_dict_sp =
+ scripter->HandleOptionArgumentCompletionForScriptedCommand(
+ m_cmd_obj_sp, option_name, request.GetCursorCharPos());
+
+ if (!completion_dict_sp) {
+ Options::HandleOptionArgumentCompletion(request, option_vec,
+ opt_element_index, interpreter);
+ return;
+ }
+
+ ProcessCompletionDict(request, completion_dict_sp);
+ }
+
private:
struct EnumValueStorage {
EnumValueStorage() {
@@ -1878,6 +2001,74 @@ public:
Status GetArgsError() { return m_args_error.Clone(); }
bool WantsCompletion() override { return true; }
+private:
+ void PrepareOptionsForCompletion(CompletionRequest &request,
+ OptionElementVector &option_vec) {
+ // First, we have to tell the Scripted side to set the values in its
+ // option store, then we call into the handle_completion passing in
+ // an array of the args, the arg index and the cursor position in the arg.
+ // We want the script side to have a chance to clear its state, so tell
+ // it argument parsing has started:
+ Options *options = GetOptions();
+ // If there are not options, this will be nullptr, and in that case we
+ // can just skip setting the options on the scripted side:
+ if (options)
+ m_options.PrepareOptionsForCompletion(request, option_vec, &m_exe_ctx);
+ }
+
+public:
+ void HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &option_vec) override {
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+
+ if (!scripter)
+ return;
+
+ // Set up the options values on the scripted side:
+ PrepareOptionsForCompletion(request, option_vec);
+
+ // Now we have to make up the argument list.
+ // The ParseForCompletion only identifies tokens in the m_parsed_line
+ // it doesn't remove the options leaving only the args as it does for
+ // the regular Parse, so we have to filter out the option ones using the
+ // option_element_vector:
+
+ Options *options = GetOptions();
+ auto defs = options->GetDefinitions();
+
+ std::unordered_set<size_t> option_slots;
+ for (const auto &elem : option_vec) {
+ if (elem.opt_defs_index == -1)
+ continue;
+ option_slots.insert(elem.opt_pos);
+ if (defs[elem.opt_defs_index].option_has_arg)
+ option_slots.insert(elem.opt_arg_pos);
+ }
+
+ std::vector<llvm::StringRef> args_vec;
+ Args &args = request.GetParsedLine();
+ size_t num_args = args.GetArgumentCount();
+ size_t cursor_idx = request.GetCursorIndex();
+ size_t args_elem_pos = cursor_idx;
+
+ for (size_t idx = 0; idx < num_args; idx++) {
+ if (option_slots.count(idx) == 0)
+ args_vec.push_back(args[idx].ref());
+ else if (idx < cursor_idx)
+ args_elem_pos--;
+ }
+ StructuredData::DictionarySP completion_dict_sp =
+ scripter->HandleArgumentCompletionForScriptedCommand(
+ m_cmd_obj_sp, args_vec, args_elem_pos, request.GetCursorCharPos());
+
+ if (!completion_dict_sp) {
+ CommandObject::HandleArgumentCompletion(request, option_vec);
+ return;
+ }
+
+ m_options.ProcessCompletionDict(request, completion_dict_sp);
+ }
+
bool IsRemovable() const override { return true; }
ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; }
diff --git a/lldb/source/Interpreter/Options.cpp b/lldb/source/Interpreter/Options.cpp
index b8a3f68a..3888a58 100644
--- a/lldb/source/Interpreter/Options.cpp
+++ b/lldb/source/Interpreter/Options.cpp
@@ -661,7 +661,9 @@ bool Options::HandleOptionCompletion(CompletionRequest &request,
} else if (opt_arg_pos == request.GetCursorIndex()) {
// Okay the cursor is on the completion of an argument. See if it has a
- // completion, otherwise return no matches.
+ // completion, otherwise return no matches. Note, opt_defs_index == -1
+ // means we're after an option, but that option doesn't exist. We'll
+ // end up treating that as an argument. Not sure we can do much better.
if (opt_defs_index != -1) {
HandleOptionArgumentCompletion(request, opt_element_vector, i,
interpreter);
@@ -688,7 +690,6 @@ void Options::HandleOptionArgumentCompletion(
int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index;
// See if this is an enumeration type option, and if so complete it here:
-
const auto &enum_values = opt_defs[opt_defs_index].enum_values;
if (!enum_values.empty())
for (const auto &enum_value : enum_values)
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
index 81ee9ea0..518a478 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
@@ -200,6 +200,15 @@ public:
LLDBSwigPythonGetRepeatCommandForScriptedCommand(PyObject *implementor,
std::string &command);
+ static StructuredData::DictionarySP
+ LLDBSwigPythonHandleArgumentCompletionForScriptedCommand(
+ PyObject *implementor, std::vector<llvm::StringRef> &args_impl,
+ size_t args_pos, size_t pos_in_arg);
+
+ static StructuredData::DictionarySP
+ LLDBSwigPythonHandleOptionArgumentCompletionForScriptedCommand(
+ PyObject *implementor, llvm::StringRef &long_option, size_t pos_in_arg);
+
static bool LLDBSwigPythonCallModuleInit(const char *python_module_name,
const char *session_dictionary_name,
lldb::DebuggerSP debugger);
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index 155efc0..db1a10e 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -2720,6 +2720,46 @@ ScriptInterpreterPythonImpl::GetRepeatCommandForScriptedCommand(
return ret_val;
}
+StructuredData::DictionarySP
+ScriptInterpreterPythonImpl::HandleArgumentCompletionForScriptedCommand(
+ StructuredData::GenericSP impl_obj_sp, std::vector<llvm::StringRef> &args,
+ size_t args_pos, size_t char_in_arg) {
+ StructuredData::DictionarySP completion_dict_sp;
+ if (!impl_obj_sp || !impl_obj_sp->IsValid())
+ return completion_dict_sp;
+
+ {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN,
+ Locker::FreeLock);
+
+ completion_dict_sp =
+ SWIGBridge::LLDBSwigPythonHandleArgumentCompletionForScriptedCommand(
+ static_cast<PyObject *>(impl_obj_sp->GetValue()), args, args_pos,
+ char_in_arg);
+ }
+ return completion_dict_sp;
+}
+
+StructuredData::DictionarySP
+ScriptInterpreterPythonImpl::HandleOptionArgumentCompletionForScriptedCommand(
+ StructuredData::GenericSP impl_obj_sp, llvm::StringRef &long_option,
+ size_t char_in_arg) {
+ StructuredData::DictionarySP completion_dict_sp;
+ if (!impl_obj_sp || !impl_obj_sp->IsValid())
+ return completion_dict_sp;
+
+ {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN,
+ Locker::FreeLock);
+
+ completion_dict_sp = SWIGBridge::
+ LLDBSwigPythonHandleOptionArgumentCompletionForScriptedCommand(
+ static_cast<PyObject *>(impl_obj_sp->GetValue()), long_option,
+ char_in_arg);
+ }
+ return completion_dict_sp;
+}
+
/// In Python, a special attribute __doc__ contains the docstring for an object
/// (function, method, class, ...) if any is defined Otherwise, the attribute's
/// value is None.
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
index d15e2fd..2dc7847 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -166,6 +166,14 @@ public:
GetRepeatCommandForScriptedCommand(StructuredData::GenericSP impl_obj_sp,
Args &args) override;
+ StructuredData::DictionarySP HandleArgumentCompletionForScriptedCommand(
+ StructuredData::GenericSP impl_obj_sp, std::vector<llvm::StringRef> &args,
+ size_t args_pos, size_t char_in_arg) override;
+
+ StructuredData::DictionarySP HandleOptionArgumentCompletionForScriptedCommand(
+ StructuredData::GenericSP impl_obj_sp, llvm::StringRef &long_options,
+ size_t char_in_arg) override;
+
Status GenerateFunction(const char *signature, const StringList &input,
bool is_callback) override;
diff --git a/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py b/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py
index c7680e9..6fac1eb 100644
--- a/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py
+++ b/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py
@@ -68,6 +68,57 @@ class ParsedCommandTestCase(TestBase):
return results
+ def handle_completion(
+ self,
+ cmd_str,
+ exp_num_completions,
+ exp_matches,
+ exp_descriptions,
+ match_description,
+ ):
+ matches = lldb.SBStringList()
+ descriptions = lldb.SBStringList()
+
+ interp = self.dbg.GetCommandInterpreter()
+ num_completions = interp.HandleCompletionWithDescriptions(
+ cmd_str, len(cmd_str), 0, 1000, matches, descriptions
+ )
+ self.assertEqual(
+ num_completions, exp_num_completions, "Number of completions is right."
+ )
+ num_matches = matches.GetSize()
+ self.assertEqual(
+ num_matches,
+ exp_matches.GetSize(),
+ "matches and expected matches of different lengths",
+ )
+ num_descriptions = descriptions.GetSize()
+ if match_description:
+ self.assertEqual(
+ num_descriptions,
+ exp_descriptions.GetSize(),
+ "descriptions and expected of different lengths",
+ )
+
+ self.assertEqual(
+ matches.GetSize(),
+ num_completions + 1,
+ "The first element is the complete additional text",
+ )
+
+ for idx in range(0, num_matches):
+ match = matches.GetStringAtIndex(idx)
+ exp_match = exp_matches.GetStringAtIndex(idx)
+ self.assertEqual(
+ match, exp_match, f"{match} did not match expectation: {exp_match}"
+ )
+ if match_description:
+ desc = descriptions.GetStringAtIndex(idx)
+ exp_desc = exp_descriptions.GetStringAtIndex(idx)
+ self.assertEqual(
+ desc, exp_desc, f"{desc} didn't match expectation: {exp_desc}"
+ )
+
def pycmd_tests(self):
source_dir = self.getSourceDir()
test_file_path = os.path.join(source_dir, "test_commands.py")
@@ -176,24 +227,10 @@ class ParsedCommandTestCase(TestBase):
descriptions = lldb.SBStringList()
# First try an enum completion:
- num_completions = interp.HandleCompletionWithDescriptions(
- "no-args -e f", 12, 0, 1000, matches, descriptions
- )
- self.assertEqual(num_completions, 1, "Only one completion for foo")
- self.assertEqual(
- matches.GetSize(), 2, "The first element is the complete additional text"
- )
- self.assertEqual(
- matches.GetStringAtIndex(0), "oo ", "And we got the right extra characters"
- )
- self.assertEqual(
- matches.GetStringAtIndex(1), "foo", "And we got the right match"
- )
- self.assertEqual(
- descriptions.GetSize(), 2, "descriptions matche the return length"
- )
- # FIXME: we don't return descriptions for enum elements
- # self.assertEqual(descriptions.GetStringAtIndex(1), "does foo things", "And we got the right description")
+ # Note - this is an enum so all the values are returned:
+ matches.AppendList(["oo ", "foo"], 2)
+
+ self.handle_completion("no-args -e f", 1, matches, descriptions, False)
# Now try an internal completer, the on disk file one is handy:
partial_name = os.path.join(source_dir, "test_")
@@ -201,24 +238,9 @@ class ParsedCommandTestCase(TestBase):
matches.Clear()
descriptions.Clear()
- num_completions = interp.HandleCompletionWithDescriptions(
- cmd_str, len(cmd_str) - 1, 0, 1000, matches, descriptions
- )
- self.assertEqual(num_completions, 1, "Only one completion for source file")
- self.assertEqual(matches.GetSize(), 2, "The first element is the complete line")
- self.assertEqual(
- matches.GetStringAtIndex(0),
- "commands.py' ",
- "And we got the right extra characters",
- )
- self.assertEqual(
- matches.GetStringAtIndex(1), test_file_path, "And we got the right match"
- )
- self.assertEqual(
- descriptions.GetSize(), 2, "descriptions match the return length"
- )
- # FIXME: we don't return descriptions for enum elements
- # self.assertEqual(descriptions.GetStringAtIndex(1), "does foo things", "And we got the right description")
+ matches.AppendList(["commands.py' ", test_file_path], 2)
+ # We don't have descriptions for the file path completer:
+ self.handle_completion(cmd_str, 1, matches, descriptions, False)
# Try a command with arguments.
# FIXME: It should be enough to define an argument and it's type to get the completer
@@ -231,6 +253,44 @@ class ParsedCommandTestCase(TestBase):
substrs=["0: First Argument", "1: Second Argument"],
)
+ # Now test custom completions - two-args has both option and arg completers. In both
+ # completers we return different values if the -p option is set, so we can test that too:
+ matches.Clear()
+ descriptions.Clear()
+ cmd_str = "two-args -p something -c other_"
+ matches.AppendString("something ")
+ matches.AppendString("other_something")
+ # This is a full match so no descriptions:
+ self.handle_completion(cmd_str, 1, matches, descriptions, False)
+
+ matches.Clear()
+ descriptions.Clear()
+ cmd_str = "two-args -c other_"
+ matches.AppendList(["", "other_nice", "other_not_nice", "other_mediocre"], 4)
+ # The option doesn't return descriptions either:
+ self.handle_completion(cmd_str, 3, matches, descriptions, False)
+
+ # Now try the argument - it says "no completions" if the proc_name was set:
+ matches.Clear()
+ descriptions.Clear()
+ cmd_str = "two-args -p something arg"
+ matches.AppendString("")
+ self.handle_completion(cmd_str, 0, matches, descriptions, False)
+
+ cmd_str = "two-args arg_"
+ matches.Clear()
+ descriptions.Clear()
+ matches.AppendList(["", "arg_cool", "arg_yuck"], 3)
+ descriptions.AppendList(["", "good idea", "bad idea"], 3)
+ self.handle_completion(cmd_str, 2, matches, descriptions, True)
+
+ # This one gets a single unique match:
+ cmd_str = "two-args correct_"
+ matches.Clear()
+ descriptions.Clear()
+ matches.AppendList(["answer ", "correct_answer"], 2)
+ self.handle_completion(cmd_str, 1, matches, descriptions, False)
+
# Now make sure get_repeat_command works properly:
# no-args turns off auto-repeat
diff --git a/lldb/test/API/commands/command/script/add/test_commands.py b/lldb/test/API/commands/command/script/add/test_commands.py
index fcde6cd..b15ea93 100644
--- a/lldb/test/API/commands/command/script/add/test_commands.py
+++ b/lldb/test/API/commands/command/script/add/test_commands.py
@@ -18,7 +18,7 @@ class ReportingCmd(ParsedCommand):
for long_option, elem in opt_def.items():
dest = elem["dest"]
result.AppendMessage(
- f"{long_option} (set: {elem['_value_set']}): {object.__getattribute__(self.ov_parser, dest)}\n"
+ f"{long_option} (set: {elem['_value_set']}): {object.__getattribute__(self.get_parser(), dest)}\n"
)
else:
result.AppendMessage("No options\n")
@@ -31,7 +31,6 @@ class ReportingCmd(ParsedCommand):
f"{idx}: {args_array.GetItemAtIndex(idx).GetStringValue(10000)}\n"
)
-
# Use these to make sure that get_repeat_command sends the right
# command.
no_args_repeat = None
@@ -49,7 +48,8 @@ class NoArgsCommand(ReportingCmd):
ParsedCommand.do_register_cmd(cls, debugger, module_name)
def setup_command_definition(self):
- self.ov_parser.add_option(
+ ov_parser = self.get_parser()
+ ov_parser.add_option(
"b",
"bool-arg",
"a boolean arg, defaults to True",
@@ -59,7 +59,7 @@ class NoArgsCommand(ReportingCmd):
default=True,
)
- self.ov_parser.add_option(
+ ov_parser.add_option(
"s",
"shlib-name",
"A shared library name.",
@@ -69,7 +69,7 @@ class NoArgsCommand(ReportingCmd):
default=None,
)
- self.ov_parser.add_option(
+ ov_parser.add_option(
"d",
"disk-file-name",
"An on disk filename",
@@ -78,7 +78,7 @@ class NoArgsCommand(ReportingCmd):
default=None,
)
- self.ov_parser.add_option(
+ ov_parser.add_option(
"l",
"line-num",
"A line number",
@@ -88,7 +88,7 @@ class NoArgsCommand(ReportingCmd):
default=0,
)
- self.ov_parser.add_option(
+ ov_parser.add_option(
"e",
"enum-option",
"An enum, doesn't actually do anything",
@@ -126,8 +126,9 @@ class OneArgCommandNoOptions(ReportingCmd):
ParsedCommand.do_register_cmd(cls, debugger, module_name)
def setup_command_definition(self):
- self.ov_parser.add_argument_set(
- [self.ov_parser.make_argument_element(lldb.eArgTypeSourceFile, "plain")]
+ ov_parser = self.get_parser()
+ ov_parser.add_argument_set(
+ [ov_parser.make_argument_element(lldb.eArgTypeSourceFile, "plain")]
)
def get_repeat_command(self, command):
@@ -154,7 +155,8 @@ class TwoArgGroupsCommand(ReportingCmd):
ParsedCommand.do_register_cmd(cls, debugger, module_name)
def setup_command_definition(self):
- self.ov_parser.add_option(
+ ov_parser = self.get_parser()
+ ov_parser.add_option(
"l",
"language",
"language defaults to None",
@@ -164,7 +166,7 @@ class TwoArgGroupsCommand(ReportingCmd):
default=None,
)
- self.ov_parser.add_option(
+ ov_parser.add_option(
"c",
"log-channel",
"log channel - defaults to lldb",
@@ -174,7 +176,7 @@ class TwoArgGroupsCommand(ReportingCmd):
default="lldb",
)
- self.ov_parser.add_option(
+ ov_parser.add_option(
"p",
"process-name",
"A process name, defaults to None",
@@ -183,25 +185,23 @@ class TwoArgGroupsCommand(ReportingCmd):
default=None,
)
- self.ov_parser.add_argument_set(
+ ov_parser.add_argument_set(
[
- self.ov_parser.make_argument_element(
+ ov_parser.make_argument_element(
lldb.eArgTypeClassName, "plain", [1, 2]
),
- self.ov_parser.make_argument_element(
+ ov_parser.make_argument_element(
lldb.eArgTypeOffset, "optional", [1, 2]
),
]
)
- self.ov_parser.add_argument_set(
+ ov_parser.add_argument_set(
[
- self.ov_parser.make_argument_element(
+ ov_parser.make_argument_element(
lldb.eArgTypePythonClass, "plain", [3, 4]
),
- self.ov_parser.make_argument_element(
- lldb.eArgTypePid, "optional", [3, 4]
- ),
+ ov_parser.make_argument_element(lldb.eArgTypePid, "optional", [3, 4]),
]
)
@@ -210,6 +210,35 @@ class TwoArgGroupsCommand(ReportingCmd):
two_arg_repeat = command
return command + " THIRD_ARG"
+ def handle_option_argument_completion(self, long_option, cursor_pos):
+ ov_parser = self.get_parser()
+ value = ov_parser.dest_for_option(long_option)[0 : cursor_pos + 1]
+ proc_value = ov_parser.proc_name
+ if proc_value != None:
+ new_str = value + proc_value
+ ret_arr = {"completion": new_str, "mode": "partial"}
+ return ret_arr
+
+ ret_arr = {"values": [value + "nice", value + "not_nice", value + "mediocre"]}
+ return ret_arr
+
+ def handle_argument_completion(self, args, arg_pos, cursor_pos):
+ ov_parser = self.get_parser()
+ orig_arg = args[arg_pos][0:cursor_pos]
+ if orig_arg == "correct_":
+ ret_arr = {"completion": "correct_answer"}
+ return ret_arr
+
+ if ov_parser.was_set("process-name"):
+ # No completions if proc_name was set.
+ return True
+
+ ret_arr = {
+ "values": [orig_arg + "cool", orig_arg + "yuck"],
+ "descriptions": ["good idea", "bad idea"],
+ }
+ return ret_arr
+
def get_short_help(self):
return "This is my short help string"
diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
index c67a2b4..3faeb58 100644
--- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
+++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
@@ -211,6 +211,19 @@ LLDBSwigPythonGetRepeatCommandForScriptedCommand(PyObject *implementor,
return std::nullopt;
}
+StructuredData::DictionarySP
+LLDBSwigPythonHandleArgumentCompletionForScriptedCommand(
+ PyObject *implementor, std::vector<llvm::StringRef> &args, size_t args_pos,
+ size_t pos_in_arg) {
+ return {};
+}
+
+StructuredData::DictionarySP
+LLDBSwigPythonHandleOptionArgumentCompletionForScriptedCommand(
+ PyObject *implementor, llvm::StringRef &long_options, size_t char_in_arg) {
+ return {};
+}
+
bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallModuleInit(
const char *python_module_name, const char *session_dictionary_name,
lldb::DebuggerSP debugger) {