【问题标题】:Is there any way to instruct argparse (Python 2.7) to remove found arguments from sys.argv?有没有办法指示 argparse (Python 2.7) 从 sys.argv 中删除找到的参数?
【发布时间】:2016-03-02 06:27:13
【问题描述】:

我完全处于开发过程的中游,即将变成一个相当重要的 Python 2.7 项目。现在我将我所有的unittest 类集中在它们自己的模块tests.py 中,该模块大约有3300 行。这太疯狂了,无法导航,到处都是不好的做法,等等。

所以,我当前的任务是将其重构为子模块。作为重构的一部分,我想让从命令行运行测试的子集变得容易。例如:

$ python tests.py --all                         <--- runs *all* tests
$ python tests.py --utils_base                  <--- runs all tests on .utils
$ python tests.py --utils_vector                <--- runs all tests on .utils.vector
$ python tests.py --utils_base --utils_vector   <--- runs all tests on .utils.vector & .utils

所以,我开始使用argparse 进行设置。我有一个基本的ArgumentParser 设置没有问题,帮助消息显示得很好:

$ python tests.py -h
usage: tests.py [-h] [--all] [--utils_base]

optional arguments:
  -h, --help    show this help message and exit

Global Options:
  --all         Run all tests (overrides any other selections)

opan.base.utils Tests:
  --utils_base  Run all .utils tests

但是,当我去运行一些测试时,它崩溃了,并出现“参数无法识别”错误:

$ python tests.py --all
option --all not recognized
Usage: tests.py [options] [test] [...]

Options:
  -h, --help       Show this message
  -v, --verbose    Verbose output
  -q, --quiet      Minimal output
  -f, --failfast   Stop on first failure
  -c, --catch      Catch control-C and display results
  -b, --buffer     Buffer stdout and stderr during test runs

Examples:
  tests.py                               - run default set of tests
  tests.py MyTestSuite                   - run suite 'MyTestSuite'
  tests.py MyTestCase.testSomething      - run MyTestCase.testSomething
  tests.py MyTestCase                    - run all 'test*' test methods
                                               in MyTestCase

经过一番调试,我终于意识到我要在tests.py 中解析的命令行参数被保留在sys.argv 中并传递给unittest.main。 (回想起来,仔细阅读错误消息的Examples: 部分应该会更快地找到我的线索。)

所以,为了解决这个问题,我在下面添加了标记的代码,以便在将控制权传递给 unittest.main 之前从 sys.argv 中清除我的自定义参数:

# Arguments for selecting test suites
ALL = 'all'
UTILS_BASE = 'utils_base'

# Collecting the args together for iteration later
test_args = [ALL, UTILS_BASE]

...

# Strip from sys.argv any test arguments that are present
for a in test_args:                 <---  
    str_arg = '--{0}'.format(a)     <--- ADDING THESE MAKES UNITTEST HAPPY
    if str_arg in sys.argv:         <---
        sys.argv.remove(str_arg)    <---

有没有办法告诉argparse.removesys.argv 找到的参数,也许是在ArgumentParser 的构造中?我已经搜索了argparse 文档页面,但我一生都找不到合适的选项。

【问题讨论】:

    标签: python python-2.7 argparse


    【解决方案1】:

    你想要parse_known_args()

    from __future__ import print_function
    import argparse
    import sys
    
    def main():
        print(sys.argv)
    
    
    if __name__ == "__main__":
        parser = argparse.ArgumentParser()
    
        parser.add_argument('--all', action='store_true')
        parser.add_argument('--utils_base', action='store_true')
    
        args, left = parser.parse_known_args()
    
        sys.argv = sys.argv[:1]+left
    
        main()
    

    虽然,我必须问。为什么要编写自己的测试运行程序? unittest 模块允许您从 cli 运行特定的测试集:

    # run test from a spefiic class
    $ python -m unittest module.tests.group.TestSpecific
    # all possible options
    $ python -m unittest --help
    Usage: python -m unittest [options] [tests]
    
    Options:
      -h, --help       Show this message
      -v, --verbose    Verbose output
      -q, --quiet      Minimal output
      -f, --failfast   Stop on first failure
      -c, --catch      Catch control-C and display results
      -b, --buffer     Buffer stdout and stderr during test runs
    
    Examples:
      python -m unittest test_module               - run tests from test_module
      python -m unittest module.TestClass          - run tests from module.TestClass
      python -m unittest module.Class.test_method  - run specified test method
    
    [tests] can be a list of any number of test modules, classes and test
    methods.
    
    Alternative Usage: python -m unittest discover [options]
    
    Options:
      -v, --verbose    Verbose output
      -f, --failfast   Stop on first failure
      -c, --catch      Catch control-C and display results
      -b, --buffer     Buffer stdout and stderr during test runs
      -s directory     Directory to start discovery ('.' default)
      -p pattern       Pattern to match test files ('test*.py' default)
      -t directory     Top level directory of project (default to
                       start directory)
    
    For test discovery all test modules must be importable from the top
    level directory of the project.
    

    如果您在分组和运行测试方面需要更大的灵活性,我建议您查看nosetest

    【讨论】:

    • 你为什么要编写自己的测试运行程序? 很多原因,有些可能比其他的更糟。 :-) 我想将它与coverage 一起使用,而coverage run -m unittest module.Suite1 module.Suite2 module.Suite3 似乎比coverage run tests.py --suite1 --suite2 --suite3 更烦人。至于nosetest,我不知道它是否与coverage 很好地互操作,并且没有什么动力花时间去调查。另外,好像nosetestmay be dying
    • 还有呸!我注意到parse_known_args(),但甚至没有想过以这种方式使用它。不错。
    猜你喜欢
    • 1970-01-01
    • 2011-05-10
    • 2018-05-06
    • 2011-10-29
    • 2019-08-20
    • 1970-01-01
    • 2016-12-18
    • 2019-04-26
    • 2020-05-13
    相关资源
    最近更新 更多