所有子解析器的可选参数 [英] Optional arguments across all subparsers

查看:63
本文介绍了所有子解析器的可选参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在测试argparse用法,但是无法正常工作.我有几个子解析器和可选参数,被称为以下方式:

I'm currently testing an argparse usage, but it's not working as expected. I have a couple of subparsers and optional arguments, being called the following way:

python3 myprogram.py positional argument --optional something

# Outcome
Namespace(optional='something')

如果可选的是最后一个,则程序将按预期方式运行,但如果以其他顺序运行,则会丢弃.

The program works as expected if the optional is the last, but if it is in any other order, it is discarded.

python3 myprogram.py positional --optional argument
python3 myprogram.py --optional positional argument

# Outcome
Namespace(optional=None)

通过查看argparse文档,我无法找到一种方法来使可选参数 global .

By looking at the argparse documentation I wasn't able to find a way to make the optional argument global.

我正在for循环中为每个位置创建位置参数,这似乎不是最好的方法.因为否则,它将只会将可选参数添加到最后一个子解析器.

I'm creating the the positional arguments for each positional in a for loop, which doesn't seem to be the best way. Because otherwise, it would add the optional arguments only to the last subparser.

import argparse

class Parsing(object):

    def __init__(self):

        parser = argparse.ArgumentParser(prog='python3 myprogram.py',
            formatter_class=argparse.RawDescriptionHelpFormatter,
            description='some description')

        self.subparser = parser.add_subparsers(title='Positional', help='help description')

        for sub in self.Generate(): # Method with a bunch of subparsers
            self.Subparser(sub)

    def Subparser(self, parsers):

        for each in sorted(parsers):
            positional = subparser.add_parser(each)
            self.Optional(positional) # Method with some optional arguments for each of the second subparsers

        self.Optional(parser) # Adding the optional arguments to the first subparser

    def Optional(self, parser):

        # ... Optional arguments

    def Generate(self):

        # ... Subparsers

在上面的示例中,我可能会丢失一些代码,试图简化我所能做的最好的事情,并希望它可以被理解.

I might be missing some code in the example above, tried to simplify the best I could and hope it to be perceptible.

问题:是否可以在所有子解析器中进行可选参数?

Question: Is there a way to make the optional arguments across all subparsers?

推荐答案

您的描述和代码难以理解,但是我得出的结论是,您的问题在于当主解析器和子解析器共享一个参数时如何处理默认值.

Your description and code is hard to follow, but I've concluded that your problem lies with how defaults are handled when the main and subparsers share an argument dest.

我稍微压缩了您的代码,以便可以进行测试运行:

I condensed your code a bit so I could make a test run:

import argparse
class Parsing(object):
    def __init__(self):
        self.parser = argparse.ArgumentParser(prog='prog',
            description='some description')
        self.subparser = self.parser.add_subparsers(dest='cmd', title='Cmds', help='help description')
        self.make_subparsers(['cmd1','cmd2'])

    def make_subparsers(self, parsers):
        for each in parsers:
            subp = self.subparser.add_parser(each)
            self.optional(subp, default='sub') 
        self.optional(self.parser, default='main') 

    def optional(self, parser, default=None):
        parser.add_argument('--foo', default=default)

args = Parsing().parser.parse_args()
print(args)

我跑了2次

1315:~/mypy$ python3.5 stack41431025.py cmd1 --foo 1
Namespace(cmd='cmd1', foo='1')

1316:~/mypy$ python3.5 stack41431025.py --foo 1 cmd1
Namespace(cmd='cmd1', foo='sub')

首先,foocmd1子解析器解析的字符串设置.

In the first, foo is set by the strings parsed by the cmd1 subparser.

在第二个中,foo获取由子解析器设置的默认值.主解析器解析--foo,但其值已被子解析器覆盖.

In the second, foo gets the default value set by the subparser. The main parser parsed --foo, but its value was over written by the subparser.

在bug/问题中对此进行了一些讨论. http://bugs.python.org/issue9351 更改了处理方式,以便子解析器的默认设置优先于main解析器值.我认为该补丁存在问题,但已经生效了两年.

There has been some discussion of this in bug/issues. http://bugs.python.org/issue9351 changed handling so that the subparser default has priority over main parser values. I think there are problems with that patch, but it's been in effect for a couple of years.

如果给他们不同的dest,则可以保留更多控制权.

You retain more control if they are given different dest.

def make_subparsers(self, parsers):
    for each in parsers:
        subp = self.subparser.add_parser(each)
        self.optional(subp, default='sub') 
    self.optional(self.parser, default='main', dest='main_foo') 

