具有嵌套命名空间的 argparse 子命令 [英] argparse subcommands with nested namespaces

查看:27
本文介绍了具有嵌套命名空间的 argparse 子命令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

argparse 是否提供内置工具来解析组或解析器进入他们自己的命名空间?我觉得我一定在某处错过了一个选项.

编辑:这个例子可能不是我应该做的来构建解析器以满足我的目标,但它是我迄今为止所做的.我的具体目标是能够为子解析器提供解析为命名空间字段的选项组.我和父母的想法只是为了同样的目的使用通用选项.

示例:

import argparse# 主解析器main_parser = argparse.ArgumentParser()main_parser.add_argument("-common")#过滤器解析器filter_parser = argparse.ArgumentParser(add_help=False)filter_parser.add_argument("-filter1")filter_parser.add_argument("-filter2")# 子命令subparsers = main_parser.add_subparsers(help='子命令帮助')parser_a = subparsers.add_parser('command_a', help="command_a help", parents=[filter_parser])parser_a.add_argument("-foo")parser_a.add_argument("-bar")parser_b = subparsers.add_parser('command_b', help="command_b help", parents=[filter_parser])parser_b.add_argument("-biz")parser_b.add_argument("-baz")# 解析命名空间 = main_parser.parse_args()打印命名空间

这显然是我得到的:

$ python test.py command_a -foo bar -filter1 val命名空间(bar=None,common=None,filter1='val',filter2=None,foo='bar')

但这正是我真正追求的:

Namespace(bar=None, common=None, foo='bar',filter=Namespace(filter1='val', filter2=None))

然后更多的选项组已经解析为命名空间:

命名空间(common=None,foo='bar', bar=None,filter=Namespace(filter1='val', filter2=None),anotherGroup=Namespace(bazers='val'),anotherGroup2=命名空间(fooers='val'),)

我发现了一个 相关问题在这里,但它涉及一些自定义解析,似乎只涵盖了一个非常特定的情况.

是否有选项可以告诉 argparse 将某些组解析为命名空间字段?

解决方案

如果重点只是将选定的参数放在它们自己的命名空间中,并且子解析器(和父解析器)的使用是附带的问题,此自定义操作可能会奏效.

class GroupedAction(argparse.Action):def __call__(self, parser, namespace, values, option_string=None):group,dest = self.dest.split('.',2)groupspace = getattr(namespace, group, argparse.Namespace())setattr(组空间,目标,值)setattr(命名空间,组,组空间)

