如何使用默认sub-command,或处理使用argparse
没有给出子命令的情况?
import argparse
a = argparse.ArgumentParser()
b = a.add_subparsers()
b.add_parser('hi')
a.parse_args()
这里我想要一个要选择的命令,或者只根据下一个最高级别的解析器(在这种情况下是顶级解析器)处理的参数。
joiner@X:~/src> python3 default_subcommand.py usage: default_subcommand.py [-h] {hi} ... default_subcommand.py: error: too few arguments
答案 0 :(得分:18)
在Python 3.2(和2.7)上,您将得到该错误,但不会在3.3和3.4(无响应)上。因此,在3.3 / 3.4上,您可以测试parsed_args
为空Namespace
。
更通用的解决方案是添加方法set_default_subparser()
(取自ruamel.std.argparse包)并在parse_args()
之前调用该方法:
import argparse
import sys
def set_default_subparser(self, name, args=None, positional_args=0):
"""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 last position before global positional
# arguments, this implies no global options are specified after
# first positional argument
if args is None:
sys.argv.insert(len(sys.argv) - positional_args, name)
else:
args.insert(len(args) - positional_args, name)
argparse.ArgumentParser.set_default_subparser = set_default_subparser
def do_hi():
print('inside hi')
a = argparse.ArgumentParser()
b = a.add_subparsers()
sp = b.add_parser('hi')
sp.set_defaults(func=do_hi)
a.set_default_subparser('hi')
parsed_args = a.parse_args()
if hasattr(parsed_args, 'func'):
parsed_args.func()
这适用于2.6(如果从PyPI安装argparse
),2.7,3.2,3.3,3.4。并允许你做两个
python3 default_subcommand.py
和
python3 default_subcommand.py hi
具有相同的效果。
允许选择新的subparser作为默认值,而不是现有的一个。
代码的第一个版本允许将以前定义的子分析符之一设置为默认子分类符。以下修改允许添加新的默认子分析程序,然后可以在用户未选择子分析程序时使用该子分析程序专门处理该情况(代码中标记的不同行)
def set_default_subparser(self, name, args=None, positional_args=0):
"""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
existing_default = False # check if default parser previously defined
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 sp_name == name: # check existance of default parser
existing_default = True
if not subparser_found:
# If the default subparser is not among the existing ones,
# create a new parser.
# As this is called just before 'parse_args', the default
# parser created here will not pollute the help output.
if not existing_default:
for x in self._subparsers._actions:
if not isinstance(x, argparse._SubParsersAction):
continue
x.add_parser(name)
break # this works OK, but should I check further?
# insert default in last position before global positional
# arguments, this implies no global options are specified after
# first positional argument
if args is None:
sys.argv.insert(len(sys.argv) - positional_args, name)
else:
args.insert(len(args) - positional_args, name)
argparse.ArgumentParser.set_default_subparser = set_default_subparser
a = argparse.ArgumentParser()
b = a.add_subparsers(dest ='cmd')
sp = b.add_parser('hi')
sp2 = b.add_parser('hai')
a.set_default_subparser('hey')
parsed_args = a.parse_args()
print(parsed_args)
“默认”选项仍未显示在帮助中:
python test_parser.py -h
usage: test_parser.py [-h] {hi,hai} ...
positional arguments:
{hi,hai}
optional arguments:
-h, --help show this help message and exit
但是,现在可以区分和单独处理调用提供的子分析符之一,并在没有提供参数时调用默认子分析符:
$ python test_parser.py hi
Namespace(cmd='hi')
$ python test_parser.py
Namespace(cmd='hey')
答案 1 :(得分:7)
我最终自己偶然发现了解决方案。
如果该命令是可选的,那么会使命令成为选项。在我原来的解析器配置中,我有一个package
命令可以采取一系列可能的步骤,或者如果没有给出,它将执行所有步骤。这使得该步骤成为一个选择:
parser = argparse.ArgumentParser()
command_parser = subparsers.add_parser('command')
command_parser.add_argument('--step', choices=['prepare', 'configure', 'compile', 'stage', 'package'])
...other command parsers
parsed_args = parser.parse_args()
if parsed_args.step is None:
do all the steps...
答案 2 :(得分:3)
这是添加perl -wMstrict -MData::Dumper -E 'my %h; my $x=1; $h{$x}->{y}=3; print Dumper \%h;'
$VAR1 = {
'1' => {
'y' => 3
}
};
方法的更好方法:
set_default_subparser
答案 3 :(得分:2)
您正在寻找的是dest
的{{1}}参数:
(警告:适用于Python 3.4,但不适用于2.7 )
add_subparsers
现在您可以使用import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='cmd')
parser_hi = subparsers.add_parser('hi')
parser.parse_args([]) # Namespace(cmd=None)
:
cmd
答案 4 :(得分:0)
你可以添加一个带有默认值的参数,当我认为没有设置时,将使用该参数。
请参阅:http://docs.python.org/dev/library/argparse.html#default
编辑:
抱歉,我读得你的问题有点快。
我认为你不会直接通过argparse做你想做的事。但你可以检查sys.argv的长度,如果它的长度是1(只有脚本名称),那么你可以手动传递默认参数进行解析,做这样的事情:
import argparse
a = argparse.ArgumentParser()
b = a.add_subparsers()
b.add_parser('hi')
if len(sys.argv) == 1:
a.parse_args(['hi'])
else:
a.parse_args()
我认为应该做你想做的事,但我同意开箱即用会很好。
答案 5 :(得分:0)
在python 2.7中,您可以在子类中覆盖错误行为(可惜没有更好的方法来区分错误):
import argparse
class ExceptionArgParser(argparse.ArgumentParser):
def error(self, message):
if "invalid choice" in message:
# throw exception (of your choice) to catch
raise RuntimeError(message)
else:
# restore normal behaviour
super(ExceptionArgParser, self).error(message)
parser = ExceptionArgParser()
subparsers = parser.add_subparsers(title='Modes', dest='mode')
default_parser = subparsers.add_parser('default')
default_parser.add_argument('a', nargs="+")
other_parser = subparsers.add_parser('other')
other_parser.add_argument('b', nargs="+")
try:
args = parser.parse_args()
except RuntimeError:
args = default_parser.parse_args()
# force the mode into namespace
setattr(args, 'mode', 'default')
print args
答案 6 :(得分:0)
您可以在主解析器上复制特定子解析器的默认操作,从而有效地使其成为默认解析器。
import argparse
p = argparse.ArgumentParser()
sp = p.add_subparsers()
a = sp.add_parser('a')
a.set_defaults(func=do_a)
b = sp.add_parser('b')
b.set_defaults(func=do_b)
p.set_defaults(func=do_b)
args = p.parse_args()
if args.func:
args.func()
else:
parser.print_help()
不适用于add_subparsers(required=True)
,这就是if args.func
在此处的原因。
答案 7 :(得分:0)
对于我来说,我发现最简单的做法是在parse_args()
为空时,将子命令名称显式提供给argv
。
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help='commands')
runParser = subparsers.add_parser('run', help='[DEFAULT ACTION]')
altParser = subparsers.add_parser('alt', help='Alternate command')
altParser.add_argument('alt_val', type=str, help='Value required for alt command.')
# Here's my shortcut: If `argv` only contains the script name,
# manually inject our "default" command.
args = parser.parse_args(['run'] if len(sys.argv) == 1 else None)
print args
示例运行:
$ ./test.py
Namespace()
$ ./test.py alt blah
Namespace(alt_val='blah')
$ ./test.py blah
usage: test.py [-h] {run,alt} ...
test.py: error: invalid choice: 'blah' (choose from 'run', 'alt')
答案 8 :(得分:-1)
供以后参考:
...
b = a.add_subparsers(dest='cmd')
b.set_defaults(cmd='hey') # <-- this makes hey as default
b.add_parser('hi')
所以,这两个是相同的: