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]

Re: [RFC][python] Add support for commands implemented in Python


El lun, 02-02-2009 a las 22:08 +0200, Eli Zaretskii escribiÃ:
> > gdb/doc/
> > 2009-02-02  Tom Tromey  <tromey@redhat.com>
> > 
> > 	* gdb.texinfo (Python API): Add entry for Commands In Python.
> > 	(Commands In Python): New node.
> 
> Approved, with comments:

Tromey worked on them, here's a new version which addresses your
comments and Daniel's. I also changed cmdpy_function and cmdpy_completer
to convert the command arguments to a Unicode string instead of an 8-bit
Python string.

> > +The help text for the new command is taken from the Python
> > +documentation string for the command's class, if there is one.  If
> > +there is no documentation string, a default value is used.
> 
> Regarding the last sentence above: when I read this I asked myself
> what was the default value for the doc string.  I suggest to state
> that.

Stated.

> > +@defmethod Command complete text word
> > +This method is called by @value{GDBN} when the user attempts @key{TAB}
> > +completion on this command.
> 
> Only TAB, or the other completion-related keys? what about M-?, for
> example?

The text explicitly mentions the other completion keys as well.

> > +@item COMMAND_RUN
> > +The command is related to running the inferior.
> > +
> > +@findex COMMAND_VARS
> > +@findex gdb.COMMAND_VARS
> > +@item COMMAND_VARS
> > +The command is related to variables.
> 
> I think it would be useful either to give a couple of examples of
> existing CLI commands that belong to each class, or at least explain
> how to use the "help" command to show commands in each class.  I
> imagined myself sitting in front of this list and trying to figure out
> to which class to assign some command I wrote, and I think I would
> find these hints useful.  Please keep in mind that users who would
> want to write new CLI commands are not necessarily GDB hackers privy
> to the add_cmd function and its ilk.

The text now both gives examples and mentions how to get help to list
the commands in the class.

> > +A new command can use a predefined completion function, either by
> > +specifying it via an argument at initialization, or by return it from
>                                                           ^^^^^^
> "returning"

Fixed.

> > +@item COMPLETE_LOCATION
> > +This constant means that location completion should be done.
> 
> A cross-reference to where "locations" are described would be a good
> idea here.

Cross-referenced.

> Finally, I think we need a NEWS entry for this feature.

NEWS entry added.
-- 
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center


gdb/
2009-02-05  Tom Tromey  <tromey@redhat.com>

	* Makefile.in (SUBDIR_PYTHON_OBS): Add python-cmd.o.
	(SUBDIR_PYTHON_SRCS): Add python-cmd.c.
	(python-cmd.o): New target.
	* cli/cli-decode.c (set_cmd_completer): Add self parameter to
	completer prototype.
	(add_cmd): Initialize destroyer member of cmd_list_element. Use
	make_symbol_completion_list_fn as completer.
	(delete_cmd): Call destroyer if one is set.
	* cli/cli-decode.h (cmd_list_element): Add cmd parameter to
	completer member.  Add destroyer member.
	(set_cmd_completer): Add self parameter to
	completer prototype.
	* command.h (set_cmd_completer): Add cmd parameter to
	completer prototype.
	* completer.c (noop_completer, filename_completer,
	location_completer, expression_completer, command_completer): Adapt
	to new completer prototype.
	(complete_line_internal): Pass new parameter to completer function.
	* completer.h (noop_completer, filename_completer,
	location_completer, expression_completer, command_completer): Adapt
	prototypes to new completer prototype.
	* interps.c (interpreter_completer): Adapt to new completer
	prototype.
	* python/python-cmd.c: New file.
	* python/python-internal.h (gdbpy_initialize_commands): Add
	prototype.
	(gdbpy_doc_cst): Add forward declaration.
	* python/python.c (gdbpy_doc_cst): Declare.
	(_initialize_python): Call gdbpy_initialize_commands.  Initialize
	gdbpy_doc_cst.
	* symtab.c (make_symbol_completion_list_fn): New function.
	* symtab.h (make_symbol_completion_list_fn): Add prototype.

gdb/doc/
2009-02-05  Tom Tromey  <tromey@redhat.com>

	* gdb.texinfo (Python API): Add entry for Commands In Python.
	(Commands In Python): New node.

gdb/testsuite/
2009-02-05  Thiago Jung Bauermann  <bauerman@br.ibm.com>

	* gdb.python/python-cmd.exp: New file.

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 6a4f77d..7400702 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -270,10 +270,12 @@ SUBDIR_TUI_CFLAGS= \
 #
 SUBDIR_PYTHON_OBS = \
 	python.o \
+	python-cmd.o \
 	python-utils.o \
 	python-value.o
 SUBDIR_PYTHON_SRCS = \
 	python/python.c \
+	python/python-cmd.c \
 	python/python-utils.c \
 	python/python-value.c
 SUBDIR_PYTHON_DEPS =
@@ -1836,6 +1838,10 @@ python.o: $(srcdir)/python/python.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python.c
 	$(POSTCOMPILE)
 
+python-cmd.o: $(srcdir)/python/python-cmd.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-cmd.c
+	$(POSTCOMPILE)
+
 python-utils.o: $(srcdir)/python/python-utils.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-utils.c
 	$(POSTCOMPILE)
diff --git a/gdb/NEWS b/gdb/NEWS
index a85caeb..d1abf0c 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -96,6 +96,8 @@ are treated as the standard definitions, regardless of context.
   GDB now has support for scripting using Python.  Whether this is
   available is determined at configure time.
 
+  New GDB commands can now be written in Python.
+
 * Ada tasking support
 
   Ada tasks can now be inspected in GDB. The following commands have
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 556c027..8760ebf 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -132,7 +132,8 @@ cmd_type (struct cmd_list_element *cmd)
 
 void
 set_cmd_completer (struct cmd_list_element *cmd,
-		   char **(*completer) (char *text, char *word))
+		   char **(*completer) (struct cmd_list_element *self,
+					char *text, char *word))
 {
   cmd->completer = completer; /* Ok.  */
 }
