Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/321.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Cement CLI-在给定无效参数时打印子Parser的帮助_Python_Python 3.x_Argparse_Cement - Fatal编程技术网

Python Cement CLI-在给定无效参数时打印子Parser的帮助

Python Cement CLI-在给定无效参数时打印子Parser的帮助,python,python-3.x,argparse,cement,Python,Python 3.x,Argparse,Cement,我正在使用Python 3.7和水泥3.0.4 我有一个带控制器的基本应用程序,控制器有一个命令,该命令接受一个可选参数。我看到的是,如果我传递一个无效参数的命令,我会得到一个无效参数错误,正如我预期的那样,但我得到的是应用程序本身的“使用”输出,而不是控制器上的命令。以下是一个例子: from cement import App, Controller, ex class Base(Controller): class Meta: label = 'base'

我正在使用Python 3.7和水泥3.0.4

我有一个带控制器的基本应用程序,控制器有一个命令,该命令接受一个可选参数。我看到的是,如果我传递一个无效参数的命令,我会得到一个无效参数错误,正如我预期的那样,但我得到的是应用程序本身的“使用”输出,而不是控制器上的命令。以下是一个例子:

from cement import App, Controller, ex

class Base(Controller):
    class Meta:
        label = 'base'


    @ex(help='example sub-command')
    def cmd1(self):
        print('Inside Base.cmd1()')

    @ex(
        arguments=[
            (['-n', '--name'],
             {'help': ''': The name you want printed out''',
              'dest': 'name',
              'required': False}),
            ],
        help=' the help for cmd2.')
    def cmd2(self):
        print(self.app.pargs.name)
此应用程序有一个名为cmd2的命令,它接受可选参数
-n
,如帮助所述,该参数将被打印出来。因此,如果我这样做:

with MyApp(argv=['cmd2', '-n', 'bob']) as app:
    app.run()
我将提供以下输出:
bob
正如所料。但是,如果我将无效参数传递给cmd2:

with MyApp(argv=['cmd2', '-a', 'bob']) as app:
    app.run()
我得到:

usage: myapp [-h] [-d] [-q] {cmd1,cmd2} ...
myapp: error: unrecognized arguments: -a bob
我希望看到的不是myapp的用法,而是cmd2命令的用法,类似于我对命令执行
-h

with MyApp(argv=['cmd2', '-h']) as app:
    app.run()
输出

usage: myapp cmd2 [-h] [-n NAME]

optional arguments:
  -h, --help            show this help message and exit
  -n NAME, --name NAME  : The name you want printed out
我意识到这大部分是委托给Argparse的,而不是由水泥处理的。我已经进行了一些调试,我发现嵌套了多个
ArgparseArgumentHandler
类。因此,在上面的例子中,
myapp
有一个
ArgparseArgumentHandler
,它的动作中有一个
subparserAction
,它有一个
choices
字段,该字段有一个包含我在控制器上的两个命令的映射,
cmd1
cmd2
映射到它们自己的
ArgparseArgumentHandler

当检测到无效参数时,它位于
myapp
的ArgparseArgumentHandler中,因此它调用
myapp
上的
print\u usage()
,而不是调用命令的
ArgparseArgumentHandler

我对Argparse的了解有限,我确实觉得导航有点复杂。我现在能想到的唯一解决办法是子类化
ArgparseArgumentHandler
,重写
error()
,并尝试确定错误是否是由可识别的参数引起的,如果是,请尝试为其找到解析器。。类似这样的伪代码:

class ArgparseArgumentOverride(ext_argparse.ArgparseArgumentHandler):

    def error(self, message):
         # determine if there are unknown args
        args, argv = self.parse_known_args(self.original_arguments, self.original_namespace)
        # we are in an error state and have unrecognized args
        if argv:
            controller_namespace = args.__controller_namespace__


            for action in self._actions:
                if action.choices is not None:
                    # we found an choice with our namespace
                    if action.choices[controller_namespace]:
                        command_parser= action.choices[controller_namespace]
                        # this should be the show_usage for the command
                        complete_command.print_usage(sys.stderr)


上面是伪代码,实际上这样做会让人感觉非常脆弱、容易出错和不可预测。我知道一定有更好的办法,我就是找不到。我已经在文档和资料中翻了好几个小时了,仍然没有找到我要找的东西。谁能告诉我我错过了什么?如果您能就如何在这里继续提供任何建议,我们将不胜感激。多谢

我不熟悉
水泥
,但您可以推断
用法
是由
argparse
生成的:

In [235]: parser = argparse.ArgumentParser(prog='myapp')                                                             
In [236]: parser.add_argument('-d');                                                                                 
In [237]: sp = parser.add_subparsers(dest='cmd')                                                                     
In [238]: sp1 = sp.add_parser('cmd1')                                                                                
In [239]: sp2 = sp.add_parser('cmd2')                                                                                
In [240]: sp2.add_argument('-n','--name');                                                                           

In [241]: parser.parse_args('cmd2 -a'.split())                                                                       
usage: myapp [-h] [-d D] {cmd1,cmd2} ...
myapp: error: unrecognized arguments: -a
如果错误与
sp2
参数相关,则用法反映:

In [242]: parser.parse_args('cmd2 -n'.split())                                                                       
usage: myapp cmd2 [-h] [-n NAME]
myapp cmd2: error: argument -n/--name: expected one argument
但是
unknown
参数由主解析器处理。例如,如果我们使用
parse_known_args

In [245]: parser.parse_known_args('cmd2 foobar'.split())                                                             
Out[245]: (Namespace(cmd='cmd2', d=None, name=None), ['foobar'])
In [246]: parser.parse_known_args('cmd2 -a'.split())                                                                 
Out[246]: (Namespace(cmd='cmd2', d=None, name=None), ['-a'])
未知参数作为
extras
列表返回
parse_args
返回错误,而不是
extras

\u SubParsers\u Action.\u调用\u
中,相关代码为:

    # parse all the remaining options into the namespace
    # store any unrecognized options on the object, so that the top
    # level parser can decide what to do with them

    # In case this subparser defines new defaults, we parse them
    # in a new namespace object and then update the original
    # namespace for the relevant parts.
    subnamespace, arg_strings = parser.parse_known_args(arg_strings, None)
    for key, value in vars(subnamespace).items():
        setattr(namespace, key, value)

    if arg_strings:
        vars(namespace).setdefault(_UNRECOGNIZED_ARGS_ATTR, [])
        getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).extend(arg_strings)
理论上,您可以构造一个备用的
\u subparserAction
类(或子类),以不同的方式处理
arg\u字符串。将
parse_known_args
调用更改为
parse_args
可能就足够了:

    subnamespace = parser.parse_args(arg_strings, None)
请注意,
parse_args
调用
parse_known_args
,如果存在未知项,则会引发错误:

def parse_args(self, args=None, namespace=None):
    args, argv = self.parse_known_args(args, namespace)
    if argv:
        msg = _('unrecognized arguments: %s')
        self.error(msg % ' '.join(argv))
    return args