Skip to content

Commit ef8bee0

Browse files
committed
gdb/python: new gdb.ParameterPrefix class
This commit adds a new gdb.ParameterPrefix class to GDB's Python API. When creating multiple gdb.Parameters, it is often desirable to group these together under a sub-command, for example, 'set print' has lots of parameters nested under it, like 'set print address', and 'set print symbol'. In the Python API the 'print' part of these commands are called prefix commands, and are created using gdb.Command objects. However, as parameters are set via the 'set ....' command list, and shown through the 'show ....' command list, creating a prefix for a parameter usually requires two prefix commands to be created, one for the 'set' command, and one for the 'show' command. This often leads to some duplication, or at the very least, each user will end up creating their own helper class to simplify creation of the two prefix commands. This commit adds a new gdb.ParameterPrefix class. Creating a single instance of this class will create both the 'set' and 'show' prefix commands, which can then be used while creating the gdb.Parameter. Here is an example of it in use: gdb.ParameterPrefix('my-prefix', gdb.COMMAND_NONE) This adds 'set my-prefix' and 'show my-prefix', both of which are prefix commands. The user can then add gdb.Parameter objects under these prefixes. The gdb.ParameterPrefix initialise method also supports documentation strings, so we can write: gdb.ParameterPrefix('my-prefix', gdb.COMMAND_NONE, "Configuration setting relating to my special extension.") which will set the documentation string for the prefix command. Also, it is possible to support prefix commands that use the `invoke` functionality to handle unknown sub-commands. This is done by sub-classing gdb.ParameterPrefix and overriding either 'invoke_set' or 'invoke_show' to handle the 'set' or 'show' prefix command respectively. Reviewed-By: Eli Zaretskii <[email protected]>
1 parent a0f6a1f commit ef8bee0

File tree

4 files changed

+502
-0
lines changed

4 files changed

+502
-0
lines changed

gdb/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ info threads [-gid] [-stopped] [-running] [ID]...
144144
sub-class to the empty string, means GDB will only display the
145145
set_doc or show_doc strings in the set/show help output.
146146

147+
** New gdb.ParameterPrefix class. This can be used to create 'set'
148+
and 'show' gdb.Command prefixes, suitable for use with new
149+
gdb.Parameters.
150+
147151
* Guile API
148152

149153
** New type <gdb:color> for dealing with colors.

gdb/doc/python.texi

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4558,6 +4558,7 @@ documentation string is provided, the default value @samp{This command
45584558
is not documented.} is used.
45594559
@end defun
45604560

4561+
@anchor{Command.dont_repeat}
45614562
@cindex don't repeat Python command
45624563
@defun Command.dont_repeat ()
45634564
By default, a @value{GDBN} command is repeated when the user enters a
@@ -4568,6 +4569,7 @@ exception). This is similar to the user command @code{dont-repeat},
45684569
see @ref{Define, dont-repeat}.
45694570
@end defun
45704571

4572+
@anchor{Command.invoke}
45714573
@defun Command.invoke (argument, from_tty)
45724574
This method is called by @value{GDBN} when this command is invoked.
45734575

@@ -5260,6 +5262,99 @@ constants provided when the parameter is created.
52605262
The value is @code{gdb.Color} instance.
52615263
@end table
52625264

