argparse 可以有条件地解析参数吗?

Can argparse parse arguments conditionally?

我在 Python 2.7 中使用 argparse 来解析命令行参数。是否有一些预定义的条件解析可以处理以下示例?

有没有办法在 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 更改为位置选择 x1x5,当然它们会相互排斥。

一个简单的测试例子:

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()