This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC][PATCH] Provide ability to have "foo set bar" parameters in python


Hi.

set/show parameters can either be added to the base "set/show" commands,
e.g., "set bar 42"
or they can be added to prefix commands
e.g., "foo set bar 42"

The Python API currently doesn't support the latter.
This patch is a sketch of adding that support.

One issue to decide on is how much implementation do we expose
to support this functionality.
For example, it's nice to be able to do "foo show" to get all
parameters attached to foo.  Thus we (somehow) need to export
the functionality provided by function cmd_show_list.
Plus we'd also want to export the help_list function.
[See functions cmdpy_set_cmd and cmdpy_show_cmd in this patch.]
We *could* export those to Python, so that they could be called from Python.

Plus, we *could* add the ability to create "set" and "show" prefixes
from Python, instead of what this patch provides, which has value,
but then we'd want to support passing back both those values when creating
new parameters (to specify which "set list" and which "show list" to add
the parameter to).

It's not clear to me we want to do this, so this patch
takes a minimal route and just adds a new option to prefix commands
"add_set_show" that lets one add set/show prefixes to the new prefix
command, and also adds a new option to parameters to let one specify
the prefix the parameter belongs to.

The testcase has a good example, copied (and cleaned up) here:

Python:

class prefix_with_setshow_cmd (gdb.Command):
  def __init__ (self):
    super (prefix_with_setshow_cmd, self).__init__ (
        "my-prefix", gdb.COMMAND_OBSCURE, prefix=True, add_set_show=True)
prefix_with_setshow_cmd ()

class PrefixedParam (gdb.Parameter):
  """When set, my-param does something useful. When disabled, does nothing."""
  show_doc = "Show the state of the boolean my-param"
  set_doc = "Set the state of the boolean my-param"
  def get_show_string (self, pvalue):
    return "The state of my-param is " + pvalue"
  def get_set_string (self):
    val = "on"
    if (self.value == False):
      val = "off"
    return "my-param has been set to " + val
  def __init__ (self):
    super (PrefixedParam, self).__init__ (
        "my-param", gdb.COMMAND_DATA, gdb.PARAM_BOOLEAN, prefix="my-prefix")
    self.value = True
prefixed_param = PrefixedParam ()

Then one can do:

my-prefix set my-param 42
my-prefix show
help my-prefix set

And so on.

Comments?

One there's general agreement on how to proceed,
or at least no disagreement :-),
I'll finish this up with all the trimmings (NEWS, etc.).

diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
index 5fc656e..5d50052 100644
--- a/gdb/python/py-cmd.c
+++ b/gdb/python/py-cmd.c
@@ -66,6 +66,13 @@ struct cmdpy_object
      for sub-commands of that prefix.  If this Command is not a prefix
      command, then this field is unused.  */
   struct cmd_list_element *sub_list;
+
+  /* If the prefix is created with add_set_show=True, then these are the
+     commands and sublists.  */
+  struct cmd_list_element *set_cmd;
+  struct cmd_list_element *show_cmd;
+  struct cmd_list_element *set_list;
+  struct cmd_list_element *show_list;
 };
 
 typedef struct cmdpy_object cmdpy_object;
@@ -110,6 +117,11 @@ cmdpy_destroyer (struct cmd_list_element *self, void *context)
   xfree ((char *) self->doc);
   xfree ((char *) self->prefixname);
 
+  /* TODO(dje): What to do if this was a prefix command?
+     GDB itself doesn't really properly support deleting prefix commands.
+     We'll want to free the set/show commands and sub/set/show lists.
+     Until then, keep the set/show prefixes Just In Case.  */
+
   do_cleanups (cleanup);
 }
 
@@ -424,6 +436,29 @@ cmdpy_completer (struct cmd_list_element *command,
   return result;
 }
 
