配置argparse以接受带引号的参数

3
我正在编写一个程序,其中包括让用户通过参数指定要加载(并用于执行操作的)模块。我试图设置一种简单的方式来将这些参数传递到内部模块,并尝试使用ArgParse的action='append'来构建一个参数列表,然后将其传递给内部模块。
以下是我使用的基本参数布局。
parser.add_argument('-M', '--module',
                    help="Module to run on changed files - should be in format MODULE:CLASS\n\
                          Specified class must have function with the signature run(src, dest)\
                          and return 0 upon success",
                    required=True)
parser.add_argument('-A', '--module_args',
                    help="Arg to be passed through to the specified module",
                    action='append',
                    default=[])

然而,如果我尝试使用 python my_program -M module:class -A "-f filename" 运行此程序(其中我想将 -f filename 传递给我的模块),它似乎将 -f 解析为自己的参数(我会得到错误 my_program: error: argument -A/--module_args: expected one argument)。
有什么想法吗?

1
看一眼 sys.argv。引号是否能通过解析器,还是被shell所吞噬了? - hpaulj
2个回答

1
解决方案是接受任意参数-在argparse的文档这里中有一个示例:

argparse.REMAINDER。所有剩余的命令行参数都被收集到一个列表中。这通常对于将分派到其他命令行实用程序的命令行实用程序非常有用:

>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('--foo')
>>> parser.add_argument('command')
>>> parser.add_argument('args', nargs=argparse.REMAINDER)
>>> print(parser.parse_args('--foo B cmd --arg1 XX ZZ'.split()))
Namespace(args=['--arg1', 'XX', 'ZZ'], command='cmd', foo='B')

这是一个有趣的解决方案 - 但我认为(至少对于我的应用程序而言)最好不要接受任意命令参数。至少通过填充空格的解决方法,用户仍然必须有意识地使用“-A”参数。 - wakey

1

With:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-M', '--module',
                    help="Module to run on changed files - should be in format MODULE:CLASS\n\
                          Specified class must have function with the signature run(src, dest)\
                          and return 0 upon success",
                    )
parser.add_argument('-A', '--module_args',
                    help="Arg to be passed through to the specified module",
                    action='append',
                    default=[])
import sys
print(sys.argv)
print(parser.parse_args())

我得到:
1028:~/mypy$ python stack45146728.py -M module:class -A "-f filename"
['stack45146728.py', '-M', 'module:class', '-A', '-f filename']
Namespace(module='module:class', module_args=['-f filename'])

这是使用Linux shell。引用字符串仍然是一个字符串,如sys.argv中所见,并且被正确解释为-A的参数。

如果没有引号,-f将被单独解释为标志。

1028:~/mypy$ python stack45146728.py -M module:class -A -f filename
['stack45146728.py', '-M', 'module:class', '-A', '-f', 'filename']
usage: stack45146728.py [-h] [-M MODULE] [-A MODULE_ARGS]
stack45146728.py: error: argument -A/--module_args: expected one argument

你是使用Windows或其他不同的操作系统/Shell,无法以相同的方式处理引号吗?

Argparse中`append`的使用不如预期

您询问了一个稍微不同的命令行:

1032:~/mypy$ python stack45146728.py  -A "-k filepath" -A "-t"
['stack45146728.py', '-A', '-k filepath', '-A', '-t']
usage: stack45146728.py [-h] [-M MODULE] [-A MODULE_ARGS]
stack45146728.py: error: argument -A/--module_args: expected one argument

如我所提到的,-k filepath 作为一个字符串传递。由于空格,argparse 不会将其解释为标志。但它会将裸露的 '-t' 解释为标志。

有一个关于将未定义的 '-xxx' 字符串解释为参数而不是标志的 bug/问题。我得查一下是否有任何东西进入了生产。

字符串被归类为标志或参数的详细信息可以在 argparse.ArgumentParser._parse_optional 方法中找到。它包含了一条注释:

    # if it contains a space, it was meant to be a positional
    if ' ' in arg_string:
        return None

http://bugs.python.org/issue9334 argparse不接受以破折号开头的带参数选项(与optparse相比退化)是一个关于此主题的旧而长的错误/问题。


谢谢你的回答!看来只能在这些引用参数中填充空格,并尝试告诉我的用户这个解决方法。 - wakey

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