在 subparser 之后停止解析
Stop parsing after subparser
在我的 CLI 脚本中,我使用 argparse 接受一些可选参数,然后是一个位置参数。位置参数用于确定要使用的子解析器,该子解析器依次运行一个函数,该函数调用一个带有自己参数的外部程序。因此,命令行用法如下所示:
myscript [OPTIONS] subcommand [SUBCOMMAND_OPTIONS]
现在我的问题是我声明的选项与外部程序中声明的 SUBCOMMAND_OPTIONS 之间存在冲突。简单的解决方法是确保我重命名 myscript 中的所有冲突,但我不能对所有选项都这样做——最显着的是用于帮助的“-h”选项。理想情况下,我希望 argparse 在遇到子命令后立即停止解析,并简单地将其余的 args 传递给外部程序。
因此,以下调用应显示 myscript 的帮助文本:
myscript -h
而相比之下,下面应该显示来自 "bar" 子解析器调用的外部程序的帮助文本:
myscript --foo bar -h
更多代码使上面的内容更清楚:
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', action='store_true')
>>> subparsers = parser.add_subparsers()
>>> subparsers.add_parser("bar")
>>> parser.parse_known_args("--foo bar --test".split())
(Namespace(foo=True), ['--test'])
# cool - this is what I want, I'll just pass --test on to the external program
>>> parser.parse_known_args("--foo bar -h".split())
usage: bar [-h]
optional arguments:
-h, --help show this help message and exit
# unfortunately the above argparse help message is NOT what I wanted, instead I was looking for the result below:
(Namespace(foo=True), ['-h'])
>>> parser.parse_known_args("bar --test -- -h".split())
# this works, sort of, it requires educating the end-user to use the '--' parameter and I'd like to avoid that if possible.
(Namespace(foo=False), ['--test', '--', '-h'])
正常 sub-commands support in argparse
does this just fine. Just note that when reusing argument names, you should specify a custom dest
以确保您的主要命令的值不被覆盖:
parser = argparse.ArgumentParser()
parser.add_argument('--foo', action='store_true')
subparsers = parser.add_subparsers()
parser_foo = subparsers.add_parser('foo')
parser_foo.add_argument('--foo', dest='foo_foo')
parser_foo.add_argument('--bar', dest='foo_bar')
parser_bar = subparsers.add_parser('bar')
parser_bar.add_argument('--foo', dest='bar_foo')
示例:
>>> parser.parse_args('-h'.split())
usage: [-h] [--foo] {foo,bar} ...
positional arguments:
{foo,bar}
optional arguments:
-h, --help show this help message and exit
--foo
>>> parser.parse_args('foo -h'.split())
usage: foo [-h] [--foo FOO_FOO] [--bar FOO_BAR]
optional arguments:
-h, --help show this help message and exit
--foo FOO_FOO
--bar FOO_BAR
>>> parser.parse_args('bar -h'.split())
usage: bar [-h] [--foo BAR_FOO]
optional arguments:
-h, --help show this help message and exit
--foo BAR_FOO
>>> parser.parse_args('--foo foo --foo test --bar baz'.split())
Namespace(foo=True, foo_bar='baz', foo_foo='test')
arpgarse
模块具有子解析器功能,您可以将其与 argparse.REMAINDER
结合使用以捕获任何参数,而无需显式声明这些参数。
更新:
如果您想拥有比 argparse
提供更多的控制权,那么查看 click
包可能是值得的。它特别支持忽略未知选项并防止处理 --help
等选项。详情见
http://click.pocoo.org/4/api/#click.Context.ignore_unknown_options
您的初始描述与子解析器非常接近,需要仔细阅读才能确定问题所在(对您而言)。
从评论来看,最大的错误似乎是子解析器捕获了 -h
给您的帮助消息,而不是将其传递给 extras
。子解析器,就像主解析器一样,接受一个 add_help=False
参数。
p=argparse.ArgumentParser()
p.add_argument('foo')
p.add_argument('--bar')
sp=p.add_subparsers(dest='cmd')
sp1=sp.add_parser('cmd1') # with a subparser help
sp2=sp.add_parser('cmd2', add_help=False) # will ignore -h
生产
p.parse_known_args('-h'.split()) # top level help
p.parse_known_args('--bar xxx foo cmd1 -h'.split())
# usage: ipython foo cmd1 [-h]
# exit msg
p.parse_known_args('--bar xxx foo cmd2 -h'.split())
# (Namespace(bar='xxx', cmd='cmd2', foo='foo'), ['-h'])
p.parse_known_args('foo cmd2 test -o --more --bar xxx'.split())
# (Namespace(bar=None, cmd='cmd2', foo='foo'),
# ['test', '-o', '--more', '--bar', 'xxx'])
在评论中我提到了两个 nargs
值,argparse.PARSER
和 argparse.REMAINDER
。对于主解析器,子解析器只是一个带有 PARSER
nargs(和 choices). It's a special
action` 类型的位置,它继续根据第一个值调用另一个解析器。
REMAINDER
类似于 *
nargs,除了它接受所有内容,甚至是看起来像标志的字符串。 PARSER
类似于 +
,至少需要一个字符串。
p=argparse.ArgumentParser()
p.add_argument('foo')
p.add_argument('--bar')
p.add_argument('rest', nargs=argparse.REMAINDER)
生产
In [32]: p.parse_args('--bar yyy foo'.split())
Out[32]: Namespace(bar='yyy', foo='foo', rest=[])
In [33]: p.parse_args('--bar yyy foo -h'.split())
Out[33]: Namespace(bar='yyy', foo='foo', rest=['-h'])
In [34]: p.parse_args('--bar yyy foo cmd2 test -o --more --bar xxx'.split())Out[34]: Namespace(bar='yyy', foo='foo', rest=['cmd2', 'test', '-o', '--more', '--bar', 'xxx'])
argparse 文档中的 REMAINDER
注释是:
argparse.REMAINDER. All the remaining command-line arguments are gathered into a list. This is commonly useful for command line utilities that dispatch to other command line utilities:
并且有一个类似于我上一个的例子。
>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('--foo')
>>> parser.add_argument('command')
>>> parser.add_argument('args', nargs=argparse.REMAINDER)
>>> print(parser.parse_args('--foo B cmd --arg1 XX ZZ'.split()))
Namespace(args=['--arg1', 'XX', 'ZZ'], command='cmd', foo='B')
在我的 CLI 脚本中,我使用 argparse 接受一些可选参数,然后是一个位置参数。位置参数用于确定要使用的子解析器,该子解析器依次运行一个函数,该函数调用一个带有自己参数的外部程序。因此,命令行用法如下所示:
myscript [OPTIONS] subcommand [SUBCOMMAND_OPTIONS]
现在我的问题是我声明的选项与外部程序中声明的 SUBCOMMAND_OPTIONS 之间存在冲突。简单的解决方法是确保我重命名 myscript 中的所有冲突,但我不能对所有选项都这样做——最显着的是用于帮助的“-h”选项。理想情况下,我希望 argparse 在遇到子命令后立即停止解析,并简单地将其余的 args 传递给外部程序。
因此,以下调用应显示 myscript 的帮助文本:
myscript -h
而相比之下,下面应该显示来自 "bar" 子解析器调用的外部程序的帮助文本:
myscript --foo bar -h
更多代码使上面的内容更清楚:
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', action='store_true')
>>> subparsers = parser.add_subparsers()
>>> subparsers.add_parser("bar")
>>> parser.parse_known_args("--foo bar --test".split())
(Namespace(foo=True), ['--test'])
# cool - this is what I want, I'll just pass --test on to the external program
>>> parser.parse_known_args("--foo bar -h".split())
usage: bar [-h]
optional arguments:
-h, --help show this help message and exit
# unfortunately the above argparse help message is NOT what I wanted, instead I was looking for the result below:
(Namespace(foo=True), ['-h'])
>>> parser.parse_known_args("bar --test -- -h".split())
# this works, sort of, it requires educating the end-user to use the '--' parameter and I'd like to avoid that if possible.
(Namespace(foo=False), ['--test', '--', '-h'])
正常 sub-commands support in argparse
does this just fine. Just note that when reusing argument names, you should specify a custom dest
以确保您的主要命令的值不被覆盖:
parser = argparse.ArgumentParser()
parser.add_argument('--foo', action='store_true')
subparsers = parser.add_subparsers()
parser_foo = subparsers.add_parser('foo')
parser_foo.add_argument('--foo', dest='foo_foo')
parser_foo.add_argument('--bar', dest='foo_bar')
parser_bar = subparsers.add_parser('bar')
parser_bar.add_argument('--foo', dest='bar_foo')
示例:
>>> parser.parse_args('-h'.split())
usage: [-h] [--foo] {foo,bar} ...
positional arguments:
{foo,bar}
optional arguments:
-h, --help show this help message and exit
--foo
>>> parser.parse_args('foo -h'.split())
usage: foo [-h] [--foo FOO_FOO] [--bar FOO_BAR]
optional arguments:
-h, --help show this help message and exit
--foo FOO_FOO
--bar FOO_BAR
>>> parser.parse_args('bar -h'.split())
usage: bar [-h] [--foo BAR_FOO]
optional arguments:
-h, --help show this help message and exit
--foo BAR_FOO
>>> parser.parse_args('--foo foo --foo test --bar baz'.split())
Namespace(foo=True, foo_bar='baz', foo_foo='test')
arpgarse
模块具有子解析器功能,您可以将其与 argparse.REMAINDER
结合使用以捕获任何参数,而无需显式声明这些参数。
更新:
如果您想拥有比 argparse
提供更多的控制权,那么查看 click
包可能是值得的。它特别支持忽略未知选项并防止处理 --help
等选项。详情见
http://click.pocoo.org/4/api/#click.Context.ignore_unknown_options
您的初始描述与子解析器非常接近,需要仔细阅读才能确定问题所在(对您而言)。
从评论来看,最大的错误似乎是子解析器捕获了 -h
给您的帮助消息,而不是将其传递给 extras
。子解析器,就像主解析器一样,接受一个 add_help=False
参数。
p=argparse.ArgumentParser()
p.add_argument('foo')
p.add_argument('--bar')
sp=p.add_subparsers(dest='cmd')
sp1=sp.add_parser('cmd1') # with a subparser help
sp2=sp.add_parser('cmd2', add_help=False) # will ignore -h
生产
p.parse_known_args('-h'.split()) # top level help
p.parse_known_args('--bar xxx foo cmd1 -h'.split())
# usage: ipython foo cmd1 [-h]
# exit msg
p.parse_known_args('--bar xxx foo cmd2 -h'.split())
# (Namespace(bar='xxx', cmd='cmd2', foo='foo'), ['-h'])
p.parse_known_args('foo cmd2 test -o --more --bar xxx'.split())
# (Namespace(bar=None, cmd='cmd2', foo='foo'),
# ['test', '-o', '--more', '--bar', 'xxx'])
在评论中我提到了两个 nargs
值,argparse.PARSER
和 argparse.REMAINDER
。对于主解析器,子解析器只是一个带有 PARSER
nargs(和 choices). It's a special
action` 类型的位置,它继续根据第一个值调用另一个解析器。
REMAINDER
类似于 *
nargs,除了它接受所有内容,甚至是看起来像标志的字符串。 PARSER
类似于 +
,至少需要一个字符串。
p=argparse.ArgumentParser()
p.add_argument('foo')
p.add_argument('--bar')
p.add_argument('rest', nargs=argparse.REMAINDER)
生产
In [32]: p.parse_args('--bar yyy foo'.split())
Out[32]: Namespace(bar='yyy', foo='foo', rest=[])
In [33]: p.parse_args('--bar yyy foo -h'.split())
Out[33]: Namespace(bar='yyy', foo='foo', rest=['-h'])
In [34]: p.parse_args('--bar yyy foo cmd2 test -o --more --bar xxx'.split())Out[34]: Namespace(bar='yyy', foo='foo', rest=['cmd2', 'test', '-o', '--more', '--bar', 'xxx'])
argparse 文档中的 REMAINDER
注释是:
argparse.REMAINDER. All the remaining command-line arguments are gathered into a list. This is commonly useful for command line utilities that dispatch to other command line utilities:
并且有一个类似于我上一个的例子。
>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('--foo')
>>> parser.add_argument('command')
>>> parser.add_argument('args', nargs=argparse.REMAINDER)
>>> print(parser.parse_args('--foo B cmd --arg1 XX ZZ'.split()))
Namespace(args=['--arg1', 'XX', 'ZZ'], command='cmd', foo='B')