aboutsummaryrefslogtreecommitdiff
path: root/gdb/python
diff options
context:
space:
mode:
authorAndrew Burgess <aburgess@redhat.com>2025-04-12 09:15:53 +0100
committerAndrew Burgess <aburgess@redhat.com>2025-06-03 15:27:15 +0100
commit0b5023cc71d3af8b18e10e6599a3f9381bc15265 (patch)
tree0c7b8df20c5a7bc19059cb5a4c97ffdfa6ebc53e /gdb/python
parent5ad0c3ef8490269f3142759cc431f0c54d45540e (diff)
downloadbinutils-0b5023cc71d3af8b18e10e6599a3f9381bc15265.zip
binutils-0b5023cc71d3af8b18e10e6599a3f9381bc15265.tar.gz
binutils-0b5023cc71d3af8b18e10e6599a3f9381bc15265.tar.bz2
gdb/python/guile: user created prefix commands get help list
Consider GDB's builtin prefix set/show prefix sub-commands, if they are invoked with no sub-command name then they work like this: (gdb) show print print address: Printing of addresses is on. print array: Pretty formatting of arrays is off. print array-indexes: Printing of array indexes is off. print asm-demangle: Demangling of C++/ObjC names in disassembly listings is off. ... cut lots of lines ... (gdb) set print List of set print subcommands: set print address -- Set printing of addresses. set print array -- Set pretty formatting of arrays. set print array-indexes -- Set printing of array indexes. set print asm-demangle -- Set demangling of C++/ObjC names in disassembly listings. ... cut lots of lines ... Type "help set print" followed by set print subcommand name for full documentation. Type "apropos word" to search for commands related to "word". Type "apropos -v word" for full documentation of commands related to "word". Command name abbreviations are allowed if unambiguous. (gdb) That is 'show print' lists the values of all settings under the 'print' prefix, and 'set print' lists the help text for all settings under the 'set print' prefix. Now, if we try to create something similar using the Python API: (gdb) python gdb.ParameterPrefix("my-prefix", gdb.COMMAND_NONE) (gdb) python gdb.Parameter("my-prefix foo", gdb.COMMAND_OBSCURE, gdb.PARAM_BOOLEAN) (gdb) show my-prefix (gdb) set my-prefix Neither 'show my-prefix' or 'set my-prefix' gives us the same details relating to the sub-commands that we get with the builtin prefix commands. This commit aims to address this. Currently, in cmdpy_init, when a new command is created, we always set the commands callback function to cmdpy_function. It is within cmdpy_function that we spot that the command is a prefix command, and that there is no gdb.Command.invoke method, and so return early. This commit changes things so that the rules are now: 1. For NON prefix commands, we continue to use cmdpy_function. 2. For prefix commands that do have a gdb.Command.invoke method (i.e. can handle unknown sub-commands), continue to use cmdpy_function. 3. For all other prefix commands, don't use cmdpy_function, instead use GDB's normal callback function for set/show prefixes. This requires splitting the current call to add_prefix_cmd into either a call to add_prefix_cmd, add_show_prefix_cmd, or add_basic_prefix_cmd, as appropriate. After these changes, we now see this: (gdb) python gdb.ParameterPrefix("my-prefix", gdb.COMMAND_NONE) │ (gdb) python gdb.Parameter("my-prefix foo", gdb.COMMAND_OBSCURE, gdb.PARAM_BOOLEAN) (gdb) show my-prefix │ my-prefix foo: The current value of 'my-prefix foo' is "off". (gdb) set my-prefix List of "set my-prefix" subcommands: set my-prefix foo -- Set the current value of 'my-prefix foo'. Type "help set my-prefix" followed by subcommand name for full documentation. Type "apropos word" to search for commands related to "word". Type "apropos -v word" for full documentation of commands related to "word". Command name abbreviations are allowed if unambiguous. (gdb) Which matches how a prefix defined within GDB would act. I have made the same changes to the Guile API.
Diffstat (limited to 'gdb/python')
-rw-r--r--gdb/python/py-cmd.c81
1 files changed, 57 insertions, 24 deletions
diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
index c53138a..5b4f813 100644
--- a/gdb/python/py-cmd.c
+++ b/gdb/python/py-cmd.c
@@ -105,19 +105,17 @@ cmdpy_function (const char *args, int from_tty, cmd_list_element *command)
gdbpy_enter enter_py;
- if (! obj)
+ if (obj == nullptr)
error (_("Invalid invocation of Python command object."));
- if (! PyObject_HasAttr ((PyObject *) obj, invoke_cst))
- {
- if (obj->command->is_prefix ())
- {
- /* A prefix command does not need an invoke method. */
- return;
- }
- error (_("Python command object missing 'invoke' method."));
- }
- if (! args)
+ /* If we get here for a prefix command then the prefix command had an
+ 'invoke' method when it was created. If the 'invoke' method is now
+ missing, then the user has done something weird (like deleting the
+ invoke method, yuck!). */
+ if (!PyObject_HasAttr ((PyObject *) obj, invoke_cst))
+ error (_("Python command object missing 'invoke' method."));
+
+ if (args == nullptr)
args = "";
gdbpy_ref<> argobj (PyUnicode_Decode (args, strlen (args), host_charset (),
NULL));
@@ -507,26 +505,61 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
if (is_prefix)
{
- int allow_unknown;
-
- /* If we have our own "invoke" method, then allow unknown
- sub-commands. */
- allow_unknown = PyObject_HasAttr (self, invoke_cst);
- cmd = add_prefix_cmd (cmd_name.get (),
- (enum command_class) cmdtype,
- NULL, docstring.release (), &obj->sub_list,
- allow_unknown, cmd_list);
+ bool has_invoke = PyObject_HasAttr (self, invoke_cst) == 1;
+ if (has_invoke)
+ {
+ /* If there's an 'invoke' method, then create the prefix
+ command, but call cmdpy_function to dispatch to the invoke
+ method when the user runs the prefix with no sub-command. */
+ cmd = add_prefix_cmd (cmd_name.get (),
+ (enum command_class) cmdtype,
+ nullptr,
+ docstring.release (), &obj->sub_list,
+ 1 /* allow_unknown */, cmd_list);
+ cmd->func = cmdpy_function;
+ }
+ else
+ {
+ /* If there is no 'invoke' method, then create the prefix
+ using the standard prefix callbacks. This means that for
+ 'set prefix' the user will get the help text listing all
+ of the sub-commands, and for 'show prefix', the user will
+ see all of the sub-command values. */
+ cmd_list_element *first = *cmd_list;
+ while (first->prefix != nullptr)
+ first = first->prefix;
+
+ bool is_show = first->subcommands == &showlist;
+
+ if (is_show)
+ cmd = add_show_prefix_cmd (cmd_name.get (),
+ (enum command_class) cmdtype,
+ docstring.release (),
+ &obj->sub_list,
+ 0 /* allow_unknown */, cmd_list);
+ else
+ cmd = add_basic_prefix_cmd (cmd_name.get (),
+ (enum command_class) cmdtype,
+ docstring.release (),
+ &obj->sub_list,
+ 0 /* allow_unknown */, cmd_list);
+ }
}
else
- cmd = add_cmd (cmd_name.get (), (enum command_class) cmdtype,
- docstring.release (), cmd_list);
+ {
+ /* For non-prefix commands, arrange to call cmdpy_function, which
+ invokes the Python 'invoke' method, or raises an exception if
+ the 'invoke' method is missing. */
+ cmd = add_cmd (cmd_name.get (), (enum command_class) cmdtype,
+ docstring.release (), cmd_list);
+ cmd->func = cmdpy_function;
+ }
/* If successful, the above takes ownership of the name, since we set
name_allocated, so release it. */
cmd_name.release ();
- /* There appears to be no API to set this. */
- cmd->func = cmdpy_function;
+ /* There appears to be no API to set these member variables. */
cmd->destroyer = cmdpy_destroyer;
cmd->doc_allocated = 1;
cmd->name_allocated = 1;