5265+
When creating multiple new parameters using @code{gdb.Parameter}, it
5266+
is often desirable to create a prefix command that can be used to
5267+
group related parameters together, for example, if you wished to add
5268+
the parameters @kbd{plugin-name feature-1} and @kbd{plugin-name
5269+
feature-2}, then the @kbd{plugin-name} would need to be a prefix
5270+
command (@pxref{CLI Commands In Python}).
5271+
5272+
However, when creating parameters, you will almost always need to
5273+
create two prefix commands, one as a @kbd{set} sub-command, and one as
5274+
a @kbd{show} sub-command. @value{GDBN} provides the
5275+
@code{gdb.ParameterPrefix} helper class to make creation of these two
5276+
prefixes easier.
5277+
5278+
@defun ParameterPrefix.__init__ (name, command_class, doc = @code{None})
5279+
The object initializer for @code{ParameterPrefix} registers two new
5280+
@code{gdb.Command} prefixes, one as a @kbd{set} sub-command, and the
5281+
other as a @kbd{show} sub-command.
5282+
5283+
@var{name}, a string, is the name of the new prefix, without either
5284+
@kbd{set} or @kbd{show}, similar to the @var{name} passed to
5285+
@code{gdb.Parameter} (@pxref{Parameters In Python}). For example, to
5286+
create the prefixes @kbd{set plugin-name} and @kbd{show plugin-name},
5287+
you would pass the string @kbd{plugin-name}.
5288+
5289+
@var{command_class} should be one of the @samp{COMMAND_} constants
5290+
(@pxref{CLI Commands In Python}). This argument tells @value{GDBN} how to
5291+
categorize the new parameter prefixes in the help system.
5292+
5293+
There are a number of ways in which the help text for the two new
5294+
prefix commands can be provided. If the @var{doc} parameter is not
5295+
@code{None}, then this will be used as the documentation string for
5296+
both prefix commands.
5297+
5298+
If @var{doc} is @code{None}, but @code{gdb.ParameterPrefix} has been
5299+
sub-classed, then the prefix command documentation will be taken from
5300+
sub-classes documentation string (i.e., the @code{__doc__} attribute).
5301+
5302+
If @var{doc} is @code{None}, and there is no @code{__doc__} string,
5303+
then the default value @samp{This command is not documented.} is used.
5304+
5305+
When writing the help text, keep in mind that the same text is used
5306+
for both the @kbd{set} and @kbd{show} prefix commands.
5307+
@end defun
5308+
5309+
@defun ParameterPrefix.invoke_set (argument, from_tty)
5310+
If a sub-class defines this method, then @value{GDBN} will call this
5311+
when the prefix command is used with an unknown sub-command. The
5312+
@var{argument} and @var{from_tty} parameters are the same as for
5313+
@code{gdb.Command.invoke} (@pxref{Command.invoke}).
5314+
5315+
If this method throws an exception, it is turned into a @value{GDBN}
5316+
@code{error} call. Otherwise, the return value is ignored.
5317+
5318+
It is not required that a @code{ParameterPrefix} sub-class override
5319+
this method. Usually, a parameter prefix only exists as a means to
5320+
group related parameters together. @value{GDBN} handles this use case
5321+
automatically with no need to implement @code{invoke_set}.
5322+
@end defun
5323+
5324+
@defun ParameterPrefix.invoke_show (argument, from_tty)
5325+
This is like the @code{invoke_set} method, but for the @kbd{show}
5326+
prefix command. As with @code{invoke_set}, implementation of this
5327+
method is optional, and usually not required.
5328+
@end defun
5329+
5330+
@cindex don't repeat Python command
5331+
@defun ParameterPrefix.dont_repeat ()
5332+
Like @code{Command.dont_repeat} (@pxref{Command.dont_repeat}), this
5333+
can be called from @code{ParameterPrefix.invoke_set} or
5334+
@code{ParameterPrefix.invoke_show} to prevent the prefix commands from
5335+
being repeated.
5336+
@end defun
5337+
5338+
Here is a small example that uses @code{gdb.ParameterPrefix} along
5339+
with @code{gdb.Parameter} to create two new parameters
5340+
@kbd{plugin-name feature-1} and @kbd{plugin-name feature-2}. As
5341+
neither @code{invoke_set} or @code{invoke_show} is needed, this
5342+
example does not sub-class @code{gdb.ParameterPrefix}:
5343+
5344+
@smallexample
5345+
class ExampleParam(gdb.Parameter):
5346+
def __init__ (self, name):
5347+
super ().__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_BOOLEAN)
5348+
self.value = True
5349+
5350+
gdb.ParameterPrefix("plugin-name", gdb.COMMAND_NONE,
5351+
"""An example parameter prefix.
5352+
5353+
This groups together some parameters.""")
5354+
ExampleParam("plugin-name feature-1")
5355+
ExampleParam("plugin-name feature-2")
5356+
@end smallexample
5357+
52635358
@node Functions In Python
52645359
@subsubsection Writing new convenience functions
52655360

