argparse可选的subparser(for --version)

时间:2011-12-15 14:27:26

标签: python command-line-interface argparse subcommand

我有以下代码(使用Python 2.7):

# shared command line options, like --version or --verbose
parser_shared = argparse.ArgumentParser(add_help=False)
parser_shared.add_argument('--version', action='store_true')

# the main parser, inherits from `parser_shared`
parser = argparse.ArgumentParser(description='main', parents=[parser_shared])

# several subcommands, which can't inherit from the main parser, since
# it would expect subcommands ad infinitum
subparsers = parser.add_subparsers('db', parents=[parser_shared])

...

args = parser.parse_args()

现在我希望能够调用此程序,例如将--version附加到正常程序或某个子命令:

$ prog --version
0.1

$ prog db --version
0.1

基本上,我需要声明可选的subparsers。我知道这不是really supported,但是有没有变通方法或替代方案?

编辑:我收到的错误消息:

$ prog db --version
# works fine

$ prog --version
usage: ....
prog: error: too few arguments

7 个答案:

答案 0 :(得分:16)

根据文档,--version action='version'(而不是action='store_true')会自动打印版本号:

parser.add_argument('--version', action='version', version='%(prog)s 2.0')

答案 1 :(得分:7)

FWIW,我也遇到了这个问题,并且最终通过不使用subparser来“解决”它(我已经有了自己的系统来打印帮助,因此没有丢失任何东西)。

相反,我这样做:

parser.add_argument("command", nargs="?",
                    help="name of command to execute")

args, subcommand_args = parser.parse_known_args()

...然后子命令创建自己的解析器(类似于subparser),它只在subcommand_args上运行。

答案 2 :(得分:4)

这似乎实现了可选子分析器的基本思想。我们解析适用于所有子命令的标准参数。然后,如果剩下任何东西,我们在其余部分调用解析器。主要参数是子命令的父级,因此-h正确显示。如果没有子命令,我计划进入交互式提示。

import argparse

p1 = argparse.ArgumentParser( add_help = False )    
p1.add_argument( ‘–flag1′ )

p2 = argparse.ArgumentParser( parents = [ p1 ] )
s = p2.add_subparsers()
p = s.add_parser( ‘group’ )
p.set_defaults( group=True )

( init_ns, remaining ) = p1.parse_known_args( )

if remaining:
    p2.parse_args( args = remaining, namespace=init_ns )
else:
    print( ‘Enter interactive loop’ )

print( init_ns )

答案 3 :(得分:3)

正如http://bugs.python.org/issue9253(argparse:optional subparsers)中所讨论的,从Python 3.3开始,subparsers现在是可选的。这是parse_args检查所需参数的方式发生变化的意外结果。

我找到了一个可以恢复之前(必需的子分析符)行为的软糖,明确设置了required操作的subparsers属性。

parser = ArgumentParser(prog='test')
subparsers = parser.add_subparsers()
subparsers.required = True   # the fudge
subparsers.dest = 'command'
subparser = subparsers.add_parser("foo", help="run foo")
parser.parse_args()

有关详细信息,请参阅该问题。我希望如果此问题得到正确修补,默认情况下将需要子分析程序,并使用某种选项将其required属性设置为False。但是argparse补丁积压很多。

答案 4 :(得分:2)

是的,我刚检查了svn,它在add_subparsers() documentation中用作对象示例,它只支持主命令中的'--version':

python zacharyyoung$ svn log --version
Subcommand 'log' doesn't accept option '--version'
Type 'svn help log' for usage.

还是:

# create common parser
parent_parser = argparse.ArgumentParser('parent', add_help=False)
parent_parser.add_argument('--version', action='version', version='%(prog)s 2.0')

# create the top-level parser
parser = argparse.ArgumentParser(parents=[parent_parser])
subparsers = parser.add_subparsers()

# create the parser for the "foo" command
parser_foo = subparsers.add_parser('foo', parents=[parent_parser])

哪个收益率:

python zacharyyoung$ ./arg-test.py --version
arg-test.py 2.0
python zacharyyoung$ ./arg-test.py foo --version
arg-test.py foo 2.0

答案 5 :(得分:1)

在我们wait for this feature发送时,我们可以使用以下代码:

# Make sure that main is the default sub-parser
if '-h' not in sys.argv and '--help' not in sys.argv:
    if len(sys.argv) < 2:
        sys.argv.append('main')
    if sys.argv[1] not in ('main', 'test'):
        sys.argv = [sys.argv[0], 'main'] + sys.argv[1:]

答案 6 :(得分:0)

尽管@ eumiro的答案解决了--version选项,但它只能这样做,因为这是optparse的一个特例。允许一般调用:

 prog
 prog --verbose
 prog --verbose main
 prog --verbose db 

并让prog --versionprog --verbose main(以及prog main --verbose)相同,您可以在调用{{{}之前向Argumentparser添加方法并使用默认子分析程序的名称调用该方法1}}:

parse_args()

import argparse import sys def set_default_subparser(self, name, args=None): """default subparser selection. Call after setup, just before parse_args() name: is the name of the subparser to call by default args: if set is the argument list handed to parse_args() , tested with 2.7, 3.2, 3.3, 3.4 it works with 2.6 assuming argparse is installed """ subparser_found = False for arg in sys.argv[1:]: if arg in ['-h', '--help']: # global help if no subparser break else: for x in self._subparsers._actions: if not isinstance(x, argparse._SubParsersAction): continue for sp_name in x._name_parser_map.keys(): if sp_name in sys.argv[1:]: subparser_found = True if not subparser_found: # insert default in first position, this implies no # global options without a sub_parsers specified if args is None: sys.argv.insert(1, name) else: args.insert(0, name) argparse.ArgumentParser.set_default_subparser = set_default_subparser def do_main(args): print 'main verbose', args.verbose def do_db(args): print 'db verbose:', args.verbose parser = argparse.ArgumentParser() parser.add_argument('--verbose', action='store_true') parser.add_argument('--version', action='version', version='%(prog)s 2.0') subparsers = parser.add_subparsers() sp = subparsers.add_parser('main') sp.set_defaults(func=do_main) sp.add_argument('--verbose', action='store_true') sp = subparsers.add_parser('db') sp.set_defaults(func=do_db) parser.set_default_subparser('main') args = parser.parse_args() if hasattr(args, 'func'): args.func(args) 方法是ruamel.std.argparse包的一部分。