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.
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.
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.
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() .
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.
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