跳到主要内容

从 Python docstring 上手并启程

pyDocusaurus 主要面向使用 VS Code 的开发者,同时采用了 Markdown 格式的 docstring 风格。基本上,本模块包遵循以下原则:

  • 不需要在 docstring 里标记类型: 用户可以将类型提示作为可选内容写在 docstring 中,但这些 docstring 中的类型提示,仅仅作为原生类型提示不全时的备选项。文档对类型的解析主要还是来自Python的类型提示系统.

  • VS Code 兼容性: pyDocusaurus 不遵守 PEP 287 docstring 规则,这是因为该规则是为 rst 风格的文字设计的。取而代之的是,我们采纳了 VS Code 的风格。本教程将会从如何撰写这种 docstring开始介绍。

  • “对半式”的解析: 目前,pyDocusaurus 同时使用了针对源代码的 AST 分析和运行时检查、来抓取类型提示和 docstring。用户需要确保待分析、转换的模块包,可以与 pyDocusaurus 同时运行。

信息

这意味着 pyDocusaurus 需要:

  1. 你的待分析模块包可以在 Python>=3.14 运行时工作
  2. 你的待分析模块包同时包含了源代码和 docstring,而非预翻译后的字节码.pyc文件。
  3. 你的待分析模块包不是用 C 写就的(以 .pyd 文件为例)。

就目前而言,pyDocusaurus 还没有在.pyi文件上测试过。因此,还是建议将 docstring 和类型提示(源码)写在一起。

1. 为模块写 docstring​

pyDocusaurus 读取如下所示的模块包/模块 docstring:

example_module.py
# -*- coding: UTF-8 -*-
"""
Module Title
============

Author
------
My name
myemail@gmail.com

License
-------
MIT License

Description
-----------
Markdown description text.

Support **multi-line** and complicated formats.

```sh
example-command
```
"""

import a_module

...
提示

pyDocusaurus 使用章节标题、例如"Auhtor"和"License"、来探测有效域。不同章节之间的顺序可以交换。

2. 为函数写 docstring​

pyDocusaurus 读取如下所示的函数/方法 docstring:

def example_func(arg1: str, arg2: int = 0) -> float:
"""An example function.

Arguments
---------
arg1: `str`
The first argument.

arg2: `int`
The second argument.

Returns
-------
#1: `float`
The returned value.
"""
raise NotImplementedError

简而言之,一个函数的 docstring 包含三部分:描述,参数,以及返回值。如果函数的签名中包含某一参数,而该参数又没有在 docstring 中提及,那么 pyDocusaurus 就会检测到这种遗漏并发出警告。如果函数没有参数、或是没有返回值(亦即是说返回值是None),则对应的 docstring 章节可以略过。

提示

pyDocusaurus 使用形如"Arguments"和"Returns"的章节标题,来将原始的 docstring 划分成不同部分。不同章节之间的顺序可以交换。

提示

支持的章节标题有:

  • Arguments, Argument, Parameters, Parameters

  • Returns, Return, Results, Result

2.1. 为迭代器写 docstring​

如果用户意在写一个迭代器函数,需要特别注意到额是,该函数需要显式地调用yield ...,例如:

def example_iterator_func(
arg1: str, arg2: int
) -> Iterator[tuple[str, str, Iterator[tuple[str]]]]:
"""An example function iteratively return items.

Arguments
---------
arg1: `str`
The first argument.

arg2: `int`
The second argument.

Yields
------
#1: `str`
The first iterator item.

#2: `str`
The second iterator item.

#3: `Iterator[tuple[str]]`
The third iterator item a nested iterator.
"""
yield NotImplemented
raise NotImplementedError
提示

在此情形下,用户需要确保

  1. 显示调用了yield。
  2. 返回值的类型标注是Iterator、Generator,或其对应的异步版本。
  3. 在 docstring 中,使用Yields、而非Returns,来作为返回值的章节标题。

3. 为类写 docstring​

pyDocusaurus 读取如下所示的类 docstring:

class ExampleClass:
"""An example class which is a mixture of methods, properties, and operators."""

def __init__(self, key: str, val: int = 10) -> None:
"""Initialization.

Arguments
---------
key: `str`
The first key to be configured.

val: `int`
The first value of the given key.
"""
raise NotImplementedError

@property
def prop(self) -> int:
"""A vanilla property with annotation."""
raise NotImplementedError

def method(self, arg: int, /, arg_any: Any, *, arg_key: str = "test") -> str:
"""A vanilla instance method.

Arguments
---------
arg: `int`
The input argument that is positional-only.

arg_any: `Any`
The input argument without specified type.

arg_key: `str`
The input argument that is keyword-only.

Returns
-------
#1: `str`
The returned value.
"""
raise NotImplementedError

总之,类顶端的 docstring,会被解析成多行的 Markdown 文本,并收录为针对类本身的描述。其他规则包括:

  1. 函数和运算符的 docstring 其解析规则和前述的“函数风格”一致。针对(实例)方法和类方法,第一个参数 self/cls 在 docstring 中应当略去不提。

  2. 类的初始化方法将会用来解析类的参数。

  3. 属性或缓存属性的 docstring 解析为多行 Markdown 文本。另外,即使属性实现了setter或deleter方法,其 docstring 也只会从默认的 getter 方法中提取。

4. 为数据类写 docstring​

在PEP 224中,有人提出了一个用作“成员 (attribute) 的 docstring”的提案,但被委员会无情拒绝了。然而,该提案现在已经成了 VS Code 用来补充常规 docstring 的一种事实标准。pyDocusaurus 会从源码中直接读取这类 docstring。此处的例子,展示了如何基于该提案的规则,注释PyDantic模型:

class ExampleModel(BaseModel):
"""An example PyDantic model that is used for testing the docstring extraction.

The initialization function are synthesized from the field definitions.
"""

val_1: int
"""The most plain field that is required."""

val_2: str = ""
"""An optional field with a default value."""

val_3: int | str = Field(default="")
"""A union field with the default value specified by the `Field` creator."""

val_4: list[str] = Field(default_factory=list)
"""A list field with a default value specified by the factory function."""

val_5: Annotated[list[str], Field(default_factory=list)]
"""A list field with the default value specified by the annotation."""

尽管 PyDantic 提供了专门用于撰写字段 docstring 的成员Field.description,本模块包却并不采纳这种运行时解决方案。因为这类 docstring 无法被静态类型检查器感知到。相比之下,本项目更倾向于上述的“attribute-docstring”风格,因为 VS Code 可以探知到这类注释。

5. 为类型写 docstrings​

PEP 224 也适用于类型别名和变量的 docstring。目前,pyDocusaurus 针对类型别名的定义,实现了对其 docstring 的探知。

注意

换言之,目前,还不支持对全局变量的 docstring 探知与解析。

假设存在以下模块:

example_aliases.py
CustomType: TypeAlias = int | str
"""docstring for CustomType"""

type SpecifiedList = list[str]
"""docstring for SpecifiedList"""

此处,这个范例模块中含有两个类型别名,各自使用了不同的定义方法。这两种情形 pyDocusaurus 均能探知到。