]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb/python: new class gdb.StyleParameterSet
authorAndrew Burgess <aburgess@redhat.com>
Wed, 23 Apr 2025 13:51:17 +0000 (14:51 +0100)
committerAndrew Burgess <aburgess@redhat.com>
Sun, 5 Oct 2025 12:47:18 +0000 (13:47 +0100)
Add a new helper class gdb.StyleParameterSet.  This new class can be
used to simplify creation of new style parameter sets.  A style
parameter set is the 'foreground', 'background', and (optionally), the
'intensity' settings, all grouped under a single prefix command.

And example usage is:

  (gdb) python s = gdb.StyleParameterSet("my-style")
  (gdb) show style my-style
  style my-style background:  The "my-style" style background color is: none
  style my-style foreground:  The "my-style" style foreground color is: none
  style my-style intensity:  The "my-style" style display intensity is: normal
  (gdb)

Having created a gdb.StyleParameterSet, the object itself can be used
to access a named style corresponding to the setting group, like this:

  (gdb) python print(s.style)
  <gdb.Style name='my-style', fg=none, bg=none, intensity=normal>
  (gdb)

Of course, having access to the gdb.Style makes it easy to change the
settings, or the settings can be adjusted via the normal CLI 'set'
commands.

As gdb.StyleParameterSet manages a set of parameters, and the
gdb.Parameter class uses Parameter.value as the attribute to read the
parameter's value, there is also StyleParameterSet.value, but this is
just an alias for StyleParameterSet.style, that is, it allows the
gdb.Style object to be read and written too.

It is worth noting that this class only creates a single level of
prefix command.  As an example GDB has style 'disassembler mnemonic',
where the 'disassembler' part is a group of related styles.  If a user
wanted to create:

  style
    my-style-group
      style-1
      style-2
      style-3

Where each of 'style-1', 'style-2', and 'style-3' will have the full
set of 'foreground', 'background', and 'intensity', then the
gdb.StyleParameterSet can be used to create the 'style-N' part, but
the user will have to create the 'my-style-group' prefix themselves,
possibly using gdb.ParameterPrefix, e.g.:

  gdb.ParameterPrefix("style my-style-group", gdb.COMMAND_NONE)
  gdb.StyleParameterSet("my-style-group style-1")
  gdb.StyleParameterSet("my-style-group style-2")
  gdb.StyleParameterSet("my-style-group style-3")

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Approved-By: Tom Tromey <tom@tromey.com>
gdb/NEWS
gdb/doc/python.texi
gdb/python/lib/gdb/__init__.py
gdb/testsuite/gdb.python/py-style-parameter-set.exp [new file with mode: 0644]

index 2b76e2fb2b9a31ce4a7137fdd6b3a4fd08f3e92d..b9c9afc1d77a44f37937e421b04e26d30fb69e39 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -76,6 +76,10 @@ qExecAndArgs
      gdb.INTENSITY_DIM for use with gdb.Style when representing
      intensities.
 
+  ** New gdb.StyleParameterSet for creating custom style settings.
+     Use gdb.StyleParameterSet(NAME) to create 'set style NAME ...'
+     and 'show style NAME ...' parameters.
+
 *** Changes in GDB 17
 
 * Debugging Linux programs that use x86-64 or x86-64 with 32-bit pointer
index 6472bd37071f3c01041332b1aa83070204cf372f..e3f95ee1d24e73b5fc0e53d8f6f7c3b247096b2b 100644 (file)
@@ -5312,6 +5312,7 @@ the parameters @kbd{plugin-name feature-1} and @kbd{plugin-name
 feature-2}, then the @kbd{plugin-name} would need to be a prefix
 command (@pxref{CLI Commands In Python}).
 
+@anchor{gdb.ParameterPrefix}
 However, when creating parameters, you will almost always need to
 create two prefix commands, one as a @kbd{set} sub-command, and one as
 a @kbd{show} sub-command.  @value{GDBN} provides the
