我正在使用Python 2.7的argparse包为命令行工具编写一些选项解析逻辑。该工具应接受以下参数之一:
“开”:打开一个功能。
“关”:关闭功能。
[没有提供参数]:回应函数的当前状态。
查看argparse文档让我相信我想要定义两个 - 可能是三个 - 子命令,因为这三个状态是互斥的,代表不同的概念活动。这是我目前对代码的尝试:
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
parser.set_defaults(func=print_state) # I think this line is wrong.
parser_on = subparsers.add_parser('ON')
parser_on.set_defaults(func=set_state, newstate='ON')
parser_off = subparsers.add_parser('OFF')
parser_off.set_defaults(func=set_state, newstate='OFF')
args = parser.parse_args()
if(args.func == set_state):
set_state(args.newstate)
elif(args.func == print_state):
print_state()
else:
args.func() # Catchall in case I add more functions later
我的印象是,如果我提供了0个参数,主解析器将设置func=print_state
,如果我提供了1个参数,主解析器将使用相应的子命令的默认值并调用func=set_state
。相反,我得到0参数的以下错误:
usage: cvsSecure.py [-h] {ON,OFF} ...
cvsSecure.py: error: too few arguments
如果我提供“关闭”或“开启”,则会调用print_state
而不是set_state
。如果我注释掉parser.set_defaults
行,则会正确调用set_state
。
我是一名熟练程度的程序员,但却是Python的初学者。关于如何让这个工作的任何建议?
编辑:我正在研究子命令的另一个原因是我正在考虑将来的第四个功能:
“FORCE txtval”:将函数的状态设置为txtval
。
答案 0 :(得分:12)
顶级解析器的默认值会覆盖子解析器的默认值,因此会忽略在子解析器上设置默认值func
,但会忽略newstate
的值子解析器默认值是正确的。
我认为您不想使用子命令。当可用选项和位置参数根据选择的子命令而更改时,将使用子命令。但是,您没有其他选项或位置参数。
以下代码似乎可以满足您的需求:
import argparse
def print_state():
print "Print state"
def set_state(s):
print "Setting state to " + s
parser = argparse.ArgumentParser()
parser.add_argument('state', choices = ['ON', 'OFF'], nargs='?')
args = parser.parse_args()
if args.state is None:
print_state()
elif args.state in ('ON', 'OFF'):
set_state(args.state)
请注意parser.add_argument
的可选参数。 “choices”参数指定允许的选项,同时将“nargs”设置为“?”指定如果可用则应消耗1个参数,否则不应消耗任何参数。
编辑:如果要添加带参数的FORCE命令并为ON和OFF命令分别提供帮助文本,则需要使用子命令。不幸的是,似乎没有办法指定默认子命令。但是,您可以通过检查空参数列表并提供自己的参数列表来解决此问题。这里有一些示例代码说明了我的意思:
import argparse
import sys
def print_state(ignored):
print "Print state"
def set_state(s):
print "Setting state to " + s
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
on = subparsers.add_parser('ON', help = 'On help here.')
on.set_defaults(func = set_state, newstate = 'ON')
off = subparsers.add_parser('OFF', help = 'Off help here.')
off.set_defaults(func = set_state, newstate = 'OFF')
prt = subparsers.add_parser('PRINT')
prt.set_defaults(func = print_state, newstate = 'N/A')
force = subparsers.add_parser('FORCE' , help = 'Force help here.')
force.add_argument('newstate', choices = [ 'ON', 'OFF' ])
force.set_defaults(func = set_state)
if (len(sys.argv) < 2):
args = parser.parse_args(['PRINT'])
else:
args = parser.parse_args(sys.argv[1:])
args.func(args.newstate)
答案 1 :(得分:0)
您的方法存在两个问题。
首先,您可能已经注意到newstate
不是子解析器的某个sub_value,需要在args
的顶级作为args.newstate
进行处理。这应该说明将默认值分配给newstate
两次将导致第一个值被覆盖。您是否使用&#39; ON&#39;或者&#39; OFF&#39;作为参数,每次使用set_state()
调用OFF
。如果你只想做python cvsSecure ON
和。{
python cvsSecure OFF
以下内容可行:
from __future__ import print_function
import sys
import argparse
def set_state(state):
print("set_state", state)
def do_on(args):
set_state('ON')
def do_off(args):
set_state('OFF')
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
parser_on = subparsers.add_parser('ON')
parser_on.set_defaults(func=do_on)
parser_on.add_argument('--fast', action='store_true')
parser_off = subparsers.add_parser('OFF')
parser_off.set_defaults(func=do_off)
args = parser.parse_args()
args.func(args)
第二个问题是argparse
确实将子分析符作为单值参数处理,因此您必须在调用parser.parse_args()
之前指定一个。您可以通过添加额外的子分析器来自动插入缺少的参数&#39; PRINT&#39;并自动插入
使用set_default_subparser
添加到argparse.ArgumentParser()
(该代码是其中的一部分)
包ruamel.std.argparse
from __future__ import print_function
import sys
import argparse
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 print_state(args):
print("print_state")
def set_state(state):
print("set_state", state)
def do_on(args):
set_state('ON')
def do_off(args):
set_state('OFF')
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
parser_print = subparsers.add_parser('PRINT', help='default action')
parser_print.set_defaults(func=print_state)
parser_on = subparsers.add_parser('ON')
parser_on.set_defaults(func=do_on)
parser_on.add_argument('--fast', action='store_true')
parser_off = subparsers.add_parser('OFF')
parser_off.set_defaults(func=do_off)
parser.set_default_subparser('PRINT')
args = parser.parse_args()
args.func(args)
您不需要在args
到do_on()
等处理,但如果您开始为不同的子分析人员指定选项,它会派上用场。