Python using argparse with cmd

Is there a way to use the argparse module connected as an interpreter for each prompt in an interface inheriting from cmd ?

I would like my cmd interface to interpret the typical line parameter in the same way as interpreting the parameters and arguments passed at run time in the bash shell using optional arguments with - as positional arguments.

+4
source share
2 answers

Well, one way to do this is to override the cmd default method and use it to parse the string using argparse , because all commands without the do_ method in your cmd.Cmd subclass will go through before using the default method. Note the extra _ before do_test so that it is not used as a cmd command.

 import argparse import cmd import shlex class TestCLI(cmd.Cmd): def __init__(self, **kwargs): cmd.Cmd.__init__(self, **kwargs) self.parser = argparse.ArgumentParser() subparsers = self.parser.add_subparsers() test_parser = subparsers.add_parser("test") test_parser.add_argument("--foo", default="Hello") test_parser.add_argument("--bar", default="World") test_parser.set_defaults(func=self._do_test) def _do_test(self, args): print args.foo, args.bar def default(self, line): args = self.parser.parse_args(shlex.split(line)) if hasattr(args, 'func'): args.func(args) else: cmd.Cmd.default(self, line) test = TestCLI() test.cmdloop() 

argparse does sys.exit if it encounters unknown commands, so you need to override or disarm your ArgumentParser error method to throw an exception instead of exiting and handle it in the default method to stay in the cmd command loop.

I suggest you look into cliff , which allows you to write commands that can automatically be used as argparse and cmd commands, which are pretty neat. It also supports boot commands from setuptools entry points, allowing you to distribute commands as plugins for your application. Note that cliff uses cmd2 , which is cmd more powerful cousin, but you can replace it with cmd as cmd2 was designed as a replacement for replacing cmd .

+3
source

A direct way would be to create an argparse parser and line.split() inside your function, expect ing SystemExit if invalid arguments are provided ( parse_args() calls sys.exit() when invalid arguments are found).

 class TestInterface(cmd.Cmd): __test1_parser = argparse.ArgumentParser(prog="test1") __test1_parser.add_argument('--bar', help="bar help") def help_test1(self): self.__test1_parser.print_help() def do_test1(self, line): try: parsed = self.__test1_parser.parse_args(line.split()) except SystemExit: return print("Test1...") print(parsed) 

If invalid arguments are parse_args() , parse_args() will print errors and the program will return to the interface without exiting.

 (Cmd) test1 --unk usage: test1 [-h] [--bar BAR] test1: error: unrecognized arguments: --unk (Cmd) 

Everything else should work just like a normal argparse use argparse , also supporting all cmd functions (help messages, list of functions, etc.)

Source: https://groups.google.com/forum/#!topic/argparse-users/7QRPlG97cak


Another way to simplify the setup above is to use the decorator below:

 class ArgparseCmdWrapper: def __init__(self, parser): """Init decorator with an argparse parser to be used in parsing cmd-line options""" self.parser = parser self.help_msg = "" def __call__(self, f): """Decorate 'f' to parse 'line' and pass options to decorated function""" if not self.parser: # If no parser was passed to the decorator, get it from 'f' self.parser = f(None, None, None, True) def wrapped_f(*args): line = args[1].split() try: parsed = self.parser.parse_args(line) except SystemExit: return f(*args, parsed=parsed) wrapped_f.__doc__ = self.__get_help(self.parser) return wrapped_f @staticmethod def __get_help(parser): """Get and return help message from 'parser.print_help()'""" f = tempfile.SpooledTemporaryFile(max_size=2048) parser.print_help(file=f) f.seek(0) return f.read().rstrip() 

It simplifies the definition of additional commands, where they take the additional parameter parsed , which contains the result of successful parse_args() . If there are any invalid arguments, the function is never entered, everything is processed by the decorator.

 __test2_parser = argparse.ArgumentParser(prog="test2") __test2_parser.add_argument('--foo', help="foo help") @WrapperCmdLineArgParser(parser=__test2_parser) def do_test2(self, line, parsed): print("Test2...") print(parsed) 

Everything works as a source example, including argparse generated help messages - without having to define the help_command() function.

Source: https://codereview.stackexchange.com/questions/134333/using-argparse-module-within-cmd-interface

+1
source

Source: https://habr.com/ru/post/1438043/


All Articles