argparse 可以有条件地解析参数吗?
Can argparse parse arguments conditionally?
我在 Python 2.7 中使用 argparse
来解析命令行参数。是否有一些预定义的条件解析可以处理以下示例?
- 如果指定
-x1
,则必须指定-x2 -x3
,但-x4
是可选的。
- 如果指定
-x5
,则必须指定-x4
,但-x2
是可选的。
有没有办法在 parser.parse_args()
之后不写条件?
是的。你可以写条件 before 最后的 parser.parse_args(...)
然后重新解析:
# ... args = parser.parse_args(arguments)
try:
if args.x1:
# add more conditions
parser.add_argument("-x2", ... )
except NameError:
# x1 not specified
pass
# add the rest
# re-parse arguments
# ... args = parser.parse_args(arguments)
有一个 Python 错误问题要求 'necessarily inclusive' 组到 argparse
,仿照 mutually exclusive groups
方法。 http://bugs.python.org/issue11588
那里提出的主要想法是在退出 parse_args
之前应用像您这样的组合规则。在这一点上,有一个已经看到的参数列表(或集合)。设计全面、合乎逻辑且直观的用户界面的主要挑战。编写反映条件的用法行也具有挑战性。
但是如果没有那个补丁,恐怕您将无法根据在 args 命名空间中找到的值编写自己的测试。如果您正确选择默认值,这应该不难。
另一种可能性是使用子解析器。您必须将 -x1
、-x5
更改为位置选择 x1
和 x5
,当然它们会相互排斥。
一个简单的测试例子:
if args.option1 is None and args.option2 is None:
parser.error('at least one of option1 and option2 is required')
这是一个完整的例子
像这样调用它:
./foo.py --first opt2 --second bar3 # bad
./foo.py --first opt2 --second bar1 # ok
./foo.py --first opt1 --second foo1 # ok
hth
#!/usr/bin/env python3
import argparse
FIRST_CHOICES = [ 'opt1', 'opt2' ]
FIRST_CHOICE_DEFAULT = 'opt1'
SECOND_CHOICES = [ 'foo1', 'foo2' ]
SECOND_CHOICE_DEFAULT = 'foo1'
SECOND_ALTERNATIVE_CHOICES = [ 'bar1', 'bar2' ]
SECOND_ALTERNATIVE_CHOICE_DEFAULT = 'bar1'
class ArgsActionFirstChoicesStrings():
def __init__(self):
self.choices = FIRST_CHOICES
def tostring(self):
return ', '.join([repr(action) for action in self.choices])
class ArgsActionFirst(argparse.Action):
def __init__(self, option_strings, dest, nargs=None, **kwargs):
if nargs is not None:
raise ValueError("nargs not allowed")
self.args = ArgsActionFirstChoicesStrings()
super(ArgsActionFirst, self).__init__(option_strings, dest, **kwargs)
def __call__(self, parser, namespace, value, option_string=None):
if value:
if value not in self.args.choices:
message = ("invalid choice: {0!r} (choose from {1})"
.format(value, self.args.tostring()))
raise argparse.ArgumentError(self, message)
setattr(namespace, self.dest, value)
else:
setattr(namespace, self.dest, FIRST_CHOICE_DEFAULT)
class ArgsActionSecondChoicesStrings():
def __init__(self):
self.choices = SECOND_CHOICES
def tostring(self):
return ', '.join([repr(action) for action in self.choices])
class ArgsActionSecond(argparse.Action):
def __init__(self, option_strings, dest, nargs=None, **kwargs):
if nargs is not None:
raise ValueError("nargs not allowed")
self.args = ArgsActionSecondChoicesStrings()
super(ArgsActionSecond, self).__init__(option_strings, dest, **kwargs)
def __call__(self, parser, namespace, value, option_string=None):
if value:
if value not in self.args.choices:
message = ("invalid choice: {0!r} (choose from {1})"
.format(value, self.args.tostring()))
raise argparse.ArgumentError(self, message)
setattr(namespace, self.dest, value)
else:
setattr(namespace, self.dest, FIRST_CHOICE_DEFAULT)
class ArgsActionSecondAlternativeChoicesStrings():
def __init__(self):
self.choices = SECOND_ALTERNATIVE_CHOICES
def tostring(self):
return ', '.join([repr(action) for action in self.choices])
class ArgsActionSecondAlternative(argparse.Action):
def __init__(self, option_strings, dest, nargs=None, **kwargs):
if nargs is not None:
raise ValueError("nargs not allowed")
self.args = ArgsActionSecondAlternativeChoicesStrings()
super(ArgsActionSecondAlternative, self).__init__(option_strings, dest, **kwargs)
def __call__(self, parser, namespace, value, option_string=None):
if value:
if value not in self.args.choices:
message = ("invalid choice: {0!r} (choose from {1})"
.format(value, self.args.tostring()))
raise argparse.ArgumentError(self, message)
setattr(namespace, self.dest, value)
else:
setattr(namespace, self.dest, SECOND_ALTERNATIVE_CHOICE_DEFAULT)
def test_common_parse_arguments():
parser = argparse.ArgumentParser("test")
parser.add_argument('--first',
action=ArgsActionFirst,
default = FIRST_CHOICE_DEFAULT,
metavar='ACTION',
help="Operating system, choose from: " +
ArgsActionFirstChoicesStrings().tostring())
args, unknown = parser.parse_known_args()
try:
if args.first == "opt1":
parser.add_argument('--second',
action=ArgsActionSecond,
default = SECOND_CHOICE_DEFAULT,
metavar='ACTION',
help="SECOND choose from: " +
ArgsActionSecondChoicesStrings().tostring())
parser.parse_args()
elif args.first == "opt2":
parser.add_argument('--second',
action=ArgsActionSecondAlternative,
default = SECOND_ALTERNATIVE_CHOICE_DEFAULT,
metavar='ACTION',
help="SECOND choose from: " +
ArgsActionSecondAlternativeChoicesStrings().tostring())
else:
raise ValueError("unknown os, choices are: " +
ArgsActionFirstChoicesStrings().tostring())
except NameError:
pass
args = parser.parse_args()
print("first option is {}".format(args.first))
print("second option is {}".format(args.second))
return args
if __name__ == "__main__":
test_common_parse_arguments()
我在 Python 2.7 中使用 argparse
来解析命令行参数。是否有一些预定义的条件解析可以处理以下示例?
- 如果指定
-x1
,则必须指定-x2 -x3
,但-x4
是可选的。 - 如果指定
-x5
,则必须指定-x4
,但-x2
是可选的。
有没有办法在 parser.parse_args()
之后不写条件?
是的。你可以写条件 before 最后的 parser.parse_args(...)
然后重新解析:
# ... args = parser.parse_args(arguments)
try:
if args.x1:
# add more conditions
parser.add_argument("-x2", ... )
except NameError:
# x1 not specified
pass
# add the rest
# re-parse arguments
# ... args = parser.parse_args(arguments)
有一个 Python 错误问题要求 'necessarily inclusive' 组到 argparse
,仿照 mutually exclusive groups
方法。 http://bugs.python.org/issue11588
那里提出的主要想法是在退出 parse_args
之前应用像您这样的组合规则。在这一点上,有一个已经看到的参数列表(或集合)。设计全面、合乎逻辑且直观的用户界面的主要挑战。编写反映条件的用法行也具有挑战性。
但是如果没有那个补丁,恐怕您将无法根据在 args 命名空间中找到的值编写自己的测试。如果您正确选择默认值,这应该不难。
另一种可能性是使用子解析器。您必须将 -x1
、-x5
更改为位置选择 x1
和 x5
,当然它们会相互排斥。
一个简单的测试例子:
if args.option1 is None and args.option2 is None:
parser.error('at least one of option1 and option2 is required')
这是一个完整的例子
像这样调用它:
./foo.py --first opt2 --second bar3 # bad ./foo.py --first opt2 --second bar1 # ok ./foo.py --first opt1 --second foo1 # ok
hth
#!/usr/bin/env python3
import argparse
FIRST_CHOICES = [ 'opt1', 'opt2' ]
FIRST_CHOICE_DEFAULT = 'opt1'
SECOND_CHOICES = [ 'foo1', 'foo2' ]
SECOND_CHOICE_DEFAULT = 'foo1'
SECOND_ALTERNATIVE_CHOICES = [ 'bar1', 'bar2' ]
SECOND_ALTERNATIVE_CHOICE_DEFAULT = 'bar1'
class ArgsActionFirstChoicesStrings():
def __init__(self):
self.choices = FIRST_CHOICES
def tostring(self):
return ', '.join([repr(action) for action in self.choices])
class ArgsActionFirst(argparse.Action):
def __init__(self, option_strings, dest, nargs=None, **kwargs):
if nargs is not None:
raise ValueError("nargs not allowed")
self.args = ArgsActionFirstChoicesStrings()
super(ArgsActionFirst, self).__init__(option_strings, dest, **kwargs)
def __call__(self, parser, namespace, value, option_string=None):
if value:
if value not in self.args.choices:
message = ("invalid choice: {0!r} (choose from {1})"
.format(value, self.args.tostring()))
raise argparse.ArgumentError(self, message)
setattr(namespace, self.dest, value)
else:
setattr(namespace, self.dest, FIRST_CHOICE_DEFAULT)
class ArgsActionSecondChoicesStrings():
def __init__(self):
self.choices = SECOND_CHOICES
def tostring(self):
return ', '.join([repr(action) for action in self.choices])
class ArgsActionSecond(argparse.Action):
def __init__(self, option_strings, dest, nargs=None, **kwargs):
if nargs is not None:
raise ValueError("nargs not allowed")
self.args = ArgsActionSecondChoicesStrings()
super(ArgsActionSecond, self).__init__(option_strings, dest, **kwargs)
def __call__(self, parser, namespace, value, option_string=None):
if value:
if value not in self.args.choices:
message = ("invalid choice: {0!r} (choose from {1})"
.format(value, self.args.tostring()))
raise argparse.ArgumentError(self, message)
setattr(namespace, self.dest, value)
else:
setattr(namespace, self.dest, FIRST_CHOICE_DEFAULT)
class ArgsActionSecondAlternativeChoicesStrings():
def __init__(self):
self.choices = SECOND_ALTERNATIVE_CHOICES
def tostring(self):
return ', '.join([repr(action) for action in self.choices])
class ArgsActionSecondAlternative(argparse.Action):
def __init__(self, option_strings, dest, nargs=None, **kwargs):
if nargs is not None:
raise ValueError("nargs not allowed")
self.args = ArgsActionSecondAlternativeChoicesStrings()
super(ArgsActionSecondAlternative, self).__init__(option_strings, dest, **kwargs)
def __call__(self, parser, namespace, value, option_string=None):
if value:
if value not in self.args.choices:
message = ("invalid choice: {0!r} (choose from {1})"
.format(value, self.args.tostring()))
raise argparse.ArgumentError(self, message)
setattr(namespace, self.dest, value)
else:
setattr(namespace, self.dest, SECOND_ALTERNATIVE_CHOICE_DEFAULT)
def test_common_parse_arguments():
parser = argparse.ArgumentParser("test")
parser.add_argument('--first',
action=ArgsActionFirst,
default = FIRST_CHOICE_DEFAULT,
metavar='ACTION',
help="Operating system, choose from: " +
ArgsActionFirstChoicesStrings().tostring())
args, unknown = parser.parse_known_args()
try:
if args.first == "opt1":
parser.add_argument('--second',
action=ArgsActionSecond,
default = SECOND_CHOICE_DEFAULT,
metavar='ACTION',
help="SECOND choose from: " +
ArgsActionSecondChoicesStrings().tostring())
parser.parse_args()
elif args.first == "opt2":
parser.add_argument('--second',
action=ArgsActionSecondAlternative,
default = SECOND_ALTERNATIVE_CHOICE_DEFAULT,
metavar='ACTION',
help="SECOND choose from: " +
ArgsActionSecondAlternativeChoicesStrings().tostring())
else:
raise ValueError("unknown os, choices are: " +
ArgsActionFirstChoicesStrings().tostring())
except NameError:
pass
args = parser.parse_args()
print("first option is {}".format(args.first))
print("second option is {}".format(args.second))
return args
if __name__ == "__main__":
test_common_parse_arguments()