[Lldb-commits] [lldb] Add the ability for Script based commands to specify their "repeat command" (PR #94823)
via lldb-commits
lldb-commits at lists.llvm.org
Tue Jul 2 14:11:17 PDT 2024
https://github.com/jimingham updated https://github.com/llvm/llvm-project/pull/94823
>From c2fea75364a0017be5e59020467d661bd00122ba Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Fri, 7 Jun 2024 17:36:34 -0700
Subject: [PATCH 1/4] Add the ability for Script based commands to specify
their "repeat command".
Among other things, returning an empty string as the repeat command disables
auto-repeat, which can be useful for state-changing commands.
---
lldb/bindings/python/python-wrapper.swig | 23 ++++++++
lldb/examples/python/cmdtemplate.py | 6 +-
lldb/include/lldb/Interpreter/CommandObject.h | 4 ++
.../lldb/Interpreter/ScriptInterpreter.h | 5 ++
.../source/Commands/CommandObjectCommands.cpp | 26 ++++++++-
lldb/source/Commands/CommandObjectThread.cpp | 2 +-
.../Python/SWIGPythonBridge.h | 4 ++
.../Python/ScriptInterpreterPython.cpp | 27 +++++++++
.../Python/ScriptInterpreterPythonImpl.h | 14 +++--
.../script/add/TestAddParsedCommand.py | 55 +++++++++++++++++++
.../command/script/add/test_commands.py | 25 ++++++++-
.../Python/PythonTestSuite.cpp | 6 ++
12 files changed, 187 insertions(+), 10 deletions(-)
diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index 1370afc885d43..bb3c9433e2032 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -831,6 +831,29 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject(
return true;
}
+std::optional<std::string>
+lldb_private::python::SWIGBridge::LLDBSwigPythonGetRepeatCommandForScriptedCommand(PyObject *implementor,
+ std::string &command) {
+ PyErr_Cleaner py_err_cleaner(true);
+
+ PythonObject self(PyRefType::Borrowed, implementor);
+ auto pfunc = self.ResolveName<PythonCallable>("get_repeat_command");
+ // If not implemented, repeat the exact command.
+ if (!pfunc.IsAllocated())
+ return std::nullopt;
+
+ PythonObject result;
+ PythonString command_str(command);
+ result = pfunc(command_str);
+
+ // A return of None is the equivalent of nullopt - means repeat
+ // the command as is:
+ if (result.IsNone())
+ return std::nullopt;
+
+ return result.Str().GetString().str();
+}
+
#include "lldb/Interpreter/CommandReturnObject.h"
bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
diff --git a/lldb/examples/python/cmdtemplate.py b/lldb/examples/python/cmdtemplate.py
index 49a08365268f8..9a96888508b6f 100644
--- a/lldb/examples/python/cmdtemplate.py
+++ b/lldb/examples/python/cmdtemplate.py
@@ -19,7 +19,7 @@ class FrameStatCommand(ParsedCommand):
@classmethod
def register_lldb_command(cls, debugger, module_name):
- ParsedCommandBase.do_register_cmd(cls, debugger, module_name)
+ ParsedCommand.do_register_cmd(cls, debugger, module_name)
print(
'The "{0}" command has been installed, type "help {0}" or "{0} '
'--help" for detailed help.'.format(cls.program)
@@ -72,6 +72,10 @@ def setup_command_definition(self):
default = True,
)
+ def get_repeat_command(self, args):
+ """As an example, make the command not auto-repeat:"""
+ return ""
+
def get_short_help(self):
return "Example command for use in debugging"
diff --git a/lldb/include/lldb/Interpreter/CommandObject.h b/lldb/include/lldb/Interpreter/CommandObject.h
index a641a468b49d2..727ea0d963734 100644
--- a/lldb/include/lldb/Interpreter/CommandObject.h
+++ b/lldb/include/lldb/Interpreter/CommandObject.h
@@ -296,6 +296,10 @@ class CommandObject : public std::enable_shared_from_this<CommandObject> {
///
/// \param[in] current_command_args
/// The command arguments.
+ ///
+ /// \param[in] index
+ /// This is for internal use - it is how the completion request is tracked
+ /// in CommandObjectMultiword, and should otherwise be ignored.
///
/// \return
/// std::nullopt if there is no special repeat command - it will use the
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index 932eaa8b8a4a2..934fd1837fcc0 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -480,6 +480,11 @@ class ScriptInterpreter : public PluginInterface {
const lldb_private::ExecutionContext &exe_ctx) {
return false;
}
+
+ virtual std::optional<std::string> GetRepeatCommandForScriptedCommand(
+ StructuredData::GenericSP impl_obj_sp, Args &args) {
+ return std::nullopt;
+ }
virtual bool RunScriptFormatKeyword(const char *impl_function,
Process *process, std::string &output,
diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp
index f4903e373b086..4144876c0751f 100644
--- a/lldb/source/Commands/CommandObjectCommands.cpp
+++ b/lldb/source/Commands/CommandObjectCommands.cpp
@@ -1142,6 +1142,14 @@ class CommandObjectScriptingObjectRaw : public CommandObjectRaw {
ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; }
+ std::optional<std::string> GetRepeatCommand(Args &args, uint32_t index) override {
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+ if (!scripter)
+ return std::nullopt;
+
+ return scripter->GetRepeatCommandForScriptedCommand(m_cmd_obj_sp, args);
+ }
+
llvm::StringRef GetHelp() override {
if (m_fetched_help_short)
return CommandObjectRaw::GetHelp();
@@ -1589,6 +1597,8 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed {
return error;
}
+ size_t GetNumOptions() { return m_num_options; }
+
private:
struct EnumValueStorage {
EnumValueStorage() {
@@ -1827,6 +1837,14 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed {
ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; }
+ std::optional<std::string> GetRepeatCommand(Args &args, uint32_t index) override {
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+ if (!scripter)
+ return std::nullopt;
+
+ return scripter->GetRepeatCommandForScriptedCommand(m_cmd_obj_sp, args);
+ }
+
llvm::StringRef GetHelp() override {
if (m_fetched_help_short)
return CommandObjectParsed::GetHelp();
@@ -1858,7 +1876,13 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed {
return CommandObjectParsed::GetHelpLong();
}
- Options *GetOptions() override { return &m_options; }
+ Options *GetOptions() override {
+ // CommandObjectParsed requires that a command with no options return
+ // nullptr.
+ if (m_options.GetNumOptions() == 0)
+ return nullptr;
+ return &m_options;
+ }
protected:
diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index db96ee2cec383..772d76e8b148b 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -132,7 +132,7 @@ class CommandObjectThreadBacktrace : public CommandObjectIterateOverThreads {
Options *GetOptions() override { return &m_options; }
std::optional<std::string> GetRepeatCommand(Args ¤t_args,
- uint32_t idx) override {
+ uint32_t index) override {
llvm::StringRef count_opt("--count");
llvm::StringRef start_opt("--start");
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
index c1a11b9134d62..c94fa7d82ab53 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
@@ -219,6 +219,10 @@ class SWIGBridge {
StructuredDataImpl &args_impl,
lldb_private::CommandReturnObject &cmd_retobj,
lldb::ExecutionContextRefSP exe_ctx_ref_sp);
+
+ static std::optional<std::string>
+ LLDBSwigPythonGetRepeatCommandForScriptedCommand(PyObject *implementor,
+ std::string &command);
static bool LLDBSwigPythonCallModuleInit(const char *python_module_name,
const char *session_dictionary_name,
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index 6e676de146b3d..c621ee8381ea9 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -2818,6 +2818,33 @@ bool ScriptInterpreterPythonImpl::RunScriptBasedParsedCommand(
return ret_val;
}
+std::optional<std::string> ScriptInterpreterPythonImpl::GetRepeatCommandForScriptedCommand(
+ StructuredData::GenericSP impl_obj_sp, Args &args) {
+ if (!impl_obj_sp || !impl_obj_sp->IsValid())
+ return std::nullopt;
+
+ lldb::DebuggerSP debugger_sp = m_debugger.shared_from_this();
+
+ if (!debugger_sp.get())
+ return std::nullopt;
+
+ std::optional<std::string> ret_val;
+
+ {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN,
+ Locker::FreeLock);
+
+ StructuredData::ArraySP args_arr_sp(new StructuredData::Array());
+
+ // For scripting commands, we send the command string:
+ std::string command;
+ args.GetQuotedCommandString(command);
+ ret_val = SWIGBridge::LLDBSwigPythonGetRepeatCommandForScriptedCommand(
+ static_cast<PyObject *>(impl_obj_sp->GetValue()), command);
+ }
+ return ret_val;
+
+}
/// In Python, a special attribute __doc__ contains the docstring for an object
/// (function, method, class, ...) if any is defined Otherwise, the attribute's
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
index fcd21dff612b1..f8c4873fafb41 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -182,12 +182,14 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython {
lldb_private::CommandReturnObject &cmd_retobj, Status &error,
const lldb_private::ExecutionContext &exe_ctx) override;
- virtual bool RunScriptBasedParsedCommand(
- StructuredData::GenericSP impl_obj_sp, Args& args,
- ScriptedCommandSynchronicity synchronicity,
- lldb_private::CommandReturnObject &cmd_retobj, Status &error,
- const lldb_private::ExecutionContext &exe_ctx) override;
-
+ bool RunScriptBasedParsedCommand(
+ StructuredData::GenericSP impl_obj_sp, Args& args,
+ ScriptedCommandSynchronicity synchronicity,
+ lldb_private::CommandReturnObject &cmd_retobj, Status &error,
+ const lldb_private::ExecutionContext &exe_ctx) override;
+
+ std::optional<std::string> GetRepeatCommandForScriptedCommand(
+ StructuredData::GenericSP impl_obj_sp, Args &args) 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 d30b0b67124ed..10a54e9fed35f 100644
--- a/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py
+++ b/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py
@@ -16,6 +16,11 @@ class ParsedCommandTestCase(TestBase):
def test(self):
self.pycmd_tests()
+ def setUp(self):
+ TestBase.setUp(self)
+ self.stdin_path = self.getBuildArtifact("stdin.txt")
+ self.stdout_path = self.getBuildArtifact("stdout.txt")
+
def check_help_options(self, cmd_name, opt_list, substrs=[]):
"""
Pass the command name in cmd_name and a vector of the short option, type & long option.
@@ -32,6 +37,38 @@ def check_help_options(self, cmd_name, opt_list, substrs=[]):
print(f"Opt Vec\n{substrs}")
self.expect("help " + cmd_name, substrs=substrs)
+ def run_one_repeat(self, commands, expected_num_errors):
+ with open(self.stdin_path, "w") as input_handle:
+ input_handle.write(commands)
+
+ in_fileH = open(self.stdin_path, "r")
+ self.dbg.SetInputFileHandle(in_fileH, False)
+
+ out_fileH = open(self.stdout_path, "w")
+ self.dbg.SetOutputFileHandle(out_fileH, False)
+ self.dbg.SetErrorFileHandle(out_fileH, False)
+
+ options = lldb.SBCommandInterpreterRunOptions()
+ options.SetEchoCommands(False)
+ options.SetPrintResults(True)
+ options.SetPrintErrors(True)
+ options.SetAllowRepeats(True)
+
+ n_errors, quit_requested, has_crashed = self.dbg.RunCommandInterpreter(
+ True, False, options, 0, False, False)
+
+ in_fileH.close()
+ out_fileH.close()
+
+ results = None
+ with open(self.stdout_path, "r") as out_fileH:
+ results = out_fileH.read()
+
+ print(f"RESULTS:\n{results}\nDONE")
+ self.assertEqual(n_errors, expected_num_errors)
+
+ return results
+
def pycmd_tests(self):
source_dir = self.getSourceDir()
test_file_path = os.path.join(source_dir, "test_commands.py")
@@ -197,3 +234,21 @@ def cleanup():
"two-args 'First Argument' 'Second Argument'",
substrs=["0: First Argument", "1: Second Argument"],
)
+
+ # Now make sure get_repeat_command works properly:
+
+ # no-args turns off auto-repeat
+ results = self.run_one_repeat("no-args\n\n", 1)
+ self.assertIn("No auto repeat", results, "Got auto-repeat error")
+
+ # one-args does the normal repeat
+ results = self.run_one_repeat("one-arg-no-opt ONE_ARG\n\n", 0)
+ self.assertEqual(results.count("ONE_ARG"), 2, "We did a normal repeat")
+
+ # two-args adds an argument:
+ results = self.run_one_repeat("two-args FIRST_ARG SECOND_ARG\n\n", 0)
+ self.assertEqual(results.count("FIRST_ARG"), 2, "Passed first arg to both commands")
+ self.assertEqual(results.count("SECOND_ARG"), 2, "Passed second arg to both commands")
+ self.assertEqual(results.count("THIRD_ARG"), 1, "Passed third arg in 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 68f5a44556366..94d8bb241b2da 100644
--- a/lldb/test/API/commands/command/script/add/test_commands.py
+++ b/lldb/test/API/commands/command/script/add/test_commands.py
@@ -32,6 +32,12 @@ def __call__(self, debugger, args_array, exe_ctx, result):
)
+# Use these to make sure that get_repeat_command sends the right
+# command.
+no_args_repeat = None
+one_arg_repeat = None
+two_arg_repeat = None
+
class NoArgsCommand(ReportingCmd):
program = "no-args"
@@ -96,6 +102,12 @@ def setup_command_definition(self):
default="foo",
)
+ def get_repeat_command(self, command):
+ # No auto-repeat
+ global no_args_repeat
+ no_args_repeat = command
+ return ""
+
def get_short_help(self):
return "Example command for use in debugging"
@@ -118,6 +130,12 @@ def setup_command_definition(self):
[self.ov_parser.make_argument_element(lldb.eArgTypeSourceFile, "plain")]
)
+ def get_repeat_command(self, command):
+ # Repeat the current command
+ global one_arg_repeat
+ one_arg_repeat = command
+ return None
+
def get_short_help(self):
return "Example command for use in debugging"
@@ -187,8 +205,13 @@ def setup_command_definition(self):
]
)
+ def get_repeat_command(self, command):
+ global two_arg_repeat
+ two_arg_repeat = command
+ return command + " THIRD_ARG"
+
def get_short_help(self):
- return "Example command for use in debugging"
+ return "This is my short help string"
def get_long_help(self):
return self.help_string
diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
index 23162436d42c9..03eb08af8182f 100644
--- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
+++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
@@ -210,6 +210,12 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
return false;
}
+std::optional<std::string>
+LLDBSwigPythonGetRepeatCommandForScriptedCommand(PyObject *implementor,
+ std::string &command) {
+ return std::nullopt;
+}
+
bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallModuleInit(
const char *python_module_name, const char *session_dictionary_name,
lldb::DebuggerSP debugger) {
>From e3f6b8ca2f05b0721b213f5a2af9e0b96d1229bf Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Fri, 7 Jun 2024 17:54:28 -0700
Subject: [PATCH 2/4] formatters
---
lldb/include/lldb/Interpreter/CommandObject.h | 2 +-
.../lldb/Interpreter/ScriptInterpreter.h | 7 ++---
.../source/Commands/CommandObjectCommands.cpp | 21 ++++++++-------
.../Python/SWIGPythonBridge.h | 4 +--
.../Python/ScriptInterpreterPython.cpp | 8 +++---
.../Python/ScriptInterpreterPythonImpl.h | 17 ++++++------
.../script/add/TestAddParsedCommand.py | 27 ++++++++++---------
.../command/script/add/test_commands.py | 2 +-
.../Python/PythonTestSuite.cpp | 2 +-
9 files changed, 48 insertions(+), 42 deletions(-)
diff --git a/lldb/include/lldb/Interpreter/CommandObject.h b/lldb/include/lldb/Interpreter/CommandObject.h
index 727ea0d963734..d48dbcdd5a5da 100644
--- a/lldb/include/lldb/Interpreter/CommandObject.h
+++ b/lldb/include/lldb/Interpreter/CommandObject.h
@@ -296,7 +296,7 @@ class CommandObject : public std::enable_shared_from_this<CommandObject> {
///
/// \param[in] current_command_args
/// The command arguments.
- ///
+ ///
/// \param[in] index
/// This is for internal use - it is how the completion request is tracked
/// in CommandObjectMultiword, and should otherwise be ignored.
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index 934fd1837fcc0..ff3306b853a14 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -480,9 +480,10 @@ class ScriptInterpreter : public PluginInterface {
const lldb_private::ExecutionContext &exe_ctx) {
return false;
}
-
- virtual std::optional<std::string> GetRepeatCommandForScriptedCommand(
- StructuredData::GenericSP impl_obj_sp, Args &args) {
+
+ virtual std::optional<std::string>
+ GetRepeatCommandForScriptedCommand(StructuredData::GenericSP impl_obj_sp,
+ Args &args) {
return std::nullopt;
}
diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp
index 4144876c0751f..c63445b7c8c86 100644
--- a/lldb/source/Commands/CommandObjectCommands.cpp
+++ b/lldb/source/Commands/CommandObjectCommands.cpp
@@ -1142,11 +1142,12 @@ class CommandObjectScriptingObjectRaw : public CommandObjectRaw {
ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; }
- std::optional<std::string> GetRepeatCommand(Args &args, uint32_t index) override {
+ std::optional<std::string> GetRepeatCommand(Args &args,
+ uint32_t index) override {
ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
if (!scripter)
return std::nullopt;
-
+
return scripter->GetRepeatCommandForScriptedCommand(m_cmd_obj_sp, args);
}
@@ -1596,9 +1597,9 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed {
options.ForEach(add_element);
return error;
}
-
+
size_t GetNumOptions() { return m_num_options; }
-
+
private:
struct EnumValueStorage {
EnumValueStorage() {
@@ -1837,14 +1838,15 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed {
ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; }
- std::optional<std::string> GetRepeatCommand(Args &args, uint32_t index) override {
+ std::optional<std::string> GetRepeatCommand(Args &args,
+ uint32_t index) override {
ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
if (!scripter)
return std::nullopt;
-
+
return scripter->GetRepeatCommandForScriptedCommand(m_cmd_obj_sp, args);
}
-
+
llvm::StringRef GetHelp() override {
if (m_fetched_help_short)
return CommandObjectParsed::GetHelp();
@@ -1875,16 +1877,15 @@ class CommandObjectScriptingObjectParsed : public CommandObjectParsed {
SetHelpLong(docstring);
return CommandObjectParsed::GetHelpLong();
}
-
+
Options *GetOptions() override {
// CommandObjectParsed requires that a command with no options return
// nullptr.
if (m_options.GetNumOptions() == 0)
return nullptr;
- return &m_options;
+ return &m_options;
}
-
protected:
void DoExecute(Args &args,
CommandReturnObject &result) override {
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
index c94fa7d82ab53..7ee5dc5b0469e 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
@@ -219,10 +219,10 @@ class SWIGBridge {
StructuredDataImpl &args_impl,
lldb_private::CommandReturnObject &cmd_retobj,
lldb::ExecutionContextRefSP exe_ctx_ref_sp);
-
+
static std::optional<std::string>
LLDBSwigPythonGetRepeatCommandForScriptedCommand(PyObject *implementor,
- std::string &command);
+ std::string &command);
static bool LLDBSwigPythonCallModuleInit(const char *python_module_name,
const char *session_dictionary_name,
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index c621ee8381ea9..bedb7c4189f2b 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -2818,8 +2818,9 @@ bool ScriptInterpreterPythonImpl::RunScriptBasedParsedCommand(
return ret_val;
}
-std::optional<std::string> ScriptInterpreterPythonImpl::GetRepeatCommandForScriptedCommand(
- StructuredData::GenericSP impl_obj_sp, Args &args) {
+std::optional<std::string>
+ScriptInterpreterPythonImpl::GetRepeatCommandForScriptedCommand(
+ StructuredData::GenericSP impl_obj_sp, Args &args) {
if (!impl_obj_sp || !impl_obj_sp->IsValid())
return std::nullopt;
@@ -2829,7 +2830,7 @@ std::optional<std::string> ScriptInterpreterPythonImpl::GetRepeatCommandForScrip
return std::nullopt;
std::optional<std::string> ret_val;
-
+
{
Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN,
Locker::FreeLock);
@@ -2843,7 +2844,6 @@ std::optional<std::string> ScriptInterpreterPythonImpl::GetRepeatCommandForScrip
static_cast<PyObject *>(impl_obj_sp->GetValue()), command);
}
return ret_val;
-
}
/// In Python, a special attribute __doc__ contains the docstring for an object
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
index f8c4873fafb41..0185a1a0422f6 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -183,14 +183,15 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython {
const lldb_private::ExecutionContext &exe_ctx) override;
bool RunScriptBasedParsedCommand(
- StructuredData::GenericSP impl_obj_sp, Args& args,
- ScriptedCommandSynchronicity synchronicity,
- lldb_private::CommandReturnObject &cmd_retobj, Status &error,
- const lldb_private::ExecutionContext &exe_ctx) override;
-
- std::optional<std::string> GetRepeatCommandForScriptedCommand(
- StructuredData::GenericSP impl_obj_sp, Args &args) override;
-
+ StructuredData::GenericSP impl_obj_sp, Args &args,
+ ScriptedCommandSynchronicity synchronicity,
+ lldb_private::CommandReturnObject &cmd_retobj, Status &error,
+ const lldb_private::ExecutionContext &exe_ctx) override;
+
+ std::optional<std::string>
+ GetRepeatCommandForScriptedCommand(StructuredData::GenericSP impl_obj_sp,
+ Args &args) 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 10a54e9fed35f..381cdf2022f48 100644
--- a/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py
+++ b/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py
@@ -40,33 +40,34 @@ def check_help_options(self, cmd_name, opt_list, substrs=[]):
def run_one_repeat(self, commands, expected_num_errors):
with open(self.stdin_path, "w") as input_handle:
input_handle.write(commands)
-
+
in_fileH = open(self.stdin_path, "r")
self.dbg.SetInputFileHandle(in_fileH, False)
-
+
out_fileH = open(self.stdout_path, "w")
self.dbg.SetOutputFileHandle(out_fileH, False)
self.dbg.SetErrorFileHandle(out_fileH, False)
-
+
options = lldb.SBCommandInterpreterRunOptions()
options.SetEchoCommands(False)
options.SetPrintResults(True)
options.SetPrintErrors(True)
options.SetAllowRepeats(True)
-
+
n_errors, quit_requested, has_crashed = self.dbg.RunCommandInterpreter(
- True, False, options, 0, False, False)
-
+ True, False, options, 0, False, False
+ )
+
in_fileH.close()
out_fileH.close()
-
+
results = None
with open(self.stdout_path, "r") as out_fileH:
results = out_fileH.read()
print(f"RESULTS:\n{results}\nDONE")
self.assertEqual(n_errors, expected_num_errors)
-
+
return results
def pycmd_tests(self):
@@ -247,8 +248,10 @@ def cleanup():
# two-args adds an argument:
results = self.run_one_repeat("two-args FIRST_ARG SECOND_ARG\n\n", 0)
- self.assertEqual(results.count("FIRST_ARG"), 2, "Passed first arg to both commands")
- self.assertEqual(results.count("SECOND_ARG"), 2, "Passed second arg to both commands")
+ self.assertEqual(
+ results.count("FIRST_ARG"), 2, "Passed first arg to both commands"
+ )
+ self.assertEqual(
+ results.count("SECOND_ARG"), 2, "Passed second arg to both commands"
+ )
self.assertEqual(results.count("THIRD_ARG"), 1, "Passed third arg in 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 94d8bb241b2da..fcde6cd3ef6dc 100644
--- a/lldb/test/API/commands/command/script/add/test_commands.py
+++ b/lldb/test/API/commands/command/script/add/test_commands.py
@@ -209,7 +209,7 @@ def get_repeat_command(self, command):
global two_arg_repeat
two_arg_repeat = command
return command + " THIRD_ARG"
-
+
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 03eb08af8182f..dce86accf9691 100644
--- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
+++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
@@ -212,7 +212,7 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
std::optional<std::string>
LLDBSwigPythonGetRepeatCommandForScriptedCommand(PyObject *implementor,
- std::string &command) {
+ std::string &command) {
return std::nullopt;
}
>From dda7d78c51a64e2bf39c082cd2fb414a30be35ba Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Tue, 2 Jul 2024 14:05:28 -0700
Subject: [PATCH 3/4] Add docs for `get_repeat_command`.
---
lldb/docs/use/python-reference.rst | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/lldb/docs/use/python-reference.rst b/lldb/docs/use/python-reference.rst
index 795e38fab3794..041e541a96f08 100644
--- a/lldb/docs/use/python-reference.rst
+++ b/lldb/docs/use/python-reference.rst
@@ -562,6 +562,18 @@ which should implement the following interface:
this call should return the short help text for this command[1]
def get_long_help(self):
this call should return the long help text for this command[1]
+ def get_repeat_command(self, command):
+ The auto-repeat command is what will get executed when the user types just
+ a return at the next prompt after this command is run. Even if your command
+ was run because it was specified as a repeat command, that invocation will still
+ get asked for IT'S repeat command, so you can chain a series of repeats, for instance
+ to implement a pager.
+
+ The command argument is the command that is about to be executed.
+
+ If this call returns None, then the ordinary repeat mechanism will be used
+ If this call returns an empty string, then auto-repeat is disabled
+ If this call returns any other string, that will be the repeat command [1]
[1] This method is optional.
>From 461007259ecf8850f23aa23b6c9bd505df72e7f5 Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Tue, 2 Jul 2024 14:10:31 -0700
Subject: [PATCH 4/4] Remove some debugging print's, responding to review
comments.
---
lldb/bindings/python/python-wrapper.swig | 3 +--
.../API/commands/command/script/add/TestAddParsedCommand.py | 5 -----
2 files changed, 1 insertion(+), 7 deletions(-)
diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index bb3c9433e2032..86f8de0b4a8b9 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -842,9 +842,8 @@ lldb_private::python::SWIGBridge::LLDBSwigPythonGetRepeatCommandForScriptedComma
if (!pfunc.IsAllocated())
return std::nullopt;
- PythonObject result;
PythonString command_str(command);
- result = pfunc(command_str);
+ PythonObject result = pfunc(command_str);
// A return of None is the equivalent of nullopt - means repeat
// the command as is:
diff --git a/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py b/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py
index 381cdf2022f48..c7680e9bb7f41 100644
--- a/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py
+++ b/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py
@@ -34,7 +34,6 @@ def check_help_options(self, cmd_name, opt_list, substrs=[]):
else:
(short_opt, type, long_opt) = elem
substrs.append(f"-{short_opt} <{type}> ( --{long_opt} <{type}> )")
- print(f"Opt Vec\n{substrs}")
self.expect("help " + cmd_name, substrs=substrs)
def run_one_repeat(self, commands, expected_num_errors):
@@ -65,7 +64,6 @@ def run_one_repeat(self, commands, expected_num_errors):
with open(self.stdout_path, "r") as out_fileH:
results = out_fileH.read()
- print(f"RESULTS:\n{results}\nDONE")
self.assertEqual(n_errors, expected_num_errors)
return results
@@ -206,9 +204,6 @@ def cleanup():
num_completions = interp.HandleCompletionWithDescriptions(
cmd_str, len(cmd_str) - 1, 0, 1000, matches, descriptions
)
- print(
- f"First: {matches.GetStringAtIndex(0)}\nSecond: {matches.GetStringAtIndex(1)}\nThird: {matches.GetStringAtIndex(2)}"
- )
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(
More information about the lldb-commits
mailing list