+/* Command function for prefix "set" commands.  */
+
+static void
+cmdpy_set_cmd (struct cmd_list_element *command, char *args, int from_tty)
+{
+  cmdpy_object *obj = (cmdpy_object *) get_cmd_context (command);
+
+  /* TODO(dje): Remove extra space in the quoted name.  */
+  printf_filtered (_("\"%s\" must be followed by the name"
+		     " of a set command.\n"), command->prefixname);
+  help_list (obj->set_list, command->prefixname, all_commands, gdb_stdout);
+}
+
+/* Command function for prefix "show" commands.  */
+
+static void
+cmdpy_show_cmd (struct cmd_list_element *command, char *args, int from_tty)
+{
+  cmdpy_object *obj = (cmdpy_object *) get_cmd_context (command);
+
+  cmd_show_list (obj->show_list, from_tty, "");
+}
+
 /* Helper for cmdpy_init which locates the command list to use and
    pulls out the command name.
 
@@ -435,6 +470,7 @@ cmdpy_completer (struct cmd_list_element *command,
    *sub-commands.
 
    START_LIST is the list in which the search starts.
+   To search from the top, pass "cmdlist".
 
    This function returns the xmalloc()d name of the new command.  On
    error sets the Python error and returns NULL.  */
@@ -486,7 +522,7 @@ gdbpy_parse_command_name (const char *name,
 
   prefix_text2 = prefix_text;
   elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, 1);
-  if (elt == NULL || elt == CMD_LIST_AMBIGUOUS)
+  if (elt == NULL || elt == CMD_LIST_AMBIGUOUS || *prefix_text2 != '\0')
     {
       PyErr_Format (PyExc_RuntimeError, _("Could not find command prefix %s."),
 		    prefix_text);
@@ -511,7 +547,8 @@ gdbpy_parse_command_name (const char *name,
 
 /* Object initializer; sets up gdb-side structures for command.
 
-   Use: __init__(NAME, COMMAND_CLASS [, COMPLETER_CLASS][, PREFIX]]).
+   Use: __init__(NAME, COMMAND_CLASS
+                 [, COMPLETER_CLASS [, PREFIX [, ADD_SET_SHOW]]])
 
    NAME is the name of the command.  It may consist of multiple words,
    in which case the final word is the name of the new command, and
@@ -526,6 +563,8 @@ gdbpy_parse_command_name (const char *name,
 
    If PREFIX is True, then this command is a prefix command.
 
+   If ADD_SET_SHOW is True, then set/show commands are added to this prefix.
+
    The documentation for the command is taken from the doc string for
    the python class.  */
 
@@ -541,8 +580,9 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
   struct cmd_list_element **cmd_list;
   char *cmd_name, *pfx_name;
   static char *keywords[] = { "name", "command_class", "completer_class",
-			      "prefix", NULL };
+			      "prefix", "add_set_show", NULL };
   PyObject *is_prefix = NULL;
+  PyObject *add_set_show = NULL;
   int cmp;
 
   if (obj->command)
@@ -554,9 +594,9 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
       return -1;
     }
 
-  if (! PyArg_ParseTupleAndKeywords (args, kw, "si|iO",
-				     keywords, &name, &cmdtype,
-			  &completetype, &is_prefix))
+  if (! PyArg_ParseTupleAndKeywords (args, kw, "si|iOO",
+				     keywords, &name, &cmdtype, &completetype,
+				     &is_prefix, &add_set_show))
     return -1;
 
   if (cmdtype != no_class && cmdtype != class_run
@@ -614,6 +654,16 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 	  return -1;
 	}
     }
