Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do a multi-level CLI in Python?

I'm trying to do a CLI, preferrably written in Python. I need a multi-level CLI, and I want tab completion.

I looked at the cmd module (from the Python standard library) and readline with the "complete" function (for tab completion).

They both lacked at something, i.e. I haven't figured out how to handle multiple levels such as:

level1
level2
    level2_subcommand_1
    level2_subcommand_2
level3
    level3_subcommand_1

Example: If I typed:

cmd> level2

, I want to see level2_subcommand_1 and level2_subcommand_2 appearing when I hit the tab key, but no level1 and no level3.

I was unable to do so with the cmd lib as well as with readline.

like image 799
daniel meier Avatar asked Oct 19 '10 07:10

daniel meier


People also ask

Is Python good for CLI?

One of the strengths of Python is that it comes with batteries included: it has a rich and versatile standard library that makes it one of the best programming languages for writing scripts for the command line.

How do I create a command line tool?

Create a file index. js in the root of the project. This will be the main entry of the CLI tool that will initialize the commands it will have. NOTE: If you are using Windows for development, make sure that the line end character is set to LF instead of CRLF or the tool will not work.

What is click Pass_context?

When a Click command callback is executed, it's passed all the non-hidden parameters as keyword arguments. Notably absent is the context. However, a callback can opt into being passed to the context object by marking itself with pass_context() .


1 Answers

It works perfectly fine for me with the cmd module in Python 2.6.5. Here is the sample code I was using to test this:

import cmd

class MyInterpreter(cmd.Cmd):
    def do_level1(self, args):
        pass

    def do_level2_subcommand_1(self, args):
        pass

    def do_level2_subcommand_2(self, args):
        pass

    def do_level3_subcommand_1(self, args):
        pass

MyInterpreter().cmdloop()

When I type "level2" on the command line and then press Tab, the line gets expanded to level2_subcommand_ as this is the common prefix to all the completion proposals. When I press Tab again without typing anything, the next line correctly shows level2_subcommand_1 and level2_subcommand_2. Is this what you are looking for?

Another variant for the case of sub-commands is to create a sub-interpreter for them:

class SubInterpreter(cmd.Cmd):
    prompt = "(level2) "

    def do_subcommand_1(self, args):
        pass

    def do_subcommand_2(self, args):
        pass

    def do_quit(self, args):
        return True
    do_EOF = do_quit

class MyInterpreter(cmd.Cmd):
    def do_level1(self, args):
        pass

    def do_level2(self, args):
        sub_cmd = SubInterpreter()
        sub_cmd.cmdloop()

    def do_level3(self, args):
        pass

The above variant gives you level1, level2 and level3 in your "main" interpreter. When you invoke level2 in your main interpreter, it constructs the sub-interpreter and calls its command loops. The sub-interpreter has a different prompt from the main interpreter, so you can always tell which interpreter you are in. The sub-interpreter then gives you subcommand_1, subcommand_2, subcommand_3 and quit. quit takes you back to the main interpreter, and so does the EOF character.

like image 123
Tamás Avatar answered Oct 31 '22 15:10

Tamás