如何在使用Python的optparse模块时遵循PEP 257文档字符串?

5 投票
3 回答
1865 浏览
提问于 2025-04-15 13:31

根据PEP 257的规定,命令行脚本的文档字符串应该是它的使用说明。

一个脚本(独立程序)的文档字符串应该可以作为它的“使用”信息,当脚本被错误地调用或者缺少参数时(或者用“-h”选项请求帮助时)会打印出来。这样的文档字符串应该描述脚本的功能和命令行的语法、环境变量和文件。使用说明可以相当详细(可能有好几屏内容),应该足够让新用户正确使用这个命令,同时也能为高级用户提供所有选项和参数的快速参考。

所以我的文档字符串可能看起来像这样:

<tool name> <copyright info>

Usage: <prog name> [options] [args]

some text explaining the usage...

Options:
  -h, --help  show this help message and exit
   ...

现在我想使用optparse模块。optparse会生成“选项”部分和一个解释命令行语法的“使用”说明:

from optparse import OptionParser

if __name__ == "__main__":
    parser = OptionParser()
    (options, args) = parser.parse_args() 

所以用“-h”标志调用脚本时会打印:

Usage: script.py [options]

Options:
    -h, --help  show this help message and exit

这可以修改成如下:

parser = OptionParser(usage="Usage: %prog [options] [args]",
                      description="some text explaining the usage...")

结果是

Usage: script.py [options] [args]

some text explaining the usage...

Options:
  -h, --help  show this help message and exit

但是我该如何在这里使用文档字符串呢?把文档字符串作为使用说明有两个问题。

  1. 如果文档字符串不以“Usage: ”开头,optparse会在前面加上“Usage: ”
  2. 文档字符串中必须使用占位符'%prog'

结果

根据回答来看,似乎没有办法重用optparse模块所期望的文档字符串。所以剩下的选择是手动解析文档字符串并构建OptionParser。(所以我会接受S.Loot的回答)

“Usage: ”部分是由IndentedHelpFormatter引入的,可以通过OptionParser.__init__()中的formatter参数进行替换。

3 个回答

1

我觉得我们在看这个PEP的建议时要保持理智。我认为把模块的__doc__设置为一个简短的描述,总结一下长篇的用法,这样是可以的。不过如果你是个追求完美的人:

'''<tool name>

The full description and usage can be generated by optparse module.

Description: ...

'''

...

# Generate usage and options using optparse.
usage, options = ... 

# Modify the docstring on the fly.
docstring = __doc__.split('\n\n')
docstring[1:2] = [__license__, usage, options]
__doc__ = '\n\n'.join(docstring)
6

我写了一个模块 docopt,正好可以满足你的需求——在文档字符串中写使用说明,并保持代码的简洁性(DRY)。

这个模块还可以让你完全避免写那些繁琐的 OptionParser 代码,因为 docopt 会根据使用说明自动生成解析器。

你可以去看看:http://github.com/docopt/docopt

"""Naval Fate.

Usage:
  naval_fate.py ship new <name>...
  naval_fate.py ship [<name>] move <x> <y> [--speed=<kn>]
  naval_fate.py ship shoot <x> <y>
  naval_fate.py mine (set|remove) <x> <y> [--moored|--drifting]
  naval_fate.py -h | --help
  naval_fate.py --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --speed=<kn>  Speed in knots [default: 10].
  --moored      Moored (anchored) mine.
  --drifting    Drifting mine.

"""
from docopt import docopt


if __name__ == '__main__':
    arguments = docopt(__doc__, version='Naval Fate 2.0')
    print(arguments)
4

选择一:复制粘贴。这种方法虽然不够简洁,但可以用。

选择二:解析你自己的文档字符串,去掉描述段落。描述段落总是第二段,所以你可以用'\n\n'来分割。

usage, description= __doc__.split('\n\n')[:2]

因为optparse会自动生成使用说明,所以你可能不想给它提供使用说明的句子。你自己写的使用说明可能会有错误。如果你坚持要给optparse提供一个使用说明字符串,我就不告诉你怎么去掉上面生成的usage字符串前面的"Usage: "部分,留给你自己去研究。

撰写回答