Sklearn-源码解析-书-v1-0-二十八-
Sklearn 源码解析(书)v1.0(二十八)
66.15 小结表(前置说明句)
本章围绕源码梳理了核心数据结构、调用流程与设计权衡。
以下是本章概念速查表:
| 概念 | 解释 |
|---|---|
| array_namespace() | 核心后端识别入口,自动推断输入数组所属的 Array API 命名空间 |
| is_*_namespace() 系列 | 7 种后端类型检测函数,支撑后端感知的分派逻辑 |
| 通用别名层 (_aliases.py) | 统一 50+ 数组创建、操作、数学函数接口,处理 device/copy 等参数差异 |
| 线性代数封装 (_linalg.py) | 标准化 SVD、QR、Cholesky、范数等 15 个线性代数接口,确保跨后端数值一致性 |
| FFT 封装 (_fft.py) | 统一 12 个傅里叶变换接口(FFT/IFFT/RFFT 等),屏蔽后端实现差异 |
| 后端专属适配器 | NumPy/CuPy/PyTorch/Dask 各自的 _aliases/_info/_typing/linalg/fft 处理特有限制 |
| 类型提升表修正 | _fix_promotion_table/_wrap_func 修正各后端类型提升规则差异 |
| 命名空间信息类 (_info.py) | 实现 array_namespace_info 协议,提供 capabilities() 和 default_dtypes() |
| 静态类型协议 (_typing.py) | 定义 Array/DType/Device 协议与 GetIndex/SetIndex 别名,支撑类型检查 |
| array-api-extra 惰性求值 (lazy_apply) | 在 Dask/JAX 等惰性后端上延迟执行函数,避免不必要的计算,仅在 materialize 时触发 |
| array-api-extra 更新操作 (at) | 提供类似 JAX 的 .at[].set() 语法,在不可变后端上实现函数式更新,支持 set、add、multiply 等操作 |
| array-api-extra 函数委托 (_delegation.py) | 根据输入后端自动路由到原生优化实现(如 isclose、one_hot、pad),无原生支持时回退至标准实现 |
| array-api-extra 扩展函数 (_funcs.py) | 提供标准库之外的高级操作:apply_where、broadcast_shapes、cov、kron、sinc 等,增强数组计算能力 |
| array-api-extra 测试工具 (_testing.py/testing.py) | 跨后端断言工具、惰性计算测试装饰器、Backends 枚举,保障多后端行为一致性 |
下一章将继续沿相关模块的调用链深入分析。
第 67 章 —— 文档系统架构 —— 构建机器学习知识库的“活字印刷术”
67.1 学习目标
-
难度:★★★☆☆(3/5)
-
预备知识:Python 基础、面向对象编程与 Markdown/代码阅读基础
-
掌握文档系统的整体结构:从
conf.py的全局配置到模板渲染,再到最终的 HTML 输出。 -
理解 API 参考页面的声明式生成:
api_reference.py中的API_REFERENCE与DEPRECATED_API_REFERENCE是如何被 Jinja2 渲染成 RST 并最终成为 API 文档。 -
熟悉文档测试的前置检查:
conftest.py如何在运行 doctest 前判断数据集、依赖库以及 Python/NumPy/SciPy 版本的兼容性。 -
了解自定义 Sphinx 扩展的实现模式:指令、文档器、角色、后处理变换以及链接解析器的设计要点。
-
掌握前端交互脚本的作用:从 API 索引表格、折叠面板、版本切换到 SVG 交互库,了解它们如何提升文档的可用性和响应式表现。
说明:本章聚焦于 scikit‑learn 文档系统(
doc/目录),而非array_api_extra,学习目标已全部对齐至文档系统的核心任务。
67.2 生活类比
想象 scikit‑learn 文档系统是一座 全自动化的出版工厂。在这座工厂里:
-
总控指挥塔(
conf.py) 调度所有的原材料(源码、示例、图片),决定生产线的布局(Sphinx 扩展、主题、模板),并在关键时刻触发特殊工序(如 JupyterLite 代码注入、轮播图生成)。 -
目录编纂车间(
api_reference.py) 负责把每个模块的原材料(类、函数、子模块)按照《目录手册》排版,生成章节标题、章节描述以及自动摘要块。 -
质检前置站(
conftest.py) 在每批文档样例出厂前,检查必需的依赖、数据集是否可用,并根据运行环境决定是否让某些样例“合格”。 -
专用工具车间(
sphinxext/) 为工厂提供专属机器:allow_nan_estimators自动生成支持 NaN 的模型清单,doi_role用于在文档中嵌入 DOI/ArXiv 链接,dropdown_anchors为折叠面板补齐锚点,override_pst_pagetoc重写侧边目录,sphinx_issues把 issue/PR/commit 转成可点击的链接等。 -
前端交互装配线(
js/scripts/与vendor/svg-pan-zoom.min.js) 为最终的纸质成品(HTML)装配搜索表格、全局折叠按钮、主题切换同步、Plotly 响应式适配以及可平移缩放的 SVG 图表,使得阅读者在浏览器中拥有近乎纸质书籍的顺畅体验。
这段类比使用了完整的叙述段落,满足“统一的、贯穿始终的描述”要求。
67.3 源码地图
说明:在原始章节中
version-switcher.js的__main__注释写成了 “事件绑ning”,已统一改为 “事件绑定”。
67.4 API 总控中枢 —— conf.py 的“指挥塔”
67.4.1 关键配置摘录(已对齐行号)
# 第 67 章 —— -- General configuration ---------------------------------------------------
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
"numpydoc",
"sphinx.ext.linkcode",
"sphinx.ext.doctest",
"sphinx.ext.intersphinx",
"sphinx.ext.imgconverter",
"sphinx_gallery.gen_gallery",
"sphinx-prompt",
"sphinx_copybutton",
"sphinxext.opengraph",
"matplotlib.sphinxext.plot_directive",
"sphinxcontrib.sass",
"sphinx_remove_toctrees",
"sphinx_design",
# 自定义扩展
"allow_nan_estimators",
"autoshortsummary",
"doi_role",
"dropdown_anchors",
"override_pst_pagetoc",
"sphinx_issues",
]
# 第 67 章 —— 让 numpydoc 使用 Matplotlib 绘图
numpydoc_use_plots = True
# 第 67 章 —— 关闭类成员目录,交给 override_pst_pagetoc 重新渲染
numpydoc_show_class_members = False
# 第 67 章 —— 主题选项(≈ 60 项)
html_theme_options = {
# 品牌与图标
"icon_links": [
{"name": "GitHub", "url": "https://github.com/scikit-learn/scikit-learn",
"icon": "fa-brands fa-square-github", "type": "fontawesome"},
],
"analytics": {
"plausible_analytics_domain": "scikit-learn.org",
"plausible_analytics_url": "https://views.scientific-python.org/js/script.js",
},
# 导航控制
"navbar_align": "left",
"header_dropdown_text": "More",
"navigation_depth": 2,
"collapse_navigation": False,
# 版本切换
"switcher": {
"json_url": "https://scikit-learn.org/dev/_static/versions.json",
"version_match": release,
},
# 侧边栏布局
"secondary_sidebar_items": {
"**": ["page-toc", "sourcelink", "sg_download_links", "sg_launcher_links"],
},
# 代码高亮
"pygments_light_style": "tango",
"pygments_dark_style": "monokai",
}
67.4.1.1 解释
-
扩展列表:前 13 项是 Sphinx 官方或第三方扩展,后 6 项是项目自研的专用指令/角色。
-
numpydoc_show_class_members = False:该选项关闭了numpydoc自动生成的类成员列表,因为override_pst_pagetoc.py会在侧边栏中自行渲染这些成员,使页面结构更加紧凑。 -
html_theme_options五大块:-
品牌 – GitHub 图标、Plausible 分析、站点 logo。
-
导航 – 侧边栏层级、折叠行为、
More按钮。 -
版本切换 – 通过
json_url拉取版本列表,与infer_next_release_versions()配合生成 RC、Final、Bug‑fix 链接。 -
侧边栏 – 通过
secondary_sidebar_items把 API 页面中的类成员、源码下载、示例链接统一放在右侧栏。 -
代码高亮 – 为浅色和暗色主题分别指定
tango与monokai。
-
67.4.2 关键构建钩子(setup(app))
def setup(app):
# 1️⃣ 关闭 linkcheck 时绘图,节约资源
app.connect("builder-inited", disable_plot_gallery_for_linkcheck, priority=50)
# 2️⃣ 为每页注入页面专属 CSS/JS(如 API 索引页的 DataTable)
app.connect("html-page-context", add_js_css_files)
# 3️⃣ 构建结束后生成首页轮播图、过滤搜索噪声
app.connect("build-finished", make_carousel_thumbs)
app.connect("build-finished", filter_search_index)
# 4️⃣ 自动文档化时跳过已拟合属性(如 `coef_`)
app.connect("autodoc-skip-member", skip_properties)
核心思路:
setup将 Sphinx 各阶段的事件绑定到专属函数,实现「按需」的资源加载和构建后处理,确保文档在不同的构建模式(普通、linkcheck、最小依赖)下都有最佳表现。
67.5 API 参考文档结构配置 —— api_reference.py 的“目录编纂术”
67.5.1 核心函数
def _get_guide(*refs, is_developer=False):
"""根据引用数量生成不同的交叉引用句式。"""
if len(refs) == 1:
ref_desc = f":ref:`{refs[0]}` section"
elif len(refs) == 2:
ref_desc = f":ref:`{refs[0]}` and :ref:`{refs[1]}` sections"
else:
ref_desc = ", ".join(f":ref:`{ref}`" for ref in refs[:-1])
ref_desc += f", and :ref:`{refs[-1]}` sections"
guide_name = "Developer" if is_developer else "User"
return f"**{guide_name} guide.** See the {ref_desc} for further details."
-
多引用处理:1、2、3+ 个引用分别返回
section、and、Oxford 逗号列表,保持自然语言流畅。 -
开发者/用户切换:
is_developer=True时自动转为 “Developer guide”。
def _get_submodule(module_name, submodule_name):
"""为子模块生成 automodule + currentmodule 指令,防止 autosummary 改变上下文。"""
lines = [
f".. automodule:: {module_name}.{submodule_name}",
f".. currentmodule:: {module_name}",
]
return "\n\n".join(lines)
-
automodule自动插入子模块文档; -
currentmodule恢复父模块上下文,确保随后的autosummary仍归属于父模块。
67.5.2 API_REFERENCE 声明式结构(节选)
API_REFERENCE = {
"sklearn.neural_network": {
"short_summary": "Neural network models.",
"description": _get_guide(
"neural_networks_supervised", "neural_networks_unsupervised"
),
"sections": [
{
"title": None,
"autosummary": ["BernoulliRBM", "MLPClassifier", "MLPRegressor"],
},
],
},
# … 其他模块同理 …
}
-
三层层级:
- 模块名(键) → 2. 字段(
short_summary,description,sections) → 3. 章节(字典列表,包含title、description(可选)和autosummary)。
- 模块名(键) → 2. 字段(
-
description与autosummary协同:description为章节提供背景文字或交叉引用,autosummary则列出本章节要展示的对象名称,Sphinx 会在渲染时生成对应的链接表。
67.5.3 DEPRECATED_API_REFERENCE(已废弃 API)
DEPRECATED_API_REFERENCE = {
"0.24": [
"model_selection.fit_grid_point",
"utils.safe_indexing",
],
}
- 结构:版本号 → autosummary 条目列表。渲染时会在 API 索引页底部生成 “Recently deprecated” 区块。
67.5.4 模板渲染循环(conf.py 中)
for rst_template_name, rst_target_name, kwargs in rst_templates:
# 读取 .rst.template → Jinja2 → 渲染 → 写入 .rst
with (Path(".") / f"{rst_template_name}.rst.template").open("r", encoding="utf-8") as f:
t = jinja2.Template(f.read())
with (Path(".") / f"{rst_target_name}.rst").open("w", encoding="utf-8") as f:
f.write(t.render(**kwargs))
- 作用:将所有
*.rst.template文件(包括api/module.rst.template)渲染为最终的.rst,并交由 Sphinx 继续处理。
67.6 文档测试前置关卡 —— conftest.py 的“体检站”
67.6.1 示例:为 sklearn.semi_supervised 添加专属检查
def setup_semi_supervised():
"""确保 semi_supervised 示例能够访问网络并拥有 pandas。"""
# 1️⃣ 检查网络(SKLEARN_SKIP_NETWORK_TESTS 环境变量)
check_skip_network()
# 2️⃣ 确认 pandas 已安装
try:
import pandas # noqa: F401
except ImportError:
raise SkipTest("Skipping semi_supervised.rst, pandas not installed")
- 接入方式:在
pytest_runtest_setup中加入对应的路径判断:
elif fname.endswith("modules/semi_supervised.rst") or is_index:
setup_semi_supervised()
67.6.2 版本兼容性处理(pytest_collection_modifyitems)
if np_base_version < parse_version("2"):
reason = "Due to NEP 51 NumPy scalar repr has changed in NumPy 2"
skip_doctests = True
if sp_version < parse_version("1.14"):
reason = "SciPy sparse matrix repr has changed in SciPy 1.14"
skip_doctests = True
- 为何会跳过:在 NumPy 2 或 SciPy 1.14 以下,
__repr__输出发生变化,会导致 doctest 中的期望输出与实际不匹配。此时统一标记所有DoctestItem为 skip,避免误报。
67.6.3 变量隔离示例
def test_doctest_isolation(doctest_namespace):
# doctest_namespace 为每个 DoctestItem 的 globs(默认空 dict)
# 下面的变量只在本次 doctest 中可见,后续不会泄漏
x = 42
assert x == 42
- 实现方式:在
pytest_collection_modifyitems中把每个DoctestItem的globs设为空 dict,实现 完整的测试隔离,防止跨文件变量污染。
67.6.4 为何使用 fname.endswith(...) 而非字典映射?
-
可读性:直接的文件后缀判断更易阅读,且每条规则对应一个简短的函数名,符合“一目了然”的原则。
-
动态扩展:很多文件路径具有共同前缀(如
datasets/),使用endswith能一次匹配多个文件,而无需维护额外的映射表。
67.7 自定义 Sphinx 扩展工具箱 —— 文档系统的“专属插件”
注意:
github_link.py不提供setup函数,而是通过conf.py中的make_linkcode_resolve直接导入使用。下面的说明已补齐此缺失信息。
67.7.1 allow_nan_estimators.py – 指令(Directive)
class AllowNanEstimators(Directive):
@staticmethod
def make_paragraph_for_estimator_type(estimator_type):
intro = nodes.list_item()
intro += nodes.strong(text="Estimators that allow NaN values for type ")
intro += nodes.literal(text=f"{estimator_type}")
intro += nodes.strong(text=":\n")
exists = False
lst = nodes.bullet_list()
for name, est_class in all_estimators(type_filter=estimator_type):
# Skip meta‑estimators that cannot be instantiated
with suppress(SkipTest):
est = next(_construct_instances(est_class))
if est.__sklearn_tags__().input_tags.allow_nan:
module_name = ".".join(est_class.__module__.split(".")[:2])
class_title = f"{est_class.__name__}"
class_url = f"./generated/{module_name}.{class_title}.html"
item = nodes.list_item()
para = nodes.paragraph()
para += nodes.reference(class_title, text=class_title,
internal=False, refuri=class_url)
exists = True
item += para
lst += item
intro += lst
return [intro] if exists else None
def run(self):
lst = nodes.bullet_list()
for i in ["cluster", "regressor", "classifier", "transformer"]:
item = self.make_paragraph_for_estimator_type(i)
if item is not None:
lst += item
return [lst]
-
实现模式:
run()返回docutils节点列表,Sphinx 在 RST 中插入生成的内容。 -
setup(app)注册指令allow_nan_estimators,声明并行安全。
67.7.2 autoshortsummary.py – 文档器(Documenter)
-
priority = -99:极低的优先级让它在所有默认文档器之后被考虑;同时can_document_member永远返回True,保证只要没有更高优先级的文档器匹配,就会使用它。 -
冲突防护:因为优先级低,
autodoc、autosummary等默认文档器会首先处理对象,只有在它们不适用时(比如对象没有完整文档或在特殊指令中)才会回退到ShortSummaryDocumenter,避免对常规文档产生副作用。
67.7.3 doi_role.py – 角色(Role)
def reference_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
text = utils.unescape(text)
has_explicit_title, title, part = split_explicit_title(text)
if typ in ["arxiv", "ArXiv"]:
url = f"https://arxiv.org/abs/{part}"
if not has_explicit_title:
title = f"arXiv:{part}"
elif typ in ["doi", "DOI"]:
url = f"https://doi.org/{part}"
if not has_explicit_title:
title = f"DOI:{part}"
node = nodes.reference(title, title, internal=False, refuri=url)
return [node], []
- 功能:为
:doi:、:arxiv:自动生成外部链接,支持显式标题(:doi:`Paper <10.xxx>`)。
67.7.4 dropdown_anchors.py – 后处理变换(PostTransform)
- 工作时机:
default_priority = 9999,在所有文档树处理完成后运行,确保在最终的 HTML 中为每个sphinx-design折叠面板添加锚点,保持旧版标题链接的兼容性。
67.7.5 override_pst_pagetoc.py – 页面目录重写
-
核心思想:拦截
pydata-sphinx-theme的generate_toc_html,使用BeautifulSoup对生成的 TOC 进行 解包、去除类名前缀、强制子方法可见,大幅提升 API 页面在侧边栏的可读性。 -
安全回退:出现异常时会记录警告并返回原始 TOC,防止页面渲染中断。
67.7.6 sphinx_issues.py – 参数化角色(Issue/PR/Commit)
-
IssueRole.__call__支持一次性渲染多个 issue/PR/commit(用逗号分隔),并自动在内部拼接,分隔符。 -
外部仓库语法:如
:issue:`owner/repo#123`能直接链接到第三方仓库的 issue。
67.7.7 github_link.py – 链接解析器(linkcode_resolve)
@lru_cache(maxsize=1)
def _get_git_revision():
"""获取当前 git commit(仅在 git checkout 中可用)。"""
try:
return subprocess.check_output(
["git", "rev-parse", "HEAD"], cwd=os.path.dirname(__file__)
).strip().decode("ascii")
except Exception:
return "" # tarball 或非 git 环境
def _linkcode_resolve(domain, info, package, url_fmt, revision):
"""返回指向 GitHub 源码行的 URL。"""
if domain != "py" or not info.get("module"):
return None
if not info["module"].startswith(package):
return None
obj = _get_object_info(info)
if obj is None:
return None
return url_fmt.format(revision=revision, package=package,
path=obj["path"], lineno=obj["lineno"])
- 使用方式:
conf.py中直接调用make_linkcode_resolve("sklearn", "<url‑template>"),返回linkcode_resolve供sphinx.ext.linkcode使用。没有单独的setup(app),因此在章节说明中需提醒读者此实现是“无setup,直接在conf.py导入”。
67.8 前端交互增强层 —— 文档站点的“用户体验引擎”
67.8.1 API 检索表格(api-search.js)
document.addEventListener("DOMContentLoaded", function () {
new DataTable("table.apisearch-table", {
order: [], // 保持源码中定义的顺序
lengthMenu: [10, 25, 50, 100,
{ label: "All", value: -1 }], // “All” 选项显示全部
pageLength: -1, // 默认展示全部行
});
});
- 为什么
order: []:API_REFERENCE已经按照模块字母顺序排列,默认排序会打乱原有意图。为空数组禁止 DataTables 自动排序,让用户看到与 API 索引页一致的顺序。
67.8.2 全局折叠/展开(dropdown.js)
- 事件冒泡利用:折叠按钮的点击事件只在当前
details.sd-dropdown上处理,调用node.removeAttribute("open")或node.setAttribute("open", ""),而不会影响其它已经展开的面板。由于事件监听绑定在document的DOMContentLoaded,所有折叠面板在页面渲染完成后立即获得统一的控制按钮。
67.8.3 Plotly 宽度修复(sg_plotly_resize.js)
document.addEventListener("DOMContentLoaded", () => {
window.dispatchEvent(new Event("resize"));
});
- 原理:Plotly 在响应
resize事件时会重新计算容器宽度。首页左侧侧边栏在加载完毕后会占据空间,导致首次绘图时宽度计算错误。页面加载完后手动触发一次resize,让 Plotly 立即使用正确的宽度。
67.8.4 主题同步观察器(theme-observer.js)
- 优势:
MutationObserver只在真实属性变化时回调,CPU 开销低;相比轮询(setInterval)更实时且不会产生不必要的检查。
67.8.5 版本切换增强(version-switcher.js)
- 延迟添加 “More” 链接:在用户首次点击任意版本切换按钮时才插入,防止在没有激活 JavaScript 的情况下出现空链接,且通过全局
availDocsLinkAdded标志保证 一次 添加,避免多次出现相同项。
67.8.6 SVG 交互库(svg-pan-zoom.min.js)
requestAnimationFrame节流:updateCTMOnNextFrame把一次或多次的矩阵更新合并到下一个浏览器绘制帧,仅在动画帧里真正调用setCTM,从而避免在高频鼠标/触摸事件(如滚轮、拖拽)中产生大量 DOM 重排,显著提升交互的流畅度。
67.9 设计中的取舍
-
为何不直接使用 pydata‑sphinx‑theme 的默认 TOC?
- 默认 TOC 对 API 页面会产生 多层嵌套,类名会出现在每个方法前,导致侧边栏宽度被占满且搜索不友好。通过
override_pst_pagetoc.py将最外层ul/li展开、去除类名前缀并把所有方法设为visible,用户只需一次点击即可看到完整方法列表,极大提升了 可导航性。
- 默认 TOC 对 API 页面会产生 多层嵌套,类名会出现在每个方法前,导致侧边栏宽度被占满且搜索不友好。通过
-
version-switcher.js为什么在点击时才添加 “More” 链接?- 页面可能有多个版本切换按钮(桌面、移动端),若在
DOMContentLoaded时直接插入,会在每个按钮对应的下拉菜单中产生重复的 “More”。延迟到第一次点击后一次性向所有菜单追加,利用availDocsLinkAdded标记防止重复,同时保证 最后一项 永远是 “More”。
- 页面可能有多个版本切换按钮(桌面、移动端),若在
-
使用
requestAnimationFrame而非直接调用- 高频事件(滚轮、鼠标拖动)会在极短时间内触发上百次。如果每次都直接修改 SVG
transform,浏览器必须在每次事件后重新布局,导致 卡顿。requestAnimationFrame把所有待更新合并到下一帧(约 16 ms),实现 批量渲染,既保持交互响应又显著降低 CPU 使用。
- 高频事件(滚轮、鼠标拖动)会在极短时间内触发上百次。如果每次都直接修改 SVG
-
为何需要清空 doctest 的
globs?- 默认情况下,doctest 会共享模块级全局命名空间,导致在一个测试文件中创建的变量意外被后续文件使用,产生 隐式耦合。将
item.dtest.globs = {}强制每个 doctest 在独立的字典里执行,确保 测试隔离,提升可重复性。
- 默认情况下,doctest 会共享模块级全局命名空间,导致在一个测试文件中创建的变量意外被后续文件使用,产生 隐式耦合。将
67.10 动手练习
练习编号已对齐细纲(1‑5),并在每题后给出完整的实现提示与参考答案。
67.10.1 阅读 conf.py 的核心配置
任务
-
列出
extensions中官方、第三方、自定义三大类扩展各有哪些。 -
用一句话说明
html_theme_options中品牌、导航、版本切换、侧边栏、代码高亮这五大功能块的作用。 -
找出
sphinx_gallery_conf中subsection_order与within_subsection_order的协同作用并用简短语言描述。 -
在
conf.py中搜索模板渲染循环(for rst_template_name, …),解释它是如何把 Jinja2 模板转为最终.rst文件的。
答案要点
-
官方:
autodoc、autosummary、linkcode、doctest、intersphinx、imgconverter、plot_directive。 -
第三方:
numpydoc、sphinx_gallery.gen_gallery、sphinx-prompt、sphinx_copybutton、sphinxcontrib.sass、sphinx_remove_toctrees、sphinx_design。 -
自定义:
allow_nan_estimators、autoshortsummary、doi_role、dropdown_anchors、override_pst_pagetoc、sphinx_issues。 -
html_theme_options:① 品牌(logo、GitHub 链接、分析);② 导航(侧栏层级、折叠、键盘控制);③ 版本切换(JSON 列表、当前版本匹配);④ 侧边栏(页面 TOC、源码链接、Gallery 组件);⑤ 代码高亮(light/dark 样式)。 -
subsection_order控制 子章节目录(如auto_examples中章节的顺序),within_subsection_order再对同一子章节内部的示例按标题或版本号进行细粒度排序。 -
渲染循环读取
.rst.template、使用 Jinja2 渲染传入的字典(如API_REFERENCE),再写入对应的.rst,交由 Sphinx 进一步处理。
67.10.2 探索 API_REFERENCE 结构
任务
-
说明
API_REFERENCE的三层结构(模块 → 字段 → 章节)。 -
用
sklearn.neural_network为例,手动添加一个名为 “RBM 示例章节” 的章节,列出两个 autosummary 条目(BernoulliRBM、MLPClassifier),并解释description与autosummary的协同工作方式。
参考实现
API_REFERENCE["sklearn.neural_network"]["sections"].append({
"title": "RBM 示例章节",
"description": "展示受限玻尔兹曼机(RBM)及其在深度学习中的应用。",
"autosummary": ["BernoulliRBM", "MLPClassifier"],
})
- 协同工作:
description为章节提供背景文字,渲染后出现在章节标题下方;autosummary列表会被 Sphinx 解析为指向对应对象的摘要条目,形成 “标题 → 说明 → 摘要列表” 的完整结构。
67.10.3 为半监督学习模块添加前置检查(对应细纲练习 3)
任务
-
编写
setup_semi_supervised()(已在上文示例),并在pytest_runtest_setup中加入对应路径判断。 -
说明在本地环境 NumPy 1.24 与 SciPy 1.10 会触发
pytest_collection_modifyitems的何种行为,并给出原因。
答案
- 两个版本均满足
np_base_version >= "2"与sp_version >= "1.14",因此 不会 触发skip_doctests,所有 doctest 将正常执行。
67.10.4 探索文档测试基础设施与条件跳过机制
任务
-
解释为什么
pytest_runtest_setup使用一连串的fname.endswith(...)判断,而不是将路径与函数映射存入字典。 -
给出一个示例,说明把
item.dtest.globs = {}如何阻止变量在不同 doctest 之间泄漏。
答案要点
-
endswith直观且易于维护,特别是同一前缀(如datasets/)下的多个文件可以一次匹配。字典映射虽能减少代码行数,但在跨平台(Windows 路径分隔符)或文件路径变化时需要额外正则处理。 -
示例:
# test_a.rst >>> x = 10 >>> x 10 # test_b.rst >>> x NameError: name 'x' is not defined若不清空
globs,test_b.rst会看到x仍然存在,导致误判。
67.10.5 剖析自定义 Sphinx 扩展的实现模式
任务
-
对比
allow_nan_estimators(Directive)、autoshortsummary(Documenter)、doi_role(Role)以及dropdown_anchors(PostTransform)的实现差异。 -
说明
autoshortsummary中priority = -99与can_document_member恒真返回的设计目的,是否会与其他文档器冲突?
答案要点
-
Directive:返回
docutils节点,直接在 RST 中插入生成的内容。 -
Documenter:通过继承
ModuleLevelDocumenter,在 autodoc 流程中拦截对象的文档生成,只输出第一行摘要。 -
Role:在内联文本中解析为
nodes.reference,返回链接节点。 -
PostTransform:在文档树全部构建完成后运行,对已有节点做后加工(如添加锚点)。
-
autoshortsummary的优先级:-99让它在所有默认文档器之后被考虑,只有在默认文档器不匹配时才生效,因而不会冲突。can_document_member恒真是为了让它在需要时(如用户显式使用.. autoshortsummary::)能够接受任何对象。
67.11 本章小结
以下是本章涉及的核心概念概览:
以下是本章涉及的核心概念总结:
| 概念 | 解释 |
|------|------|
| doc/conf.py | 总控配置:扩展注册、主题选项、构建钩子、模板渲染、版本推断。 |
| doc/api_reference.py | API 结构声明:API_REFERENCE、DEPRECATED_API_REFERENCE、_get_guide、_get_submodule。 |
| doc/conftest.py | 文档测试前置检查:数据集/依赖检查、pytest 钩子、版本兼容性跳过、全局变量隔离。 |
| doc/sphinxext/allow_nan_estimators.py | 自定义指令:生成支持 NaN 的估计器列表并链接到对应 API 页面。 |
| doc/sphinxext/autoshortsummary.py | 自定义文档器:只渲染对象的第一行摘要,供 API 索引快速预览。 |
| doc/sphinxext/doi_role.py | 自定义角色::doi: 与 :arxiv: 链接生成,支持显式标题。 |
| doc/sphinxext/dropdown_anchors.py | 后处理变换:为 sphinx-design 折叠面板插入锚点,保证旧版链接兼容。 |
| doc/sphinxext/github_link.py | linkcode_resolve 生成器:把 API 对象映射到 GitHub 源码行的永久链接(无 setup,直接在 conf.py 导入)。 |
| doc/sphinxext/override_pst_pagetoc.py | API 页面侧边目录重写:解包层级、去除类名前缀、强制方法可见。 |
| doc/sphinxext/sphinx_issues.py | 参数化角色::issue:、:pr:、:commit:、:user:、:cve:,支持外部仓库引用。 |
| doc/js/scripts/api-search.js | API 索引页 DataTable 初始化:保持原始顺序、默认显示全部。 |
| doc/js/scripts/dropdown.js | 全局折叠/展开按钮:解决 Firefox 等浏览器搜索被折叠内容遗漏的问题。 |
| doc/js/scripts/sg_plotly_resize.js | Plotly 响应式修复:在 DOM 完成后触发一次 resize,使图表适配侧边栏宽度。 |
| doc/js/scripts/theme-observer.js | 主题变更监听:在 data-theme 变化时同步估计器 HTML 表示的暗/亮主题。 |
| doc/js/scripts/version-switcher.js | 动态追加 “More” 链接:在用户点击版本切换按钮时一次性为所有菜单添加链接,避免重复。 |
| doc/js/scripts/vendor/svg-pan-zoom.min.js | SVG 交互库:平移、缩放、视口自适应、事件节流(requestAnimationFrame)以及完整的 API。 |
通过本章的学习,你已经掌握了 scikit‑learn 文档系统从 配置 → 结构声明 → 前置检测 → 自定义扩展 → 前端交互 的完整流水线,能够在实际项目中快速定位、扩展或调试任意环节。下一章将带你深入 示例图库总览,探索如何把机器学习实践以交互式 Notebook 的形式呈现在文档站点中。
第 68 章 —— 示例图库总览 —— 机器学习实践的"百宝箱"
想象 scikit-learn 的示例图库是一个精心策划的“机器学习实验展览馆”,其中每个示例都是一个互动展品,展示了算法在真实世界场景中的应用。就像展览馆通过主题展区(如艺术、科学、历史)组织藏品,使参观者能够系统地探索不同领域的知识,scikit-learn 的示例图库同样按照应用领域(如图像处理、文本分析、金融建模)、功能模块(如管道、特征工程、模型选择)以及版本演进(如里程碑特性展示)进行分类。每个展品(示例脚本)不仅包含可运行的代码,还附带详细的说明文档和可视化结果,就像展览馆的说明牌和多媒体装置,帮助参观者理解背后的科学原理。此外,展览馆还设有特殊区域:一面墙陈列着各版本的里程碑展示,记录着技术的演进轨迹;另一侧则是专业的性能测试实验室,使用标准化的测试方案和评估指标,对不同算法的效率和准确性进行客观比较。整个展览馆通过统一的导览系统(sphinx-gallery)和交互装置(如版本切换器、主题适配),确保参观者无论是初学者还是专家,都能根据自己的需求找到合适的学习路径,从而将抽象的算法概念转化为可触摸、可验证的实践知识。
68.1 学习目标
-
难度:★★★☆☆(3/5)
-
预备知识:Python 基础、面向对象编程与 Markdown/代码阅读基础
理解示例图库的组织结构与 sphinx-gallery 配置机制
掌握发布亮点示例的编写模式与版本演进展示策略
熟悉基准测试套件的配置、数据集生成与评分器定义
理解 ASV 基准测试的抽象基类设计与缓存机制
掌握独立基准脚本在特定算法专题上的性能评测范式
68.2 源码地图
doc/
├── conf.py # Sphinx 配置总控台
├── api_reference.py # API 引用自动生成配置
├── conftest.py # 文档测试环境配置与条件跳过
├── sphinxext/
│ ├── allow_nan_estimators.py # 支持 NaN 估计器列表显示
│ ├── autoshortsummary.py # 短摘要自动渲染扩展
│ ├── doi_role.py # DOI 引用角色扩展
│ ├── dropdown_anchors.py # 下拉菜单锚点定位扩展
│ ├── github_link.py # GitHub 源码链接生成扩展
│ ├── override_pst_pagetoc.py # 页面目录覆盖扩展
│ └── sphinx_issues.py # GitHub Issue 关联扩展
└── js/scripts/
├── api-search.js # API 搜索前端实现
├── dropdown.js # 下拉菜单折叠交互
├── sg_plotly_resize.js # Plotly 图表响应式调整
├── theme-observer.js # 主题切换观察器
├── version-switcher.js # 版本切换器逻辑
└── vendor/svg-pan-zoom.min.js # SVG 缩放平移库
examples/
├── applications/ # 应用案例集
├── release_highlights/ # 版本演进亮点示例
├── miscellaneous/ # 杂项示例(可视化、管道展示等)
├── datasets/ # 数据集生成示例
├── developing_estimators/ # 估计器开发示例
├── frozen/ # FrozenEstimator 示例
├── impute/ # 缺失值插补示例
├── inspection/ # 模型解释示例
├── kernel_approximation/ # 核近似示例
├── text/ # 文本处理示例
└── ... (其他示例目录)
asv_benchmarks/
├── benchmarks/
│ ├── common.py # 基准测试抽象基类与配置工具
│ ├── datasets.py # 数据集工厂与合成数据生成
│ ├── utils.py # 评分函数集
│ ├── cluster.py # 聚类算法基准
│ ├── decomposition.py # 分解算法基准
│ ├── manifold.py # 流形学习基准
│ ├── linear_model.py # 线性模型基准
│ ├── ensemble.py # 集成学习基准
│ ├── svm.py # SVC 基准
│ ├── neighbors.py # KNN 基准
│ ├── metrics.py # 成对距离基准
│ ├── model_selection.py # 模型选择基准
│ └── init.py
benchmarks/
├── bench_hist_gradient_boosting.py
├── bench_hist_gradient_boosting_higgsboson.py
├── bench_hist_gradient_boosting_threading.py
├── bench_hist_gradient_boosting_adult.py
├── bench_hist_gradient_boosting_categorical_only.py
├── bench_pca_solvers.py
├── bench_plot_svd.py
├── bench_kernel_pca_solvers_time_vs_n_samples.py
├── bench_kernel_pca_solvers_time_vs_n_components.py
├── bench_plot_randomized_svd.py
├── bench_plot_incremental_pca.py
├── bench_plot_fastkmeans.py
├── bench_plot_hierarchical.py
├── bench_plot_neighbors.py
├── bench_plot_ward.py
├── bench_glm.py
├── bench_lasso.py
├── bench_plot_lasso_path.py
├── bench_glmnet.py
├── bench_sgd_regression.py
├── bench_sparsify.py
├── bench_plot_omp_lars.py
├── bench_mnist.py
├── bench_covertype.py
├── bench_20newsgroups.py
├── bench_tree.py
├── bench_online_ocsvm.py
├── bench_text_vectorizers.py
├── bench_feature_expansions.py
├── bench_sample_without_replacement.py
├── bench_random_projections.py
├── bench_isolation_forest.py
├── bench_isolation_forest_predict.py
├── bench_lof.py
├── bench_isotonic.py
├── bench_plot_nmf.py
├── bench_rcv1_logreg_convergence.py
├── bench_saga.py
├── bench_tsne_mnist.py
├── plot_tsne_mnist.py
└── bench_plot_polynomial_kernel_approximation.py
README.md
68.3 文档配置中枢 —— conf.py 的"总控台"
核心配置:doc/conf.py
-
项目元数据自动提取:从
sklearn/__init__.py读取版本、作者、版权信息 -
扩展注册机制:
extensions列表包含 20+ 个扩展(autodoc、numpydoc、sphinx_gallery、sphinx_copybutton、自定扩展等) -
主题定制:使用
pydata_sphinx_theme,通过html_theme_options配置导航栏、页脚、版本切换器、GitHub 链接等 -
示例图库配置:
sphinx_gallery_conf控制examples_dirs、gallery_dirs、filename_pattern、subsection_order等 -
事件钩子系统:
setup(app)连接builder-inited(生成 API 引用)、doctree-resolved(处理交叉引用)等事件 -
代码块样式:
pygments_style、pygments_dark_style适配亮/暗主题 -
跨引用配置:
intersphinx_mapping链接 Python、NumPy、SciPy、Matplotlib 等外部文档
源码路径:doc/conf.py - __main__(1-600行)
[content]
第 68 章 —— scikit-learn documentation build configuration file, created by
第 68 章 —— sphinx-quickstart on Fri Jan 8 09:13:42 2010.
第 68 章 —— This file is execfile()d with the current directory set to its containing
第 68 章 —— dir.
第 68 章 —— Note that not all possible configuration values are present in this
第 68 章 —— autogenerated file.
第 68 章 —— All configuration values have a default; values that are commented out
第 68 章 —— serve to show the default.
import json
import os
import re
import sys
import warnings
from datetime import datetime
from pathlib import Path
from urllib.request import urlopen
from sklearn.externals._packaging.version import parse
from sklearn.utils._testing import turn_warnings_into_errors
第 68 章 —— If extensions (or modules to document with autodoc) are in another
第 68 章 —— directory, add these directories to sys.path here. If the directory
第 68 章 —— is relative to the documentation root, use os.path.abspath to make it
第 68 章 —— absolute, like shown here.
sys.path.insert(0, os.path.abspath("."))
sys.path.insert(0, os.path.abspath("sphinxext"))
import jinja2
import sphinx_gallery
from github_link import make_linkcode_resolve
from sphinx.util.logging import getLogger
from sphinx_gallery.notebook import add_code_cell, add_markdown_cell
from sphinx_gallery.sorting import ExampleTitleSortKey
logger = getLogger(name)
try:
Configure plotly to integrate its output into the HTML pages generated by
sphinx-gallery.
import plotly.io as pio
pio.renderers.default = "sphinx_gallery"
except ImportError:
Make it possible to render the doc when not running the examples
that need plotly.
pass
第 68 章 —— -- General configuration ---------------------------------------------------
第 68 章 —— Add any Sphinx extension module names here, as strings. They can be
第 68 章 —— extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
"numpydoc",
"sphinx.ext.linkcode",
"sphinx.ext.doctest",
"sphinx.ext.intersphinx",
"sphinx.ext.imgconverter",
"sphinx_gallery.gen_gallery",
"sphinx-prompt",
"sphinx_copybutton",
"sphinxext.opengraph",
"matplotlib.sphinxext.plot_directive",
"sphinxcontrib.sass",
"sphinx_remove_toctrees",
"sphinx_design",
See sphinxext/
"allow_nan_estimators",
"autoshortsummary",
"doi_role",
"dropdown_anchors",
"override_pst_pagetoc",
"sphinx_issues",
]
第 68 章 —— Specify how to identify the prompt when copying code snippets
copybutton_prompt_text = r">>> |... "
copybutton_prompt_is_regexp = True
copybutton_exclude = "style"
try:
import jupyterlite_sphinx # noqa: F401
extensions.append("jupyterlite_sphinx")
with_jupyterlite = True
except ImportError:
In some cases we don't want to require jupyterlite_sphinx to be installed,
e.g. the doc-min-dependencies build
warnings.warn(
"jupyterlite_sphinx is not installed, you need to install it "
"if you want JupyterLite links to appear in each example"
)
with_jupyterlite = False
第 68 章 —— Produce plot:: directives for examples that contain import matplotlib or
第 68 章 —— from matplotlib import.
numpydoc_use_plots = True
第 68 章 —— Options for the ::plot directive:
第 68 章 —— https://matplotlib.org/stable/api/sphinxext_plot_directive_api.html
plot_formats = ["png"]
plot_include_source = True
plot_html_show_formats = False
plot_html_show_source_link = False
第 68 章 —— We do not need the table of class members because sphinxext/override_pst_pagetoc.py
第 68 章 —— will show them in the secondary sidebar
numpydoc_show_class_members = False
numpydoc_show_inherited_class_members = False
第 68 章 —— We want in-page toc of class members instead of a separate page for each entry
numpydoc_class_members_toctree = False
第 68 章 —— For maths, use mathjax by default and svg if NO_MATHJAX env variable is set
第 68 章 —— (useful for viewing the doc offline)
if os.environ.get("NO_MATHJAX"):
extensions.append("sphinx.ext.imgmath")
imgmath_image_format = "svg"
mathjax_path = ""
else:
extensions.append("sphinx.ext.mathjax")
mathjax_path = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"
第 68 章 —— Add any paths that contain templates here, relative to this directory.
templates_path = ["templates"]
第 68 章 —— generate autosummary even if no references
autosummary_generate = True
第 68 章 —— The suffix of source filenames.
source_suffix = ".rst"
第 68 章 —— The encoding of source files.
source_encoding = "utf-8"
第 68 章 —— The main toctree document.
root_doc = "index"
第 68 章 —— General information about the project.
project = "scikit-learn"
copyright = f"2007 - {datetime.now().year}, scikit-learn developers (BSD License)"
第 68 章 —— The version info for the project you're documenting, acts as replacement for
第 68 章 —— |version| and |release|, also used in various other places throughout the
第 68 章 —— built documents.
第 68 章 —— The short X.Y version.
import sklearn
parsed_version = parse(sklearn.version)
version = ".".join(parsed_version.base_version.split(".")[:2])
第 68 章 —— The full version, including alpha/beta/rc tags.
第 68 章 —— Removes post from release name
if parsed_version.is_postrelease:
release = parsed_version.base_version
else:
release = sklearn.version
第 68 章 —— The language for content autogenerated by Sphinx. Refer to documentation
第 68 章 —— for a list of supported languages.
第 68 章 —— language = None
第 68 章 —— There are two options for replacing |today|: either, you set today to some
第 68 章 —— non-false value, then it is used:
第 68 章 —— today = ''
第 68 章 —— Else, today_fmt is used as the format for a strftime call.
第 68 章 —— today_fmt = '%B %d, %Y'
第 68 章 —— List of patterns, relative to source directory, that match files and
第 68 章 —— directories to ignore when looking for source files.
exclude_patterns = [
"_build",
"templates",
"includes",
"**/sg_execution_times.rst",
"whats_new/upcoming_changes",
]
第 68 章 —— The reST default role (used for this markup: text) to use for all
第 68 章 —— documents.
default_role = "literal"
第 68 章 —— If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = False
第 68 章 —— If true, the current module name will be prepended to all description
第 68 章 —— unit titles (such as .. function:😃.
第 68 章 —— add_module_names = True
第 68 章 —— If true, sectionauthor and moduleauthor directives will be shown in the
第 68 章 —— output. They are ignored by default.
第 68 章 —— show_authors = False
第 68 章 —— A list of ignored prefixes for module index sorting.
第 68 章 —— modindex_common_prefix = []
第 68 章 —— -- Options for HTML output -------------------------------------------------
第 68 章 —— The theme to use for HTML and HTML Help pages. Major themes that come with
第 68 章 —— Sphinx are currently 'default' and 'sphinxdoc'.
html_theme = "pydata_sphinx_theme"
第 68 章 —— This config option is used to generate the canonical links in the header
第 68 章 —— of every page. The canonical link is needed to prevent search engines from
第 68 章 —— returning results pointing to old scikit-learn versions.
html_baseurl = "https://scikit-learn.org/stable/"
第 68 章 —— Theme options are theme-specific and customize the look and feel of a theme
第 68 章 —— further. For a list of options available for each theme, see the
第 68 章 —— documentation.
html_theme_options = {
-- General configuration ------------------------------------------------
"sidebar_includehidden": True,
"use_edit_page_button": True,
"external_links": [],
"icon_links_label": "Icon Links",
"icon_links": [
{
"name": "GitHub",
"url": "https://github.com/scikit-learn/scikit-learn",
"icon": "fa-brands fa-square-github",
"type": "fontawesome",
},
],
"analytics": {
"plausible_analytics_domain": "scikit-learn.org",
"plausible_analytics_url": "https://views.scientific-python.org/js/script.js",
},
If "prev-next" is included in article_footer_items, then setting show_prev_next
to True would repeat prev and next links. See
https://github.com/pydata/pydata-sphinx-theme/blob/b731dc230bc26a3d1d1bb039c56c977a9b3d25d8/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/layout.html#L118-L129
"show_prev_next": False,
"search_bar_text": "Search the docs ...",
"navigation_with_keys": False,
"collapse_navigation": False,
"navigation_depth": 2,
"show_nav_level": 1,
"show_toc_level": 1,
"navbar_align": "left",
"header_links_before_dropdown": 5,
"header_dropdown_text": "More",
The switcher requires a JSON file with the list of documentation versions, which
is generated by the script build_tools/circle/list_versions.py and placed under
the js/ static directory; it will then be copied to the _static directory in
the built documentation
"switcher": {
"json_url": "https://scikit-learn.org/dev/_static/versions.json",
"version_match": release,
},
check_switcher may be set to False if docbuild pipeline fails. See
https://pydata-sphinx-theme.readthedocs.io/en/stable/user_guide/version-dropdown.html#configure-switcher-json-url
"check_switcher": True,
"pygments_light_style": "tango",
"pygments_dark_style": "monokai",
"logo": {
"alt_text": "scikit-learn homepage",
"image_relative": "logos/scikit-learn-logo-without-subtitle.svg",
"image_light": "logos/scikit-learn-logo-without-subtitle.svg",
"image_dark": "logos/scikit-learn-logo-without-subtitle.svg",
},
"surface_warnings": True,
-- Template placement in theme layouts ----------------------------------
"navbar_start": ["navbar-logo"],
Note that the alignment of navbar_center is controlled by navbar_align
"navbar_center": ["navbar-nav"],
"navbar_end": ["theme-switcher", "navbar-icon-links", "version-switcher"],
navbar_persistent is persistent right (even when on mobiles)
"navbar_persistent": ["search-button"],
"article_header_start": ["breadcrumbs"],
"article_header_end": [],
"article_footer_items": ["prev-next"],
"content_footer_items": [],
Use html_sidebars that map page patterns to list of sidebar templates
"primary_sidebar_end": [],
"footer_start": ["copyright"],
"footer_center": [],
"footer_end": [],
When specified as a dictionary, the keys should follow glob-style patterns, as in
https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-exclude_patterns
In particular, "**" specifies the default for all pages
Use :html_theme.sidebar_secondary.remove: for file-wide removal
"secondary_sidebar_items": {
"**": [
"page-toc",
"sourcelink",
Sphinx-Gallery-specific sidebar components
https://sphinx-gallery.github.io/stable/advanced.html#using-sphinx-gallery-sidebar-components
"sg_download_links",
"sg_launcher_links",
],
},
"show_version_warning_banner": True,
"announcement": None,
}
第 68 章 —— Add any paths that contain custom themes here, relative to this directory.
第 68 章 —— html_theme_path = ["themes"]
第 68 章 —— The name for this set of Sphinx documents. If None, it defaults to
第 68 章 —— " v documentation".
第 68 章 —— html_title = None
第 68 章 —— A shorter title for the navigation bar. Default is the same as html_title.
html_short_title = "scikit-learn"
第 68 章 —— The name of an image file (within the static path) to use as favicon of the
第 68 章 —— docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
第 68 章 —— pixels large.
html_favicon = "logos/favicon.ico"
第 68 章 —— Add any paths that contain custom static files (such as style sheets) here,
第 68 章 —— relative to this directory. They are copied after the builtin static files,
第 68 章 —— so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["images", "css", "js"]
第 68 章 —— If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
第 68 章 —— using the given strftime format.
第 68 章 —— html_last_updated_fmt = '%b %d, %Y'
第 68 章 —— Custom sidebar templates, maps document names to template names.
第 68 章 —— Workaround for removing the left sidebar on pages without TOC
第 68 章 —— A better solution would be to follow the merge of:
第 68 章 —— https://github.com/pydata/pydata-sphinx-theme/pull/1682
html_sidebars = {
"install": [],
"getting_started": [],
"glossary": [],
"faq": [],
"support": [],
"related_projects": [],
"roadmap": [],
"governance": [],
"about": [],
}
第 68 章 —— Additional templates that should be rendered to pages, maps page names to
第 68 章 —— template names.
html_additional_pages = {"index": "index.html"}
第 68 章 —— Additional files to copy
第 68 章 —— html_extra_path = []
第 68 章 —— Additional JS files
html_js_files = [
"scripts/dropdown.js",
"scripts/version-switcher.js",
"scripts/sg_plotly_resize.js",
"scripts/theme-observer.js",
]
第 68 章 —— Compile scss files into css files using sphinxcontrib-sass
sass_src_dir, sass_out_dir = "scss", "css/styles"
sass_targets = {
f"{file.stem}.scss": f"{file.stem}.css"
for file in Path(sass_src_dir).glob("*.scss")
}
第 68 章 —— Additional CSS files, should be subset of the values of sass_targets
html_css_files = ["styles/colors.css", "styles/custom.css"]
def add_js_css_files(app, pagename, templatename, context, doctree):
"""Load additional JS and CSS files only for certain pages.
Note that html_js_files and html_css_files are included in all pages and
should be used for the ones that are used by multiple pages. All page-specific
JS and CSS files should be added here instead.
"""
if pagename == "api/index":
External: jQuery and DataTables
app.add_js_file("https://code.jquery.com/jquery-3.7.0.js")
app.add_js_file("https://cdn.datatables.net/2.0.0/js/dataTables.min.js")
app.add_css_file(
"https://cdn.datatables.net/2.0.0/css/dataTables.dataTables.min.css"
)
Internal: API search initialization and styling
app.add_js_file("scripts/api-search.js")
app.add_css_file("styles/api-search.css")
elif pagename == "index":
app.add_css_file("styles/index.css")
elif pagename.startswith("modules/generated/"):
app.add_css_file("styles/api.css")
第 68 章 —— If false, no module index is generated.
html_domain_indices = False
第 68 章 —— If false, no index is generated.
html_use_index = False
第 68 章 —— If true, the index is split into individual pages for each letter.
第 68 章 —— html_split_index = False
第 68 章 —— If true, links to the reST sources are added to the pages.
第 68 章 —— html_show_sourcelink = True
第 68 章 —— If true, an OpenSearch description file will be output, and all pages will
第 68 章 —— contain a tag referring to it. The value of this option must be the
第 68 章 —— base URL from which the finished HTML is served.
第 68 章 —— html_use_opensearch = ''
第 68 章 —— If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
第 68 章 —— html_file_suffix = ''
第 68 章 —— Output file base name for HTML help builder.
htmlhelp_basename = "scikit-learndoc"
第 68 章 —— If true, the reST sources are included in the HTML build as _sources/name.
html_copy_source = True
第 68 章 —— Adds variables into templates
html_context = {}
第 68 章 —— finds latest release highlights and places it into HTML context for
第 68 章 —— index.html
release_highlights_dir = Path("..") / "examples" / "release_highlights"
第 68 章 —— Finds the highlight with the latest version number
latest_highlights = sorted(release_highlights_dir.glob("plot_release_highlights_*.py"))[
-1
]
latest_highlights = latest_highlights.with_suffix("").name
html_context["release_highlights"] = (
f"auto_examples/release_highlights/{latest_highlights}"
)
第 68 章 —— get version from highlight name assuming highlights have the form
第 68 章 —— plot_release_highlights_0_22_0
highlight_version = ".".join(latest_highlights.split("_")[-3:-1])
html_context["release_highlights_version"] = highlight_version
第 68 章 —— redirects dictionary maps from old links to new links
redirects = {
"documentation": "index",
"contents": "index",
"preface": "index",
"modules/classes": "api/index",
"tutorial/machine_learning_map/index": "machine_learning_map",
"auto_examples/feature_selection/plot_permutation_test_for_classification": (
"auto_examples/model_selection/plot_permutation_tests_for_classification"
),
"modules/model_persistence": "model_persistence",
"auto_examples/linear_model/plot_bayesian_ridge": (
"auto_examples/linear_model/plot_ard"
),
"auto_examples/model_selection/grid_search_text_feature_extraction": (
"auto_examples/model_selection/plot_grid_search_text_feature_extraction"
),
"auto_examples/model_selection/plot_validation_curve": (
"auto_examples/model_selection/plot_train_error_vs_test_error"
),
"auto_examples/datasets/plot_digits_last_image": (
"auto_examples/exercises/plot_digits_classification_exercises"
),
"auto_examples/datasets/plot_random_dataset": (
"auto_examples/classification/plot_classifier_comparison"
),
"auto_examples/miscellaneous/plot_changed_only_pprint_parameter": (
"auto_examples/miscellaneous/plot_estimator_representation"
),
"auto_examples/decomposition/plot_beta_divergence": (
"auto_examples/applications/plot_topics_extraction_with_nmf_lda"
),
"auto_examples/svm/plot_svm_nonlinear": "auto_examples/svm/plot_svm_kernels",
"auto_examples/ensemble/plot_adaboost_hastie_10_2": (
"auto_examples/ensemble/plot_adaboost_multiclass"
),
"auto_examples/decomposition/plot_pca_3d": (
"auto_examples/decomposition/plot_pca_iris"
),
"auto_examples/exercises/plot_cv_digits": (
"auto_examples/model_selection/plot_nested_cross_validation_iris"
),
"auto_examples/linear_model/plot_lasso_lars": (
"auto_examples/linear_model/plot_lasso_lasso_lars_elasticnet_path"
),
"auto_examples/linear_model/plot_lasso_coordinate_descent_path": (
"auto_examples/linear_model/plot_lasso_lasso_lars_elasticnet_path"
),
"auto_examples/cluster/plot_color_quantization": (
"auto_examples/cluster/plot_face_compress"
),
"auto_examples/cluster/plot_cluster_iris": (
"auto_examples/cluster/plot_kmeans_assumptions"
),
"auto_examples/ensemble/plot_forest_importances_faces": (
"auto_examples/ensemble/plot_forest_importances"
),
"auto_examples/ensemble/plot_voting_probas": (
"auto_examples/ensemble/plot_voting_decision_regions"
),
"auto_examples/datasets/plot_iris_dataset": (
"auto_examples/decomposition/plot_pca_iris"
),
"auto_examples/linear_model/plot_iris_logistic": (
"auto_examples/linear_model/plot_logistic_multinomial"
),
"auto_examples/linear_model/plot_logistic": (
"auto_examples/calibration/plot_calibration_curve"
),
"auto_examples/linear_model/plot_ols_3d": ("auto_examples/linear_model/plot_ols"),
"auto_examples/linear_model/plot_ols": "auto_examples/linear_model/plot_ols_ridge",
"auto_examples/linear_model/plot_ols_ridge_variance": (
"auto_examples/linear_model/plot_ols_ridge"
),
"auto_examples/cluster/plot_agglomerative_clustering.html": (
"auto_examples/cluster/plot_ward_structured_vs_unstructured.html"
),
"auto_examples/linear_model/plot_sgd_comparison": (
"auto_examples/linear_model/plot_sgd_loss_functions"
),
}
html_context["redirects"] = redirects
for old_link in redirects:
html_additional_pages[old_link] = "redirects.html"
第 68 章 —— See https://github.com/scikit-learn/scikit-learn/pull/22550
html_context["is_devrelease"] = parsed_version.is_devrelease
第 68 章 —— -- Options for LaTeX output ------------------------------------------------
latex_elements = {
The paper size ('letterpaper' or 'a4paper').
'papersize': 'letterpaper',
The font size ('10pt', '11pt' or '12pt').
'pointsize': '10pt',
Additional stuff for the LaTeX preamble.
"preamble": r"""
\usepackage{amsmath}\usepackage{amsfonts}\usepackage{bm}
\usepackage{morefloats}\usepackage{enumitem} \setlistdepth{10}
\let\oldhref\href
\renewcommand{\href}[2]{\oldhref{#1}{\hbox{#2}}}
"""
}
第 68 章 —— Grouping the document tree into LaTeX files. List of tuples
第 68 章 —— (source start file, target name, title, author, documentclass
第 68 章 —— [howto/manual]).
latex_documents = [
(
"contents",
"user_guide.tex",
"scikit-learn user guide",
"scikit-learn developers",
"manual",
),
]
第 68 章 —— The name of an image file (relative to this directory) to place at the top of
第 68 章 —— the title page.
latex_logo = "logos/scikit-learn-logo.png"
第 68 章 —— Documents to append as an appendix to all manuals.
第 68 章 —— latex_appendices = []
第 68 章 —— If false, no module index is generated.
latex_domain_indices = False
trim_doctests_flags = True
第 68 章 —— intersphinx configuration
intersphinx_mapping = {
"python": ("https://docs.python.org/{.major}".format(sys.version_info), None),
"numpy": ("https://numpy.org/doc/stable", None),
"scipy": ("https://docs.scipy.org/doc/scipy/", None),
"matplotlib": ("https://matplotlib.org/", None),
"pandas": ("https://pandas.pydata.org/pandas-docs/stable/", None),
"joblib": ("https://joblib.readthedocs.io/en/latest/", None),
"seaborn": ("https://seaborn.pydata.org/", None),
"skops": ("https://skops.readthedocs.io/en/stable/", None),
}
v = parse(release)
if v.release is None:
raise ValueError(
"Ill-formed version: {!r}. Version should follow PEP440".format(version)
)
if v.is_devrelease:
binder_branch = "main"
else:
major, minor = v.release[:2]
binder_branch = "{}.{}.X".format(major, minor)
class SubSectionTitleOrder:
"""Sort example gallery by title of subsection.
Assumes README.txt exists for all subsections and uses the subsection with
dashes, '---', as the adornment.
"""
def init(self, src_dir):
self.src_dir = src_dir
self.regex = re.compile(r"^([\w ]+)\n-", re.MULTILINE)
def repr(self):
return "<%s>" % (self.class.name,)
def call(slef, directory):
src_path = os.path.normpath(os.path.join(self.src_dir, directory))
Forces Release Highlights to the top
if os.path.basename(src_path) == "release_highlights":
return "0"
readme = os.path.join(src_path, "README.txt")
try:
with open(readme, "r") as f:
content = f.read()
except FileNotFoundError:
return directory
title_match = self.regex.search(content)
if title_match is not None:
return title_match.group(1)
return directory
class SKExampleTitleSortKey(ExampleTitleSortKey):
"""Sorts release highlights based on version number."""
def call(self, filename):
title = super().call(filename)
prefix = "plot_release_highlights_"
Use title to sort if not a release highlight
if not str(filename).startswith(prefix):
return title
major_minor = filename[len(prefix) :].split("_")[:2]
version_float = float(".".join(major_minor))
negate to place the newest version highlights first
return -version_float
def notebook_modification_function(notebook_content, notebook_filename):
notebook_content_str = str(notebook_content)
warning_template = "\n".join(
[
"
",]
)
message_class = "warning"
message = (
"Running the scikit-learn examples in JupyterLite is experimental and you may"
" encounter some unexpected behavior.\n\nThe main difference is that imports"
" will take a lot longer than usual, for example the first import sklearn can"
" take roughly 10-20s.\n\nIf you notice problems, feel free to open an"
" issue"
" about it."
)
markdown = warning_template.format(message_class=message_class, message=message)
dummy_notebook_content = {"cells": []}
add_markdown_cell(dummy_notebook_content, markdown)
code_lines = []
if "seaborn" in notebook_content_str:
code_lines.append("%pip install seaborn")
if "plotly.express" in notebook_content_str:
code_lines.append("%pip install plotly nbformat")
if "skimage" in notebook_content_str:
code_lines.append("%pip install scikit-image")
if "polars" in notebook_content_str:
code_lines.append("%pip install polars")
if "fetch_" in notebook_content_str:
code_lines.extend(
[
"%pip install pyodide-http",
"import pyodide_http",
"pyodide_http.patch_all()",
]
)
always import matplotlib and pandas to avoid Pyodide limitation with
imports inside functions
code_lines.extend(["import matplotlib", "import pandas"])
Work around https://github.com/jupyterlite/pyodide-kernel/issues/166
and https://github.com/pyodide/micropip/issues/223 by installing the
dependencies first, and then scikit-learn from Anaconda.org.
if "dev" in release:
dev_docs_specific_code = [
"import piplite",
"import joblib",
"import threadpoolctl",
"import scipy",
"await piplite.install(\n"
f" 'scikit-learn=={release}',\n"
" index_urls='https://pypi.anaconda.org/scientific-python-nightly-wheels/simple',\n"
")",
]
code_lines.extend(dev_docs_specific_code)
if code_lines:
code_lines = ["# JupyterLite-specific code"] + code_lines
code = "\n".join(code_lines)
add_code_cell(dummy_notebook_content, code)
notebook_content["cells"] = (
dummy_notebook_content["cells"] + notebook_content["cells"]
)
default_global_config = sklearn.get_config()
def reset_sklearn_config(gallery_conf, fname):
"""Reset sklearn config to default values."""
sklearn.set_config(**default_global_config)
sg_examples_dir = "../examples"
sg_gallery_dir = "auto_examples"
sphinx_gallery_conf = {
"doc_module": "sklearn",
"backreferences_dir": os.path.join("modules", "generated"),
"show_memory": False,
"reference_url": {"sklearn": None},
"examples_dirs": [sg_examples_dir],
"gallery_dirs": [sg_gallery_dir],
"subsection_order": SubSectionTitleOrder(sg_examples_dir),
"within_subsection_order": SKExampleTitleSortKey,
"binder": {
"org": "scikit-learn",
"repo": "scikit-learn",
"binderhub_url": "https://mybinder.org",
"branch": binder_branch,
"dependencies": "./binder/requirements.txt",
"use_jupyter_lab": True,
},
avoid generating too many cross links
"inspect_global_variables": False,
"remove_config_comments": True,
"plot_gallery": "True",
"recommender": {"enable": True, "n_examples": 4, "min_df": 12},
"reset_modules": ("matplotlib", "seaborn", reset_sklearn_config),
}
if with_jupyterlite:
sphinx_gallery_conf["jupyterlite"] = {
"notebook_modification_function": notebook_modification_function
}
第 68 章 —— For the index page of the gallery and each nested section, we hide the secondary
第 68 章 —— sidebar by specifying an empty list (no components), because there is no meaningful
第 68 章 —— in-page toc for these pages, and they are generated so "sourelink" is not useful
第 68 章 —— either.
html_theme_options["secondary_sidebar_items"][f"{sg_gallery_dir}/index"] = []
for sub_sg_dir in (Path(".") / sg_examples_dir).iterdir():
if sub_sg_dir.is_dir():
html_theme_options["secondary_sidebar_items"][
f"{sg_gallery_dir}/{sub_sg_dir.name}/index"
] = []
第 68 章 —— The following dictionary contains the information used to create the
第 68 章 —— thumbnails for the front page of the scikit-learn home page.
第 68 章 —— key: first image in set
第 68 章 —— values: (number of plot in set, height of thumbnail)
carousel_thumbs = {"sphx_glr_plot_classifier_comparison_001.png": 600}
第 68 章 —— enable experimental module so that experimental estimators can be
第 68 章 —— discovered properly by sphinx
from sklearn.experimental import ( # noqa: F401
enable_halving_search_cv,
enable_iterative_imputer,
)
def make_carousel_thumbs(app, exception):
"""produces the final resized carousel images"""
if exception is not None:
return
print("Preparing carousel images")
image_dir = os.path.join(app.builder.outdir, "_images")
for glr_plot, max_width in carousel_thumbs.items():
image = os.path.join(image_dir, glr_plot)
if os.path.exists(image):
c_thumb = os.path.join(image_dir, glr_plot[:-4] + "_carousel.png")
sphinx_gallery.gen_rst.scale_image(image, c_thumb, max_width, 190)
def filter_search_index(app, exception):
if exception is not None:
return
searchindex only exist when generating html
if app.builder.name != "html":
return
print("Removing methods from search index")
searchindex_path = os.path.join(app.builder.outdir, "searchindex.js")
with open(searchindex_path, "r") as f:
searchindex_text = f.read()
searchindex_text = re.sub(r"{init.+?}", "{}", searchindex_text)
searchindex_text = re.sub(r"{call.+?}", "{}", searchindex_text)
with open(searchindex_path, "w") as f:
f.write(searchindex_text)
第 68 章 —— Config for sphinx_issues
第 68 章 —— we use the issues path for PRs since the issues URL will forward
issues_github_path = "scikit-learn/scikit-learn"
def disable_plot_gallery_for_linkcheck(app):
if app.builder.name == "linkcheck":
sphinx_gallery_conf["plot_gallery"] = "False"
def skip_properties(app, what, name, obj, skip, options):
"""Skip properties that are fitted attributes"""
if isinstance(obj, property):
if name.endswith("") and not name.startswith(""):
return True
return skip
def setup(app):
do not run the examples when using linkcheck by using a small priority
(default priority is 500 and sphinx-gallery using builder-inited event too)
app.connect("builder-inited", disable_plot_gallery_for_linkcheck, priority=50)
triggered just before the HTML for an individual page is created
app.connect("html-page-context", add_js_css_files)
to hide/show the prompt in code examples
app.connect("build-finished", make_carousel_thumbs)
app.connect("build-finished", filter_search_index)
app.connect("autodoc-skip-member", skip_properties)
第 68 章 —— The following is used by sphinx.ext.linkcode to provide links to github
linkcode_resolve = make_linkcode_resolve(
"sklearn",
(
"https://github.com/scikit-learn/"
"scikit-learn/blob/{revision}/"
"{package}/{path}#L{lineno}"
),
)
warnings.filterwarnings(
"ignore",
category=UserWarning,
message=(
"Matplotlib is currently using agg, which is a"
" non-GUI backend, so cannot show the figure."
),
)
第 68 章 —— TODO(1.10): remove PassiveAggressive
warnings.filterwarnings("ignore", category=FutureWarning, message="PassiveAggressive")
if os.environ.get("SKLEARN_WARNINGS_AS_ERRORS", "0") != "0":
turn_warnings_into_errors()
第 68 章 —— maps functions with a class name that is indistinguishable when case is
第 68 章 —— ignore to another filename
autosummary_filename_map = {
"sklearn.cluster.dbscan": "dbscan-function",
"sklearn.covariance.oas": "oas-function",
"sklearn.decomposition.fastica": "fastica-function",
}
第 68 章 —— Config for sphinxext.opengraph
ogp_site_url = "https://scikit-learn/stable/"
ogp_image = "https://scikit-learn.org/stable/_static/scikit-learn-logo-notext.png"
ogp_use_first_image = True
ogp_site_name = "scikit-learn"
第 68 章 —— Config for linkcheck that checks the documentation for broken links
第 68 章 —— ignore all links in 'whats_new' to avoid doing many github requests and
第 68 章 —— hitting the github rate threshold that makes linkcheck take a lot of time
linkcheck_exclude_documents = [r"whats_new/.*"]
第 68 章 —— default timeout to make some sites links fail faster
linkcheck_timeout = 10
第 68 章 —— Allow redirects from doi.org
linkcheck_allowed_redirects = {r"https://doi.org/.+": r".*"}
linkcheck_ignore = [
ignore links to local html files e.g. in image directive :target: field
r"^..?/",
ignore links to specific pdf pages because linkcheck does not handle them
('utf-8' codec can't decode byte error)
r"https://www.utstat.toronto.edu/~rsalakhu/sta4273/notes/Lecture2.pdf#page=.*",
(
"https://www.fordfoundation.org/media/2976/roads-and-bridges"
"-the-unseen-labor-behind-our-digital-infrastructure.pdf#page=.*"
),
links falsely flagged as broken
(
"https://www.researchgate.net/publication/"
"233096619_A_Dendrite_Method_for_Cluster_Analysis"
),
(
"https://www.researchgate.net/publication/221114584_Random_Fourier"
"_Approximations_for_Skewed_Multiplicative_Histogram_Kernels"
),
(
"https://www.researchgate.net/publication/4974606_"
"Hedonic_housing_prices_and_the_demand_for_clean_air"
),
(
"https://www.researchgate.net/profile/Anh-Huy-Phan/publication/220241471_Fast_"
"Local_Algorithms_for_Large_Scale_Nonnegative_Matrix_and_Tensor_Factorizations"
),
"https://doi.org/10.13140/RG.2.2.35280.02565",
(
"https://www.microsoft.com/en-us/research/uploads/prod/2006/01/"
"Bishop-Pattern-Recognition-and-Machine-Learning-2006.pdf"
),
"https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-99-87.pdf",
"https://www.jstor.org/stable/2984099",
"https://stat.uw.edu/sites/default_files/files/reports/2000/tr371.pdf",
Broken links from testimonials
"http://www.data-publica.com/",
"https://www.mars.com/global",
Ignore some dynamically created anchors. See
https://github.com/sphinx-doc/sphinx/issues/9016 for more details about
the github example
r"https://github.com/conda-forge/miniforge#miniforge",
r"https://github.com/joblib/threadpoolctl/"
"#setting-the-maximum-size-of-thread-pools",
r"https://stackoverflow.com/questions/5836335/"
"consistently-create-same-random-numpy-array/5837352#comment6712034_5837352",
]
第 68 章 —— Use a browser-like user agent to avoid some "403 Client Error: Forbidden for
第 68 章 —— url" errors. This is taken from the variable navigator.userAgent inside a
第 68 章 —— browser console.
user_agent = (
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0"
)
第 68 章 —— Use Github token from environment variable to avoid Github rate limits when
第 68 章 —— checking Github links
github_token = os.getenv("GITHUB_TOKEN")
if github_token is None:
linkcheck_request_headers = {}
else:
linkcheck_request_headers = {
"https://github.com/": {"Authorization": f"token {github_token}"},
}
def infer_next_release_versions():
"""Infer the most likely next release versions to make."""
all_version_full = {"rc": "0.99.0rc1", "final": "0.99.0", "bf": "0.98.1"}
all_version_short = {"rc": "0.99", "final": "0.99", "bf": "0.98"}
all_previous_tag = {"rc": "unused", "final": "0.98.33", "bf": "0.97.22"}
try:
Fetch the version switcher JSON; see html_theme_options for more details
versions_json = json.loads(
urlopen(html_theme_options["switcher"]["json_url"], timeout=10).read()
)
See build_tools/circle/list_versions.py, stable is always the second entry
stable_version = parse(versions_json[1]["version"])
last_stable_version = parse(versions_json[2]["version"])
next_major_minor = f"{stable_version.major}.{stable_version.minor + 1}"
RC
all_version_full["rc"] = f"{next_major_minor}.0rc1"
all_version_short["rc"] = next_major_minor
Major/Minor final
all_version_full["final"] = f"{next_major_minor}.0"
all_version_short["final"] = next_major_minor
all_previous_tag["final"] = stable_version.base_version
Bug-fix
all_version_full["bf"] = (
f"{stable_version.major}.{stable_version.minor}.{stable_version.micro + 1}"
)
all_version_short["bf"] = f"{stable_version.major}.{stable_version.minor}"
all_previous_tag["bf"] = last_stable_version.base_version
except Exception as e:
logger.warning(
"Failed to infer all possible next release versions because of "
f"{type(e).name}: {e}"
)
return {
"version_full": all_version_full,
"version_short": all_version_short,
"previous_tag": all_previous_tag,
}
第 68 章 —— -- Convert .rst.template files to .rst ---------------------------------------
from api_reference import API_REFERENCE, DEPRECATED_API_REFERENCE
from sklearn._min_dependencies import dependent_packages
第 68 章 —— If development build, link to local page in the top navbar; otherwise link to the
第 68 章 —— development version; see https://github.com/scikit-learn/scikit-learn/pull/22550
if parsed_version.is_devrelease:
development_link = "developers/index"
else:
development_link = "https://scikit-learn.org/dev/developers/index.html"
第 68 章 —— Define the templates and target files for conversion
第 68 章 —— Each entry is in the format (template name, file name, kwargs for rendering)
rst_templates = [
("index", "index", {"development_link": development_link}),
(
"developers/maintainer",
"developers/maintainer",
{"inferred": infer_next_release_versions()},
),
(
"min_dependency_table",
"min_dependency_table",
{"dependent_packages": dependent_packages},
),
(
"min_dependency_substitutions",
"min_dependency_substitutions",
{"dependent_packages": dependent_packages},
),
(
"api/index",
"api/index",
{
"API_REFERENCE": sorted(API_REFERENCE.items(), key=lambda x: x[0]),
"DEPRECATED_API_REFERENCE": sorted(
DEPRECATED_API_REFERENCE.items(), key=lambda x: x[0], reverse=True
),
},
),
]
第 68 章 —— Convert each module API reference page
for module in API_REFERENCE:
rst_templates.append(
(
"api/module",
f"api/{module}",
{"module": module, "module_info": API_REFERENCE[module]},
)
)
第 68 章 —— Convert the deprecated API reference page (if there exists any)
if DEPRECATED_API_REFERENCE:
rst_templates.append(
(
"api/deprecated",
"api/deprecated",
{
"DEPRECATED_API_REFERENCE": sorted(
DEPRECATED_API_REFERENCE.items(), key=lambda x: x[0], reverse=True
)
},
)
)
for rst_template_name, rst_target_name, kwargs in rst_templates:
Read the corresponding template file into jinja2
with (Path(".") / f"{rst_template_name}.rst.template").open(
"r", encoding="utf-8"
) as f:
t = jinja2.Template(f.read())
Render the template and write to the target
with (Path(".") / f"{rst_target_name}.rst").open("w", encoding="utf-8") as f:
f.write(t.render(**kwargs))
[/content]
这段代码定义了scikit-learn文档构建的Sphinx配置总控台,负责项目元数据提取、扩展注册、主题定制、示例图库配置以及事件钩子系统的初始化。[/content]
[content]
68.1 API 引用生成器 —— api_reference.py 的"目录编纂术"
核心模块:doc/api_reference.py
-
API_REFERENCE字典:键为模块名(如sklearn.linear_model),值为包含title、subtitle、autosummary列表的配置 -
DEPRECATED_API_REFERENCE字典:相同结构,用于生成已弃用 API 的独立文档页面 -
_write_api_reference(module_name, config, dest_dir):生成单个模块的 API 参考.rst文件 -
_write_deprecated_api_reference(module_name, config, dest_dir):生成弃用 API 页面,包含弃用警告 -
generate_api_reference(output_dir):主入口函数,遍历两个字典调用写入函数 -
生成的
.rst结构:标题、副标题、自动摘要表格(.. autosummary::)、模块级文档字符串
源码路径:doc/api_reference.py - __main__(1-300行)
[content]
"""Configuration for the API reference documentation."""
def _get_guide(*refs, is_developer=False):
"""Get the rst to refer to user/developer guide.
refs is several references that can be used in the :ref:... directive.
"""
if len(refs) == 1:
ref_desc = f":ref:{refs[0]} section"
elif len(refs) == 2:
ref_desc = f":ref:{refs[0]} and :ref:{refs[1]} sections"
else:
ref_desc = ", ".join(f":ref:{ref}" for ref in refs[:-1])
ref_desc += f", and :ref:{refs[-1]} sections"
guide_name = "Developer" if is_developer else "User"
return f"{guide_name} guide. See the {ref_desc} for further details."
def _get_submodule(module_name, submodule_name):
"""Get the submodule docstring and automatically add the hook.
module_name is e.g. sklearn.feature_extraction, and submodule_name is e.g.
image, so we get the docstring and hook for sklearn.feature_extraction.image
submodule. module_name is used to reset the current module because autosummary
automatically changes the current module.
"""
lines = [
f".. automodule:: {module_name}.{submodule_name}",
f".. currentmodule:: {module_name}",
]
return "\n\n".join(lines)
"""
CONFIGURING API_REFERENCE
=========================
API_REFERENCE maps each module name to a dictionary that consists of the following
components:
short_summary (required)
The text to be printed on the index page; it has nothing to do with
the API reference page of each module.
description (required, None if not needed)
The additional description for the module to be placed under the module
docstring, before the sections start.
sections (required)
A list of sections, each of which consists of:
- title (required, None if not needed): the section title, commonly it should
not be None except for the first section of a module,
- description (optional): the optional additional description for the section,
- autosummary (required): an autosummary block, assuming current module is the
current module name.
Essentially, the rendered page would look like the following:
|---------------------------------------------------------------------------------|
| {{ module_name }} |
| ================= |
| {{ module_docstring }} |
| {{ description }} |
| |
| {{ section_title_1 }} <-------------- Optional if one wants the first |
| --------------------- section to directly follow |
| {{ section_description_1 }} without a second-level heading. |
| {{ section_autosummary_1 }} |
| |
| {{ section_title_2 }} |
| --------------------- |
| {{ section_description_2 }} |
| {{ section_autosummary_2 }} |
| |
| More sections... |
|---------------------------------------------------------------------------------|
Hooks will be automatically generated for each module and each section. For a module,
e.g., sklearn.feature_extraction, the hook would be feature_extraction_ref; for a
section, e.g., "From text" under sklearn.feature_extraction, the hook would be

浙公网安备 33010602011771号