Differences between revisions 1 and 17 (spanning 16 versions)
Revision 1 as of 2002-09-20 10:31:25
Size: 13135
Editor: 217
Comment:
Revision 17 as of 2008-11-15 14:00:48
Size: 14246
Editor: localhost
Comment: converted to 1.6 markup
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
The {{{cmd}}} module provide a powerfull tool for creating command line oriented
user interface
.  

= Command line advocacy =

In theses days of graphical user interface, a command line
interpreter can seems antique. I agree that GUI are often
more friendly (and in fact I'm happy to have something other
than "ed" to create this document). But a command
line interface can have several advantages:

  * '''portability''' almost any computer is able to drive a text terminal, so a command line interface can really run everywhere.

 
* '''resources''' the CPU and memory cost of a command line interface is far lighter than a GUI library.

 
* '''speed''' for advanced users, it's often faster to type a command than to dive into menus and windows.

 
* '''development''' It is far faster to create a text oriented interface.

 
* '''driving''' you can easily drive a text oriented program with the popen command. That mean that the whole application can be tested automatically.

And even if you plan to create a GUI software, it's often
good to start with a text interface. This will allow you
to focus on the applicative logic independently of the interface.
This is often a good way to create modular software.
The {{{cmd}}} module makes it easy to make command line interfaces in your programs.

{{{cmd}}} is different than OptParse, in that OptParse is a tool for making command line tools.

{{{cmd}}}, on the other hand, makes it so you can embed a command line within your program.


