从unittest测试调用时,argparse失败

Ren*_*nan 2 python python-2.7 argparse python-unittest

在一个文件中(比方说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()
Run Code Online (Sandbox Code Playgroud)

果然,当从命令行调用它时,它会起作用并给我很多我期望的东西:

$ ./parser.py --first-param 123 --second-param 456
{'first_param': '123', 'second_param': '456'}
Run Code Online (Sandbox Code Playgroud)

但后来我想要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()
Run Code Online (Sandbox Code Playgroud)

然后我收到以下错误:

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)
Run Code Online (Sandbox Code Playgroud)

可以看出,我指定的命令行(--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(每个字符用空格分隔).

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

aba*_*ert 8

argparse 想要一个"参数向量" - 即一个单独的参数列表 - 而不是一个"命令行"字符串.

只是打电话split不会解决问题.例如,来自shell的这个命令行:

python script.py --first-param '123 456' --second-param 789
Run Code Online (Sandbox Code Playgroud)

...将正确给你123 456789,但此行的代码:

parse_cmdline("--first-param '123 456' --second-param 789")
Run Code Online (Sandbox Code Playgroud)

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

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

parse_cmdline("--first-param '123' --second-param 789")
Run Code Online (Sandbox Code Playgroud)

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


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

首先,如果你知道你要传递什么,你可以先把它作为一个列表而不是一个字符串传递给你,你不必担心引用和分割细节:

parse_cmdline(["--first-param", "123 456", "--second-param", "789"])
Run Code Online (Sandbox Code Playgroud)

或者,如果你不确切知道你想要通过什么,你只需知道它在shell上的样子,你可以使用shlex:

if cmdline is not None:
    args = parser.parse_args(shlex.split(cmdline))
Run Code Online (Sandbox Code Playgroud)

...现在Python将以与标准Unix shell相同的方式拆分命令行,将其123 456作为单个参数.