argparse 未正确处理子解析器中的缩写

Posted

技术标签:

【中文标题】argparse 未正确处理子解析器中的缩写【英文标题】:argparse not handling abbreviations in subparser properly 【发布时间】:2018-04-23 15:40:15 【问题描述】:

(在 python 3.6.0 上运行)

TL;DR

Usage: prog.py caesar | vigenere [key]

parser = argparse.ArgumentParser()
subp = parser.add_subparsers()
caesar = subp.add_parser("caesar", aliases=["c"], allow_abbrev=True)
args = parser.parse_args()
$ python prog.py caes 123
prog.py: error: invalid choice: 'caes' (choose from 'caesar', 'c')

为什么subparser的缩写即使是allow_abbrev=True也是无效的?


长版

基本上,让argparse 接受缩写的subparsers 名称/别名时遇到问题。

代码如下:

Usage: prog.py [caesar] [key]

import sys, argparse

def main(argv):
parser = argparse.ArgumentParser
         (description="runs text through X cipher")
subp = parser.add_subparsers\
         (help="sub-command help")

#<ArgumentParser object>
caesar = subp.add_parser\
         ("caesar", aliases=["c"], allow_abbrev=True)
caesar.add_argument\
         ("key", metavar = "key (any integer)",\
          type = int, default = 0)


args = parser.parse_args()
print(caesar)

if __name__ == "__main__":
sys.argv = list(str(c).lower() for c in sys.argv[0:])
main(sys.argv)

所以从上面的代码来看,应该可以接受以下任何一项:

- "Caesar" or "caesar"
- "C" or "c" 
- Any abbreviation in between "c" and "caesar" 

那么问题来了:

这有效:$ python prog.py c 123O

这给出了一个错误:$ python prog.py caes 123 X

prog.py: error: invalid choice: 'cae' (choose from 'caesar', 'c')

现在是令人困惑的部分。

根据 argparse 文档

ArgumentParser 支持使用 add_subparsers() 方法。 add_subparsers() 方法通常是 不带参数调用并返回一个特殊的操作对象。这 object 有一个方法,add_parser(),它接受一个命令名 和任何 ArgumentParser 构造函数参数,并且 返回一个 可以照常修改的 ArgumentParser 对象

    好的,所以任何object created with add_subparser() 都可以创建自己的ArgumentParser objectobject.add_parser() 对吧?

    ...这意味着这个新创建的ArgumentParser 对象应该能够接受任何ArgumentParser 参数是吗?

ArgumentParser 定义:

class 
argparse.ArgumentParser(
prog=None, usage=None, 
description=None, epilog=None, 
parents=[],formatter_class=argparse.HelpFormatter, 
prefix_chars='-',fromfile_prefix_chars=None, 
argument_default=None,conflict_handler='error', 
add_help=True, allow_abbrev=True)

创建一个新的 ArgumentParser 对象。所有参数都应作为关键字传递 论据。每个参数在下面都有自己更详细的描述,但总之 他们是:

allow_abbrev - 如果缩写明确,则允许缩写长选项。

(默认:

3.5 版更改:添加了 allow_abbrev 参数。

(这是在 python 3.6.0 上)


提前谢谢各位

【问题讨论】:

缩写参数适用于解析器的标记参数,而不适用于调用子解析器的名称或别名。 请注意,allow_abbrev 默认为 true。 3.6 中真正添加的是关闭它的能力。早期版本总是接受缩写。但它从未应用于参数choices,仅应用于--foo 之类的标志。 【参考方案1】:

实现了一个允许子解析器名称缩写的补丁,但当它被证明有问题时被撤回:

Issue 12713: allow abbreviation of sub commands by users

允许用户关闭长选项的缩写是一个不同的问题,在

中处理

Issue 14910: argparse: disable abbreviation

代码的两个不同部分。

allow_abbrev - 如果缩写明确,则允许缩写 long 选项。

一个长选项是通过以下方式创建的:

caesar.add_argument('-f','--foobar')

使用默认的allow_abbrev 值,这将适用于“-f”、“--foo”和“--foobar”。在这种情况下,long_option 是“--foobar”。有了它False,'--foo' 将不起作用。

主要的parser 决定ccaesarcae 是有效的子解析器命令(通过subp,由parser.add_subparsers 创建的特殊操作对象) .这更像是choices 的定位。

parser.add_argument('foo', choices = ['c', 'caesar'])

【讨论】:

感谢您的彻底回复!这绝对比试图找出文档要好。【参考方案2】:

我得到的错误是:

usage: [-h] caesar,c ...
: error: unrecognized arguments: a e s

暗示缩写应该是可组合的,因为可以通过传递ca 引用两个不同的缩写“c”和“a”。

那里真正应该发生的事情是什么? ca 既是 c 和(不存在的)a 短格式的组合,也是缩写。解析器应该更喜欢哪个?因此,在设计库时必须明确解决这个问题:为了可预测性,您不能同时拥有两者。

话虽如此,也许您可​​以通过传递conflict_handler='resolve' 来调整结果? https://docs.python.org/3/library/argparse.html#allow-abbrev

【讨论】:

你给解析器什么? parser.parse_args(list('caes'])?看起来argv 列表是['c','a','e','s'] 其实我说的是parser.parse_args(["caes"])。这里的问题是应该可以组合短命令行标志。与 POSIX getopt.h 完全一样,想想tr -cdgrep -vERF 或著名的tar xvzf 短选项可以组合,如python prog.py -caes。但这不适用于子解析器命令。只有一个“词”有效。

以上是关于argparse 未正确处理子解析器中的缩写的主要内容,如果未能解决你的问题,请参考以下文章

Python:正确处理子命令的全局选项的参数解析器

带有通用子解析器命令的 Python argparse

如何从 Python 3 中的现有程序创建带有 argparse 的子解析器?

Python Argparse 子解析器

带有多级解析器/子解析器的 Argparse 可选参数

有没有办法在 argparse 中添加一个已经创建的解析器作为子解析器?