7

有没有办法使用argparse挂钩的模块作为接口中每个提示的解释器继承自cmd

我希望我的 cmd 接口以与解释line在运行时在 bash shell 上传递的选项和参数相同的方式解释典型参数,使用可选参数-以及位置参数。

4

2 回答 2

4

好吧,一种方法是重写cmddefault方法并使用它来解析带有 的行,因为cmd.Cmd 子类中argparse没有方法的所有命令都将无法使用该方法。请注意之前的附加内容,以避免将其用作' 命令。do_default_do_testcmd

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如果遇到未知命令,则执行 a sys.exit,因此您需要覆盖或修改您ArgumentParser的 'error方法以引发异常,而不是退出并在default方法中处理该异常,以便留在cmd' 的命令循环中。

我建议你研究一下悬崖,它允许你编写可以自动用作 asargparsecmdcommands 的命令,这非常简洁。它还支持从setuptools入口点加载命令,这允许您将命令作为插件分发到您的应用程序。但是请注意,cliff使用cmd2,它是cmd更强大的表亲,但您可以替换它cmd,因为它cmd2是作为cmd.

于 2014-04-15T14:12:58.297 回答
2

直接的方法是创建一个argparse解析器,并在您的函数中解析,line.split()以防提供无效参数(在找到无效参数时调用)。expectSystemExitparse_args()sys.exit()

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)

如果传递了无效的参数,parse_args()将打印错误,程序将返回界面而不退出。

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

其他一切都应该与常规argparse用例一样工作,同时维护所有cmd的功能(帮助消息、功能列表等)

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


另一种简化上述设置的方法是使用下面的装饰器:

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

它使定义附加命令变得更简单,它们采用parsed包含成功结果的额外参数parse_args()。如果有任何无效参数,则永远不会进入该函数,所有内容都由装饰器处理。

__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)

一切都像原始示例一样工作,包括argparse生成的帮助消息 - 无需定义help_command()函数。

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

于 2016-07-11T19:15:34.890 回答