In these days of graphical user interfaces, a command line interpreter can seems antique. I agree that GUI are often more friendly (and in fact I'm happy to have something other than "ed" to create this document). But a command line interface can have several advantages:

 * '''portability''' almost any computer is able to drive a text terminal, so a command line interface can really run everywhere.
 * '''resources''' the CPU and memory cost of a command line interface is far lighter than a GUI library.
 * '''speed''' for advanced users, it's often faster to type a command than to dive into menus and windows.
 * '''development''' It is far faster to create a text oriented interface.
 * '''driving''' you can easily drive a text oriented program with the popen command. That mean that the whole application can be tested automatically.
And even if you plan to create a GUI software, it's often good to start with a text interface. This will allow you to focus on the applicative logic independently of the interface. This is often a good way to create modular software.
Line 28: Line 17:

The module define only one class: the {{{Cmd}}} class. Creating
a command line interpreter is done by sub-classing the {{{cmd.Cmd}}}
class.
The module define only one class: the {{{Cmd}}} class. Creating a command line interpreter is done by sub-classing the {{{cmd.Cmd}}} class.
Line 34: Line 20:

The main goal of an interpreter is to respond to commands.
A command is the first part of a text line entered at the
interpreter prompt. This part is defined as the longer string
of characters contained in the {{{identchars}}}
member. By default {{{identchars}}} contain non accented letters,
digits and the underscore symbol. The end of the line is the
command parameters.

Commands handling is really easy: if you want to define the
command spam, you only have to define the {{{do_spam}}} method
in your derived class.
The main goal of an interpreter is to respond to commands. A command is the first part of a text line entered at the interpreter prompt. This part is defined as the longer string of characters contained in the {{{identchars}}} member. By default {{{identchars}}} contain non accented letters, digits and the underscore symbol. The end of the line is the command parameters.

Commands handling is really easy: if you want to define the command spam, you only have to define the {{{do_spam}}} method in your derived class.
Line 48: Line 25:

The {{{do_xxx}}} method should only take one extra parameter. This
parameter correspond to the part of the string entered by
the user after the command name. The {{{do_xxx}}} job is to parse
this string and to find the command parameters values. Python provide
many helpful tools to parse this string but this is quite out of the scope
of his how-to.
The {{{do_xxx}}} method should only take one extra parameter. This parameter correspond to the part of the string entered by the user after the command name. The {{{do_xxx}}} job is to parse this string and to find the command parameters values. Python provide many helpful tools to parse this string but this is quite out of the scope of his how-to.
Line 57: Line 28:
Line 63: Line 33:

It's generally a good idea to use the same format for application
errors.
It's generally a good idea to use the same format for application errors.
Line 68: Line 36:

In the most common case: commands shouldn't return a value.
The exception is when you want to exit the interpreter loop:
any command that return a true value stop the interpreter.
In the most common case: commands shouldn't return a value. The exception is when you want to exit the interpreter loop: any command that return a true value stop the interpreter.
Line 74: Line 39:

The following function define a command which take two numerical
arguments and print the result of the addition.

{{{
def do_add(self,s): 
    l = s.split() 
    if len(l)!=2: 
       print "*** invalid number of arguments"         return
    try: 
       l = [int(i) for i in l] 
    except ValueError: 
       print "*** arguments should be numbers"         return
The following function define a command which take two numerical arguments and print the result of the addition.

{{{
def do_add(self,s):
    l = s.split()
    if len(l)!=2:
       print "*** invalid number of arguments"
       return

    try:
       l = [int(i) for i in l]
    except ValueError:
       print "*** arguments should be numbers"
       return
Line 91: Line 54:
Line 95: Line 57:
(Cmd) add 4 
*** invalid number of arguments 
(Cmd) add 5 4 
(Cmd) add 4
*** invalid number of arguments
(Cmd) add 5 4
Line 100: Line 62:
Line 102: Line 63:

Help support is another strength of the cmd module. You can
provide documentation for the xxx command by defining the
{{{help_xxx}}} method. For the add command, you could for sample
define:
Help support is another strength of the cmd module. You can provide documentation for the xxx command by defining the {{{help_xxx}}} method. For the add command, you could for sample define:
Line 112: Line 69:
Line 116: Line 72:
(Cmd) help add   (Cmd) help add
Line 120: Line 75:

You can also define help for topics that are not related
to commands.
You can also define help for topics that are not related to commands.
Line 129: Line 82:

The interpreter understand the ? character as a shortcut
for the help command.
The interpreter understand the ? character as a shortcut for the help command.
Line 134: Line 85:

Completion is a very interesting feature: when the user press
the TAB key, the interpreter will try to complete the command
or propose several alternatives. Completion will be available
only if the computer support the readline module. You can
also disable completion by passing the None value to the
completekey attribute of the Cmd class constructor.

The interpreter is able to process completion for commands
names, but for commands arguments you will have to help
it. For the command xxx, this is done by defining a {{{complete_xxx}}}
method. For sample, if you have defined a color command,
completion method for this command could be:
Completion is a very interesting feature: when the user press the TAB key, the interpreter will try to complete the command or propose several alternatives. Completion will be available only if the computer support the readline module. You can also disable completion by passing the None value to the completekey attribute of the Cmd class constructor.

The interpreter is able to process completion for commands names, but for commands arguments you will have to help it. For the command xxx, this is done by defining a {{{complete_xxx}}} method. For sample, if you have defined a color command, completion method for this command could be:
Line 150: Line 91:
Line 153: Line 93:

}}}
}}}
Line 158: Line 96:
   * '''text''' is the string we are matching against, all returned matches must begin with it
   * '''line''' is is the current input line
   * '''begidx''' is the beginning index in the line of the text being matched 
   * '''endidx''' is the end index in the line of the text being matched  
And should return a list (possibly empty) of strings
representing the possible completions. The arguments begidx
and endidx are useful when completion depend of the position
of the argument.
 * '''text''' is the string we are matching against, all returned matches must begin with it
 * '''line''' is is the current input line
 * '''begidx''' is the beginning index in the line of the text being matched
 * '''endidx''' is the end index in the line of the text being matched
And should return a list (possibly empty) of strings representing the possible completions. The arguments begidx and endidx are useful when completion depend of the position of the argument.
Line 169: Line 103:

Once you have defined your own interpreter class, the only
thing to do is to create an instance and to call the mainloop
method:
Once you have defined your own interpreter class, the only thing to do is to create an instance and to call the mainloop method:
Line 178: Line 109:
In python 2.1 and 2.2 (and possibly some older releases too as well as future releases?) mainloop() has been renamed to cmdloop()
Line 181: Line 112:

The cmd module provide several hooks to change the behavior
of the interpreter. You should notice that your users won't
necessary thanks you to deviate from the standard behavior.
The cmd module provide several hooks to change the behavior of the interpreter. You should notice that your users won't necessary thanks you to deviate from the standard behavior.
Line 187: Line 115:

By default when an empty line is entered, the last command
is repeated. You can change this behavior by overriding
the {{{emptyline}}} method. For sample to disable the repetition
of the last command:
By default when an empty line is entered, the last command is repeated. You can change this behavior by overriding the {{{emptyline}}} method. For sample to disable the repetition of the last command:
Line 197: Line 121:
Line 199: Line 122:

When the help command is called without arguments, it print
a summary of all the documentation topics:
When the help command is called without arguments, it print a summary of all the documentation topics:
Line 205: Line 126:
Line 207: Line 127:
======================================== 
EOF add exit macro shell test  
========================================
EOF add exit macro shell test
Line 211: Line 130:
========================== 
intro  
Undocumented commands: 
====================== 
line help   
(Cmd)  
}}}
==========================
intro
Undocumented commands:
======================
line help
(Cmd)
}}}
Line 224: Line 139:
  * '''documented commands''' are commands which have a {{{help_xxx}}} methods
  * '''miscellaneous help topics''' contain the {{{help_xxx}}} methods without {{{do_xxx}}} method
  * '''undocumented commands''' contain the {{{do_xxx}}} method without {{{help_xxx}}} method
 * '''documented commands''' are commands which have a {{{help_xxx}}} methods
 * '''miscellaneous help topics''' contain the {{{help_xxx}}} methods without {{{do_xxx}}} method
 * '''undocumented commands''' contain the {{{do_xxx}}} method without {{{help_xxx}}} method