+
+  /* Error check the add_set_show parameter now, so there are less things to
+     have to free if there's an error.  We won't use it until later.  */
+  if (add_set_show != NULL && PyObject_IsTrue (add_set_show) < 0)
+    {
+      xfree (cmd_name);
+      xfree (pfx_name);
+      return -1;
+    }
+
   if (PyObject_HasAttr (self, gdbpy_doc_cst))
     {
       PyObject *ds_obj = PyObject_GetAttr (self, gdbpy_doc_cst);
@@ -667,6 +717,28 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
       if (completetype == -1)
 	set_cmd_completer_handle_brkchars (cmd,
 					   cmdpy_completer_handle_brkchars);
+
+      if (add_set_show != NULL && PyObject_IsTrue (add_set_show))
+	{
+	  obj->set_cmd
+	    = add_prefix_cmd ("set", (enum command_class) cmdtype, NULL,
+			      xstrprintf (_("\
+Set GDB parameters for use with the %scommand."), pfx_name),
+			      &obj->set_list,
+			      concat (pfx_name, "set ", NULL),
+			      0/*allow-unknown*/, &obj->sub_list);
+	  obj->set_cmd->func = cmdpy_set_cmd;
+	  set_cmd_context (obj->set_cmd, self);
+	  obj->show_cmd
+	    = add_prefix_cmd ("show", (enum command_class) cmdtype, NULL,
+			      xstrprintf (_("\
+Show GDB parameters for use with the %scommand."), pfx_name),
+			      &obj->show_list,
+			      concat (pfx_name, "show ", NULL),
+			      0/*allow-unknown*/, &obj->sub_list);
+	  obj->show_cmd->func = cmdpy_show_cmd;
+	  set_cmd_context (obj->show_cmd, self);
+	}
     }
   if (except.reason < 0)
     {
diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c
index fca1e44..c05892f 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -631,41 +631,48 @@ compute_enum_values (parmpy_object *self, PyObject *enum_values)
 
 /* Object initializer; sets up gdb-side structures for command.
 
-   Use: __init__(NAME, CMDCLASS, PARMCLASS, [ENUM])
+   Use: __init__(NAME, CMD_CLASS, PARAM_CLASS, [ENUM_VALUES [,PREFIX]])
 
    NAME is the name of the parameter.  It may consist of multiple
    words, in which case the final word is the name of the new command,
    and earlier words must be prefix commands.
 
-   CMDCLASS is the kind of command.  It should be one of the COMMAND_*
+   CMD_CLASS is the kind of command.  It should be one of the COMMAND_*
    constants defined in the gdb module.
 
-   PARMCLASS is the type of the parameter.  It should be one of the
+   PARAM_CLASS is the type of the parameter.  It should be one of the
    PARAM_* constants defined in the gdb module.
 
-   If PARMCLASS is PARAM_ENUM, then the final argument should be a
+   If PARAM_CLASS is PARAM_ENUM, then ENUM_VALUES should be a
    collection of strings.  These strings are the valid values for this
    parameter.
 
+   If PREFIX is provided, the parameter is added to the prefix's set/show
+   commands, which must have been already created.
+
    The documentation for the parameter is taken from the doc string
    for the python class.
 
    Returns -1 on error, with a python exception set.  */
 
 static int
-parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
+parmpy_init (PyObject *self, PyObject *args, PyObject *kw)
 {
+  static char *keywords[] = { "name", "command_class", "param_class",
+			      "enum_values", "prefix", NULL };
   parmpy_object *obj = (parmpy_object *) self;
   const char *name;
   char *set_doc, *show_doc, *doc;
   char *cmd_name;
   int parmclass, cmdtype;
   PyObject *enum_values = NULL;
+  const char *prefix = NULL;
   struct cmd_list_element **set_list, **show_list;
   volatile struct gdb_exception except;
 
-  if (! PyArg_ParseTuple (args, "sii|O", &name, &cmdtype, &parmclass,
-			  &enum_values))
+  if (! PyArg_ParseTupleAndKeywords (args, kw, "sii|Os", keywords,
+				     &name, &cmdtype, &parmclass,
+				     &enum_values, &prefix))
     return -1;
 
   if (cmdtype != no_class && cmdtype != class_run
@@ -707,15 +714,29 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
   obj->type = (enum var_types) parmclass;
   memset (&obj->value, 0, sizeof (obj->value));
 
-  cmd_name = gdbpy_parse_command_name (name, &set_list,
-				       &setlist);
+  if (prefix != NULL)
+    {
+      char *tmp_name = concat (prefix, " set ", name, NULL);
 
-  if (! cmd_name)
+      cmd_name = gdbpy_parse_command_name (tmp_name, &set_list, &cmdlist);
+      xfree (tmp_name);
+    }
+  else
+    cmd_name = gdbpy_parse_command_name (name, &set_list, &setlist);
+  if (cmd_name == NULL)
     return -1;
   xfree (cmd_name);
-  cmd_name = gdbpy_parse_command_name (name, &show_list,
-				       &showlist);
-  if (! cmd_name)
+
+  if (prefix != NULL)
+    {
+      char *tmp_name = concat (prefix, " show ", name, NULL);
+
+      cmd_name = gdbpy_parse_command_name (tmp_name, &show_list, &cmdlist);
+      xfree (tmp_name);
+    }
+  else
+    cmd_name = gdbpy_parse_command_name (name, &show_list, &showlist);
+  if (cmd_name == NULL)
     return -1;
 
   set_doc = get_doc_string (self, set_doc_cst);
diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp
index 36c109d..99b6cdd 100644
--- a/gdb/testsuite/gdb.python/py-parameter.exp
+++ b/gdb/testsuite/gdb.python/py-parameter.exp
@@ -178,3 +178,39 @@ gdb_test "python print (test_param.value)" "False" "Test parameter value"
 gdb_test "help show print test-param" "State of the Test Parameter.*" "Test show help"
 gdb_test "help set print test-param" "Set the state of the Test Parameter.*" "Test set help"
 gdb_test "help set print" "set print test-param -- Set the state of the Test Parameter.*" "Test general help"
+
+# Test set/show added to a prefix command.
+gdb_py_test_multiple "Prefix with set/show" \
+    "python" "" \
+    "class prefix_with_setshow_cmd (gdb.Command):" "" \
+    "  def __init__ (self):" "" \
+    "    super (prefix_with_setshow_cmd, self).__init__ (\"my-prefix\", gdb.COMMAND_OBSCURE, prefix=True, add_set_show=True)" "" \
+    "prefix_with_setshow_cmd ()" "" \
+    "end" ""
+gdb_py_test_multiple "Prefixed parameter" \
+    "python" "" \
+    "class PrefixedParam (gdb.Parameter):" "" \
+    "  \"\"\"When set, my-param does something useful. When disabled, does nothing.\"\"\"" "" \
+    "  show_doc = \"Show the state of the boolean my-param\"" ""\
+    "  set_doc = \"Set the state of the boolean my-param\"" "" \
+    "  def get_show_string (self, pvalue):" ""\
+    "    return \"The state of my-param is \" + pvalue" ""\
+    "  def get_set_string (self):" ""\
+    "    val = \"on\"" ""\
+    "    if (self.value == False):" ""\
+    "      val = \"off\"" ""\
+    "    return \"my-param has been set to \" + val" ""\
+    "  def __init__ (self):" "" \
+    "    super (PrefixedParam, self).__init__ (\"my-param\", gdb.COMMAND_DATA, gdb.PARAM_BOOLEAN, prefix=\"my-prefix\")" "" \
+    "    self.value = True" "" \
+    "prefixed_param = PrefixedParam ()" ""\
+    "end"
+
+gdb_test "help my-prefix" \
+    "Set GDB parameters for use with the my-prefix command.*Show GDB parameters for use with the my-prefix command.*"
+gdb_test "my-prefix show my-param" "The state of my-param is on"
+gdb_test "my-prefix set my-param off" "my-param has been set to off"
+gdb_test "my-prefix show my-param" "The state of my-param is off"
+gdb_test "my-prefix show" "my-param: *The state of my-param is off"
+gdb_test "help my-prefix set" "my-prefix set my-param.*"
+gdb_test "help my-prefix set my-param" "Set the state of the boolean my-param\[\r\n\]*When set, my-param does something useful. When disabled, does nothing."


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]