有多种指定group 名称的方法.它可以在定义 Action 时作为参数传递.它可以作为参数添加.这里我选择从dest中解析出来(这样namespace.filter.filter1就可以得到filter.filter1的值.

# 主解析器main_parser = argparse.ArgumentParser()main_parser.add_argument("-common")filter_parser = argparse.ArgumentParser(add_help=False)filter_parser.add_argument("--filter1", action=GroupedAction, dest='filter.filter1', default=argparse.SUPPRESS)filter_parser.add_argument("--filter2", action=GroupedAction, dest='filter.filter2', default=argparse.SUPPRESS)subparsers = main_parser.add_subparsers(help='子命令帮助')parser_a = subparsers.add_parser('command_a', help="command_a help", parents=[filter_parser])parser_a.add_argument("--foo")parser_a.add_argument("--bar")parser_a.add_argument("--bazers", action=GroupedAction, dest='anotherGroup.bazers', default=argparse.SUPPRESS)...命名空间 = main_parser.parse_args()打印命名空间

我必须添加 default=argparse.SUPPRESS 所以 bazers=None 条目不会出现在主命名空间中.

结果:

<预><代码>>>>python PROG command_a --foo bar --filter1 val --bazers val命名空间(anotherGroup=命名空间(bazers='val'),条=无,普通=无,过滤器=命名空间(过滤器1='val'),foo='bar')

如果您需要嵌套命名空间中的默认条目,您可以事先定义命名空间:

filter_namespace = argparse.Namespace(filter1=None, filter2=None)命名空间 = argparse.Namespace(filter=filter_namespace)namespace = main_parser.parse_args(namespace=namespace)

结果和之前一样,除了:

filter=Namespace(filter1='val', filter2=None)

Does argparse provide built-in facilities for having it parse groups or parsers into their own namespaces? I feel like I must be missing an option somewhere.

Edit: This example is probably not exactly what I should be doing to structure the parser to meet my goal, but it was what I worked out so far. My specific goal is to be able to give subparsers groups of options that are parsed into namespace fields. The idea I had with parent was simply to use common options for this same purpose.

Example:

import argparse

# Main parser
main_parser = argparse.ArgumentParser()
main_parser.add_argument("-common")

# filter parser
filter_parser = argparse.ArgumentParser(add_help=False)
filter_parser.add_argument("-filter1")
filter_parser.add_argument("-filter2")

# sub commands
subparsers = main_parser.add_subparsers(help='sub-command help')

parser_a = subparsers.add_parser('command_a', help="command_a help", parents=[filter_parser])
parser_a.add_argument("-foo")
parser_a.add_argument("-bar")

parser_b = subparsers.add_parser('command_b', help="command_b help", parents=[filter_parser])
parser_b.add_argument("-biz")
parser_b.add_argument("-baz")

# parse
namespace = main_parser.parse_args()
print namespace

This is what I get, obviously:

$ python test.py command_a -foo bar -filter1 val
Namespace(bar=None, common=None, filter1='val', filter2=None, foo='bar')

But this is what I am really after:

Namespace(bar=None, common=None, foo='bar', 
          filter=Namespace(filter1='val', filter2=None))

And then even more groups of options already parsed into namespaces:

Namespace(common=None, 
          foo='bar', bar=None,  
          filter=Namespace(filter1='val', filter2=None),
          anotherGroup=Namespace(bazers='val'),
          anotherGroup2=Namespace(fooers='val'),
          )

I've found a related question here but it involves some custom parsing and seems to only covers a really specific circumstance.

Is there an option somewhere to tell argparse to parse certain groups into namespaced fields?

解决方案

If the focus is on just putting selected arguments in their own namespace, and the use of subparsers (and parents) is incidental to the issue, this custom action might do the trick.

class GroupedAction(argparse.Action):    
    def __call__(self, parser, namespace, values, option_string=None):
        group,dest = self.dest.split('.',2)
        groupspace = getattr(namespace, group, argparse.Namespace())
        setattr(groupspace, dest, values)
        setattr(namespace, group, groupspace)

There are various ways of specifying the group name. It could be passed as an argument when defining the Action. It could be added as parameter. Here I chose to parse it from the dest (so namespace.filter.filter1 can get the value of filter.filter1.

# Main parser
main_parser = argparse.ArgumentParser()
main_parser.add_argument("-common")

filter_parser = argparse.ArgumentParser(add_help=False)
filter_parser.add_argument("--filter1", action=GroupedAction, dest='filter.filter1', default=argparse.SUPPRESS)
filter_parser.add_argument("--filter2", action=GroupedAction, dest='filter.filter2', default=argparse.SUPPRESS)

subparsers = main_parser.add_subparsers(help='sub-command help')

parser_a = subparsers.add_parser('command_a', help="command_a help", parents=[filter_parser])
parser_a.add_argument("--foo")
parser_a.add_argument("--bar")
parser_a.add_argument("--bazers", action=GroupedAction, dest='anotherGroup.bazers', default=argparse.SUPPRESS)
...
namespace = main_parser.parse_args()
print namespace

I had to add default=argparse.SUPPRESS so a bazers=None entry does not appear in the main namespace.

Result:

>>> python PROG command_a --foo bar --filter1 val --bazers val
Namespace(anotherGroup=Namespace(bazers='val'), 
    bar=None, common=None, 
    filter=Namespace(filter1='val'), 
    foo='bar')

If you need default entries in the nested namespaces, you could define the namespace before hand:

filter_namespace = argparse.Namespace(filter1=None, filter2=None)
namespace = argparse.Namespace(filter=filter_namespace)
namespace = main_parser.parse_args(namespace=namespace)

result as before, except for:

filter=Namespace(filter1='val', filter2=None)

这篇关于具有嵌套命名空间的 argparse 子命令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