@@ -207,7 +208,8 @@ add_cmd (char *name, enum command_class class, void (*fun) (char *, int),
   c->prefixname = NULL;
   c->allow_unknown = 0;
   c->abbrev_flag = 0;
-  set_cmd_completer (c, make_symbol_completion_list);
+  set_cmd_completer (c, make_symbol_completion_list_fn);
+  c->destroyer = NULL;
   c->type = not_set_cmd;
   c->var = NULL;
   c->var_type = var_boolean;
@@ -688,6 +690,8 @@ delete_cmd (char *name, struct cmd_list_element **list,
     {
       if (strcmp (iter->name, name) == 0)
 	{
+	  if (iter->destroyer)
+	    iter->destroyer (iter, iter->context);
 	  if (iter->hookee_pre)
 	    iter->hookee_pre->hook_pre = 0;
 	  *prehook = iter->hook_pre;
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 56ea9bf..26ca2f7 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -167,7 +167,12 @@ struct cmd_list_element
        returned relative to this position.  For example, suppose TEXT is "foo"
        and we want to complete to "foobar".  If WORD is "oo", return
        "oobar"; if WORD is "baz/foo", return "baz/foobar".  */
-    char **(*completer) (char *text, char *word);
+    char **(*completer) (struct cmd_list_element *cmd, char *text, char *word);
+
+    /* Destruction routine for this command.  If non-NULL, this is
+       called when this command instance is destroyed.  This may be
+       used to finalize the CONTEXT field, if needed.  */
+    void (*destroyer) (struct cmd_list_element *self, void *context);
 
     /* Type of "set" or "show" command (or SET_NOT_SET if not "set"
        or "show").  */
@@ -242,7 +247,8 @@ extern void set_cmd_sfunc (struct cmd_list_element *cmd,
 					  struct cmd_list_element * c));
 
 extern void set_cmd_completer (struct cmd_list_element *cmd,
-			       char **(*completer) (char *text, char *word));
+			       char **(*completer) (struct cmd_list_element *self,
+						    char *text, char *word));
 
 /* HACK: cagney/2002-02-23: Code, mostly in tracepoints.c, grubs
    around in cmd objects to test the value of the commands sfunc().  */
diff --git a/gdb/command.h b/gdb/command.h
index b3f7013..bed615c 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -138,7 +138,8 @@ extern void set_cmd_sfunc (struct cmd_list_element *cmd,
 			   cmd_sfunc_ftype *sfunc);
 
 extern void set_cmd_completer (struct cmd_list_element *cmd,
-			       char **(*completer) (char *text, char *word));
+			       char **(*completer) (struct cmd_list_element *cmd,
+						    char *text, char *word));
 
 /* HACK: cagney/2002-02-23: Code, mostly in tracepoints.c, grubs
    around in cmd objects to test the value of the commands sfunc().  */
diff --git a/gdb/completer.c b/gdb/completer.c
index 298cdd0..43fcf7a 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -105,14 +105,14 @@ readline_line_completion_function (const char *text, int matches)
 /* This can be used for functions which don't want to complete on symbols
    but don't want to complete on anything else either.  */
 char **
-noop_completer (char *text, char *prefix)
+noop_completer (struct cmd_list_element *ignore, char *text, char *prefix)
 {
   return NULL;
 }
 
 /* Complete on filenames.  */
 char **
-filename_completer (char *text, char *word)
+filename_completer (struct cmd_list_element *ignore, char *text, char *word)
 {
   int subsequent_name;
   char **return_val;
@@ -195,7 +195,7 @@ filename_completer (char *text, char *word)
 
    This is intended to be used in commands that set breakpoints etc.  */
 char **
-location_completer (char *text, char *word)
+location_completer (struct cmd_list_element *ignore, char *text, char *word)
 {
   int n_syms = 0, n_files = 0;
   char ** fn_list = NULL;
@@ -412,7 +412,7 @@ add_struct_fields (struct type *type, int *nextp, char **output,
    names, but some language parsers also have support for completing
    field names.  */
 char **
-expression_completer (char *text, char *word)
+expression_completer (struct cmd_list_element *ignore, char *text, char *word)
 {
   struct type *type;
   char *fieldname, *p;
@@ -456,7 +456,7 @@ expression_completer (char *text, char *word)
     ;
 
   /* Not ideal but it is what we used to do before... */
-  return location_completer (p, word);
+  return location_completer (ignore, p, word);
 }
 
 /* Here are some useful test cases for completion.  FIXME: These should
@@ -651,7 +651,7 @@ complete_line_internal (const char *text, char *line_buffer, int point,
 			   p--)
 			;
 		    }
-		  list = (*c->completer) (p, word);
+		  list = (*c->completer) (c, p, word);
 		}
 	    }
 	  else
@@ -719,7 +719,7 @@ complete_line_internal (const char *text, char *line_buffer, int point,
 		       p--)
 		    ;
 		}
-	      list = (*c->completer) (p, word);
+	      list = (*c->completer) (c, p, word);
 	    }
 	}
     }
@@ -737,7 +737,7 @@ complete_line (const char *text, char *line_buffer, int point)
 
 /* Complete on command names.  Used by "help".  */
 char **
-command_completer (char *text, char *word)
+command_completer (struct cmd_list_element *ignore, char *text, char *word)
 {
   return complete_line_internal (word, text, strlen (text), 1);
 }
diff --git a/gdb/completer.h b/gdb/completer.h
index da1c381..6adbf1b 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -21,15 +21,15 @@ extern char **complete_line (const char *text, char *line_buffer, int point);
 
 extern char *readline_line_completion_function (const char *text, int matches);
 
-extern char **noop_completer (char *, char *);
+extern char **noop_completer (struct cmd_list_element *, char *, char *);
 
-extern char **filename_completer (char *, char *);
+extern char **filename_completer (struct cmd_list_element *, char *, char *);
 
-extern char **expression_completer (char *, char *);
+extern char **expression_completer (struct cmd_list_element *, char *, char *);
 
-extern char **location_completer (char *, char *);
+extern char **location_completer (struct cmd_list_element *, char *, char *);
 
-extern char **command_completer (char *, char *);
+extern char **command_completer (struct cmd_list_element *, char *, char *);
 
 extern char *get_gdb_completer_quote_characters (void);
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index a2ed0b8..b729b06 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -18064,6 +18064,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Basic Python::                Basic Python Functions.
 * Exception Handling::
 * Values From Inferior::
+* Commands In Python::          Implementing new commands in Python.
 @end menu
 
 @node Basic Python
@@ -18246,6 +18247,259 @@ The optional @var{errors} argument is the same as the corresponding
 argument to Python's @code{string.decode} method.
 @end defmethod
 
+@node Commands In Python
+@subsubsection Commands In Python
+
+@cindex commands in python
+@cindex python commands
+@tindex Command
+@tindex gdb.Command
+You can implement new @value{GDBN} CLI commands in Python.  A CLI
+command is implemented using an instance of the @code{gdb.Command}
+class, most commonly using a subclass.
+
+@defmethod Command __init__ name @var{command-class} @r{[}@var{completer-class} @var{prefix}@r{]}
+The object initializer for @code{Command} registers the new command
+with @value{GDBN}.  This initializer is normally invoked from the
+subclass' own @code{__init__} method.
+
+@var{name} is the name of the command.  If @var{name} consists of
+multiple words, then the initial words are looked for as prefix
+commands.  In this case, if one of the prefix commands does not exist,
+an exception is raised.
+
+There is no support for multi-line commands.
+
+@var{command-class} should be one of the @samp{COMMAND_} constants
+defined below.  This argument tells @value{GDBN} how to categorize the
+new command in the help system.
+
+@var{completer-class} is an optional argument.  If given, it should be
+one of the @samp{COMPLETE_} constants defined below.  This argument
+tells @value{GDBN} how to perform completion for this command.  If not
+given, @value{GDBN} will attempt to complete using the object's
+@code{complete} method (see below); if no such method is found, an
+error will occur when completion is attempted.
+
+@var{prefix} is an optional argument.  If @code{True}, then the new
+command is a prefix command; sub-commands of this command may be
+registered.
+
+The help text for the new command is taken from the Python
+documentation string for the command's class, if there is one.  If no
+documentation string is provided, the default value ``This command is
+not documented.'' is used.
+@end defmethod
+
+@defmethod Command dont_repeat
+By default, a @value{GDBN} command is repeated when the user enters a
+blank line at the command prompt.  A command can suppress this
+behavior by invoking the @code{dont_repeat} method.  This is similar
+to the user command @code{dont-repeat}, see @ref{Define, dont-repeat}.
+@end defmethod
+
+@defmethod Command invoke argument from_tty
+This method is called by @value{GDBN} when this command is invoked.
+
+@var{argument} is a string.  It is the argument to the command, after
+leading and trailing whitespace has been stripped.
+
+@var{from_tty} is a boolean argument.  When true, this means that the
+command was entered by the user at the terminal; when false it means
+that the command came from elsewhere.
+
+If this method throws an exception, it is turned into a @value{GDBN}
+@code{error} call.  Otherwise, the return value is ignored.
+@end defmethod
+
+@defmethod Command complete text word
+This method is called by @value{GDBN} when the user attempts
+completion on this command.  All forms of completion are handled by
+this method, that is, the @key{TAB} and @key{M-?} key bindings, and
+the @code{complete} command.
+
+The arguments @var{text} and @var{word} are both strings.  @var{text}
+holds the complete command line up to the cursor's location.
+@var{word} holds the last word of the command line; this is computed
+using a word-breaking heuristic.
+
+The @code{complete} method can return several values:
+@itemize @bullet
+@item
+If the return value is a sequence, the contents of the sequence are
+used as the completions.  It is up to @code{complete} to ensure that the
+contents actually do complete the word.  A zero-length sequence is
+allowed, it means that there were no completions available.  Only
+string elements of the sequence are used; other elements in the
+sequence are ignored.
+
+@item
+If the return value is one of the @samp{COMPLETE_} constants defined
+below, then the corresponding @value{GDBN}-internal completion
+function is invoked, and its result is used.
+
+@item
+All other results are treated as though there were no available
+completions.
+@end itemize
+@end defmethod
+
+
+When a new command is registered, it must be declared as a member of
+some general class of commands.  This is used to classify top-level
+commands in the on-line help system; note that prefix commands are not
+listed under their own category but rather that of their top-level
+command.  The available classifications are represented by constants
+defined in the @code{gdb} module:
+
+@table @code
+@findex COMMAND_NONE
+@findex gdb.COMMAND_NONE
+@item COMMAND_NONE
+The command does not belong to any particular class.  A command in
+this category will not be displayed in any of the help categories.
+
+@findex COMMAND_RUN
+@findex gdb.COMMAND_RUN
+@item COMMAND_RUN
+The command is related to running the inferior.  For example,
+@code{start}, @code{step}, and @code{continue} are in this category.
+Type @code{help running} at the @value{GDBN} prompt to see a list of
+commands in this category.
+
+@findex COMMAND_VARS
+@findex gdb.COMMAND_VARS
+@item COMMAND_VARS
+The command is related to data or variables.  For example,
+@code{call}, @code{find}, and @code{print} are in this category.  Type
+@code{help data} at the @value{GDBN} prompt to see a list of commands
+in this category.
+
+@findex COMMAND_STACK
+@findex gdb.COMMAND_STACK
+@item COMMAND_STACK
+The command has to do with manipulation of the stack.  For example,
+@code{backtrace}, @code{frame}, and @code{return} are in this
+category.  Type @code{help stack} at the @value{GDBN} prompt to see a
+list of commands in this category.
+
+@findex COMMAND_FILES
+@findex gdb.COMMAND_FILES
+@item COMMAND_FILES
+This class is used for file-related commands.  For example,
+@code{file}, @code{list} and @code{section} are in this category.
+Type @code{help files} at the @value{GDBN} prompt to see a list of
+commands in this category.
+
+@findex COMMAND_SUPPORT
+@findex gdb.COMMAND_SUPPORT
+@item COMMAND_SUPPORT
+This should be used for ``support facilities'', generally meaning
+things that are useful to the user when interacting with @value{GDBN},
+but not related to the state of the inferior.  For example,
+@code{help}, @code{make}, and @code{shell} are in this category.  Type
+@code{help support} at the @value{GDBN} prompt to see a list of
+commands in this category.
+
+@findex COMMAND_INFO
+@findex gdb.COMMAND_INFO
+@item COMMAND_INFO
+The command is an @samp{info}-related command, that is, related to the
+state of @value{GDBN} itself.  For example, @code{info}, @code{macro},
+and @code{show} are in this category.  Type @code{help status} at the
+@value{GDBN} prompt to see a list of commands in this category.
+
+@findex COMMAND_BREAKPOINT
+@findex gdb.COMMAND_BREAKPOINT
+@item COMMAND_BREAKPOINT
+The command has to do with breakpoints.  For example, @code{break},
+@code{clear}, and @code{delete} are in this category.  Type @code{help
+breakpoints} at the @value{GDBN} prompt to see a list of commands in
+this category.
+
+@findex COMMAND_TRACE
+@findex gdb.COMMAND_TRACE
+@item COMMAND_TRACE
+The command has to do with tracepoints.  For example, @code{trace},
+@code{actions}, and @code{tfind} are in this category.  Type
+@code{help tracepoints} at the @value{GDBN} prompt to see a list of
+commands in this category.
+
+@findex COMMAND_OBSCURE
+@findex gdb.COMMAND_OBSCURE
+@item COMMAND_OBSCURE
+The command is only used in unusual circumstances, or is not of
+general interest to users.  For example, @code{checkpoint},
+@code{fork}, and @code{stop} are in this category.  Type @code{help
+obscure} at the @value{GDBN} prompt to see a list of commands in this
+category.
+
+@findex COMMAND_MAINTENANCE
+@findex gdb.COMMAND_MAINTENANCE
+@item COMMAND_MAINTENANCE
+The command is only useful to @value{GDBN} maintainers.  The
+@code{maintenance} and @code{flushregs} commands are in this category.
+Type @code{help internals} at the @value{GDBN} prompt to see a list of
+commands in this category.
+@end table
+
+
+A new command can use a predefined completion function, either by
+specifying it via an argument at initialization, or by returning it
+from the @code{complete} method.  These predefined completion
+constants are all defined in the @code{gdb} module:
+
+@table @code
+@findex COMPLETE_NONE
+@findex gdb.COMPLETE_NONE
+@item COMPLETE_NONE
+This constant means that no completion should be done.
+
+@findex COMPLETE_FILENAME
+@findex gdb.COMPLETE_FILENAME
+@item COMPLETE_FILENAME
+This constant means that filename completion should be performed.
+
+@findex COMPLETE_LOCATION
+@findex gdb.COMPLETE_LOCATION
+@item COMPLETE_LOCATION
+This constant means that location completion should be done.
+@xref{Specify Location}.
+
+@findex COMPLETE_COMMAND
+@findex gdb.COMPLETE_COMMAND
+@item COMPLETE_COMMAND
+This constant means that completion should examine @value{GDBN}
+command names.
+
+@findex COMPLETE_SYMBOL
+@findex gdb.COMPLETE_SYMBOL
+@item COMPLETE_SYMBOL
+This constant means that completion should be done using symbol names
+as the source.
+@end table
+
+The following code snippet shows how a trivial CLI command can be
+implemented in Python:
+
+@smallexample
+class HelloWorld (gdb.Command):
+  """Greet the whole world."""
+
+  def __init__ (self):
+    super (HelloWorld, self).__init__ ("hello-world", gdb.COMMAND_OBSCURE)
+
+  def invoke (self, arg, from_tty):
+    print "Hello, World!"
+
+HelloWorld ()
+@end smallexample
+
+The last line instantiates the class, and is necessary to trigger the
+registration of the command with @value{GDBN}.  Depending on how the
+Python code is read into @value{GDBN}, you may need to import the
+@code{gdb} module explicitly.
+
 @node Interpreters
 @chapter Command Interpreters
 @cindex command interpreters
diff --git a/gdb/interps.c b/gdb/interps.c
index 6814a72..da05ee2 100644
--- a/gdb/interps.c
+++ b/gdb/interps.c
@@ -71,7 +71,8 @@ struct interp
 
 /* Functions local to this file. */
 static void initialize_interps (void);
-static char **interpreter_completer (char *text, char *word);
+static char **interpreter_completer (struct cmd_list_element *cmd,
+				     char *text, char *word);
 
 /* The magic initialization routine for this module. */
 
@@ -416,7 +417,7 @@ interpreter_exec_cmd (char *args, int from_tty)
 
 /* List the possible interpreters which could complete the given text. */
 static char **
-interpreter_completer (char *text, char *word)
+interpreter_completer (struct cmd_list_element *ignore, char *text, char *word)
 {
   int alloced = 0;
   int textlen;
diff --git a/gdb/python/python-cmd.c b/gdb/python/python-cmd.c
new file mode 100644
index 0000000..92ae4fb
--- /dev/null
+++ b/gdb/python/python-cmd.c
@@ -0,0 +1,584 @@
+/* gdb commands implemented in Python
+
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+
+#include "defs.h"
+#include "value.h"
+#include "exceptions.h"
+#include "python-internal.h"
+#include "charset.h"
+#include "gdbcmd.h"
+#include "cli/cli-decode.h"
+#include "completer.h"
+
+/* Struct representing built-in completion types.  */
+struct cmdpy_completer
+{
+  /* Python symbol name.  */
+  char *name;
+  /* Completion function.  */
+  char **(*completer) (struct cmd_list_element *, char *, char *);
+};
+
+static struct cmdpy_completer completers[] =
+{
+  { "COMPLETE_NONE", noop_completer },
+  { "COMPLETE_FILENAME", filename_completer },
+  { "COMPLETE_LOCATION", location_completer },
+  { "COMPLETE_COMMAND", command_completer },
+  { "COMPLETE_SYMBOL", make_symbol_completion_list_fn },
+};
+
+#define N_COMPLETERS (sizeof (completers) / sizeof (completers[0]))
+
+/* A gdb command.  For the time being only ordinary commands (not
+   set/show commands) are allowed.  */
+struct cmdpy_object
+{
+  PyObject_HEAD
+
+  /* The corresponding gdb command object, or NULL if the command is
+     no longer installed.  */
+  struct cmd_list_element *command;
+
+  /* A prefix command requires storage for a list of its sub-commands.
+     A pointer to this is passed to add_prefix_command, and to add_cmd
+     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;
+};
+
+typedef struct cmdpy_object cmdpy_object;
+
+static PyTypeObject cmdpy_object_type;
+
+
+/* Constants used by this module.  */
+static PyObject *invoke_cst;
+static PyObject *complete_cst;
+
+
+
+/* Python function which wraps dont_repeat.  */
+static PyObject *
+cmdpy_dont_repeat (PyObject *self, PyObject *args)
+{
+  dont_repeat ();
+  Py_RETURN_NONE;
+}
+
+
+
+/* Called if the gdb cmd_list_element is destroyed.  */
+static void
+cmdpy_destroyer (struct cmd_list_element *self, void *context)
+{
+  cmdpy_object *cmd;
+  PyGILState_STATE state;
+
+  state = PyGILState_Ensure ();
+
+  /* Release our hold on the command object.  */
+  cmd = (cmdpy_object *) context;
+  cmd->command = NULL;
+  Py_DECREF (cmd);
+
+  /* We allocated the name, doc string, and perhaps the prefix
+     name.  */
+  xfree (self->name);
+  xfree (self->doc);
+  xfree (self->prefixname);
+
+  PyGILState_Release (state);
+}
+
+/* Called by gdb to invoke the command.  */
+static void
+cmdpy_function (struct cmd_list_element *command, char *args, int from_tty)
+{
+  cmdpy_object *obj = (cmdpy_object *) get_cmd_context (command);
+  PyObject *argobj, *ttyobj, *result;
+  struct cleanup *cleanup;
+  PyGILState_STATE state;
+
+  state = PyGILState_Ensure ();
+  cleanup = make_cleanup_py_restore_gil (&state);
+
+  if (! obj)
+    error (_("Invalid invocation of Python command object."));
+  if (! PyObject_HasAttr ((PyObject *) obj, invoke_cst))
+    {
+      if (obj->command->prefixname)
+	{
+	  /* A prefix command does not need an invoke method.  */
+	  do_cleanups (cleanup);
+	  return;
+	}
+      error (_("Python command object missing 'invoke' method."));
+    }
+
+  if (! args)
+    args = "";
+  argobj = PyUnicode_Decode (args, strlen (args), host_charset (), NULL);
+  if (! argobj)
+    error (_("Could not convert arguments to Python string."));
+
+  ttyobj = from_tty ? Py_True : Py_False;
+  Py_INCREF (ttyobj);
+  result = PyObject_CallMethodObjArgs ((PyObject *) obj, invoke_cst, argobj,
+				       ttyobj, NULL);
+  Py_DECREF (argobj);
+  Py_DECREF (ttyobj);
+  if (! result)
+    {
+      PyObject *ptype, *pvalue, *ptraceback;
+      char *s, *str;
+
+      PyErr_Fetch (&ptype, &pvalue, &ptraceback);
+
+      if (pvalue && PyString_Check (pvalue))
+	{
+	  /* Make a temporary copy of the string data.  */
+	  char *s = PyString_AsString (pvalue);
+	  char *copy = alloca (strlen (s) + 1);
+	  strcpy (copy, s);
+
+	  PyErr_Restore (ptype, pvalue, ptraceback);
+	  gdbpy_print_stack ();
+	  error (_("Error occurred in Python command: %s"), copy);
+	}
+      else
+	{
+	  PyErr_Restore (ptype, pvalue, ptraceback);
+	  gdbpy_print_stack ();
+	  error (_("Error occurred in Python command."));
+	}
+    }
+  Py_DECREF (result);
+  do_cleanups (cleanup);
+}
+
+/* Called by gdb for command completion.  */
+static char **
+cmdpy_completer (struct cmd_list_element *command, char *text, char *word)
+{
+  cmdpy_object *obj = (cmdpy_object *) get_cmd_context (command);
+  PyObject *textobj, *wordobj, *resultobj = NULL;
+  char **result = NULL;
+  struct cleanup *cleanup;
+  PyGILState_STATE state;
+
+  state = PyGILState_Ensure ();
+  cleanup = make_cleanup_py_restore_gil (&state);
+
+  if (! obj)
+    error (_("Invalid invocation of Python command object."));
+  if (! PyObject_HasAttr ((PyObject *) obj, complete_cst))
+    {
+      /* If there is no complete method, don't error -- instead, just
+	 say that there are no completions.  */
+      goto done;
+    }
+
+  textobj = PyUnicode_Decode (text, strlen (text), host_charset (), NULL);
+  if (! textobj)
+    error (_("Could not convert argument to Python string."));
+  wordobj = PyUnicode_Decode (word, strlen (word), host_charset (), NULL);
+  if (! wordobj)
+    error (_("Could not convert argument to Python string."));
+
+  resultobj = PyObject_CallMethodObjArgs ((PyObject *) obj, complete_cst,
+					  textobj, wordobj, NULL);
+  Py_DECREF (textobj);
+  Py_DECREF (wordobj);
+  if (! resultobj)
+    {
+      /* Just swallow errors here.  */
+      PyErr_Clear ();
+      goto done;
+    }
+  make_cleanup_py_decref (resultobj);
+
+  result = NULL;
+  if (PySequence_Check (resultobj))
+    {
+      Py_ssize_t i, len = PySequence_Size (resultobj);
+      Py_ssize_t out;
+      if (len < 0)
+	goto done;
+
+      result = (char **) xmalloc ((len + 1) * sizeof (char *));
+      for (i = out = 0; i < len; ++i)
+	{
+	  int l;
+	  PyObject *elt = PySequence_GetItem (resultobj, i);
+	  if (elt == NULL || ! gdbpy_is_string (elt))
+	    {
+	      /* Skip problem elements.  */
+	      PyErr_Clear ();
+	      continue;
+	    }
+	  result[out] = python_string_to_host_string (elt);
+	  ++out;
+	}
+      result[out] = NULL;
+    }
+  else if (PyInt_Check (resultobj))
+    {
+      /* User code may also return one of the completion constants,
+	 thus requesting that sort of completion.  */
+      long value = PyInt_AsLong (resultobj);
+      if (value >= 0 && value < (long) N_COMPLETERS)
+	result = completers[value].completer (command, text, word);
+    }
+
+ done:
+
+  do_cleanups (cleanup);
+
+  return result;
+}
+
+/* Helper for cmdpy_init which locates the command list to use and
+   pulls out the command name.
+   
+   TEXT is the command name list.  The final word in the list is the
+   name of the new command.  All earlier words must be existing prefix
+   commands.
+
+   *BASE_LIST is set to the final prefix command's list of
+   *sub-commands.
+   
+   This function returns the xmalloc()d name of the new command.  On
+   error sets the Python error and returns NULL.  */
+static char *
+parse_command_name (char *text, struct cmd_list_element ***base_list)
+{
+  struct cmd_list_element *elt;
+  int len = strlen (text);
+  int i, lastchar;
+  char *prefix_text;
+  char *result;
+
+  /* Skip trailing whitespace.  */
+  for (i = len - 1; i >= 0 && (text[i] == ' ' || text[i] == '\t'); --i)
+    ;
+  if (i < 0)
+    {
+      PyErr_SetString (PyExc_RuntimeError, _("no command name found"));
+      return NULL;
+    }
+  lastchar = i;
+
+  /* Find first character of the final word.  */
+  for (; i > 0 && (isalnum (text[i - 1])
+		   || text[i - 1] == '-'
+		   || text[i - 1] == '_');
+       --i)
+    ;
+  result = xmalloc (lastchar - i + 2);
+  memcpy (result, &text[i], lastchar - i + 1);
+  result[lastchar - i + 1] = '\0';
+
+  /* Skip whitespace again.  */
+  for (--i; i >= 0 && (text[i] == ' ' || text[i] == '\t'); --i)
+    ;
+  if (i < 0)
+    {
+      *base_list = &cmdlist;
+      return result;
+    }
+
+  prefix_text = xmalloc (i + 2);
+  memcpy (prefix_text, text, i + 1);
+  prefix_text[i + 1] = '\0';
+
+  text = prefix_text;
+  elt = lookup_cmd_1 (&text, cmdlist, NULL, 1);
+  if (!elt || elt == (struct cmd_list_element *) -1)
+    {
+      PyErr_Format (PyExc_RuntimeError, _("could not find command prefix %s"),
+		    prefix_text);
+      xfree (prefix_text);
+      xfree (result);
+      return NULL;
+    }
+
+  if (elt->prefixlist)
+    {
+      xfree (prefix_text);
+      *base_list = elt->prefixlist;
+      return result;
+    }
+
+  PyErr_Format (PyExc_RuntimeError, _("'%s' is not a prefix command"),
+		prefix_text);
+  xfree (prefix_text);
+  xfree (result);
+  return NULL;
+}
+
+/* Object initializer; sets up gdb-side structures for command.
+
+   Use: __init__(NAME, CMDCLASS, [COMPLETERCLASS, [PREFIX]]).
+
+   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
+   earlier words must be prefix commands.
+
+   CMDCLASS is the kind of command.  It should be one of the COMMAND_*
+   constants defined in the gdb module.
+
+   COMPLETERCLASS is the kind of completer.  If not given, the
+   "complete" method will be used.  Otherwise, it should be one of the
+   COMPLETE_* constants defined in the gdb module.
+
+   If PREFIX is True, then this command is a prefix command.
+
+   The documentation for the command is taken from the doc string for
+   the python class.
+   
+*/
+static int
+cmdpy_init (PyObject *self, PyObject *args, PyObject *kwds)
+{
+  cmdpy_object *obj = (cmdpy_object *) self;
+  char *name;
+  int cmdtype;
+  int completetype = -1;
+  char *docstring = NULL;
+  volatile struct gdb_exception except;
+  struct cmd_list_element **cmd_list;
+  char *cmd_name, *pfx_name;
+  PyObject *is_prefix = NULL;
+  int cmp;
+
+  if (obj->command)
+    {
+      /* Note: this is apparently not documented in Python.  We return
+	 0 for success, -1 for failure.  */
+      PyErr_Format (PyExc_RuntimeError,
+		    _("command object already initialized"));
+      return -1;
+    }
+
+  if (! PyArg_ParseTuple (args, "si|iO", &name, &cmdtype,
+			  &completetype, &is_prefix))
+    return -1;
+
+  if (cmdtype != no_class && cmdtype != class_run
+      && cmdtype != class_vars && cmdtype != class_stack
+      && cmdtype != class_files && cmdtype != class_support
+      && cmdtype != class_info && cmdtype != class_breakpoint
+      && cmdtype != class_trace && cmdtype != class_obscure
+      && cmdtype != class_maintenance)
+    {
+      PyErr_Format (PyExc_RuntimeError, _("invalid command class argument"));
+      return -1;
+    }
+
+  if (completetype < -1 || completetype >= (int) N_COMPLETERS)
+    {
+      PyErr_Format (PyExc_RuntimeError, _("invalid completion type argument"));
+      return -1;
+    }
+
+  cmd_name = parse_command_name (name, &cmd_list);
+  if (! cmd_name)
+    return -1;
+
+  pfx_name = NULL;
+  if (is_prefix != NULL) 
+    {
+      cmp = PyObject_IsTrue (is_prefix);
+      if (cmp == 1)
+	{
+	  int i, out;
+	  
+	  /* Make a normalized form of the command name.  */
+	  pfx_name = xmalloc (strlen (name) + 2);
+	  
+	  i = 0;
+	  out = 0;
+	  while (name[i])
+	    {
+	      /* Skip whitespace.  */
+	      while (name[i] == ' ' || name[i] == '\t')
+		++i;
+	      /* Copy non-whitespace characters.  */
+	      while (name[i] && name[i] != ' ' && name[i] != '\t')
+		pfx_name[out++] = name[i++];
+	      /* Add a single space after each word -- including the final
+		 word.  */
+	      pfx_name[out++] = ' ';
+	    }
+	  pfx_name[out] = '\0';
+	}
+      else if (cmp < 0)
+	  return -1;
+    }
+  if (PyObject_HasAttr (self, gdbpy_doc_cst))
+    {
+      PyObject *ds_obj = PyObject_GetAttr (self, gdbpy_doc_cst);
+      if (ds_obj && gdbpy_is_string (ds_obj))
+	docstring = python_string_to_host_string (ds_obj);
+    }
+  if (! docstring)
+    docstring = xstrdup (_("This command is not documented."));
+
+  Py_INCREF (self);
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      struct cmd_list_element *cmd;
+
+      if (pfx_name)
+	{
+	  int allow_unknown;
+
+	  /* If we have our own "invoke" method, then allow unknown
+	     sub-commands.  */
+	  allow_unknown = PyObject_HasAttr (self, invoke_cst);
+	  cmd = add_prefix_cmd (cmd_name, (enum command_class) cmdtype,
+				NULL, docstring, &obj->sub_list,
+				pfx_name, allow_unknown, cmd_list);
+	}
+      else
+	cmd = add_cmd (cmd_name, (enum command_class) cmdtype, NULL,
+		       docstring, cmd_list);
+
+      /* There appears to be no API to set this.  */
+      cmd->func = cmdpy_function;
+      cmd->destroyer = cmdpy_destroyer;
+
+      obj->command = cmd;
+      set_cmd_context (cmd, self);
+      set_cmd_completer (cmd, ((completetype == -1) ? cmdpy_completer
+			       : completers[completetype].completer));
+    }
+  if (except.reason < 0)
+    {
+      xfree (cmd_name);
+      xfree (docstring);
+      xfree (pfx_name);
+      Py_DECREF (self);
+      PyErr_Format (except.reason == RETURN_QUIT
+		    ? PyExc_KeyboardInterrupt : PyExc_RuntimeError,
+		    "%s", except.message);
+      return -1;
+    }
+  return 0;
+}
+
+
+
+/* Initialize the 'commands' code.  */
+void
+gdbpy_initialize_commands (void)
+{
+  int i;
+
+  if (PyType_Ready (&cmdpy_object_type) < 0)
+    return;
+
+  /* Note: alias and user are special; pseudo appears to be unused,
+     and there is no reason to expose tui or xdb, I think.  */
+  if (PyModule_AddIntConstant (gdb_module, "COMMAND_NONE", no_class) < 0
+      || PyModule_AddIntConstant (gdb_module, "COMMAND_RUN", class_run) < 0
+      || PyModule_AddIntConstant (gdb_module, "COMMAND_VARS", class_vars) < 0
+      || PyModule_AddIntConstant (gdb_module, "COMMAND_STACK", class_stack) < 0
+      || PyModule_AddIntConstant (gdb_module, "COMMAND_FILES", class_files) < 0
+      || PyModule_AddIntConstant (gdb_module, "COMMAND_SUPPORT",
+				  class_support) < 0
+      || PyModule_AddIntConstant (gdb_module, "COMMAND_INFO", class_info) < 0
+      || PyModule_AddIntConstant (gdb_module, "COMMAND_BREAKPOINT",
+				  class_breakpoint) < 0
+      || PyModule_AddIntConstant (gdb_module, "COMMAND_TRACE", class_trace) < 0
+      || PyModule_AddIntConstant (gdb_module, "COMMAND_OBSCURE",
+				  class_obscure) < 0
+      || PyModule_AddIntConstant (gdb_module, "COMMAND_MAINTENANCE",
+				  class_maintenance) < 0)
+    return;
+
+  for (i = 0; i < N_COMPLETERS; ++i)
+    {
+      if (PyModule_AddIntConstant (gdb_module, completers[i].name, i) < 0)
+	return;
+    }
+
+  Py_INCREF (&cmdpy_object_type);
+  PyModule_AddObject (gdb_module, "Command",
+		      (PyObject *) &cmdpy_object_type);
+
+  invoke_cst = PyString_FromString ("invoke");
+  complete_cst = PyString_FromString ("complete");
+}
+
+
+
+static PyMethodDef cmdpy_object_methods[] =
+{
+  { "dont_repeat", cmdpy_dont_repeat, METH_NOARGS,
+    "Prevent command repetition when user enters empty line." },
+
+  { 0 }
+};
+
+static PyTypeObject cmdpy_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,				  /*ob_size*/
+  "gdb.Command",		  /*tp_name*/
+  sizeof (cmdpy_object),	  /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  0,				  /*tp_dealloc*/
+  0,				  /*tp_print*/
+  0,				  /*tp_getattr*/
+  0,				  /*tp_setattr*/
+  0,				  /*tp_compare*/
+  0,				  /*tp_repr*/
+  0,				  /*tp_as_number*/
+  0,				  /*tp_as_sequence*/
+  0,				  /*tp_as_mapping*/
+  0,				  /*tp_hash */
+  0,				  /*tp_call*/
+  0,				  /*tp_str*/
+  0,				  /*tp_getattro*/
+  0,				  /*tp_setattro*/
+  0,				  /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+  "GDB command object",		  /* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  cmdpy_object_methods,		  /* tp_methods */
+  0,				  /* tp_members */
+  0,				  /* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  cmdpy_init,			  /* tp_init */
+  0,				  /* tp_alloc */
+  PyType_GenericNew		  /* tp_new */
+};
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 1457928..02dbfc4 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -70,6 +70,7 @@ PyObject *value_to_value_object (struct value *v);
 struct value *convert_value_from_python (PyObject *obj);
 
 void gdbpy_initialize_values (void);
+void gdbpy_initialize_commands (void);
 
 struct cleanup *make_cleanup_py_decref (PyObject *py);
 struct cleanup *make_cleanup_py_restore_gil (PyGILState_STATE *state);
@@ -94,4 +95,6 @@ char *python_string_to_host_string (PyObject *obj);
 PyObject *target_string_to_unicode (const gdb_byte *str, int length);
 int gdbpy_is_string (PyObject *obj);
 
+extern PyObject *gdbpy_doc_cst;
+
 #endif /* GDB_PYTHON_INTERNAL_H */
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 96bb5f5..4f97416 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -46,6 +46,8 @@ static PyMethodDef GdbMethods[];
 
 PyObject *gdb_module;
 
+PyObject *gdbpy_doc_cst;
+
 /* Given a command_line, return a command string suitable for passing
    to Python.  Lines in the string are separated by newlines.  The
    return value is allocated using xmalloc and the caller is
@@ -407,9 +409,12 @@ Enables or disables printing of Python stack traces."),
   PyModule_AddStringConstant (gdb_module, "TARGET_CONFIG", (char*) target_name);
 
   gdbpy_initialize_values ();
+  gdbpy_initialize_commands ();
 
   PyRun_SimpleString ("import gdb");
 
+  gdbpy_doc_cst = PyString_FromString ("__doc__");
+
   /* Create a couple objects which are used for Python's stdout and
      stderr.  */
   PyRun_SimpleString ("\
diff --git a/gdb/symtab.c b/gdb/symtab.c
index b9befed..97d7950 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -3890,6 +3890,16 @@ make_symbol_completion_list (char *text, char *word)
   return current_language->la_make_symbol_completion_list (text, word);
 }
 
+/* Like make_symbol_completion_list, but suitable for use as a
+   completion function.  */
+
+char **
+make_symbol_completion_list_fn (struct cmd_list_element *ignore,
+				char *text, char *word)
+{
+  return make_symbol_completion_list (text, word);
+}
+
 /* Like make_symbol_completion_list, but returns a list of symbols
    defined in a source file FILE.  */
 
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 2446d1e..8b086f3 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1238,6 +1238,8 @@ extern void select_source_symtab (struct symtab *);
 
 extern char **default_make_symbol_completion_list (char *, char *);
 extern char **make_symbol_completion_list (char *, char *);
+extern char **make_symbol_completion_list_fn (struct cmd_list_element *,
+					      char *, char *);
 
 extern char **make_file_symbol_completion_list (char *, char *, char *);
 
diff --git a/gdb/testsuite/gdb.python/python-cmd.exp b/gdb/testsuite/gdb.python/python-cmd.exp
new file mode 100644
index 0000000..6c73ff2
--- /dev/null
+++ b/gdb/testsuite/gdb.python/python-cmd.exp
@@ -0,0 +1,107 @@
+# Copyright (C) 2009 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 the mechanism
+# for defining new GDB commands in Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+# Usage: gdb_py_test_multiple NAME INPUT RESULT {INPUT RESULT}...
+# Run a test named NAME, consisting of multiple lines of input.
+# After each input line INPUT, search for result line RESULT.
+# Succeed if all results are seen; fail otherwise.
+proc gdb_py_test_multiple {name args} {
+    global gdb_prompt
+    foreach {input result} $args {
+	if {[gdb_test_multiple $input "$name - $input" {
+	    -re "\[\r\n\]*($result)\[\r\n\]+($gdb_prompt | *>)$" {
+		pass "$name - $input"
+	    }
+	}]} {
+	    return 1
+	}
+    }
+    return 0
+}
+
+# Start with a fresh gdb.
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+
+gdb_test_multiple "python print 'hello, world!'" "verify python support" {
+    -re "not supported.*$gdb_prompt $"	{
+      unsupported "python support is disabled"
+      return -1
+    }
+    -re "$gdb_prompt $"	{}
+}
+
+# Test a simple command.
+
+gdb_py_test_multiple "input simple command" \
+  "python" "" \
+  "class test_cmd (gdb.Command):" "" \
+  "  def __init__ (self):" "" \
+  "    super (test_cmd, self).__init__ (\"test_cmd\", gdb.COMMAND_OBSCURE)" "" \
+  "  def invoke (self, arg, from_tty):" "" \
+  "    print \"test_cmd output, arg = %s\" % arg" "" \
+  "test_cmd ()" "" \
+  "end" ""
+
+gdb_test "test_cmd ugh" "test_cmd output, arg = ugh" "call simple command"
+
+# Test a prefix command, and a subcommand within it.
+
+gdb_py_test_multiple "input prefix command" \
+  "python" "" \
+  "class prefix_cmd (gdb.Command):" "" \
+  "  def __init__ (self):" "" \
+  "    super (prefix_cmd, self).__init__ (\"prefix_cmd\", gdb.COMMAND_OBSCURE, gdb.COMPLETE_NONE, True)" "" \
+  "  def invoke (self, arg, from_tty):" "" \
+  "    print \"prefix_cmd output, arg = %s\" % arg" "" \
+  "prefix_cmd ()" "" \
+  "end" ""
+
+gdb_test "prefix_cmd ugh" "prefix_cmd output, arg = ugh" "call prefix command"
+
+gdb_py_test_multiple "input subcommand" \
+  "python" "" \
+  "class subcmd (gdb.Command):" "" \
+  "  def __init__ (self):" "" \
+  "    super (subcmd, self).__init__ (\"prefix_cmd subcmd\", gdb.COMMAND_OBSCURE)" "" \
+  "  def invoke (self, arg, from_tty):" "" \
+  "    print \"subcmd output, arg = %s\" % arg" "" \
+  "subcmd ()" "" \
+  "end" ""
+
+gdb_test "prefix_cmd subcmd ugh" "subcmd output, arg = ugh" "call subcmd"
+
+# Test a subcommand in an existing GDB prefix.
+
+gdb_py_test_multiple "input new subcommand" \
+  "python" "" \
+  "class newsubcmd (gdb.Command):" "" \
+  "  def __init__ (self):" "" \
+  "    super (newsubcmd, self).__init__ (\"info newsubcmd\", gdb.COMMAND_OBSCURE)" "" \
+  "  def invoke (self, arg, from_tty):" "" \
+  "    print \"newsubcmd output, arg = %s\" % arg" "" \
+  "newsubcmd ()" "" \
+  "end" ""
+
+gdb_test "info newsubcmd ugh" "newsubcmd output, arg = ugh" "call newsubcmd"



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