@@ -5398,6 +5399,117 @@ ExampleParam("plugin-name feature-1")
 ExampleParam("plugin-name feature-2")
 @end smallexample
 
+@anchor{Creating Style Parameters}
+The helper class @code{gdb.StyleParameterSet} exists to make it easier
+to create new styles.  @value{GDBN} places style settings under
+@samp{show style @dots{}} and @samp{set style @dots{}}, an example of a style
+is @samp{filename}.  Each style is really a prefix command (@pxref{CLI
+Commands In Python}), with sub-commands @samp{foreground},
+@samp{background}, and optionally, @samp{intensity}.
+
+It is simple enough to create a new style using two @code{gdb.Command}
+objects for the prefix commands (one for @samp{set}, and one for
+@samp{show}), and three @code{gdb.Parameter} objects, one each for the
+@samp{foreground}, @samp{background}, and @samp{intensity}.  You would
+also want to take care to craft the help text so that the new style
+behaves the same as the existing styles.
+
+Or, you can use the @code{gdb.StyleParameterSet} class, which takes
+care of all this, as the following example shows:
+
+@smallexample
+@group
+(@value{GDBP}) python s = gdb.StyleParameterSet("my-style")
+(@value{GDBP}) show style my-style
+style my-style background:  The "my-style" style background color is: none
+style my-style foreground:  The "my-style" style foreground color is: none
+style my-style intensity:  The "my-style" style display intensity is: normal
+(@value{GDBP})
+@end group
+@end smallexample
+
+You might also want to group a number of styles within a new prefix,
+similar to how @value{GDBN} groups disassembler related styles within
+the @samp{style disassembler} prefix.  This can be done using
+@code{gdb.ParameterPrefix} (@pxref{gdb.ParameterPrefix}), as in this
+example:
+
+@smallexample
+@group
+(@value{GDBP}) python gdb.ParameterPrefix("style group", gdb.COMMAND_NONE)
+(@value{GDBP}) python s_a = gdb.StyleParameterSet("group aa")
+(@value{GDBP}) python s_b = gdb.StyleParameterSet("group bb")
+(@value{GDBP}) show style group
+style group aa background:  The "group aa" style background color is: none
+style group aa foreground:  The "group aa" style foreground color is: none
+style group aa intensity:  The "group aa" style display intensity is: normal
+style group bb background:  The "group bb" style background color is: none
+style group bb foreground:  The "group bb" style foreground color is: none
+style group bb intensity:  The "group bb" style display intensity is: normal
+(@value{GDBP})
+@end group
+@end smallexample
+
+The @code{gdb.StyleParameterSet} class has the following methods and
+attributes:
+
+@defun StyleParameterSet.__init__(name, @w{add_intensity=@code{True}}, @w{doc=@code{None}})
+Create a new style group based on @var{name}, which is a string.  For
+example if @var{name} is @samp{my-style}, then @value{GDBN} will
+create the prefix commands @samp{set style my-style} and @samp{show
+style my-style}.  Within these prefix commands will be
+@samp{foreground}, @samp{background}, and @samp{intensity} parameters
+with the appropriate types.
+
+It is also possible for @var{name} to consist of multiple words, so
+long as each prefix command (except the last one) already exists.  For
+example, it is valid to use a @var{name} value of @samp{disassembler
+my-style}, as the @samp{disassembler} prefix command already exists.
+@value{GDBN} would then create @samp{set style disassembler my-style}
+and @samp{show style disassembler my-style}, and within the
+@samp{my-style} prefixes will be the @samp{foreground},
+@samp{background}, and @samp{intensity} parameters with the
+appropriate types.
+
+Every style requires a @samp{foreground} and @samp{background}, but
+not every style needs an @samp{intensity}.  If @var{add_intensity} is
+@code{True} (the default), then the @samp{intensity} parameter will be
+created.  If @var{add_intensity} is @code{False}, then the
+@samp{intensity} parameter will not be created.
+
+If the @samp{intensity} parameter is not created, then the
+@code{gdb.Style} (@pxref{Styles In Python}) created from this
+@code{gdb.StyleParameterSet} will have @code{gdb.INTENSITY_NORMAL}.
+
+The @var{doc} should be a string which will be used as the help text
+for the @var{name} prefix command.  This text is used as the
+@code{Command.__doc__} value for the @code{gdb.Command} object that is
+the prefix command object (@pxref{CLI Commands In Python}).  If
+@var{doc} is @code{None} (the default) then a basic default help text
+is used.
+@end defun
+
+@defun StyleParameterSet.apply(string)
+Equivalent to @code{StyleParameterSet.style.apply(string)}.  Returns a
+copy of @var{string} with escape sequences added to the start and end.
+The escape sequence at the start applies this style, and the escape
+sequence at the end restores the terminal default.
+
+If styling is disabled (i.e.@: @samp{set style enabled off}), then no
+escape sequences are added and this method returns a copy of
+@var{string}.
+@end defun
+
+@defvar StyleParameterSet.style
+This read/write attribute holds a @code{gdb.Style} object
+(@pxref{Styles In Python}), that is a named style associated with this
+style parameter group.
+@end defvar
+
+@defvar StyleParameterSet.value
+This is an alias for @code{StyleParameterSet.style}, see above.
+@end defvar
+
 @node Functions In Python
 @subsubsection Writing new convenience functions
 
