]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb/python: allow empty gdb.Parameter.__doc__ string
authorAndrew Burgess <aburgess@redhat.com>
Fri, 11 Apr 2025 22:45:51 +0000 (23:45 +0100)
committerAndrew Burgess <aburgess@redhat.com>
Tue, 13 May 2025 13:33:20 +0000 (14:33 +0100)
I was recently attempting to create some parameters via the Python
API.  I wanted these parameters to appear similar to how GDB handles
the existing 'style' parameters.

Specifically, I was interested in this behaviour:

  (gdb) help show style filename foreground
  Show the foreground color for this property.
  (gdb) help set style filename foreground
  Set the foreground color for this property.
  (gdb)

Notice how each 'help' command only gets a single line of output.

I tried to reproduce this behaviour via the Python API and was unable.

The problem is that, in order to get just a single line of output like
this, the style parameters are registered with a call to
add_setshow_color_cmd with the 'help_doc' being passed as nullptr.

On the Python side, when parameters are created, the 'help_doc' is
obtained with a call to get_doc_string (python/py-param.c).  This
function either returns the __doc__ string, or a default string: "This
command is not documented.".

To avoid returning the default we could try setting __doc__ to an
empty string, but setting this field to any string means that GDB
prints a line for that string, like this:

  class test_param(gdb.Parameter):
     __doc__ = ""
     def __init__(self, name):
        super ().__init__(name, gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)
        self.value = True

  test_param('print test')

Then in GDB:

  (gdb) help set print test
  Set the current value of 'print test'.

  (gdb)

The blank line is the problem I'd like to solve.

This commit makes a couple of changes to how parameter doc strings are
handled.

If the doc string is set to an empty string, then GDB now converts
this to nullptr, which removes the blank line problem, the new
behaviour in GDB (for the above `test_param`) is:

  (gdb) help set print test
  Set the current value of 'print test'.
  (gdb)

Next, I noticed that if the set/show docs are set to empty strings,
then the results are less than ideal:

  class test_param(gdb.Parameter):
     set_doc = ""
     def __init__(self, name):
        super ().__init__(name, gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)
        self.value = True

  test_param('print test')

And in GDB:

  (gdb) help set print test

  This command is not documented.
  (gdb)

So, if the set/show docs are the empty string, GDB now forces these to
be the default string instead, the new behaviour in GDB is:

  (gdb) help set print test
  Set the current value of 'print test'.
  This command is not documented.
  (gdb)

I've added some additional asserts; the set/show docs should always be
non-empty strings, which I believe is the case after this commit.  And
the 'doc' string returned from get_doc_string should never nullptr,
but could be empty.

There are new tests to cover all these changes.

gdb/NEWS
gdb/doc/python.texi
gdb/python/py-param.c
gdb/testsuite/gdb.python/py-parameter.exp

index d3699de1653275ad8b8f2c97321ebe494d221b46..a50f0979759b0d6edc35dfeb053604562b9f7053 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -140,6 +140,10 @@ info threads [-gid] [-stopped] [-running] [ID]...
      when output is going to standard output, and False when output is
      going to a string.
 
+  ** Setting the documentation string (__doc__) of a gdb.Parameter
+     sub-class to the empty string, means GDB will only display the
+     set_doc or show_doc strings in the set/show help output.
+
 * Guile API
 
   ** New type <gdb:color> for dealing with colors.
index 7bb650347f7735217f90932b81be9b1e3a5c40d8..1a2d7724aa743d2026c469a3da9488a339727c53 100644 (file)
@@ -5079,7 +5079,9 @@ string from the parameter's class, if there is one.  If there is no
 documentation string, a default value is used.  The documentation
 string is included in the output of the parameters @code{help set} and
 @code{help show} commands, and should be written taking this into
-account.
+account.  If the documentation string for the parameter's class is the
+empty string then @value{GDBN} will only use @code{Parameter.set_doc}
+or @code{Parameter.show_doc} (see below) in the @kbd{help} output.
 @end defun
 
 @defvar Parameter.set_doc
index 763680e3ddaebccbbff9fde86e5a08d2432d1dc6..06237b6f1b3ea7b0a9efc42b062578707a5a4ec0 100644 (file)
@@ -495,7 +495,11 @@ get_doc_string (PyObject *object, enum doc_string_type doc_type,
        }
     }
 
