【问题标题】:argparse fails when called from unittest test从 unittest 测试调用 argparse 失败
【发布时间】:2013-08-21 22:09:44
【问题描述】:

在一个文件中(比如parser.py)我有:

import argparse

def parse_cmdline(cmdline=None):
    parser = argparse.ArgumentParser()
    parser.add_argument('--first-param',help="Does foo.")
    parser.add_argument('--second-param',help="Does bar.")

    if cmdline is not None:
        args = parser.parse_args(cmdline)
    else:
        args = parser.parse_args()

    return vars(args)

if __name__=='__main__':
    print parse_cmdline()

果然,当从命令行调用它时,它可以工作并且给我几乎我所期望的:

$ ./parser.py --first-param 123 --second-param 456
{'first_param': '123', 'second_param': '456'}

然后我想unittest它,因此我写了一个test_parser.py文件:

import unittest
from parser import parse_cmdline

class TestParser(unittest.TestCase):
    def test_parse_cmdline(self):
        parsed = parse_cmdline("--first-param 123 --second-param 456")

        self.assertEqual(parsed['first_param'],'123')
        self.assertEqual(parsed['second_param'],'456')

if __name__ == '__main__':
    unittest.main()

然后我得到以下错误:

usage: test_parser.py [-h] [--first-param FIRST_PARAM]
                      [--second-param SECOND_PARAM]
test_parser.py: error: unrecognized arguments: - - f i r s t - p a r a m   1 2 3   - - s e c o n d - p a r a m   4 5 6
E
======================================================================
ERROR: test_parse_cmdline (__main__.TestParser)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./test_parser.py", line 8, in test_parse_cmdline
    parsed = parse_cmdline("--first-param 123 --second-param 456")
  File "/home/renan/test_argparse/parser.py", line 12, in parse_cmdline
    args = parser.parse_args(cmdline)
  File "/usr/lib/python2.7/argparse.py", line 1691, in parse_args
    self.error(msg % ' '.join(argv))
  File "/usr/lib/python2.7/argparse.py", line 2361, in error
    self.exit(2, _('%s: error: %s\n') % (self.prog, message))
  File "/usr/lib/python2.7/argparse.py", line 2349, in exit
    _sys.exit(status)
SystemExit: 2

----------------------------------------------------------------------
Ran 1 test in 0.004s

FAILED (errors=1)

可以看出,我指定的命令行(--first-param 123 --second-param 456)变成了 - - f i r s t - p a r a m 1 2 3 - - s e c o n d - p a r a m 4 5 6(每个字符用空格隔开)。

我不明白为什么:我做错了什么?

【问题讨论】:

    标签: python python-2.7 argparse python-unittest


    【解决方案1】:

    argparse 想要一个“参数向量”——即一个单独参数的列表——而不是一个“命令行”字符串。

    仅仅打电话split 并不能解决问题。例如,shell 中的这个命令行:

    python script.py --first-param '123 456' --second-param 789
    

    …会正确地给你123 456789,但是这行代码:

    parse_cmdline("--first-param '123 456' --second-param 789")
    

    不会;它会给你'123789,还有一个额外的456',它不知道如何处理。

    事实上,即使这样也是错误的:

    parse_cmdline("--first-param '123' --second-param 789")
    

    ...因为你会得到'123' 而不是123


    有两种方法可以解决这个问题。

    首先,如果您知道要传递的内容,则可以首先将其作为列表而不是字符串传递,并且您无需担心繁琐的引用和拆分细节:

    parse_cmdline(["--first-param", "123 456", "--second-param", "789"])
    

    或者,如果你不知道你想要传递的确切内容,你只知道它在 shell 上的样子,你可以使用shlex

    if cmdline is not None:
        args = parser.parse_args(shlex.split(cmdline))
    

    ...现在 Python 将像标准 Unix shell 一样拆分命令行,将 123 456 作为单个参数。

    【讨论】:

    • 基于 shlex 的解决方案现在对我来说已经足够好了。谢谢!
    • 虽然最好记住splitshlex 之间的区别,但它不会测试arpgarse 本身。 test_argparse.py 大量使用split,但不导入shlex。如果参数中的空格很重要,我只会给parse_args 提供一个单词测试列表。
    【解决方案2】:

    回答自己(几分钟后我意识到自己的错误):

    我应该这样做

    if cmdline is not None:
        args = parser.parse_args(cmdline.split())
    else:
        args = parser.parse_args()
    

    现在测试正确通过了!

    【讨论】:

    • 这有一个严重的问题。试试parse_cmdline("--first-param '123 456' --second-param 789")。这不会像你从实际的 shell 中传递同样的东西那样做同样的事情。
    • @abarnert 我明白了;那么,最好的解决方案是什么?
    • 这取决于您的具体用例。我已经写了一个解释两种标准解决方案的答案。
    • 是的,在您原来的情况下,parse_args 将您的测试字符串包装在 list() 中,将其变成单个字符的列表。因此“- - f i ...”中的空格。代码中的注释是“确保参数是可变的”。显然,它更关心的是获取一个元组而不是一个字符串。如果给定一个字符串,也许它应该反对。
    猜你喜欢
    • 2016-12-25
    • 2016-06-19
    • 2017-10-12
    • 2014-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-23
    • 2020-05-02
    相关资源
    最近更新 更多