From patchwork Fri Apr 11 23:04:53 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 110324 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 76A43382E6BF for ; Fri, 11 Apr 2025 23:05:47 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 76A43382E6BF Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Ps5CvVbH X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 5AE6F382E6A3 for ; Fri, 11 Apr 2025 23:05:01 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5AE6F382E6A3 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 5AE6F382E6A3 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1744412701; cv=none; b=Q+Gm6Xa2aWW8iDWR5pbCms1cXh8SzxuduU7OwM2eAwTnJfH6wYOm8KYwKDitsnEL+Un4PEXRsQa62OXBPh1fIn+RKCNDaZozMuW/Poc+PVnufJYPYKP0/iYfpd2EniG9WrWs7I2NNaAM8vrXpD1c46zDxGjuhFhyr8NxYVWYnUc= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1744412701; c=relaxed/simple; bh=Y1xLN8HXbj614YTygLBsjRwtJ3/HE0tBtrHI9RTp4WQ=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=sSsk0a4UL25ITpj3fvKyIlU2hqdx+KGdz+MphCDDA4qdN6nMCCD7j2nwIBEsOayyMrxRdnf0tdeKwMa/aZmrv5iy1duxn78Yf27JnB9hvn8EyJF2HNkEHfQDLcz+Dh9LRiLJtb9eDilAt8F8V7GXg4XCil8v0Itfrqx6vX38qwE= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 5AE6F382E6A3 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1744412701; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=m8tg0271D2UU+bUs9WXDICjUqOd9UFQ0EdgKK876Lu8=; b=Ps5CvVbHDJBZe3nASgBAGkFyKg1muPAEn7oARNk2U+49Xq+kj6OFX4hh0vcAkjoTHWw88p UCESp4GD7R6rTHo8vamPt2/SFOnSY06oBLoz1SskS9ja1SY+RD/K4m1Dy+Zjn79/RuBfR5 S0P3w+FO1NkfFS5YoG8FwgQHtZh/5h4= Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-661-cqMs39EwOXuvyA001HsYVQ-1; Fri, 11 Apr 2025 19:04:59 -0400 X-MC-Unique: cqMs39EwOXuvyA001HsYVQ-1 X-Mimecast-MFC-AGG-ID: cqMs39EwOXuvyA001HsYVQ_1744412698 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-3913f97d115so1281304f8f.0 for ; Fri, 11 Apr 2025 16:04:59 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744412698; x=1745017498; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=m8tg0271D2UU+bUs9WXDICjUqOd9UFQ0EdgKK876Lu8=; b=azNQbgVCtFrwwaN61T7rwJDFQgJgDdMwIAyTS2uvQdLhE7qkEFvY+z1SbwbTVAjazD lYvee4+xsz4sJmpUBNW3E6xRoGxovphhzcfCs5RHxKKIlYNO+QI6jG4BS+fa9EM2W2Ws 2GtEle+stKAvfTq3ybEBgELmEuvaGFwf+KVebJEP+YPzdfvGfxAIdKWaOZrwlC/Ht87B KvrE9c5X7b4lOdAALWl6x3BsWW9G6cITkwvJ7FZqOXu7ejdZqa4/nKow5b7H7Lbz7P+g ViS74ejmAXLe2yLVVeIV23Jgpvd1BGmmhC2CuJNhxNp3LnxOq4MFeH4crKWd6mh6YDii EnRg== X-Gm-Message-State: AOJu0YyQLZBVl0RBd7P1bHQpZGprq7YluRGBwZWVxfAA1Q6Ggb/V5TCy AOsiGwXrIJQLMFKaBVH7JAliINvnqjqGwnOFi4RQbMmFgvgxFh26EDqEXgmq50GWsjIv15sM7zN idaz0Kh7ux1J7osnsm4x3+9C4U0ajNiSZsONvNVUgvkivdbENlVW3sf7z1VgO6rq/mUYkCMZaXz eU0yHMcSlA+clLZpZsc8eVigQ4he5F4LEkYmgqEvbvyM8= X-Gm-Gg: ASbGncv+NRp4RP0js0bcfJ6sajUI48q1B2sFaF+mRzTge+4QNQ+JbTRj+K2f81c3BGj Fbalm+Gof+yW4qoYSXpxknmzUJXtQ2joH04n0aJ9p/tp9O2y5GAnn6X5luz6S8XQplz1UIEd33M hz5i5MNbgPfyeV0NKvGMDzu3H0vAAR4DJ6iLaG1egBs92qAVNdKbjaG0NKADURthO1b77l5Hr7s 8ocI6EqC1AnWIUoIMVL/+ch3sBTiYb5SiGq0Blj2jpRvqCbcZPUxzLp82esrI456NhJoUpi2NIh wQ/BJ6gDGHq9mvqLJ5tcYo7h56CYA9sC0GYd X-Received: by 2002:a5d:648d:0:b0:391:65c:1b05 with SMTP id ffacd0b85a97d-39e6e48d03dmr3817759f8f.11.1744412698136; Fri, 11 Apr 2025 16:04:58 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEtW0a+Vb9i/jLWiW+DlVPCYKG/WjaNoC7i6T/kITfZUBEKwaZaVj7YulzzlnkuRS24anHiFA== X-Received: by 2002:a5d:648d:0:b0:391:65c:1b05 with SMTP id ffacd0b85a97d-39e6e48d03dmr3817739f8f.11.1744412697499; Fri, 11 Apr 2025 16:04:57 -0700 (PDT) Received: from localhost (30.226.159.143.dyn.plus.net. [143.159.226.30]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-39eaf43cc7esm3369773f8f.77.2025.04.11.16.04.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 11 Apr 2025 16:04:57 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH] gdb/python: allow empty gdb.Parameter.__doc__ string Date: Sat, 12 Apr 2025 00:04:53 +0100 Message-ID: X-Mailer: git-send-email 2.47.1 MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: iFxmEDZ9EWvuepdvNsQfZzE0_F3NocuGXKuI8mHxW5o_1744412698 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-12.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~patchwork=sourceware.org@sourceware.org I was recently attempting to create some parameter 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 gets a single line out output only. 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 need to set __doc__ to a string for any parameter we create, 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 is: (gdb) help set print test Set the current value of 'print test'. 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 return from get_doc_string should never nullptr, but could be empty. There are new tests to cover these changes. --- gdb/NEWS | 4 ++ gdb/doc/python.texi | 4 +- gdb/python/py-param.c | 18 ++++- gdb/testsuite/gdb.python/py-parameter.exp | 87 +++++++++++++++++++++++ 4 files changed, 111 insertions(+), 2 deletions(-) base-commit: 52c5091dfb18113156d645500ab86b600f1cdfe0 diff --git a/gdb/NEWS b/gdb/NEWS index 99ec392d4c4..c3f956dcf57 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -79,6 +79,10 @@ info sharedlibrary 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 for dealing with colors. diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 50342bbba5f..da77976320c 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -5079,7 +5079,9 @@ Parameters In Python 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 diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c index 763680e3dda..06237b6f1b3 100644 --- a/gdb/python/py-param.c +++ b/gdb/python/py-param.c @@ -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 diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp index c15bef15a2b..f355e3ac061 100644 --- a/gdb/testsuite/gdb.python/py-parameter.exp +++ b/gdb/testsuite/gdb.python/py-parameter.exp @@ -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 @@ -679,6 +764,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