3

This is a two parts question, please see below:

  • I need to create some sort of console to be used by testers to run some commands manually! Is the 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!"
theAlse
  • 5,577
  • 11
  • 68
  • 110

2 Answers2

4

From the cmd documentation:

The optional argument completekey is the readline name of a completion key; it defaults to Tab. If completekey is not None and readline 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.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • So the only problem was that I ran this on windows where readline is not available? (because as soon as I changed to Linux, it worked) – theAlse Jul 19 '13 at 13:28
  • 1
    Exactly. See [Readline functionality on windows with python 2.7](http://stackoverflow.com/q/6024952) for a Windows alternative for that problem. :-) – Martijn Pieters Jul 19 '13 at 13:32
1

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.

user3076105
  • 336
  • 3
  • 6