argparse:当存在子解析器时如何分离未知的(和可选的)参数。(子解析器也是可选的)

3
我有以下代码
parser = argparse.ArgumentParser(allow_abbrev=False, add_help=False)
parser.add_argument('--conf', nargs=1)
parser.add_argument('-h', '--help', nargs='?', const='generic')
parser.add_argument('-v', '--version', action="store_true")

subparsers = parser.add_subparsers()
subparsers.required = False
parser_start = subparsers.add_parser('start')
group1 = parser_start.add_mutually_exclusive_group()
group1.add_argument('--quiet', action="store_true")
group1.add_argument('-V', '--verbose', nargs="*")

# parser_console = subparsers.add_parser('console')

print(argv)
parsed_args = parser.parse_known_args(argv)

现在,当我将 argv 作为 argv = ['abc', 'def']argv = ['abc'] 传递时,
我会得到:
['abc', 'def']
usage: lbrynet [--conf CONF] [-h [HELP]] [-v] {start} ...
lbrynet: error: invalid choice: 'abc' (choose from 'start')

我原本期望在未知参数的元组中获得['abc', 'def']。我查看了很多stackoverflow答案(比如回答1回答2回答3回答4)和bug报告(例如报告1),但似乎找不到实现的方法。这是一个未解决的问题吗?我的期望是错的吗?如果无法实现,是否有任何解决方法?
另外需要澄清的是,从元组中获取的['abc', 'def']必须传递给其他函数进行处理。
欢迎提供任何进一步的澄清和/或消除任何不确定性。

1
对于主解析器而言,subparsers 只是一个 位置参数(带有特殊的 action 参数)。由于您没有定义任何其他位置参数,第一个非标志字符串被分配给 subparsers,在那里它未通过 choices 测试。argparse 将字符串分配给位置参数,然后测试其有效性,而不是相反。 - hpaulj
1
换句话说,仅仅因为subparsers不是必需的,并不意味着它是一个标记(optional)参数。这只是意味着如果你没有提供一个字符串(在positional参数的意义上),它不会抱怨。 - hpaulj
1
['start', 'abc', 'def'] 应该将这2个字符串放在“额外变量”中。这是因为“开始”解析器被调用,但它不使用额外的字符串,并将它们传回到主程序中。 - hpaulj
有没有办法不提供['start'],仍然能获取额外的参数。假设我的主命令是“调用”该程序为X。这意味着我将以X abc def的形式运行该程序。 - hackrush
我也看了argparse的代码,发现abc, def首先被标记为位置参数(模式为“AA”),然后由start子解析器解析。但是既然abcdef都不匹配start子命令,它们不应该进入start子命令的“空间”吗? - hackrush
1个回答

1

正如我在评论中所写的那样,subparsers 是一个位置参数。

为了举例说明一个普通的位置参数:

In [307]: parser = argparse.ArgumentParser()
In [308]: a1 = parser.add_argument('foo')

In [309]: parser.parse_known_args(['one','two'])
Out[309]: (Namespace(foo='one'), ['two'])

'one'被分配给第一个位置。现在给foo提供选择:

In [310]: a1.choices = ['bar','test']
In [311]: parser.parse_known_args(['one','two'])
usage: ipython3 [-h] {bar,test}
ipython3: error: argument foo: invalid choice: 'one' (choose from 'bar', 'test')

它仍在尝试将第一个字符串分配给foo。由于它与choices不匹配,因此会引发错误。
In [312]: parser.parse_known_args(['bar','one','two'])
Out[312]: (Namespace(foo='bar'), ['one', 'two'])

字符串根据位置而非值分配给参数。任何值的检查,例如类型或选择,都是在分配后进行的。
将“choices”更改为“type”测试:
In [313]: a1.choices = None
In [314]: a1.type = int
In [315]: parser.parse_known_args(['bar','one','two'])
usage: ipython3 [-h] foo
ipython3: error: argument foo: invalid int value: 'bar'

In [316]: parser.parse_known_args(['12','one','two'])
Out[316]: (Namespace(foo=12), ['one', 'two'])

我理解你想表达的意思。那很有帮助。有没有一种方法可以实现我想要的功能?(不需要基于子解析器) 该功能如下: X --version -> 获取版本 X start --quiet -> 以安静模式启动守护程序 X get media1 -> 发送命令 get media1 进行进一步处理 我无法预先计算开发人员将在 get 的位置输入什么,因此 getmedia1 将被“收集”到某个地方,然后发送进行进一步的“内部”处理。 - hackrush

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接