When writing an Command Line Interface for an application it could be nice to have an interactive shell with command completition and history. The cmd library of Python provides a framework for that.
We will build an application step-by-step. Scroll down to the end of the page to see a full example with all the bells and whistles.
Skeleton of the Interactive shell
The recommendation is to subclass the Cmd class, so that's what we do here, though as being a skeleton we don't do anything else with it.
examples/python/cli/skeleton.py
from cmd import Cmd
class MyPrompt(Cmd):
pass
MyPrompt().cmdloop()
We create an instance object of the MyPrompt
class and immediately call the cmdloop
method.
We could have used a temporary variable there if we wanted to be a bit more verbose like this:
p = MyPrompt()
p.cmdloop()
but the result is the same.
When we run this script it will display a default prompt:
(Cmd)
You can't do much with it. You can type in ?
and press ENTER to get the following help:
Documented commands (type help <topic>):
========================================
help
help
(Cmd) Ctrl-C
You can ask for help on help
by typing in:
help help
And it will print:
List available commands with "help" or detailed help with "help cmd".
If you would like to exit the application you need to press Ctlr-C
and get a KeyboardInterrupt
.
First commands
You can add commands to the system by implementing the corresponding do_*
methods.
examples/python/cli/commands.py
from cmd import Cmd
class MyPrompt(Cmd):
def do_exit(self, inp):
print("Bye")
return True
def do_add(self, inp):
print("Adding '{}'".format(inp))
MyPrompt().cmdloop()
print("after")
We implemented to commands: exit
and add
.
When we run the script it will show use the standard prompt:
(Cmd)
If at this point we press TAB
twice we get the list of all the available command. In our case that is
add exit help
If we type in help
we get the following output:
(Cmd) help
Documented commands (type help <topic>):
========================================
help
Undocumented commands:
======================
add exit
If we type in help add
as the help window suggests for the documented commands we get:
(Cmd) help add
*** No help on add
If we type in add
followed by some text and press ENTER the system will run the do_add
method and
pass the text to the method. In our case we get:
(Cmd) add Hello World
Adding 'Hello World'
If we type in exit
it will call the do_exit
method, print "Bye" and exit the cmdloop
.
In our case it means it will go on and print the string "after".
(Cmd) exit
Bye
after
Help - documenting commands
As you could see above, the built-in help
command had some documentation, but the two command we added did not
have any. There are two ways to add documentation to a command. Either by adding a method with the help_*
prefix
or by adding docstring to the appropriate do_*
method.
from cmd import Cmd
class MyPrompt(Cmd):
def do_exit(self, inp):
'''exit the application.'''
print("Bye")
return True
def do_add(self, inp):
print("Adding '{}'".format(inp))
def help_add(self):
print("Add a new entry to the system.")
MyPrompt().cmdloop()
Entering ?
not all the commands are listed under the "Documented commands":
(Cmd) ?
Documented commands (type help <topic>):
========================================
add exit help
We can get help by typing in help
and the name of the command:
(Cmd) help add
Add a new entry to the system.
(Cmd) help exit
exit the application.
We can still exit the application:
(Cmd) exit
Bye
Prompt and banner
The default prompt is (Cmd)
but we can override it using the prompt
attribute of the class.
In addition we can set a text to be the banner, that is the text shown when we launch the application, before the first prompt
is shown. We only need to assign the text to the intro
attribute.
from cmd import Cmd
class MyPrompt(Cmd):
prompt = 'pb> '
intro = "Welcome! Type ? to list commands"
def do_exit(self, inp):
'''exit the application.'''
print("Bye")
return True
MyPrompt().cmdloop()
If we run this application we'll see:
Welcome! Type ? to list commands
pb>
Default actions
Sometime you might want to be able to freely parse the input. For example if you'd like to implement short, one-letter alternatives
of longer commands. If you implement a method called default
that method will be called every time a command is
entered that does not correspond to any of the do_*
methods.
examples/python/cli/default.py
from cmd import Cmd
class MyPrompt(Cmd):
def do_exit(self, inp):
'''exit the application. Shorthand: x q.'''
print("Bye")
return True
def default(self, inp):
if inp == 'x' or inp == 'q':
return self.do_exit(inp)
print("Default: {}".format(inp))
MyPrompt().cmdloop()
In this example we catch stand-alone x
and q
characters and call the do_exit
method for them.
Ctrl-d EOF
You might have noticed thet if you press Ctrl-d
, the standard way to exit most command line application, our examples print *** Unknown syntax: EOF. That's because Ctrl-d send an EOF (End Of File) signal and by default Cmd does not know what to do with it.
The solution is to implement the do_EOF
method that will be called when the user presses Ctl-d
. As we already have a do_exit
method, we can just assign that to the do_EOF
and have both do the same. In order to provide help for the EOF, we can include a function
called help_EOF
that is assigned the help_exit
function.
Full example
This examples includes all of the above techniques.
from cmd import Cmd
class MyPrompt(Cmd):
prompt = 'pb> '
intro = "Welcome! Type ? to list commands"
def do_exit(self, inp):
print("Bye")
return True
def help_exit(self):
print('exit the application. Shorthand: x q Ctrl-D.')
def do_add(self, inp):
print("adding '{}'".format(inp))
def help_add(self):
print("Add a new entry to the system.")
def default(self, inp):
if inp == 'x' or inp == 'q':
return self.do_exit(inp)
print("Default: {}".format(inp))
do_EOF = do_exit
help_EOF = help_exit
if __name__ == '__main__':
MyPrompt().cmdloop()
See also the Cmd wiki page.