- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在使用 Python 3.7 和 Cement 3.0.4。
我拥有的是一个带有 Controller 的基本应用程序,并且 Controller 有一个接受单个可选参数的命令。我看到的是,如果我向命令传递一个无效的参数,我会得到一个无效的参数错误,正如我所期望的,但我得到的是应用程序本身的“使用”输出,而不是 Controller 上的命令。这是一个例子:
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()
我将输出:鲍勃正如预期的那样。但是,如果我将无效参数传递给 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
我意识到其中大部分工作都委托(delegate)给了 Argparse,而不是由 Cement 处理。我已经进行了一些调试,发现有多个嵌套的 ArgparseArgumentHandler 类。因此,在上面的例子中,myapp
有一个 ArgparseArgumentHandler
,它的操作中有一个 SubParsersAction
,它有一个 choices
> 字段,其中包含 Controller 上的两个命令的映射,cmd1
和 cmd2
映射到它们自己的 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)
上面还是伪代码,实际上做类似的事情会感觉非常脆弱、容易出错且不可预测。我知道必须有更好的方法来做到这一点,但我只是没有找到。我已经在文档和源代码中挖掘了几个小时,但仍然没有找到我要找的东西。谁能告诉我我错过了什么?任何有关如何继续这里的建议将不胜感激。非常感谢!
最佳答案
我不熟悉cement
,但正如您推断的usage
是由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
参数相关,则用法反射(reflect)了这一点:
In [242]: parser.parse_args('cmd2 -n'.split())
usage: myapp cmd2 [-h] [-n NAME]
myapp cmd2: error: argument -n/--name: expected one argument
但是未知
参数由主解析器处理。例如,如果我们使用 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
。
在_SubParsers_Action.__call__
中,相关代码为:
# 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)
理论上,您可以构造一个替代的 _SubParsersAction
类(或子类),以不同的方式处理 arg_strings
。将 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
关于Python Cement CLI - 当给出的参数无效时打印子解析器的帮助,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60779504/
我正在使用 Python 3.7 和 Cement 3.0.4。 我拥有的是一个带有 Controller 的基本应用程序,并且 Controller 有一个接受单个可选参数的命令。我看到的是,如果我
我正在使用 Python 水泥框架,并尝试创建一个包含多个子命令的应用程序,每个子命令都有自己的参数。 Diving Right In示例展示了如何创建子命令,但这些子命令都没有自己的参数。 Argu
寻找有关如何将 python cement 框架 ( http://builtoncement.com/ ) 与 asyncio ( https://docs.python.org/3.4/libra
我用 Python 开发了一个应用程序,并且正在使用 Cement CLI 库。我将 py.test 与 CementTestCase 一起使用。我可以在测试用例中毫无问题地捕获 stdout 的输出
我是一名优秀的程序员,十分优秀!