解析非互斥的命令行参数组

时间:2016-01-21 10:50:43

标签: python-2.7 argparse

我试图找到一种解析相关参数序列的方法,最好使用argparse

例如:

command --global-arg --subgroup1 --arg1 --arg2 --subgroup2 --arg1 --arg3 --subgroup3 --arg4 --subcommand1 --arg1 --arg3

其中--global-arg适用于整个命令,但每个--subgroupN参数都有仅适用于它的子参数(可能具有相同的名称,例如--arg1和{{ 1}}以上),并且一些子参数是可选的,因此子参数的数量不是常数。但是,我知道每个--arg3子参数集都是通过另一个--subgroupN的存在或参数列表的结尾来完成的(如果全局参数不能出现在最后,我不会讨论,尽管我想这是可能的,只要它们不与子参数名称发生冲突)。

--subgroupN元素本质上是子命令,但我似乎无法使用--subgroupN的子解析器功能,因为它也会覆盖任何后续的argparse条目(因此带有意外参数的barfs)。

(xmlstarlet使用此样式的参数列表的示例)

除了编写自己的解析器之外还有什么建议吗?我认为如果这是唯一的选择,我至少可以利用argparse的东西...

实施例

以下示例试图找到一种解析参数结构的方法,如下所示:

--subgroupN

在第一个例子中我希望--a和--b引入一组由subparser处理的参数。

我希望得到一些可能与

相符的东西
(a --name <name>|b --name <name>)+

subparser示例失败

Namespace(a=Namespace(name="dummya"), b=Namespace(name="dummyb"))

互斥组失败

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
parser_a = subparsers.add_parser("a")
parser_b = subparsers.add_parser("b")
parser_a.add_argument("--name")
parser_b.add_argument("--name")
parser.parse_args(["a", "--name", "dummy"])
> Namespace(name='dummy') (Good)
parser.parse_args(["b", "--name", "dummyb", "a", "--name", "dummya"])
> error: unrecognized arguments: a (BAD)

(我真的没想到这会起作用,试图看看我是否可以重复分组参数。)

2 个答案:

答案 0 :(得分:1)

除了subparser机制之外,argparse不是为处理参数组而设计的。除了nargs分组之外,它按照它们出现在argv列表中的顺序处理参数。

正如我在评论中提到的那样,早期的问题可能通过搜索multiple这样的问题找到。但是,无论如何,他们都试图解决argparse的基本顺序无关设计。

https://stackoverflow.com/search?q=user%3A901925+[argparse]+multiple

我认为最直接的解决方案是事先处理sys.argv列表,将其分组,然后将这些子列表传递给一个或多个parsers

parse [command --global-arg], 
parse [--subgroup1 --arg1 --arg2], 
parse [--subgroup2 --arg1 --arg3], 
parse [--subgroup3 --arg4], 
parse [--subcommand1 --arg1 --arg3]

实际上唯一的选择是使用该subparser“slurp everything else”行为来获取可以再次解析的其余参数。使用parse_known_args返回未知参数列表(如果该列表不为空,则parse_args会引发错误。)

答案 1 :(得分:0)

使用上面的hpaulj's reply,我想出了以下内容:

args = [
    "--a", "--name", "dummya", 
    "--b", "--name", "dummyb",
    "--a", "--name", "another_a", "--opt"
]
parser_globals = argparse.ArgumentParser()
parser_globals.add_argument("--test")

parser_a = argparse.ArgumentParser()
parser_a.add_argument("--name")
parser_a.add_argument("--opt", action="store_true")

parser_b = argparse.ArgumentParser()
parser_b.add_argument("--name")

command_parsers = {
    "--a": parser_a,
    "--b": parser_b
}

the_namespace = argparse.Namespace()
if globals is not None:
    (the_namespace, rest) = parser_globals.parse_known_args(args)

subcommand_dict = vars(the_namespace)
subcommand = []
val = rest.pop()
while val:
    if val in command_parsers:
        the_args = command_parsers[val].parse_args(subcommand)
        if val in subcommand_dict:
            if "list" is not type(subcommand_dict[val]):
                subcommand_dict[val] = [subcommand_dict[val]]
            subcommand_dict[val].append(the_args)
        else:
            subcommand_dict[val] = the_args
        subcommand = []
    else:
        subcommand.insert(0, val)
    val = None if not rest else rest.pop()

我最终得到了:

Namespace(
    --a=[
        Namespace(
            name='another_a',
            opt=True
        ),
        Namespace(
            name='dummya',
            opt=False
        )
    ],
    --b=Namespace(
        name='dummyb'
    ),
    test=None
)

这似乎符合我的目的。