Line 230: Line 144:
  * {{{self.ruler}}} define the character used to underline section titles
  * {{{self.doc_header }}} define the title of the ''documented commands'' section
  * {{{self.misc_header }}} define the title of the ''miscalleanous help topics'' section
  * {{{self.undoc_header }}} define the title of the ''undocumented commands'' section
 * {{{self.ruler}}} define the character used to underline section titles
 * {{{self.doc_header }}} define the title of the ''documented commands'' section
 * {{{self.misc_header }}} define the title of the ''miscalleanous help topics'' section
 * {{{self.undoc_header }}} define the title of the ''undocumented commands'' section
Line 236: Line 149:

At the startup, the interpreter print the {{{self.intro}}} string.
This string can be overridden via an optional argument to
the {{{cmdloop()}}} method. 
At the startup, the interpreter print the {{{self.intro}}} string. This string can be overridden via an optional argument to the {{{cmdloop()}}} method.
Line 242: Line 152:
Line 244: Line 153:

   * The {{{default}}} method can be overridden for handling commands for which there is no {{{do_xxx}}} method

  
* The {{{completedefault}}} method may be overridden to intercept completion for commands that have no {{{complete_xxx}}} methods.

Theses methods have the same parameters than the {{{do_xxx}}} and
{{{complete_xxx}}} methods.
 * The {{{default}}} method can be overridden for handling commands for which there is no {{{do_xxx}}} method
 * The {{{completedefault}}} method may be overridden to intercept completion for commands that have no {{{complete_xxx}}} methods.
Theses methods have the same parameters than the {{{do_xxx}}} and {{{complete_xxx}}} methods.
Line 253: Line 158:

If your program become complex, or if your data structure
is hierarchical, it can be interesting to define nested
interpreters (calling an interpreter inside an other interpreter).
In that case, I like having prompt like: 
If your program become complex, or if your data structure is hierarchical, it can be interesting to define nested interpreters (calling an interpreter inside an other interpreter). In that case, I like having prompt like:
Line 264: Line 165:

You can do this by changing the prompt attribute of the nested
interpreter:
You can do this by changing the prompt attribute of the nested interpreter:
Line 274: Line 173:

Note that it can be a better practice to do this in the constructor
of the nested interpreter.
Note that it can be a better practice to do this in the constructor of the nested interpreter.
Line 279: Line 176:

Sometime, it can be useful to have more directive interaction
sessions with the users. The Cmd class allow you to use
the {{{print}}} and {{{raw_input}}} functions without problems.
Sometime, it can be useful to have more directive interaction sessions with the users. The Cmd class allow you to use the {{{print}}} and {{{raw_input}}} functions without problems.
Line 289: Line 183:

}}}
}}}
Line 295: Line 187:

At the start of the interpreter loop the preloop method is
called. At the end of the loop this is the postloop method.
This methods take no arguments and shouldn't return any
value. The following show how to make the interpreter more
polite:

