简体   繁体   English

python argparse store --foo = bar as args.key ='foo',args.value ='bar'

[英]python argparse store --foo=bar as args.key='foo', args.value='bar'

I'd like to parse a command line that has a mutually exclusive group of options. 我想解析一个具有互斥组选项的命令行。 Normally, I'd just use --foo bar which would produce, in the namespace, args.foo = 'bar' 通常情况下,我只使用--foo bar ,它会在命名空间中产生args.foo = 'bar'

However, since all of these options are mutually exclusive, and I'm interested in both the option name and the argument passed to the option, and I have several of these options that need to be fed downstream, what I'd really like is to get back args.option_name = 'foo', args.option_value = 'bar' in my namespace instead of args.foo='bar' . 但是,由于所有这些选项都是互斥的,而且我对选项名称和传递给选项的参数感兴趣,而且我有几个需要向下游输入的选项,我真正喜欢的是在我的命名空间中取回args.option_name = 'foo', args.option_value = 'bar'而不是args.foo='bar'

What I've done is: 我所做的是:

class KeyAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        setattr(namespace, self.dest, values) 
        setattr(namespace, self.dest+'_key', option_string)

frob = parser.add_mutually_exclusive_group()
frob.add_argument('--foo', dest='thing', nargs='?', action=KeyAction)
frob.add_argument('--nar', dest='thing', nargs='?', action=KeyAction)

when run, my namespace will look like: 运行时,我的命名空间将如下所示:

Namespace(thing_key='--foo', thing='bar')

when --foo=bar is parsed. 何时解析--foo=bar Of course, sadly, if --foo or --nar is never passed in, namespace.thing_key doesn't get defined, so I have to use a getattr() . 当然,遗憾的是,如果从未传入--foo或--nar,则不会定义namespace.thing_key ,因此我必须使用getattr()

The action override, while functional, doesn't seem really right. 动作覆盖虽然功能正常,但似乎并不合适。

I suspect the brilliant minds behind argparse already got this right somehow, and I'm just missing it in the documentation and from my read of argparse.py. 我怀疑argparse背后的聪明才智已经以某种方式正确地做到了这一点,我只是在文档中和我读到的argparse.py中错过了它。

What's the best, right, pythonic, way to do this? 什么是最好的,正确的,pythonic,这样做的方式? Subparsers? Subparsers? I'm using python 3.5. 我正在使用python 3.5。

So I ended up using data from both of your answers to construct this, which handles the option, it's argument, and sets everything sanely at initialization time. 所以我最终使用了来自你的两个答案的数据来构造它,它处理选项,它的参数,并在初始化时设置一切。

Thank you very much for the hints, clues, and validation. 非常感谢您的提示,线索和验证。 I'm surprised this hasn't come up in argparse before and become something standardized. 我很惊讶,之前没有出现过argparse并成为标准化的东西。 It is a corner case, but isn't that much of a corner case when using mutually exclusive options . 一个极端情况,但在使用互斥选项时并不是一个极端情况。

    class ValueAction(argparse.Action):
        """Override to store both the format type as well as the argument"""
        # pylint: disable=too-few-public-methods
        def __init__(self, option_strings, dest, **kwargs):
            self._dest = dest
            dest = dest + '_arguments'
            container = kwargs.pop('container')
            kwargs['action'] = kwargs.pop('subaction')
            action_class = container._pop_action_class(kwargs)
            if not callable(action_class):
                raise ValueError('unknown action "%s"' % (action_class,))
            self._action = action_class(option_strings, dest, **kwargs)
            super().__init__(option_strings, dest, **kwargs)

        def __call__(self, parser, namespace, values, option_string=None):
            self._action(parser, namespace, values, option_string)
            if isinstance(option_string, str):
                while option_string[0] in parser.prefix_chars:
                    option_string = option_string[1:]
            setattr(namespace, self._dest, option_string)

As I understand it, you would like to be able to specify a composite action so that you both save the option name used in one Namespace slot and have the updates for some other action performed. 据我了解,您希望能够指定复合操作,以便您既保存一个名称空间槽中使用的选项名称,又可以执行其他一些操作的更新。 Eg, you want to be able to write something like: 例如,您希望能够编写如下内容:

