2017-01-02 36 views
1

我目前正在测试​​的用法,但未按预期工作。我有一对夫妇subparsers和可选参数的,被称为以下方式:所有子分析器的可选参数

python3 myprogram.py positional argument --optional something 

# Outcome 
Namespace(optional='something') 

该项目按预期工作如果可选的是最后一个,但如果是在任何其他命令,它是丢弃

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

# Outcome 
Namespace(optional=None) 

通过查看​​文档我没能找到一种方法,使可选参数全球

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

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 

我可能会丢失在上面的例子中的一些代码,试图简化尽我所能,希望它是察觉。

问题:有没有办法让所有的子分析器的可选参数?

回答

2

您的描述和代码很难遵循,但我已经断定您的问题在于主和子分析器共享参数dest时如何处理默认值。

我凝聚你的代码了一点,所以我可以做一个测试运行:

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') 

在第一,foo是通过解析字符串设定cmd1 subparser。

第二,foo获取由subparser设置的默认值。主分析器解析为--foo,但其值已由分析器重写。

有一些关于bug /问题的讨论。 http://bugs.python.org/issue9351更改了处理方式,以便子分析器默认优先于主分析器值。我认为这个补丁存在问题,但它已经生效了几年。

如果给予不同的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') 

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

(刚才的答复)

我会尽力勾勒出参数的可能组合

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的位置。

为main定义的可选项必须发生在子分析器名称之前。为子分析器定义的可选项必须发生在之后。他们可以有相同的flagdest,但他们必须单独定义。如果它们具有相同的dest,则可能会发生数值冲突,尤其是默认值。

parser.parse_args()开始将输入字符串与其参数进行匹配。如果它看到--mainopt正在解析该可选参数。否则,它期望两个postionals。第二个必须是子分析器名称之一。

一旦它得到一个子分析器名称,它会将剩余的字符串传递给该子分析器。子分析器处理其余部分,并将这些值放在主名称空间中。而且分析器的第一件事是设置其默认值。该动作是否覆盖主分析器设置的值取决于两者之间如何传递namespace

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

解析是通过在命令行参数的顺序驱动。它试图允许以任何顺序标记参数。但是一旦解析被传递给子解析器,主解析器就不会再解析了。它只是做一些清理任务。

但是,如果我使用parse_known_args,我可以收集这两个解析器都没有处理的字符串,并采取另一个解析它们的方法。

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问题。

+0

感谢您的详细解答Hpaulj让我测试它以适合我的代码 – Flippym

+0

如果我理解正确,'argparse'不支持全局参数,我将不得不在每个子分析器中用'dest'来控制它们,right ? – Flippym

+0

我已经添加了一个使用'parse_known_args'和第二个解析运行的例子。 – hpaulj