{{{
class polite_cmd(cmd.Cmd,object): 
    def preloop(self): 
        print 'Hello' 
        super(polite_cmd,self).preloop()  
    def postloop(self): 
At the start of the interpreter loop the preloop method is called. At the end of the loop this is the postloop method. This methods take no arguments and shouldn't return any value. The following show how to make the interpreter more polite:

{{{
class polite_cmd(cmd.Cmd,object):
    def preloop(self):
        print 'Hello'
        super(polite_cmd,self).preloop()
    def postloop(self):
Line 310: Line 196:
        super(polite_cmd,self).postloop() 
}}}
        super(polite_cmd,self).postloop()
}}}
Line 314: Line 199:
Line 317: Line 201:
    * {{{precmd}}} method is called with the a string corresponding to the line entered at the interpreter prompt as argument and should return a string which will be used as parameter by the onecmd method.


   
* {{{onecmd}}} take the return value of precmd and return a boolean value (True will stop the interpreter). This is this method which do the real works: extracting the command, finding the corresponding do_xxx method and calling it.


   
* {{{postcmd}}} this method take two parameters: the return value of the onecmd method and the string returned by precmd and should return a true value to exit the interpreter loop.

The precmd and postcmd methods do nothing by default and
was only intended as hook for derived class. In fact with
the Python 2.2 super method, they are useless because anything
can be done by overriding the onecmd method. So you should probably
avoid to use this two hooks.
 * {{{precmd}}} method is called with the a string corresponding to the line entered at the interpreter prompt as argument and should return a string which will be used as parameter by the onecmd method.
 * {{{onecmd}}} take the return value of precmd and return a boolean value (True will stop the interpreter). This is this method which do the real works: extracting the command, finding the corresponding do_xxx method and calling it.
 * {{{postcmd}}} this method take two parameters: the return value of the onecmd method and the string returned by precmd and should return a true value to exit the interpreter loop.
The precmd and postcmd methods do nothing by default and was only intended as hook for derived class. In fact with  the Python 2.2 super method, they are useless because anything can be done by overriding the onecmd method. So you should probably avoid to use this two hooks.
Line 333: Line 208:
Line 344: Line 218:

But, if you want to simulate an interpreter entry you should
call this three methods in the good order. For sample if
you want to print the help message at startup:
But, if you want to simulate an interpreter entry you should call this three methods in the good order. For sample if you want to print the help message at startup:
Line 357: Line 228:

This will prevent you from troubles if you want later to
inherit for a class which would have modified the hooks.
This will prevent you from troubles if you want later to inherit for a class which would have modified the hooks.
Line 362: Line 231:

One other strength of the cmd module is that it handle multiple
inheritance. That mean that you can create helper class
intended to provide additional features.
One other strength of the cmd module is that it handle multiple inheritance. That mean that you can create helper class intended to provide additional features.
Line 368: Line 234:
Line 371: Line 236:

class shell_cmd(cmd.Cmd,object):  
    def do_shell(self, s): 
        os.system(s)  
    def help_shell(self): 
class shell_cmd(cmd.Cmd,object):
    def do_shell(self, s):
        os.system(s)
    def help_shell(self):
Line 380: Line 242:

By deriving from this class, you will be able to execute
any shell command:

{{{
(Cmd) shell date  
Thu Sep 9 08:57:14 CEST 2002  
(Cmd) ! ls /usr/local/lib/python2.2/config  
By deriving from this class, you will be able to execute any shell command:

{{{
(Cmd) shell date
Thu Sep 9 08:57:14 CEST 2002
(Cmd) ! ls /usr/local/lib/python2.2/config
Line 394: Line 251:

By the way the cmd module understand the ! character as a
shortcut for the shell command.
By the way the cmd module understand the ! character as a shortcut for the shell command.
Line 400: Line 254:

{{{
class exit_cmd(cmd.Cmd,object):  
    def can_exit(self): 
        return True  
{{{
class exit_cmd(cmd.Cmd,object):
    def can_exit(self):
        return True
Line 409: Line 260:
        if r and (self.can_exit() or 
           raw_input('exit anyway ? (yes/no):')=='yes'): 
        if r and (self.can_exit() or
           raw_input('exit anyway ? (yes/no):')=='yes'):
Line 412: Line 263:
        return False           return False
Line 416: Line 266:

def help_exit(self): 
    def help_exit(self):
Line 419: Line 268:
        print "You can also use the Ctrl-D shortcut."  
    do_EOF = do_exit 
        print "You can also use the Ctrl-D shortcut."
    do_EOF = do_exit
Line 424: Line 272:

This class provide the exit command to abort the interpreter.
You can protect exit by overriding the can_exit method.
This class provide the exit command to abort the interpreter. You can protect exit by overriding the can_exit method.
Line 430: Line 275:
Now with a class that inherit both from {{{exit_cmd}}} and
{{{shell_cmd}}} you will be able to define an interpreter that
understand the shell and exit commands.
Now with a class that inherit both from {{{exit_cmd}}} and {{{shell_cmd}}} you will be able to define an interpreter that understand the shell and exit commands.
Line 435: Line 278:

[http://www.python.org/doc/current/lib/Cmd-objects.html cmd module reference]





























[[http://www.python.org/doc/current/lib/Cmd-objects.html|cmd module reference]]

= Example Code =
<<BR>>[[http://www.eskimo.com/~jet/python/examples/cmd/console.py|console.py ]] - by [[http://www.eskimo.com/~jet/|James Theiele]]

<<BR>>[[http://www.google.com/codesearch?hl=en&q=+python+Cmd+precmd+show:BNSUqEn_LU0:rUlwOhxEhPQ:z0W6orrQIxk&sa=N&cd=12&ct=rc&cs_p=http://downloads.activestate.com/Komodo/RemoteDebugging/dbgp-1.0-262212.tar.gz&cs_f=dbgp-1.0-262212/dbgp/listcmd.py#a0|listcmd.py]] - from Komodo Remote Debugger

= Discussion =
It would be cool if you could call these mini-command lines from "the" command line.

If there were some sort of default OptParse-like behavior, that would totally rock.

That way, I could pass in instructions, scripts, and get back the results on stdout.

Something like "-c" for Python, where you can pass in a line, and get back the result, without seeing the intro text.

-- LionKimbro <<DateTime(2006-03-06T19:06:56Z)>>

The {{{onecmd()}}} function may be what you're after. Write your interpreter as you normally would. Then, in main, parse the command line for a '-c' option. If you find it, call {{{onecmd()}}} with the string following the '-c' as the parameter, else call {{{cmdloop()}}}.

-- Mark Workman <<DateTime>>

The cmd module makes it easy to make command line interfaces in your programs.

cmd is different than OptParse, in that OptParse is a tool for making command line tools.

cmd, on the other hand, makes it so you can embed a command line within your program.

In these days of graphical user interfaces, a command line interpreter can seems antique. I agree that GUI are often more friendly (and in fact I'm happy to have something other than "ed" to create this document). But a command line interface can have several advantages:

  • portability almost any computer is able to drive a text terminal, so a command line interface can really run everywhere.

  • resources the CPU and memory cost of a command line interface is far lighter than a GUI library.

  • speed for advanced users, it's often faster to type a command than to dive into menus and windows.

  • development It is far faster to create a text oriented interface.

  • driving you can easily drive a text oriented program with the popen command. That mean that the whole application can be tested automatically.

And even if you plan to create a GUI software, it's often good to start with a text interface. This will allow you to focus on the applicative logic independently of the interface. This is often a good way to create modular software.

cmd module basics

The module define only one class: the Cmd class. Creating a command line interpreter is done by sub-classing the cmd.Cmd class.

Creating a command

The main goal of an interpreter is to respond to commands. A command is the first part of a text line entered at the interpreter prompt. This part is defined as the longer string of characters contained in the identchars member. By default identchars contain non accented letters, digits and the underscore symbol. The end of the line is the command parameters.

Commands handling is really easy: if you want to define the command spam, you only have to define the do_spam method in your derived class.

parameters

The do_xxx method should only take one extra parameter. This parameter correspond to the part of the string entered by the user after the command name. The do_xxx job is to parse this string and to find the command parameters values. Python provide many helpful tools to parse this string but this is quite out of the scope of his how-to.

errors

The interpreter use the following format to signal errors:

*** <error description>: <additional parameters>

It's generally a good idea to use the same format for application errors.

return value

In the most common case: commands shouldn't return a value. The exception is when you want to exit the interpreter loop: any command that return a true value stop the interpreter.

sample

The following function define a command which take two numerical arguments and print the result of the addition.

def do_add(self,s):
    l = s.split()
    if len(l)!=2:
       print "*** invalid number of arguments"
       return
    try:
       l = [int(i) for i in l]
    except ValueError:
       print "*** arguments should be numbers"
       return
    print l[0]+l[1]

Now if you run the interpreter, you will have:

(Cmd) add 4
*** invalid number of arguments
(Cmd) add 5 4
9

Help

Help support is another strength of the cmd module. You can provide documentation for the xxx command by defining the help_xxx method. For the add command, you could for sample define:

def help_add(self):
    print 'add two integral numbers'

And then, in the interactive interpreter you will have:

(Cmd) help add
add two integral numbers

You can also define help for topics that are not related to commands.

def help_introduction(self):
    print 'introduction'
    print 'a good place for a tutorial'

The interpreter understand the ? character as a shortcut for the help command.

Completion

Completion is a very interesting feature: when the user press the TAB key, the interpreter will try to complete the command or propose several alternatives. Completion will be available only if the computer support the readline module. You can also disable completion by passing the None value to the completekey attribute of the Cmd class constructor.

The interpreter is able to process completion for commands names, but for commands arguments you will have to help it. For the command xxx, this is done by defining a complete_xxx method. For sample, if you have defined a color command, completion method for this command could be:

_AVAILABLE_COLORS = ('blue', 'green', 'yellow', 'red', 'black')
def complete_color(self, text, line, begidx, endidx):
    return [i for i in _AVAILABLE_COLORS if i.startswith(text)]

The complete_xxx method take four arguments:

  • text is the string we are matching against, all returned matches must begin with it

  • line is is the current input line

  • begidx is the beginning index in the line of the text being matched

  • endidx is the end index in the line of the text being matched

And should return a list (possibly empty) of strings representing the possible completions. The arguments begidx and endidx are useful when completion depend of the position of the argument.

Starting the interpreter

Once you have defined your own interpreter class, the only thing to do is to create an instance and to call the mainloop method:

interpreter = MyCmdInterpreter()
interpreter.mainloop()

In python 2.1 and 2.2 (and possibly some older releases too as well as future releases?) mainloop() has been renamed to cmdloop()

Interface customization

The cmd module provide several hooks to change the behavior of the interpreter. You should notice that your users won't necessary thanks you to deviate from the standard behavior.

Empty lines

By default when an empty line is entered, the last command is repeated. You can change this behavior by overriding the emptyline method. For sample to disable the repetition of the last command:

def emptyline(self):
    pass

Help summary

When the help command is called without arguments, it print a summary of all the documentation topics:

(Cmd) help
Documented commands (type help <topic>):
========================================
EOF add exit macro shell test
Miscellaneous help topics:
==========================
intro
Undocumented commands:
======================
line help
(Cmd)

This summary is separated in three parts:

  • documented commands are commands which have a help_xxx methods

  • miscellaneous help topics contain the help_xxx methods without do_xxx method

  • undocumented commands contain the do_xxx method without help_xxx method

You can customize this screen, with several data members:

  • self.ruler define the character used to underline section titles

  • self.doc_header  define the title of the documented commands section

  • self.misc_header  define the title of the miscalleanous help topics section

  • self.undoc_header  define the title of the undocumented commands section

Introduction message

At the startup, the interpreter print the self.intro string. This string can be overridden via an optional argument to the cmdloop() method.

Advanced material

Defaults handling

  • The default method can be overridden for handling commands for which there is no do_xxx method

  • The completedefault method may be overridden to intercept completion for commands that have no complete_xxx methods.

Theses methods have the same parameters than the do_xxx and complete_xxx methods.

Nested interpreters

If your program become complex, or if your data structure is hierarchical, it can be interesting to define nested interpreters (calling an interpreter inside an other interpreter). In that case, I like having prompt like:

(Cmd) test
(Cmd:Test) exit
(Cmd)

You can do this by changing the prompt attribute of the nested interpreter:

def do_test(self, s):
    i = TestCmd()
    i.prompt = self.prompt[:-1]+':Test)'
    i.cmdloop()

Note that it can be a better practice to do this in the constructor of the nested interpreter.

Sometime, it can be useful to have more directive interaction sessions with the users. The Cmd class allow you to use the print and raw_input functions without problems.

def do_hello(self, s):
    if s=='':
        s = raw_input('Your name please: ')
    print 'Hello',s

FIXME: How to change completion behavior of raw_input?

The interpreter loop

At the start of the interpreter loop the preloop method is called. At the end of the loop this is the postloop method. This methods take no arguments and shouldn't return any value. The following show how to make the interpreter more polite:

class polite_cmd(cmd.Cmd,object):
    def preloop(self):
        print 'Hello'
        super(polite_cmd,self).preloop()
    def postloop(self):
        print 'Goodbye'
        super(polite_cmd,self).postloop()

Command processing

When a command line is processed, several methods are called

  • precmd method is called with the a string corresponding to the line entered at the interpreter prompt as argument and should return a string which will be used as parameter by the onecmd method.

  • onecmd take the return value of precmd and return a boolean value (True will stop the interpreter). This is this method which do the real works: extracting the command, finding the corresponding do_xxx method and calling it.

  • postcmd this method take two parameters: the return value of the onecmd method and the string returned by precmd and should return a true value to exit the interpreter loop.

The precmd and postcmd methods do nothing by default and was only intended as hook for derived class. In fact with the Python 2.2 super method, they are useless because anything can be done by overriding the onecmd method. So you should probably avoid to use this two hooks.

class dollar_cmd(cmd.Cmd, object):
    def onecmd(self, line):
        ''' define $ as a shortcut for the dollar command
            and ask for confirmation when the interpreter exit'''
        if line[:1] == '$':
            line = 'dollar '+line[1:]
        r = super (dollar_cmd, self).onecmd(line)
        if r:
            r = raw_input('really exit ?(y/n):')=='y'
        return r

But, if you want to simulate an interpreter entry you should call this three methods in the good order. For sample if you want to print the help message at startup:

interpreter = MyCmdInterpreter()
l = interpreter.precmd('help')
r = interpreter.onecmd(l)
r = interpreter.postcmd(r, l)
if not r:
    interpreter.mainloop()

This will prevent you from troubles if you want later to inherit for a class which would have modified the hooks.

Creating components

One other strength of the cmd module is that it handle multiple inheritance. That mean that you can create helper class intended to provide additional features.

Shell access

import os
class shell_cmd(cmd.Cmd,object):
    def do_shell(self, s):
        os.system(s)
    def help_shell(self):
        print "execute shell commands"

By deriving from this class, you will be able to execute any shell command:

(Cmd) shell date
Thu Sep 9 08:57:14 CEST 2002
(Cmd) ! ls /usr/local/lib/python2.2/config
Makefile Setup.config config.c install-sh makesetup Setup
Setup.local config.c.in libpython2.2.a python.o

By the way the cmd module understand the ! character as a shortcut for the shell command.

Exit

class exit_cmd(cmd.Cmd,object):
    def can_exit(self):
        return True
    def onecmd(self, line):
        r = super (exit_cmd, self).onecmd(line)
        if r and (self.can_exit() or
           raw_input('exit anyway ? (yes/no):')=='yes'):
             return True
        return False
    def do_exit(self, s):
        return True
    def help_exit(self):
        print "Exit the interpreter."
        print "You can also use the Ctrl-D shortcut."
    do_EOF = do_exit
    help_EOF= help_exit

This class provide the exit command to abort the interpreter. You can protect exit by overriding the can_exit method.

Gluing all together

Now with a class that inherit both from exit_cmd and shell_cmd you will be able to define an interpreter that understand the shell and exit commands.

References

cmd module reference

Example Code


console.py - by James Theiele


listcmd.py - from Komodo Remote Debugger

Discussion

It would be cool if you could call these mini-command lines from "the" command line.

If there were some sort of default OptParse-like behavior, that would totally rock.

That way, I could pass in instructions, scripts, and get back the results on stdout.

Something like "-c" for Python, where you can pass in a line, and get back the result, without seeing the intro text.

-- LionKimbro 2006-03-06 19:06:56

The onecmd() function may be what you're after. Write your interpreter as you normally would. Then, in main, parse the command line for a '-c' option. If you find it, call onecmd() with the string following the '-c' as the parameter, else call cmdloop().

-- Mark Workman 2024-04-19 06:45:47

CmdModule (last edited 2017-06-27 08:06:21 by MarcAndreLemburg)

Unable to edit the page? See the FrontPage for instructions.