用 autodoc 编写 Python 文档¶
Guidedog 通过读取源代码生成 Python 文档,不会导入软件包。docstring 和函数签名成为文档图的一部分。仅在运行时创建的对象需要显式说明。这是有意划定的边界。
配置方法¶
列出扩展,并告诉 Guidedog 软件包相对于源目录的位置,就像 sys.path 告诉 Python 那样:
extensions = ["sphinx.ext.autodoc"]
autodoc_source_paths = ["../src"]
guidedog migrate 会读取 conf.py 中用于定位软件包的语句,例如 sys.path.insert(0, os.path.abspath('..')),并为你生成 autodoc_source_paths。对于安装在其他位置的软件包,必须添加其所在目录,例如依赖项的源代码检出目录;Guidedog 不会搜索 Python 自己的 site-packages。
接下来,这些指令的用法与 Sphinx 相同:
.. automodule:: shop.cart
:members:
:show-inheritance:
.. autoclass:: shop.Cart
:members: add, remove
:inherited-members:
支持 Sphinx 9.1 的全部选项,含义也相同::members:、:undoc-members:、:private-members:、:special-members:、:inherited-members:、:exclude-members:、:member-order:、:show-inheritance:、:imported-members:、:ignore-module-all:、:class-doc-from:、:no-value:、:annotation:、:no-index:、:no-index-entry:、:synopsis:、:platform:、:deprecated:,以及取消 autodoc_default_options 的 no- 形式。
设置¶
这些 conf.toml 设置沿用 Sphinx 的名称、取值和默认值。
autodoc_source_paths-
Guidedog 专用设置:按顺序列出查找 Python 模块的目录,路径相对于源文件目录。只读取这些目录下的文件;若符号链接指向目录外部,则拒绝访问。
autoclass_content-
指定用于描述类的文档字符串:
"class"(默认)、"init"或"both"。 autodoc_class_signature-
"mixed"(默认)或"separated";后者将__init__单独作为方法记入文档。 autodoc_default_options-
为所有指令设置默认选项的表,例如
members = true或member-order = "bysource"。值为false时不添加该选项。 autodoc_docstring_signature-
true(默认):若文档字符串首行为name(args) -> result这样的形式,则将其作为签名。 autodoc_inherit_docstrings-
true(默认):没有文档字符串的成员继承基类的文档字符串。 autodoc_member_order-
"alphabetical"(默认)、"groupwise"或"bysource"。 autodoc_preserve_defaults-
false(默认):按repr()的表示方式显示默认值;true则保留源码中的写法。 autodoc_typehints-
"signature"(默认)、"description"(使用:type:和:rtype:字段)、"both"或"none"。 autodoc_typehints_description_target-
"all"(默认)、"documented"或"documented_params"。 autodoc_typehints_format-
"short"(默认)或"fully-qualified"。 autodoc_use_type_comments-
true(默认):将# type:注释作为类型注解。 strip_signature_backslash-
与 Sphinx 一样,将签名中的反斜杠加倍。
autodoc_mock_imports,autodoc_type_aliases,autodoc_warningiserror-
接受这些设置。由于不会导入模块,因此无需模拟导入;不应用类型别名。
Guidedog 如何推断 Python 的行为¶
名称按照 Python 的绑定规则解析。例如,在包的 __init__.py 中写入 from .app import Flask,flask.Flask 就指向 flask/app.py 中定义的类,autodoc 会标注 :canonical: flask.app.Flask。基类也按相同方式解析;方法解析顺序按 Python 的规则计算,继承的成员从基类源码中读取。仅在 if TYPE_CHECKING: 下导入的名称在运行时不存在,因此引用这些名称的注解会保留原样,与 Sphinx 一致。
文档字符串与 Python 实际存储的内容一致:处理转义,并按 Python 3.13 编译器的规则去除缩进。属性文档来自 Sphinx 分析器读取的位置:赋值行末尾或上方的 #: 注释,以及赋值后紧接的字符串字面量。函数签名来自 def。类的签名按照 Python 的查找规则来自元类的 __call__、__new__ 或 __init__;dataclass 或 NamedTuple 的签名来自字段。@overload 变体会替代实现本身的签名。
不运行 Python 就无法获知的内容¶
如果源码无法揭示 Python 的实际行为,Guidedog 会明确指出涉及的对象。无法生成文档时给出警告;能生成文档但信息少于 Sphinx 时给出说明,使用 -v 可查看。
- 编译后的模块(
.so、.pyd)没有可读取的源码,因此找不到其中的对象。 - 运行代码得到的值(
app = Flask(__name__)、now = datetime.now())不会生成:value:;需要计算的默认值保留源码写法。 - 由源码搜索路径之外的装饰器(
@click.command())生成的函数,按被装饰的原函数生成文档。 - 源码搜索路径之外的包中的类,使用导入时的路径命名;该路径可能与定义类的模块不同。它的成员、构造函数签名以及可继承的文档字符串都无法确定。
- 无法读取内置类型的文档字符串。因此,如果某个方法没有自己的文档字符串,只能从
dict继承,就不将它列入文档。 - 不会执行处理 autodoc 事件(
autodoc-process-docstring、autodoc-skip-member等)的 Python 扩展。 - 嵌套深度远超正常代码的模块不会进行分析,并给出
autodoc.too_deep警告。Python 本身拒绝 200 层嵌套括号;Guidedog 也拒绝含数百个操作数的+链。 - 会产生倍增的工作量都有上限。沿导入和别名查找名称时,单条路径最多 48 步,总计最多 100,000 步。类型别名或常量展开最多 10,000 步,超过后保留源码写法。类的 MRO 最多追踪 100 层;单条指令最多生成 10,000 条指令,超过时给出
autodoc.limit警告。只有以自身名称包含自身的类才会达到最后这一上限。
将缺失包的源码目录加入 autodoc_source_paths,即可解决上述大部分限制。
重新构建¶
autodoc 指令读取的每个 Python 文件都是该页面的依赖项。修改文件后,下次构建会重新读取页面。未找到对象的页面会在每次构建时重读,因此源码中出现该对象后即可识别。
安全性¶
构建文档绝不运行项目代码。Guidedog 将 Python 源码作为文本读取,并使用与 include 相同的路径限制:仅访问 autodoc_source_paths 下的文件,不通过链接访问外部路径。