无法让 argparse 读取带有破折号的引号字符串?
- 2025-03-04 08:24:00
- admin 原创
- 86
问题描述:
有没有办法让 argparse 将两个引号之间的任何内容识别为单个参数?它似乎一直看到破折号,并假设它是新选项的开始
我有类似的东西:
mainparser = argparse.ArgumentParser()
subparsers = mainparser.add_subparsers(dest='subcommand')
parser = subparsers.add_parser('queue')
parser.add_argument('-env', '--extraEnvVars', type=str,
help='String of extra arguments to be passed to model.')
...other arguments added to parser...
但是当我跑步时:
python Application.py queue -env "-s WHATEVER -e COOL STUFF"
它给了我:
Application.py queue: error: argument -env/--extraEnvVars: expected one argument
如果我省略第一个破折号,它完全可以正常工作,但能够传入带有破折号的字符串至关重要。我尝试使用 \ 对其进行转义,这使其成功,但将 \ 添加到参数字符串中 有人知道如何解决这个问题吗?无论 -s 是否是解析器中的参数,都会发生这种情况。
编辑:我正在使用 Python 2.7。
编辑2:
python Application.py -env " -env"
运行良好,但是
python Application.py -env "-env"
没有。
EDIT3:看起来这实际上是一个正在讨论的错误:http://www.gossamer-threads.com/lists/python/bugs/89529,http : //python.6.x6.nabble.com/issue9334-argparse-does-not-accept-options-taking-arguments-beginning-with-dash-regression-from-optp-td578790.html。它只存在于 2.7 中,而不存在于 optparse 中。
EDIT4:当前打开的错误报告是:http://bugs.python.org/issue9334
解决方案 1:
更新答案:
调用它时你可以放置一个等号:
python Application.py -env="-env"
原始答案:
我也遇到过你尝试做的事情,但 argparse 中有一个解决方法,即parse_known_args方法。这将允许所有未定义的参数通过解析器,并假设你会将它们用于子进程。缺点是,如果参数不正确,你不会收到错误报告,并且你必须确保你的选项和子进程的选项之间没有冲突。
另一种选择是强制用户使用加号而不是减号:
python Application.py -e "+s WHATEVER +e COOL STUFF"
然后在传递到子进程之前,在后期处理中将“+”更改为“-”。
解决方案 2:
这个问题在http://bugs.python.org/issue9334中有深入讨论。大部分活动发生在 2011 年。我去年添加了一个补丁,但argparse
补丁积压了相当多。
问题在于像 这样的字符串可能存在歧义'--env'
,或者"-s WHATEVER -e COOL STUFF"
当它跟在带有参数的选项后面时。
optparse
执行从左到右的简单解析。第一个--env
是接受一个参数的选项标志,因此它会使用下一个参数,而不管它看起来如何。 argparse
另一方面,它会循环两次遍历字符串。首先,它会将它们分类为“O”或“A”(选项标志或参数)。在第二次循环中,它会使用它们,使用re
类似的模式匹配来处理变量nargs
值。在这种情况下,我们似乎有OO
两个标志,但没有参数。
使用时的解决方案argparse
是确保参数字符串不会与选项标志混淆。这里(以及在错误问题中)显示的可能性包括:
--env="--env" # clearly defines the argument.
--env " --env" # other non - character
--env "--env " # space after
--env "--env one two" # but not '--env "-env one two"'
本身'--env'
看起来像一个标志(即使加引号,请参阅sys.argv
),但当其后跟其他字符串时则不是。但它"-env one two"
存在问题,因为它可以被解析为['-e','nv one two']
,即“-e”标志后跟一个字符串(或更多选项)。
--
并且nargs=argparse.PARSER
还可用于强制argparse
将所有后续字符串视为参数。但它们仅在参数列表末尾起作用。
在 issue9334 中有一个建议的补丁来添加一种args_default_to_positional=True
模式。在此模式下,解析器仅当能够明确地将字符串与定义的参数匹配时,才会将其归类为选项标志。因此,'--env --one' 中的 '--one' 将被归类为参数。但 '--env --env' 中的第二个 '--env' 仍将被归类为选项标志。
扩展相关案例
使用 argparse 和以破折号(“-”)开头的参数值
parser = argparse.ArgumentParser(prog="PROG")
parser.add_argument("-f", "--force", default=False, action="store_true")
parser.add_argument("-e", "--extra")
args = parser.parse_args()
print(args)
生产
1513:~/mypy$ python3 stack16174992.py --extra "--foo one"
Namespace(extra='--foo one', force=False)
1513:~/mypy$ python3 stack16174992.py --extra "-foo one"
usage: PROG [-h] [-f] [-e EXTRA]
PROG: error: argument -e/--extra: expected one argument
1513:~/mypy$ python3 stack16174992.py --extra "-bar one"
Namespace(extra='-bar one', force=False)
1514:~/mypy$ python3 stack16174992.py -fe one
Namespace(extra='one', force=True)
“-foo one” 案例失败,因为-foo
被解释为标志加上未指定的附加项。这与允许被解释为 的-f
操作相同。-fe
`['-f','-e']`
如果我将 更改nargs
为REMAINDER
(不是PARSER
),则之后的所有内容都-e
将被解释为该标志的参数:
parser.add_argument("-e", "--extra", nargs=argparse.REMAINDER)
所有情况都适用。请注意,该值是一个列表。不需要引号:
1518:~/mypy$ python3 stack16174992.py --extra "--foo one"
Namespace(extra=['--foo one'], force=False)
1519:~/mypy$ python3 stack16174992.py --extra "-foo one"
Namespace(extra=['-foo one'], force=False)
1519:~/mypy$ python3 stack16174992.py --extra "-bar one"
Namespace(extra=['-bar one'], force=False)
1519:~/mypy$ python3 stack16174992.py -fe one
Namespace(extra=['one'], force=True)
1520:~/mypy$ python3 stack16174992.py --extra --foo one
Namespace(extra=['--foo', 'one'], force=False)
1521:~/mypy$ python3 stack16174992.py --extra -foo one
Namespace(extra=['-foo', 'one'], force=False)
argparse.REMAINDER
类似于 '*',只不过它会接受后面的所有内容,无论它是否看起来像标志。 argparse.PARSER
更像 '+',因为它positional
首先需要一个类似的参数。它是使用nargs
的subparsers
。
这个用途REMAINDER
是有记录的,https://docs.python.org/3/library/argparse.html#nargs
解决方案 3:
您可以使用空格开始参数,python tst.py -e ' -e blah'
这是一个非常简单的解决方法。lstrip()
如果您愿意,只需选择将其恢复正常即可。
或者,如果第一个“子参数”不是原始函数的有效参数,那么您根本不需要执行任何操作。也就是说,python tst.py -e '-s hi -e blah'
不起作用的唯一原因是因为-s
是 的有效选项tst.py
。
此外,现在已弃用的optparse模块仍可正常运行。
解决方案 4:
我已经将一个脚本从 optparse 移植到 argparse,其中某些参数的值可以以负数开头。我遇到这个问题是因为该脚本在许多地方使用时没有使用“=”符号将负值连接到标志。在阅读了此处和http://bugs.python.org/issue9334中的讨论后,我知道参数只接受一个值,并且接受后续参数(即缺失值)作为值没有任何风险。FWIW,我的解决方案是预处理参数,并在传递给 parse_args() 之前用“=”连接有问题的参数:
def preprocess_negative_args(argv, flags=None):
if flags is None:
flags = ['--time', '--mtime']
result = []
i = 0
while i < len(argv):
arg = argv[i]
if arg in flags and i+1 < len(argv) and argv[i+1].startswith('-'):
arg = arg + "=" + argv[i+1]
i += 1
result.append(arg)
i += 1
return result
这种方法至少不需要任何用户更改,它只修改明确需要允许负值的参数。
>>> import argparse
>>> parser = argparse.ArgumentParser("prog")
>>> parser.add_argument("--time")
>>> parser.parse_args(preprocess_negative_args("--time -1d,2".split()))
Namespace(time='-1d,2')
告诉 argparse 哪些参数应该明确允许以破折号开头的值会更方便,但这种方法似乎是一个合理的折衷方案。
解决方案 5:
类似的问题。我通过将空格替换为“\”来解决这个问题。例如:
替换
python Application.py "cmd -option"
为
python Application.py "cmd -option"
。
不确定是否适合您的问题。
解决方案 6:
paser.add_argument("--argument_name", default=None, nargs=argparse.REMAINDER)
python_file.py --参数名称“--abc=10 -a=1 -b=2 cdef”
注意:参数值只能在双引号内传递,单引号则不行
解决方案 7:
为了避免必须处理 argparse,即使查看不是您想要的标志的“-”,您可以在 argparse 读取它之前编辑 sys.argv。只需保存您不想看到的参数,将填充参数放在它的位置,然后在 argparse 处理 sys.argv 之后将填充参数替换为原始参数。我只是必须为自己的代码执行此操作。它并不漂亮,但它有效并且很容易。如果您的标志不总是按相同的顺序排列,您还可以使用 for 循环遍历 sys.argv。
parser.add_argument('-n', '--input', nargs='*')
spot_saver = ''
if sys.argv[1] == '-n': #'-n' can be any flag you use
if sys.argv[2][0] == '-': #This checks the first character of the element
spot_saver = sys.argv[2]
sys.argv[2] = "fillerText"
args = parser.parse_args()
if args.input[0] == 'fillerText':
args.input[0] = spot_saver
解决方案 8:
不确定较新的 Python 版本怎么样,但在 3.6.8 中我做了类似的事情:
parser.add_argument('--eat-args',
type=str,
nargs='+',
action=EatArgsAction)
EatArgsAction
看起来像
class ToDistCpAction(argparse.Action):
"""
Turns all `"` or `'` into `` (nothing).
"""
def __call__(self, parser, namespace, values, option_string) -> None:
setattr(namespace, self.dest, ' '.join(map(lambda arg: re.sub(r'(?<!w)(+)', '-', arg), values)))
并且这可以吃掉传递的参数,例如:++eat-args +p +I +whatever+anything
你会得到--eat-args -p -I -whatever+anything
编辑:@tripleee 谴责后进行了更改
扫码咨询,免费领取项目管理大礼包!