This is a two parts question, please see below:
cmd
module a good way to go?Below Is the code I have so far using cmd
module, I am only trying to learn and I have two questions so far:
why is the auto-completion feature not working? If I double <TAB>
I get nothing and the cursor just moves forward. Isn't the auto-complete feature provided by default?
Do I have to handle faulty number of arguments for each method? I would like the methods-'help' text to show up automatically if methods are called with faulty number of arguments or when they should be called with argument and they were not.
.
class InteractiveConsole(cmd.Cmd):
""" Interactive command line """
def __init__(self):
cmd.Cmd.__init__(self)
self.prompt = "=>> "
self.intro = "Welcome to IRT console!"
def do_hist(self, args):
"""Print a list of commands that have been entered"""
print self._hist
def do_exit(self, args):
"""Exits from the console"""
return -1
def do_help(self, args):
"""Get help on commands
'help' or '?' with no arguments prints a list of commands for which help is available
'help <command>' or '? <command>' gives help on <command>
"""
# # The only reason to define this method is for the help text in the doc string
cmd.Cmd.do_help(self, args)
# # Override methods in Cmd object ##
def preloop(self):
"""Initialization before prompting user for commands.
Despite the claims in the Cmd documentaion, Cmd.preloop() is not a stub.
"""
cmd.Cmd.preloop(self) # # sets up command completion
self._hist = [] # # No history yet
self._locals = {} # # Initialize execution namespace for user
self._globals = {}
def postloop(self):
"""Take care of any unfinished business.
Despite the claims in the Cmd documentaion, Cmd.postloop() is not a stub.
"""
cmd.Cmd.postloop(self) # # Clean up command completion
print "Exiting..."
def precmd(self, line):
""" This method is called after the line has been input but before
it has been interpreted. If you want to modify the input line
before execution (for example, variable substitution) do it here.
"""
if line != '':
self._hist += [ line.strip() ]
return line
def postcmd(self, stop, line):
"""If you want to stop the console, return something that evaluates to true.
If you want to do some post command processing, do it here.
"""
return stop
def default(self, line):
"""Called on an input line when the command prefix is not recognized.
In that case we execute the line as Python code.
"""
try:
exec(line) in self._locals, self._globals
except Exception, e:
print e.__class__, ":", e
def emptyline(self):
"""Do nothing on empty input line"""
pass
def do_install(self, pathToBuild):
"""install [pathToBuild]
install using the specified file"""
if pathToBuild:
print "installing %s" % pathToBuild
else:
print "<ERROR> You must specify the absolute path to a file which should be used!"
def do_configure(self, pathToConfiguration):
"""configure [pathToConfiguration]
configure using the specified file"""
if pathToConfiguration:
print "configuring %s" % pathToConfiguration
else:
print "<ERROR> You must specify the absolute path to a file which should be used!"
From the cmd
documentation:
The optional argument completekey is the
readline
name of a completion key; it defaults toTab
. If completekey is notNone
andreadline
is available, command completion is done automatically.
You need to have readline
available for tab completion to work.
Command methods only ever take one argument and you need to do the parsing of the argument in the command method itself. You can, of course, call self.do_help()
or self.help_<cmd>()
methods if necessary.
For the first part, yes, I find the cmd module easy to use and powerful enough to implement a CLI similar to the Python's built-in command prompt.
For the second part's first question, you need to tell the module how to complete the command line, by implementing a method like complete_install(self, word, line, begindex, endindex) which takes the current word, line, begin, and end indexes in the line, and returns a list or tuple of strings representing valid completions. You should compute and filter the list typically based on the current word (first argument).
For example, I use a command 'll' with which I set the logging level, implemented as follows:
def complete_ll(self, a, ln, bi, ei):
return tuple(
k for k in logging._nameToLevel.keys()
if k.casefold().find(a.casefold()) >= 0)
def do_ll(self, a):
"Set or get debug level: DL [10 .. 50 | levelName]"
def ll():
n = log.getEffectiveLevel()
return f"{logging.getLevelName(n)} ({n})"
print(ll())
if a:
try:
log.setLevel(eval(a.upper(), logging._nameToLevel))
print("Logging level changed to", ll())
except Exception as e:
log.exception(f"{e}, value {a}", exc_info=1)
For the second question, yes, you should check the number, type, and validity of the arguments in the "do_..." method, which has been done to some extent in your example. Of course, you can also call the 'help_..." method at that point, if it would really help.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With