def optional(self, parser, default=None, dest=None):
    parser.add_argument('--foo', default=default, dest=dest)

1325:~/mypy$ python3.5 stack41431025.py --foo 1 cmd1
Namespace(cmd='cmd1', foo='sub', main_foo='1')
1325:~/mypy$ python3.5 stack41431025.py cmd1
Namespace(cmd='cmd1', foo='sub', main_foo='main')
1325:~/mypy$ python3.5 stack41431025.py --foo 1 cmd1 --foo 2
Namespace(cmd='cmd1', foo='2', main_foo='1')

===================

====================

(较早的答案)

我将尝试绘制参数的可能组合

I'll try to sketch the possible combinations of arguments

parser = argparse.ArgumentParser()
parser.add_argument('mainpos', help='positional for main')
parser.add_argument('--mainopt', help='optional defined for main')
sp = parser.add_subparser(dest='cmd')
p1 = sp.add_parser('cmd1')
p1.add_argument('subpos', help='postional for sub')
p1.add_argument('--subopt', help='optional defined for sub')

合成的usage如下所示:

python prog.py foo [--mainopt bar] cmd1 sfoo [--subopt baz]

相应的positionals必须以正确的顺序给出.子解析器cmd实际上是main的位置.

The respective positionals have to be given in the right order. The subparser cmd is effectively a positional for the main.

为main定义的可选选项必须位于子解析器名称之前.为子解析器定义的可选定义必须在此之后发生.它们可以具有相同的flagdest,但是必须分别定义.而且,如果它们具有相同的dest,则值之间可能会发生冲突,尤其是默认值.

The optional defined for main has to occur before the subparser name. The optional defined for the subparser has to occur after. They could have the same flag or dest, but they have to be defined separately. And if they have the same dest, there could be a conflict over values, especially defaults.

parser.parse_args()开始将输入字符串与其参数匹配.如果看到--mainopt,则解析该可选参数.否则,它需要两个位置.第二个必须是子解析器名称之一.

parser.parse_args() starts matching the input strings with its arguments. If it sees --mainopt is parses that optional argument. Otherwise it expects two postionals. The 2nd has to be one of the subparser names.

一旦获得子解析器名称,它将其余字符串传递给该子解析器.子解析器处理其余部分,并将值放在主名称空间中.子解析器要做的第一件事就是设置其默认值.该操作是否覆盖主解析器设置的值,取决于namespace在两者之间传递的方式.

Once it gets a subparser name it passes the remaining strings to that subparser. The subparser handles the rest, and puts the values in the main namespace. And the first thing the subparser does is set its defaults. Whether that action overwrites values set by the main parser or not depends on just how the namespace is passed between the two.

================

================

通过命令行中参数的顺序来驱动解析.它尝试允许以任何顺序标记的参数.但是一旦将解析传递给子解析器,主解析器就不会再进行解析了.它只是执行一些清理任务.

Parsing is driven by the order of arguments in the command line. It tries to allow flagged arguments in any order. But once parsing is passed to the subparser, the main parser does not get another go at parsing. It just does a few clean up tasks.

但是,如果我使用parse_known_args,则可以收集解析器都未处理的字符串,然后再进行另一次解析.

But if I use parse_known_args, I can collect the strings that neither parser handled, and take another stab a parsing them.

parser1 = argparse.ArgumentParser()
parser1.add_argument('--foo')
sp = parser1.add_subparsers(dest='cmd')
sp1 = sp.add_parser('cmd1')
args, extra = parser1.parse_known_args()

parser2 = argparse.ArgumentParser()
parser2.add_argument('--foo')
if extra:
    args = parser2.parse_args(extra)
print(args)

运行

1815:~/mypy$ python stack41431025.py --foo 1 cmd1
Namespace(cmd='cmd1', foo='1')

1815:~/mypy$ python stack41431025.py cmd1 --foo 2
Namespace(foo='2')

1815:~/mypy$ python stack41431025.py --foo 1 cmd1 --foo 3
Namespace(foo='3')

我还没有以更复杂的方式测试过这个想法,所以可能有一些我没有想到的交互.但这是我可以最接近的一个带标记的参数,该参数可以在任何地方出现,并且不会遇到冲突的default问题.

I haven't tested this idea in anything more complex, so there might be some interactions that I haven't thought of. But this is the closest I can come to a flagged argument that can occur anywhere, and not be subject to conflicting default problems.

这篇关于所有子解析器的可选参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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