argparse在处理子命令缩写时存在问题

4

(运行在Python 3.6.0)

简短概括

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

为什么即使使用了allow_abbrev=Truesubparser的缩写仍然无效?

详细说明

实际上,我在让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 123 O
但这个会出错:$ python prog.py caes 123 X
prog.py: error: invalid choice: 'cae' (choose from 'caesar', 'c')

现在是令人困惑的部分。
根据 argparse 文档:
ArgumentParser 支持使用 add_subparsers() 方法创建这种子命令。add_subparsers() 方法通常不带参数调用,并返回一个特殊的 Action 对象。该对象有一个方法 add_parser(),它接受一个命令名称和任何 ArgumentParser 构造函数参数,并返回一个 ArgumentParser 对象,可以像通常一样进行修改。
1. 好的,那么使用 add_subparser() 创建的任何对象都可以使用 object.add_parser() 创建自己的 ArgumentParser 对象,对吧? 2. ……这意味着这个新创建的 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 - 如果缩写不会引起歧义,则允许长选项被缩写。
(默认值:True
在3.5版本中发生了更改:添加了allow_abbrev参数。
(这是在Python 3.6.0上)

缩写参数适用于解析器的标记参数,而不适用于调用子解析器的名称或别名。 - hpaulj
1
请注意,allow_abbrev的默认值为true。3.6版本真正添加的功能是可以关闭它。早期版本始终接受缩写。但它从未应用于参数“choices”,只适用于类似于“--foo”的标志。 - hpaulj
2个回答

3
一项允许缩写子命令名称的补丁已经实现,但后来被撤回了,因为它被证明存在缺陷:Issue 12713: allow abbreviation of sub commands by users。允许用户关闭长选项的缩写是一个不同的问题,在Issue 14910: argparse: disable abbreviation中处理。这是代码的两个不同部分。
allow_abbrev - 如果缩写是无歧义的,则允许对选项进行缩写。
使用以下方法创建长选项:
caesar.add_argument('-f','--foobar')

默认情况下,使用 '-f'、'--foo' 和 '--foobar' 可以使用 allow_abbrev 值。在这种情况下,long_option 是 '--foobar'。如果将其设置为 False,则 '--foo' 将无法使用。

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

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

非常感谢您详尽的回复!这绝对比试图弄清文档要好得多。 - cdpp

0
我遇到的错误是这样的:
usage: [-h] {caesar,c} ...
: error: unrecognized arguments: a e s

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

那么真正应该发生什么呢?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'] - hpaulj
事实上,我谈论的是 parser.parse_args(["caes"])。这里的问题是应该可以结合使用短命令行标志。与 POSIX 的 getopt.h 完全相同,比如思考 tr -cdgrep -vERF 或者著名的 tar xvzf - mar77i
1
短选项可以组合使用,例如 python prog.py -caes。但是这不适用于子解析器命令。只有一个“单词”有效。 - hpaulj

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