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

时间:2015-01-22 16:14:30

标签: python argparse

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

  • 如果指定了-x1,则必须指定-x2 -x3,但-x4是可选的。
  • 如果指定了-x5,则必须指定-x4,但-x2是可选的。

如果没有在parser.parse_args()之后写条件,有没有办法做到这一点?

3 个答案:

答案 0 :(得分:3)

是。您可以在最终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)

答案 1 :(得分:1)

有一个Python bug问题,要求“必然包容性”#39;以argparse方式为基础的mutually exclusive groups组。 http://bugs.python.org/issue11588

提出的主要想法是在退出parse_args之前应用像你这样的组合规则。此时,已经看到了一个列表(或集合)的参数。设计全面,合理且直观的用户界面的主要挑战。编写反映条件的使用线也很有挑战性。

但是如果没有那个补丁,我担心你会根据你在args命名空间中找到的值编写自己的测试。如果你正确选择默认值,那就不难了。

另一种可能性是使用subparser。您必须将-x1-x5更改为位置选择,x1x5,当然它们将是互斥的。

https://stackoverflow.com/a/27258765/901925 一个简单的测试示例:

if args.option1 is None and args.option2 is None:
    parser.error('at least one of option1 and option2 is required')

答案 2 :(得分:0)

这是一个完整的例子

像这样调用它:

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