group = argparse.mutually_exclusive_group()
group.add_argument('--foo', action=StoreOption, subaction='store_true')
group.add_argument('--bar', nargs='?', action=StoreOption, subaction='store')

The action class is StoreOption , but it will call the action specified by subaction to perform additional updates to the Namespace object. 动作类是StoreOption ,但它会调用由指定的操作subaction执行额外的更新Namespace对象。

The code I got working (with very limited testing) is shown below: 我工作的代码(测试非常有限)如下所示:

import argparse

class StoreOption(argparse.Action):
  def __init__(self, **kwargs):
    kwargs['action'] = kwargs.pop('subaction')
    container = kwargs.pop('container')

    action_class = container._pop_action_class(kwargs)
    if not callable(action_class):
      raise ValueError('unknown action "%s"' % (action_class,))

    kwargs['dest'] = 'thing'
    self._action = action_class(**kwargs)
    print "_action:", self._action

    super(StoreOption, self).__init__(
        option_strings= self._action.option_strings,
        dest= self._action.dest,
        nargs= self._action.nargs,
        const= self._action.const,
        default= self._action.default,
        type= self._action.type,
        choices= self._action.choices,
        required= self._action.required,
        help= self._action.help,
        metavar= self._action.metavar)

  def __call__(self, parser, namespace, values, option_string=None):
    print "got here:", option_string, namespace
    setattr(namespace, 'key', option_string)
    self._action(parser, namespace, values, option_string)

A test: 一个测试:

parser = argparse.ArgumentParser(prog='PROG')
group = parser.add_mutually_exclusive_group()
group.add_argument('--foo', container=group, action=StoreOption, subaction='store_true')
group.add_argument('--bar', container=group, nargs=2, action=StoreOption, subaction='store')

print parser.parse_args(['--bar', 'asd', 'qwe'])
-- prints: Namespace(key='--bar', thing=['asd', 'qwe'])

Basically StoreOption is an Action which wraps another Action (the one specified by subaction ). 基本上StoreOption是包装另一个动作(由指定的一个Action subaction )。 When adding arguments you need to pass the container= parameter so that it can construct the sub-action. 添加参数时,您需要传递container=参数,以便它可以构造子操作。 Also, there is some fiddling with the keyword arguments to set them up correctly for the sub-action. 此外,还有一些摆弄关键字参数,以便为子操作正确设置它们。

Again, this has undergone very limited testing, so it may not work for all sub-actions, but it should point you in the right direction. 同样,这已经经历了非常有限的测试,因此它可能不适用于所有子操作,但它应该指向正确的方向。

Subclassing Action is exactly what the developers expect you to do. Subclassing Action正是开发人员期望您做的。 Even the default, most common action, 'store' is a subclass, argparse._StoreAction . 即使是默认的,最常见的动作,'store'也是一个子类argparse._StoreAction argparse._StoreTrueAction is a subclass of argparse._StoreConstAction , differing only in the default and const defaults. argparse._StoreTrueAction是的子类argparse._StoreConstAction ,只是在不同的defaultconst的默认值。

To deal with the missing attribute case you could initialize them in a couple of ways: 要处理缺少的属性情况,您可以通过以下几种方式初始化它们:

set_defaults lets you define any defaults, regardless of whether any arguments use them or not. set_defaults允许您定义任何默认值,无论是否有任何参数使用它们。 In the documentation that is used to add function objects to the namespace when using subparsers. 在使用子分析程序时用于向命名空间添加函数对象的文档中。

parser.set_defaults(thing_key=None, thing=None)

You could also create a namespace with the defaults, and pass that as an argument to parse_args . 您还可以使用默认值创建命名空间,并将其作为参数传递给parse_args

myNamespace = argparse.Namespace(thing_key=None, thing=None)
parser.parse_args(names=myNamespace)

If you don't want a foo=None default in the namespace, define its default as argparse.SUPPRESS . 如果您不想在命名空间中使用foo=None默认值,请将其默认值定义为argparse.SUPPRESS

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM