【问题标题】:Python doctest for shell scripts that test argument parsing without polluting docstring with os.popen()用于测试参数解析而不用 os.popen() 污染文档字符串的 shell 脚本的 Python doctest
【发布时间】:2012-04-15 23:07:52
【问题描述】:

有没有办法编写 python doctest 字符串来测试旨在从命令行(终端)启动的脚本,该脚本不会使用 os.popen 调用污染文档示例?

#!/usr/bin/env python
# filename: add
"""
Example:
>>> import os
>>> os.popen('add -n 1 2').read().strip()
'3'
"""

if __name__ == '__main__':
    from argparse import ArgumentParser
    p = ArgumentParser(description=__doc__.strip())
    p.add_argument('-n',type = int, nargs   = 2, default = 0,help  = 'Numbers to add.')
    p.add_argument('--test',action = 'store_true',help  = 'Test script.')
    a = p.parse_args()
    if a.test:
        import doctest
        doctest.testmod()
    if a.n and len(a.n)==2:
        print a.n[0]+a.n[1]

在不使用 popen 的情况下运行 doctest.testmod() 只会导致测试失败,因为脚本是在 python shell 而不是 bash(或 DOS)shell 中运行的。

LLNL 的高级 Python 课程建议将脚本放在与 .py 模块分开的文件中。但随后 doctest 字符串仅测试模块,没有 arg 解析。我的 os.popen() 方法污染了示例文档。有没有更好的办法?

【问题讨论】:

  • 我是否遗漏了什么或者可以通过添加main 函数来解决这个问题?在if __main__ 块中进行参数解析,然后调用main(parsed_args)
  • 不幸的是,这不会改变任何事情。 main 函数并不特殊。将 if main 中的一些内容分解成一个单独的函数根本不会改变 doctest 的行为。您仍然不能像 shell 命令一样运行它,因为该脚本旨在被使用(并记录在案)。

标签: python shell argparse doctest


【解决方案1】:

刚刚找到了一些看起来像您想要的答案的东西: shell-doctest.

【讨论】:

  • 太棒了。正是我想要的。现在,python 开始进入 shell 脚本世界的可能性要大得多,我不必用 shell 脚本转换的东西弄乱我的 doctext。
  • 看起来很可疑,仅限 Python 2。 :-(
【解决方案2】:

doctest 是用来运行 python 代码的,所以你必须在某个地方进行转换。如果您决定直接通过doctest 测试命令行界面,一种可能性是在将__doc__ 传递给argparse 之前对其进行正则表达式替换,以取出os.popen 包装器:

clean = re.sub(r"^>>> os\.popen\('(.*)'\).*", r"% \1", __doc__)
p = ArgumentParser(description=clean, ...)

(当然,有各种更好的方法可以做到这一点,具体取决于您认为“好的”)。

这将为最终用户清理它。如果您还希望它在源代码中看起来更干净,您可以采用另一种方式:将命令行示例放在 docstring 中,不要使用 doctest.testmodule()。通过doctest.script_from_examples 运行您的文档字符串并对其进行后处理以插入os 调用。 (然后你必须将它嵌入到某些东西中,这样你就可以用run_docstring_examples 对其进行测试。)doctest 不关心输入是否是有效的 python,所以你可以执行以下操作:

>>> print doctest.script_from_examples("""
Here is a commandline example I want converted:
>>> add -n 3 4
7
""")
# Here is a commandline example I want converted:
add -n 3 4
# Expected:
## 7

这仍然会在帮助中显示 python 提示 >>>。如果这让您感到困扰,您可能只需要在两个方向上处理字符串。

【讨论】:

  • 这是隐藏测试参数解析器的文档字符串的好方法,但是当用户从 OS shell 运行 add --help 时,它不提供任何示例(带有预期输出)。您对sys.argv 的使用似乎大致相当于我的代码中的os.popen,当文档字符串用于文档和“帮助”而不是用于文档测试时,它看起来同样难看。
  • 好的,如果您真的想要面向命令行的文档和测试,请查看新答案。
  • 哇。相当棘手。谢谢你让我摆脱束缚。我现在明白为什么您没有在第一个答案中提出所有这些复杂性。也许 doctest 或 argparse 将来会包含一些 shell 测试功能。 $$$ 而不是 >>> 有人吗?
【解决方案3】:

您也可以自己加载文档字符串并执行命令,如in this test

import sys

module = sys.modules[__name__]
docstring = module.__doc__
# search in docstring for certain regex, and check that the following line(s) matches a pattern.

【讨论】:

  • 这听起来很像创建自己的 doctest 模块。
  • 是的。你可以让它调用parse_args,所以它比生成进程快得多。
  • 是的,这会加快速度。但是你会给你的测试基础设施增加很多复杂性和潜在的错误......这对我来说真的很可怕。
  • 我不确定它是不是很多。至少不适合我。我在这里做到了:github.com/allenai/allennlp/pull/3185/…
  • 我印象深刻!一个正则表达式对我来说很重要。正则表达式很挑剔,容易出现意外的错误行为。你花了一些试验和错误来让那个适合你的例子。如果它在你所有的项目和文档字符串中都能可靠地工作,我会感到惊讶,更不用说其他人的了。
猜你喜欢
  • 2022-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-08
  • 1970-01-01
  • 2012-07-25
相关资源
最近更新 更多