【问题标题】:Can't get argparse to read quoted string with dashes in it?无法让 argparse 读取带有破折号的引号字符串?
【发布时间】:2013-04-17 00:01:07
【问题描述】:

有没有办法让 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/89529http://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

【问题讨论】:

  • 你用的是什么版本的 Python?
  • 我使用的是 Python 2.7。
  • 这在 Python 2.7 上对我来说很好用。您是否配置了任何其他参数?
  • 是的,有很多。此外, -e 是我的程序的子解析器之一的参数。我会贴一个更完整的代码sn-p,让大家更清楚。
  • 嗯...非常确定。我所有的其他选项都可以正常工作,只要引用的字符串不以破折号开头,extraEnvVars 就会做它应该做的事情。例如,python Application.py queue -env " -env" 工作正常。

标签: python argparse command-line-interface


【解决方案1】:

更新答案:

调用时可以加等号:

python Application.py -env="-env"

原答案:

我在做你想做的事情时也遇到了麻烦,但是 argparse 中有一个解决方法,即parse_known_args 方法。这将使您尚未定义的所有参数都通过解析器,并假设您会将它们用于子进程。缺点是您不会收到带有错误参数的错误报告,并且您必须确保您的选项和子流程的选项之间没有冲突。

另一种选择是强制用户使用加号而不是减号:

python Application.py -e "+s WHATEVER +e COOL STUFF"

然后在传递给子进程之前,在后处理中将“+”更改为“-”。

【讨论】:

  • 我认为 parse_known_args 对我没有帮助。我根本不想阅读引号中的论点;我希望将带引号的字符串作为单个对象传递给-env。我考虑过后处理路线,如果我没有从这里得到更好的答案,我可能会这样做,但感觉很hacky,这意味着字符串中的+字符被更改为-。我真的很希望能够传递一个包含任何字符的字符串。
  • 我明白你在问什么...如果你想读入多个不带引号的字符串,那么使用nargs='+' 告诉-env 读入一个或多个字符串。
  • 但我也希望其中一些字符串包含破折号,甚至可能与我的子解析器中的参数具有相同的名称。像 python Application.py queue -env "-env blah" 这样的东西应该可以工作。
  • 对不起,我没办法了。我试图做同样的事情,但最终选择简单地重新实现我的解析器中的选项来传递子进程,因为我无法得到你想要做的工作。祝你好运!我希望有人提出一个好的建议,我们可以试试。
  • @sfendel 尝试使用等号:python Application.py -env="-env"
【解决方案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' 仍将被归类为选项标志。


展开相关案例

Using argparse with argument values that begin with a dash ("-")

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 类似的参数。 subparsers 使用的是 nargs

REMAINDER 的这种用法已记录在案,https://docs.python.org/3/library/argparse.html#nargs

【讨论】:

  • 非常感谢:nargs=argparse.PARSER帮助了我。
  • 我不太确定发生了什么变化,但现在python Application.py queue -env "-s WHATEVER -e COOL STUFF" 可以工作了。 queue -env "-foo" 仍然会引发错误,因为独立的“-foo”(或“--foo”)仍被解释为标志。学徒标志后面的空格确实有所不同。
【解决方案3】:

您可以使用空格python tst.py -e ' -e blah' 作为一个非常简单的解决方法来开始参数。如果您愿意,只需 lstrip() 将其恢复正常的选项。

或者,如果第一个“子参数”不是原始函数的有效参数,那么您根本不需要做任何事情。也就是说,python tst.py -e '-s hi -e blah' 不起作用的唯一原因是因为-stst.py 的有效选项。

此外,optparse 模块现已弃用,可以正常工作。

【讨论】:

  • 我认为它不会发生,因为 -s 是子解析器的有效选项。我用 python Application.py queue -e "-notarealoption" 试过了,得到了同样的错误。我喜欢使用 lstrip 比 replace + with - 虽然像 SethMMorton 建议的那样好一点,但似乎应该有一种方法来引用一个字符串,这样它里面的任何东西都不会被 argparse 替换/更改/读取。
  • 哦,真的吗?我刚刚根据我现在的简短测试得出了这个假设。我制作了一个带有一个参数-a 的脚本,然后简单地将其发送到-a '-b hello',它工作得很好。但我猜我使用的是不同版本的 Python。
  • 我编辑了我原来的问题。显然,这是 >2.7 中 argparse 中的一个已知错误 :(。我在调用 parser.parse_args() 之前更改了 sys.argv 以在 -env 选项的开头添加一个虚拟字符,然后将其剥离。它是hacky 和 ​​unpythonic,但我终于得到了我想要的。
  • 我成功地使用了以空格开头的引号中的字符串。所以我的解析器在-10:a 10:b 的参数多值输入上失败,但对' -10:a' 10:b 有效。
  • 我无法相信这个包在处理一个简单的破折号作为参数时很糟糕。 Django admin 仍然使用它,所以我需要使用它。如果没有,我可以使用 Click
【解决方案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 --argument_name "--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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-11-25
          • 2015-10-29
          • 2016-06-06
          • 1970-01-01
          相关资源
          最近更新 更多