-  if (result == nullptr)
+  /* For the set/show docs, if these strings are empty then we set then to
+     a non-empty string.  This ensures that the command has some sane
+     documentation for its 'help' text.  */
+  if (result == nullptr
+      || (doc_type != doc_string_description && *result == '\0'))
     {
       if (doc_type == doc_string_description)
        result.reset (xstrdup (_("This command is not documented.")));
@@ -904,6 +908,18 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
   show_doc = get_doc_string (self, doc_string_show, name);
   doc = get_doc_string (self, doc_string_description, cmd_name.get ());
 
+  /* The set/show docs should always be a non-empty string.  */
+  gdb_assert (set_doc != nullptr && *set_doc != '\0');
+  gdb_assert (show_doc != nullptr && *show_doc != '\0');
+
+  /* For the DOC string only, if it is the empty string, then we convert it
+     to NULL.  This means GDB will not even display a blank line for this
+     part of the help text, instead the set/show line is all the user will
+     get.  */
+  gdb_assert (doc != nullptr);
+  if (*doc == '\0')
+    doc = nullptr;
+
   Py_INCREF (self);
 
   try
index 39a24ca1663ea4f0da1a4864f76974d29758937f..2ca56dc83fc6cd615dcc283b43c4c10c9da1500a 100644 (file)
@@ -346,6 +346,91 @@ proc_with_prefix test_really_undocumented_parameter { } {
        "test general help"
 }
 
+# Test a parameter in which the __doc__ string is empty or None.
+proc_with_prefix test_empty_doc_parameter {} {
+    gdb_test_multiline "empty __doc__ parameter" \
+       "python" "" \
+       "class EmptyDocParam(gdb.Parameter):" "" \
+       "   __doc__ = \"\"" "" \
+       "   def __init__(self, name):" "" \
+       "      super ().__init__(name, gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \
+       "      self.value = True" "" \
+       "test_empty_doc_param = EmptyDocParam('print test-empty-doc-param')" ""\
+       "end"
+
+    # Setting the __doc__ string to empty means GDB will completely
+    # elide it from the output.
+    gdb_test "help set print test-empty-doc-param" \
+       "^Set the current value of 'print test-empty-doc-param'\\."
+
+    gdb_test_multiline "None __doc__ parameter" \
+       "python" "" \
+       "class NoneDocParam(gdb.Parameter):" "" \
+       "   __doc__ = None" "" \
+       "   def __init__(self, name):" "" \
+       "      super ().__init__(name, gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \
+       "      self.value = True" "" \
+       "test_none_doc_param = NoneDocParam('print test-none-doc-param')" ""\
+       "end"
+
+    # Setting the __doc__ string to None, or anything else that isn't
+    # a string, causes GDB to use a default string instead.
+    gdb_test "help set print test-none-doc-param" \
+       [multi_line \
+            "^Set the current value of 'print test-none-doc-param'\\." \
+            "This command is not documented\\."]
+}
+
+# Test a parameter in which the set_doc/show_doc strings are either
+# empty, or None.
+proc_with_prefix test_empty_set_show_doc_parameter {} {
+    gdb_test_multiline "empty set/show doc parameter" \
+       "python" "" \
+       "class EmptySetShowParam(gdb.Parameter):" "" \
+       "   set_doc = \"\"" "" \
+       "   show_doc = \"\"" "" \
+       "   def __init__(self, name):" "" \
+       "      super ().__init__(name, gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \
+       "      self.value = True" "" \
+       "test_empty_set_show_param = EmptySetShowParam('print test-empty-set-show-param')" ""\
+       "end"
+
+    # Setting the set_doc/show_doc string to empty means GDB will use
+    # a suitable default string.
+    gdb_test "help set print test-empty-set-show-param" \
+       [multi_line \
+            "^Set the current value of 'print test-empty-set-show-param'\\." \
+            "This command is not documented\\."]
+
+    gdb_test "help show print test-empty-set-show-param" \
+       [multi_line \
+            "^Show the current value of 'print test-empty-set-show-param'\\." \
+            "This command is not documented\\."]
+
+    gdb_test_multiline "None set/show doc parameter" \
+       "python" "" \
+       "class NoneSetShowParam(gdb.Parameter):" "" \
+       "   set_doc = None" "" \
+       "   show_doc = None" "" \
+       "   def __init__(self, name):" "" \
+       "      super ().__init__(name, gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \
+       "      self.value = True" "" \
+       "test_none_set_show_param = NoneSetShowParam('print test-none-set-show-param')" ""\
+       "end"
+
+    # Setting the set_doc/show_doc string to None (or any non-string
+    # value) means GDB will use a suitable default string.
+    gdb_test "help set print test-none-set-show-param" \
+       [multi_line \
+            "^Set the current value of 'print test-none-set-show-param'\\." \
+            "This command is not documented\\."]
+
+    gdb_test "help show print test-none-set-show-param" \
+       [multi_line \
+            "^Show the current value of 'print test-none-set-show-param'\\." \
+            "This command is not documented\\."]
+}
+
 # Test deprecated API. Do not use in your own implementations.
 proc_with_prefix test_deprecated_api_parameter { } {
     clean_restart
@@ -716,6 +801,8 @@ test_color_parameter
 test_file_parameter
 test_undocumented_parameter
 test_really_undocumented_parameter
+test_empty_doc_parameter
+test_empty_set_show_doc_parameter
 test_deprecated_api_parameter
 test_gdb_parameter
 test_integer_parameter