【发布时间】:2020-07-01 21:44:38
【问题描述】:
我正在使用 Python 3.7 和 Cement 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,而不是由 Cement 处理。我已经进行了一些调试,我看到嵌套了多个 ArgparseArgumentHandler 类。所以在上面的例子中,myapp 有一个ArgparseArgumentHandler,它的动作中有一个SubParsersAction,它有一个choices 字段,其中有一个包含我在控制器上的两个命令的映射,cmd1 和@987654336 @映射到他们自己的ArgparseArgumentHandler。
当检测到无效参数时,它位于 myapp 的 ArgparseArgumentHandler 中,因此它在 myapp 上调用 print_usage(),而不是在 ArgparseArgumentHandler 上调用命令 cmd2。
我对 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)
上面还是伪代码,实际上做这样的事情会感觉非常脆弱、容易出错和不可预测。我知道必须有更好的方法来做到这一点,我只是没有找到它。我已经研究了几个小时的文档和源代码,但仍然没有找到我要找的东西。谁能告诉我我错过了什么?任何关于如何在此处进行的建议将不胜感激。非常感谢!
【问题讨论】:
标签: python python-3.x argparse cement