@@ -7294,7 +7406,8 @@ to apply this styling to output produced from Python.
 
 @value{GDBN} has many styles builtin (@pxref{Output Styling}), and
 style objects can be created that apply these builtin styles to Python
-output.
+output.  It is also possible to create new styles which can be used to
+style Python output (@pxref{Creating Style Parameters}).
 
 The style class is called @code{gdb.Style}, and has the following
 methods and attributes:
index cedd897ab0f2727e0ec63f87e3c944660e18f428..0388c6a2c00adccba8fb05b87e128b22c70e37fb 100644 (file)
@@ -515,3 +515,212 @@ class ParameterPrefix:
     def dont_repeat(self):
         if self.active_prefix is not None:
             self.active_prefix.dont_repeat()
+
+
+class StyleParameterSet:
+    """Create new style parameters.
+
+    A style parameter is a set of parameters that start with 'set style ...'
+    and 'show style ...'.  For example 'filename' is a style parameter, and
+    'disassembler symbol' is another style parameter.
+
+    The name of the style parameter is really a prefix command.  Under this
+    we must have two commands 'foreground' and 'background', which are color
+    parameters.  A third, optional command 'intensity', is an enum with
+    values 'normal', 'bold', and 'dim'.
+
+    A StyleParameterSet is initialised with a name, e.g. 'filename' or
+    'disassembler symbol'.  The StyleParameterSet creates the prefix
+    commands in the 'set style' and 'show style' name space, and then adds
+    the 'foreground', 'background', and optionally, the 'intensity'
+    commands.
+
+    If you want a whole new style group, similar to 'disassembler',
+    then you need to add this yourself first, then StyleParameterSet
+    can be used to create styles within the new prefix group.
+
+    The 'value' attribute on this object can be used to get and set a
+    gdb.Style object which controls all aspects of this style.
+
+    For readability, the alias 'style' is the same as 'value'.
+    """
+
+    def __init__(self, name, add_intensity=True, doc=None):
+        # The STYLE_NAME is something like 'filename' is 'set style
+        # filename ...', and PARAM_NAME is one of 'foreground',
+        # 'background', or 'intensity'.  The DESC_TEXT is the long
+        # form used in help text, like 'foreground color' or 'display
+        # intensity'.  The DEFAULT_VALUE is used to set the SELF.value
+        # attribute.  And PARAM_TYPE is a gdb.PARAM_* constant.  The
+        # ARGS is used for gdb.PARAM_ENUM, which ARGS should be the
+        # enum value list.
+        class style_parameter(Parameter):
+            def __init__(
+                self,
+                style_name,
+                parent_obj,
+                param_name,
+                desc_text,
+                default_value,
+                param_type,
+                *args
+            ):
+                # Setup documentation must be done before calling
+                # parent's __init__ method, as the __init__ reads (and
+                # copies) these values.
+                self.show_doc = "Show the " + desc_text + " for this property."
+                self.set_doc = "Set the " + desc_text + " for this property."
+                self.__doc__ = ""
+
+                # Call the parent's __init__ method to actually create
+                # the parameter.
+                super().__init__(
+                    "style " + style_name + " " + param_name,
+                    COMMAND_NONE,
+                    param_type,
+                    *args
+                )
+
+                # Store information we need in other methods.
+                self._style_name = style_name
+                self._desc_text = desc_text
+                self._parent = parent_obj
+
+                # Finally, setup the default value.
+                self.value = default_value
+
+            # Return the 'show style <style-name> <attribute>' string,
+            # which has styling applied.
+            def get_show_string(self, value):
+                s = self._parent.style
+                return (
+                    "The "
+                    + s.apply('"' + self._style_name + '" style')
+                    + " "
+                    + self._desc_text
+                    + " is: "
+                    + value
+                )
+
+        class style_foreground_parameter(style_parameter):
+            def __init__(self, name, parent):
+                super().__init__(
+                    name,
+                    parent,
+                    "foreground",
+                    "foreground color",
+                    Color(),
+                    PARAM_COLOR,
+                )
+
+        class style_background_parameter(style_parameter):
+            def __init__(self, name, parent):
+                super().__init__(
+                    name,
+                    parent,
+                    "background",
+                    "background color",
+                    Color(),
+                    PARAM_COLOR,
+                )
+
+        class style_intensity_parameter(style_parameter):
+            def __init__(self, name, parent):
+                super().__init__(
+                    name,
+                    parent,
+                    "intensity",
+                    "display intensity",
+                    "normal",
+                    PARAM_ENUM,
+                    ["normal", "bold", "dim"],
+                )
+
+        if doc is None:
+            doc = (
+                "The "
+                + name
+                + " display styling.\nConfigure "
+                + name
+                + " colors and display intensity."
+            )
+
+        ParameterPrefix("style " + name, COMMAND_NONE, doc)
+        self._foreground = style_foreground_parameter(name, self)
+        self._background = style_background_parameter(name, self)
+        if add_intensity:
+            self._intensity = style_intensity_parameter(name, self)
+        self._name = name
+        self._style = None
+
+    @property
+    def value(self):
+        """Return the gdb.Style object for this parameter set."""
+        if self._style is None:
+            self._style = Style(self._name)
+        return self._style
+
+    @property
+    def style(self):
+        """Return the gdb.Style object for this parameter set.
+
+        This is an alias for self.value."""
+        return self.value
+
+    @value.setter
+    def value(self, new_value):
+        """Set this parameter set to NEW_VALUE, a gdb.Style object.
+
+        The attributes of NEW_VALUE are used to update the current settings
+        of this parameter set.  If this parameter set was created without
+        an intensity setting, then the intensity of NEW_VALUE is ignored."""
+        if not isinstance(new_value, Style):
+            raise TypeError("value must be gdb.Style, not %s" % type(new_value))
+        self._foreground.value = new_value.foreground
+        self._background.value = new_value.background
+        if hasattr(self, "_intensity"):
+            intensity_value = new_value.intensity
+            if intensity_value == INTENSITY_BOLD:
+                intensity_string = "bold"
+            elif intensity_value == INTENSITY_DIM:
+                intensity_string = "dim"
+            elif intensity_value == INTENSITY_NORMAL:
+                intensity_string = "normal"
+            else:
+                raise ValueError(
+                    "unknown intensity value %d from Style" % intensity_value
+                )
+
+            self._intensity.value = intensity_string
+
+    @style.setter
+    def style(self, new_value):
+        """Set this parameter set to NEW_VALUE, a gdb.Style object.
+
+        This is an alias for self.value."""
+        self.value = new_value
+
+    def apply(self, *args, **kwargs):
+        """Apply this style to the arguments.
+
+        Forwards all arguments to self.style.apply().  The arguments should be a string,
+        to which this style is applied.  This function returns the same string with
+        escape sequences added to apply this style.
+
+        If styling is globally disabled ('set style enabled off') then no escape sequences
+        will be added, the input string is returned."""
+        return self.style.apply(*args, **kwargs)
+
+    def __repr__(self):
+        """A string representation of SELF."""
+
+        def full_typename(obj):
+            module = type(obj).__module__
+            qualname = type(obj).__qualname__
+
+            if module is None or module == "builtins":
+                return qualname
+            else:
+                return module + "." + qualname
+
+        return "<" + full_typename(self) + " name='" + self._name + "'>"
diff --git a/gdb/testsuite/gdb.python/py-style-parameter-set.exp b/gdb/testsuite/gdb.python/py-style-parameter-set.exp
new file mode 100644 (file)
index 0000000..73d94d1
--- /dev/null
@@ -0,0 +1,366 @@
+# Copyright (C) 2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests gdb.StyleParameterSet.
+
+load_lib gdb-python.exp
+
+require allow_python_tests
+
+# Create a regexp that can be used to match the output of a 'show style
+# NAME' command.  HAS_INTENSITY is a boolean and indicates if style NAME has
+# an intensity attribute.
+proc gen_show_style_re { name has_intensity } {
+    set output \
+       [list \
+            "style ${name} background:  The \"${name}\" style background color is: none" \
+            "style ${name} foreground:  The \"${name}\" style foreground color is: none"]
+
+    if { $has_intensity } {
+       lappend output \
+           "style ${name} intensity:  The \"${name}\" style display intensity is: normal"
+    }
+
+    return [multi_line {*}$output]
+}
+
+# Create a regexp to match against the output of a 'set style NAME' command,
+# that is, a 'set' command that doesn't actually set an attribute of a
+# style, in this case GDB will print some useful help text. HAS_INTENSITY is
+# a boolean and indicates if style NAME has an intensity attribute or not.
+proc gen_set_style_re { name has_intensity } {
+    set output \
+       [list \
+            "List of \"set style $name\" subcommands:" \
+            "" \
+            "set style $name background -- Set the background color for this property\\." \
+            "set style $name foreground -- Set the foreground color for this property\\."]
+
+    if { $has_intensity } {
+       lappend output \
+           "set style $name intensity -- Set the display intensity for this property\\."
+    }
+
+    lappend output \
+       "" \
+       "Type \"help set style $name\" 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\\."
+
+    return [multi_line {*}$output]
+}
+
+# Create a regexp to match against the output of a 'help show style NAME'
+# command.  HAS_INTENSITY is a boolean and indicates if style NAME has an
+# intensity attribute or not.  DOC is a regexp that matches the doc string
+# for style NAME.
+proc gen_help_show_style_re { name has_intensity doc } {
+    set output \
+       [list \
+            $doc \
+            "" \
+            "List of \"show style ${name}\" subcommands:" \
+            "" \
+            "show style $name background -- Show the background color for this property\\." \
+            "show style $name foreground -- Show the foreground color for this property\\."]
+    if { $has_intensity } {
+       lappend output \
+           "show style $name intensity -- Show the display intensity for this property\\."
+    }
+
+    lappend output \
+       "" \
+       "Type \"help show style $name\" 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\\."
+
+    return [multi_line {*}$output]
+}
+
+# Create a regexp to match against the output of a 'help set style NAME'
+# command.  HAS_INTENSITY is a boolean and indicates if style NAME has an
+# intensity attribute or not.  DOC is a regexp that matches the doc string
+# for style NAME.
+proc gen_help_set_style_re { name has_intensity doc } {
+    return \
+       [multi_line \
+            $doc \
+            "" \
+            [gen_set_style_re $name $has_intensity]]
+}
+
+# Create styles with and without intensity.  Use named and unnamed
+# argument passing, and vary the argument passing order.  Create
+# styles with and without documentation.
+#
+# Confirm that the styles contain the expected sub-commands, and that
+# the documentation is as expected.
+proc_with_prefix test_basic_usage {} {
+    gdb_test_no_output \
+       "python style_1 = gdb.StyleParameterSet(\"style-1\")" \
+       "create style-1"
+
+    gdb_test_no_output \
+       "python style_2 = gdb.StyleParameterSet(add_intensity=True, name=\"style-2\")" \
+       "create style-2"
+
+    gdb_test_no_output \
+       "python style_3 = gdb.StyleParameterSet(name=\"style-3\", doc=\"Style-3 display styling.\\nThis is a multi-line documentation\\nstring describing style-3.\")" \
+       "create style-3"
+
+    gdb_test_no_output \
+       "python style_4 = gdb.StyleParameterSet(\"style-4\", add_intensity=False)" \
+       "create style-4"
+
+    foreach style { style-1 style-2 style-3 } {
+       gdb_test "show style $style" \
+           [gen_show_style_re $style true]
+       gdb_test "set style $style" \
+           [gen_set_style_re $style true]
+    }
+
+    gdb_test "show style style-4" \
+       [gen_show_style_re "style-4" false]
+
+    foreach style { style-1 style-2 } {
+       gdb_test "help show style $style" \
+           [gen_help_show_style_re $style true \
+                [multi_line \
+                     "The $style display styling\\." \
+                     "Configure $style colors and display intensity\\."]]
+
+
+       set out             [gen_help_set_style_re $style true \
+                [multi_line \
+                     "The $style display styling\\." \
+                     "Configure $style colors and display intensity\\."]]
+
+       gdb_test "help set style $style" \
+           [gen_help_set_style_re $style true \
+                [multi_line \
+                     "The $style display styling\\." \
+                     "Configure $style colors and display intensity\\."]]
+    }
+
+    gdb_test "help show style style-3" \
+       [gen_help_show_style_re "style-3" true \
+            [multi_line \
+                 "Style-3 display styling\\." \
+                 "This is a multi-line documentation" \
+                 "string describing style-3\\."]]
+
+    gdb_test "help show style style-4" \
+       [gen_help_show_style_re "style-4" false \
+            [multi_line \
+                 "The style-4 display styling\\." \
+                 "Configure style-4 colors and display intensity\\."]]
+
+    for { set i 1 } { $i < 5 } { incr i } {
+       gdb_test "python print(style_$i)" \
+           "<gdb.StyleParameterSet name='style-$i'>" \
+           "print repr of style_$i"
+    }
+
+    # There is no 'style-4 intensity' property.
+    gdb_test "show style style-4 foreground" \
+       "The \"style-4\" style foreground color is: none"
+    gdb_test "show style style-4 background" \
+       "The \"style-4\" style background color is: none"
+    gdb_test "show style style-4 intensity" \
+       "Undefined show style style-4 command: \"intensity\"\\.  Try \"help show style style-4\"\\."
+
+    # There is a 'style-1 intensity' property.
+    gdb_test "show style style-1 foreground" \
+       "The \"style-1\" style foreground color is: none" \
+       "show style-1 foreground before changes"
+    gdb_test "show style style-1 background" \
+       "The \"style-1\" style background color is: none" \
+       "show style-1 background before changes"
+    gdb_test "show style style-1 intensity" \
+       "The \"style-1\" style display intensity is: normal" \
+       "show style-1 intensity before changes"
+
+    # Grab the gdb.Style objects from 'style-1'.
+    gdb_test_no_output "python s1 = style_1.style"
+    gdb_test_no_output "python s2 = style_1.value"
+
+    # Check both represent the same style.
+    gdb_test "python print(s1)" \
+       "<gdb.Style name='style-1', fg=none, bg=none, intensity=normal>" \
+       "print s1 style before changes"
+    gdb_test "python print(s2)" \
+       "<gdb.Style name='style-1', fg=none, bg=none, intensity=normal>" \
+       "print s2 style before changes"
+
+    gdb_test_no_output \
+       "python s1.foreground=gdb.Color('red')" "set foreground"
+    gdb_test_no_output \
+       "python s1.background=gdb.Color('blue')" "set background"
+    gdb_test_no_output \
+       "python s1.intensity=gdb.INTENSITY_DIM" "set intensity"
+
+    gdb_test "python print(s1)" \
+       "<gdb.Style name='style-1', fg=red, bg=blue, intensity=dim>" \
+       "print s1 style after first set of changes"
+    gdb_test "python print(s2)" \
+       "<gdb.Style name='style-1', fg=red, bg=blue, intensity=dim>" \
+       "print s2 style after first set of changes"
+
+    # Check the style properties have updated.
+    gdb_test "show style style-1 foreground" \
+       "The \"style-1\" style foreground color is: red" \
+       "show style-1 foreground after first set of changes"
+    gdb_test "show style style-1 background" \
+       "The \"style-1\" style background color is: blue" \
+       "show style-1 background after first set of changes"
+    gdb_test "show style style-1 intensity" \
+       "The \"style-1\" style display intensity is: dim" \
+       "show style-1 intensity after first set of changes"
+
+    # Change the style properties, check gdb.Style objects update.
+    gdb_test_no_output "set style style-1 foreground yellow"
+    gdb_test_no_output "set style style-1 background cyan"
+    gdb_test_no_output "set style style-1 intensity bold"
+
+    gdb_test "python print(s1)" \
+       "<gdb.Style name='style-1', fg=yellow, bg=cyan, intensity=bold>" \
+       "print s1 style after second set of changes"
+    gdb_test "python print(s2)" \
+       "<gdb.Style name='style-1', fg=yellow, bg=cyan, intensity=bold>" \
+       "print s2 style after second set of changes"
+
+    # Assign a gdb.Style to set 'style-1'.  First create some unnamed
+    # style objects that can be used.
+    gdb_test_no_output \
+       "python uns1 = gdb.Style(foreground=gdb.Color('white'), background=gdb.Color('black'), intensity=gdb.INTENSITY_BOLD)" \
+       "create uns1"
+    gdb_test_no_output \
+       "python uns2 = gdb.Style(foreground=gdb.Color('black'), background=gdb.Color('white'), intensity=gdb.INTENSITY_DIM)" \
+       "create uns2"
+
+    gdb_test_no_output "python style_1.value = uns1"
+    gdb_test "show style style-1 foreground" \
+       "The \"style-1\" style foreground color is: white" \
+       "show style-1 foreground after assigning uns1"
+    gdb_test "show style style-1 background" \
+       "The \"style-1\" style background color is: black" \
+       "show style-1 background after assigning uns1"
+    gdb_test "show style style-1 intensity" \
+       "The \"style-1\" style display intensity is: bold" \
+       "show style-1 intensity after assigning uns1"
+
+    gdb_test_no_output "python style_1.style = uns2"
+    gdb_test "show style style-1 foreground" \
+       "The \"style-1\" style foreground color is: black" \
+       "show style-1 foreground after assigning uns2"
+    gdb_test "show style style-1 background" \
+       "The \"style-1\" style background color is: white" \
+       "show style-1 background after assigning uns2"
+    gdb_test "show style style-1 intensity" \
+       "The \"style-1\" style display intensity is: dim" \
+       "show style-1 intensity after assigning uns2"
+
+    # Assign a style with an intensity that is not 'NORMAL' to a
+    # StyleParameterSet that doesn't have an intensity.  The new
+    # intensity setting should be ignored.
+    gdb_test_no_output "python style_4.style = uns1"
+    gdb_test "show style style-4 foreground" \
+       "The \"style-4\" style foreground color is: white" \
+       "show style-4 foreground after assigning uns1"
+    gdb_test "show style style-4 background" \
+       "The \"style-4\" style background color is: black" \
+       "show style-4 background after assigning uns1"
+    gdb_test "show style style-4 intensity" \
+       "Undefined show style style-4 command: \"intensity\"\\.  Try \"help show style style-4\"\\." \
+       "show style-4 intensity after assigning uns1"
+
+    gdb_test "python print(style_4.style)" \
+       "<gdb.Style name='style-4', fg=white, bg=black, intensity=normal>" \
+       "print string repr of style_4's style"
+}
+
+# Test creating a style prefix with gdb.ParameterPrefix, then adding
+# some styles within the new prefix.  Change the style through the CLI
+# and confirm that the associated Python object updated as expected.
+proc_with_prefix test_style_prefix {} {
+    gdb_test_no_output \
+       "python gdb.ParameterPrefix(\"style my-style-group\", gdb.COMMAND_NONE)"
+    gdb_test_no_output \
+       "python style_1 = gdb.StyleParameterSet(\"my-style-group style-1\")" \
+       "create style-1"
+    gdb_test_no_output \
+       "python style_2 = gdb.StyleParameterSet(\"my-style-group style-2\")" \
+       "create style-2"
+
+    gdb_test "python print(style_1.style)" \
+       "<gdb.Style name='my-style-group style-1', fg=none, bg=none, intensity=normal>" \
+       "print 'my-style-group style-1' style before changes"
+    gdb_test "python print(style_2.style)" \
+       "<gdb.Style name='my-style-group style-2', fg=none, bg=none, intensity=normal>" \
+       "print 'my-style-group style-2' style before changes"
+
+    gdb_test_no_output "set style my-style-group style-1 foreground red"
+    gdb_test_no_output "set style my-style-group style-1 background yellow"
+    gdb_test_no_output "set style my-style-group style-1 intensity bold"
+    gdb_test_no_output "set style my-style-group style-2 foreground black"
+    gdb_test_no_output "set style my-style-group style-2 background blue"
+    gdb_test_no_output "set style my-style-group style-2 intensity dim"
+
+    gdb_test "python print(style_1.style)" \
+       "<gdb.Style name='my-style-group style-1', fg=red, bg=yellow, intensity=bold>" \
+       "print 'my-style-group style-1' style after changes"
+    gdb_test "python print(style_2.style)" \
+       "<gdb.Style name='my-style-group style-2', fg=black, bg=blue, intensity=dim>" \
+       "print 'my-style-group style-2' style after changes"
+}
+
+# Test that gdb.StyleParameterSet.apply() works as expected.
+proc_with_prefix test_applying {} {
+    # Create a new StyleParameterSet, and adjust its settings.
+    gdb_test_no_output \
+       "python style_1 = gdb.StyleParameterSet(\"style-1\")" \
+       "create style-1"
+    gdb_test_no_output \
+       "python uns1 = gdb.Style(foreground=gdb.Color('red'), background=gdb.Color('blue'), intensity=gdb.INTENSITY_BOLD)" \
+       "create uns1"
+    gdb_test_no_output "python style_1 = uns1"
+
+    # When styling is off (which it currently is), no escape sequences
+    # should be added.
+    gdb_test \
+       "python print(style_1.apply('xxx'))" "^xxx" \
+       "apply StyleParameterSet to a string when styling is off"
+
+    # When styling is on, we should see an escape sequence added.
+    gdb_test "with style enabled on -- python print(style_1.apply('xxx'))" \
+       "\033\\\[31;44;1;23;24;27mxxx\033\\\[m" \
+       "apply a style when styling is on"
+}
+
+# Start GDB.
+with_ansi_styling_terminal {
+    clean_restart
+}
+
+# Turn styling off so that the output of 'show style ...' isn't styled, this
+# makes it easier to match the output.
+gdb_test_no_output "set style enabled off"
+
+# Run the tests.
+test_basic_usage
+test_style_prefix
+test_applying