gdb/python/lib/gdb/__init__.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,3 +392,121 @@ def _handle_missing_objfile(pspace, buildid, filename):
392392
return _handle_missing_files(
393393
pspace, "objfile", lambda h: h(pspace, buildid, filename)
394394
)
395+
396+
397+
class ParameterPrefix:
398+
# A wrapper around gdb.Command for creating set/show prefixes.
399+
#
400+
# When creating a gdb.Parameter sub-classes, it is sometimes necessary
401+
# to first create a gdb.Command object in order to create the needed
402+
# command prefix. However, for parameters, we actually need two
403+
# prefixes, a 'set' prefix, and a 'show' prefix. With this helper
404+
# class, a single instance of this class will create both prefixes at
405+
# once.
406+
#
407+
# It is important that this class-level documentation not be a __doc__
408+
# string. Users are expected to sub-class this ParameterPrefix class
409+
# and add their own documentation. If they don't, then GDB will
410+
# generate a suitable doc string. But, if this (parent) class has a
411+
# __doc__ sting of its own, then sub-classes will inherit that __doc__
412+
# string, and GDB will not understand that it needs to generate one.
413+
414+
class _PrefixCommand(Command):
415+
"""A gdb.Command used to implement both the set and show prefixes.
416+
417+
This documentation string is not used as the prefix command
418+
documentation as it is overridden in the __init__ method below."""
419+
420+
# This private method is connected to the 'invoke' attribute within
421+
# this _PrefixCommand object if the containing ParameterPrefix
422+
# object has an invoke_set or invoke_show method.
423+
#
424+
# This method records within self.__delegate which _PrefixCommand
425+
# object is currently active, and then calls the correct invoke
426+
# method on the delegat object (the ParameterPrefix sub-class
427+
# object).
428+
#
429+
# Recording the currently active _PrefixCommand object is important;
430+
# if from the invoke method the user calls dont_repeat, then this is
431+
# forwarded to the currently active _PrefixCommand object.
432+
def __invoke(self, args, from_tty):
433+
434+
# A helper class for use as part of a Python 'with' block.
435+
# Records which gdb.Command object is currently running its
436+
# invoke method.
437+
class MarkActiveCallback:
438+
# The CMD is a _PrefixCommand object, and the DELEGATE is
439+
# the ParameterPrefix class, or sub-class object. At this
440+
# point we simple record both of these within the
441+
# MarkActiveCallback object.
442+
def __init__(self, cmd, delegate):
443+
self.__cmd = cmd
444+
self.__delegate = delegate
445+
446+
# Record the currently active _PrefixCommand object within
447+
# the outer ParameterPrefix sub-class object.
448+
def __enter__(self):
449+
self.__delegate.active_prefix = self.__cmd
450+
451+
# Once the invoke method has completed, then clear the
452+
# _PrefixCommand object that was stored into the outer
453+
# ParameterPrefix sub-class object.
454+
def __exit__(self, exception_type, exception_value, traceback):
455+
self.__delegate.active_prefix = None
456+
457+
# The self.__cb attribute is set when the _PrefixCommand object
458+
# is created, and is either invoke_set or invoke_show within the
459+
# ParameterPrefix sub-class object.
460+
assert callable(self.__cb)
461+
462+
# Record the currently active _PrefixCommand object within the
463+
# ParameterPrefix sub-class object, then call the relevant
464+
# invoke method within the ParameterPrefix sub-class object.
465+
with MarkActiveCallback(self, self.__delegate):
466+
self.__cb(args, from_tty)
467+
468+
@staticmethod
469+
def __find_callback(delegate, mode):
470+
"""The MODE is either 'set' or 'show'. Look for an invoke_MODE method
471+
on DELEGATE, if a suitable method is found, then return it, otherwise,
472+
return None.
473+
"""
474+
cb = getattr(delegate, "invoke_" + mode, None)
475+
if callable(cb):
476+
return cb
477+
return None
478+
479+
def __init__(self, mode, name, cmd_class, delegate, doc=None):
480+
"""Setup this gdb.Command. Mode is a string, either 'set' or 'show'.
481+
NAME is the name for this prefix command, that is, the
482+
words that appear after both 'set' and 'show' in the
483+
command name. CMD_CLASS is the usual enum. And DELEGATE
484+
is the gdb.ParameterPrefix object this prefix is part of.
485+
"""
486+
assert mode == "set" or mode == "show"
487+
if doc is None:
488+
self.__doc__ = delegate.__doc__
489+
else:
490+
self.__doc__ = doc
491+
self.__cb = self.__find_callback(delegate, mode)
492+
self.__delegate = delegate
493+
if not self.__cb is None:
494+
self.invoke = self.__invoke
495+
super().__init__(mode + " " + name, cmd_class, prefix=True)
496+
497+
def __init__(self, name, cmd_class, doc=None):
498+
"""Create a _PrefixCommand for both the set and show prefix commands.
499+
NAME is the command name without either the leading 'set ' or
500+
'show ' strings, and CMD_CLASS is the usual enum value.
501+
"""
502+
self.active_prefix = None
503+
self._set_prefix_cmd = self._PrefixCommand("set", name, cmd_class, self, doc)
504+
self._show_prefix_cmd = self._PrefixCommand("show", name, cmd_class, self, doc)
505+
506+
# When called from within an invoke method the self.active_prefix
507+
# attribute should be set to a gdb.Command sub-class (a _PrefixCommand
508+
# object, see above). Forward the dont_repeat call to this object to
509+
# register the actual command as none repeating.
510+
def dont_repeat(self):
511+
if self.active_prefix is not None:
512+
self.active_prefix.dont_repeat()

0 commit comments

Comments
 (0)