Julia 仓库 jldoctest 编写与验证实战指南:基于 Agent Skill(SKILL.md)的完整工作流
【免费下载链接】juliaThe Julia Programming Language项目地址: https://gitcode.com/gh_mirrors/ju/julia
jldoctest是 Julia 文档系统中将 docstring 内代码示例变成可自动测试用例的机制,也是 Julia 主仓库(base/、stdlib/、Compiler/ 与 doc/ 目录)维护代码示例正确性的核心工具。本文以仓库内置的 Agent Skill 文档 doctests/SKILL.md 为主体,结合 jldoctests.md 最佳实践与 doc/make.jl 的构建实现,系统讲解jldoctest的编写要点、常见过滤器、setup/teardown 机制以及本地验证的完整命令流程。读完本文,你将能够在 Julia 仓库中安全地新增或修改jldoctest代码块,并在提交 PR 前独立完成本地验证。
一、背景:Agent Skill 与 doctests 技能
Julia 仓库将面向 AI Agent 的项目级技能集中存放于doc/src/devdocs/agents/skills/目录,每个技能遵循 Agent Skills)。其中doctests技能的元信息如下:
name: doctests description: Write and verify Julia `jldoctest` code blocks. Use whenever adding or changing a `jldoctest` block in a docstring (base/, stdlib/, Compiler/) or under doc/, and before opening a PR that touches doctests.这段描述界定了技能的适用范围:只要在 docstring(base/、stdlib/、Compiler/ 下的源码注释)或 doc/ 下的文档中新增、修改了任何jldoctest代码块,并且在打开涉及 doctest 的 PR 之前,都应启用该技能。仓库的文档构建系统(doc/make.jl 中的generate_agent_skill_docs())会读取每个技能的SKILL.md,将其渲染进 "Agentic Devdocs" 章节,因此这份技能文档本身就是 Julia 文档站点的一部分。
该技能的核心工作流分两步:先复习编写规范,再执行验证。
二、编写 jldoctest:先复习最佳实践
在动手写新的 doctest 之前,技能明确要求先通读 doc/src/devdocs/contributing/jldoctests.md,重点确认三件事:filters(过滤器)、labels(标签)与 setup code(前置代码)。这份指南的要点如下。
2.1 过滤器(filter):应对非确定性输出
只要输出内容在不同运行环境下可能变化,就必须使用filter =选项。常见场景包括:
- 包含未初始化内存的数组(来自
undef或similar); - 包含随机数;
- 包含计时信息;
- 包含文件系统路径。
仓库文档沉淀了一批反复使用的过滤正则,可直接套用:
| 过滤正则 | 用途 |
|---|---|
r"int.jl:\\d+" | 去掉内省宏输出中的行号 |
r"Stacktrace:(\\n \\[0-9+\\].*)*" | 演示错误时隐藏堆栈回溯 |
r"Closest candidates.*\\n .*" | 跳过MethodError打印的方法建议 |
r"@ .*" | 去掉methods或@which输出中的文件位置 |
r"\\@world\\(MyStruct, \\d+:\\d+\\)" | 过滤 world age 编号 |
r"with \\d+ methods" | 忽略重定义函数时的方法计数 |
r"[0-9\\.]+ seconds \\(.*?\\)" | 移除带内存信息的计时输出 |
r"[0-9\\.]+ seconds" | 移除简单计时结果 |
r"[0-9\\.]+" | 过滤匿名函数名中的数字 |
r"([A-B] [0-5])"、r"[A-B] [X-Z] [0-5]" | 处理非确定性的进程输出 |
r"(world\\nhello\|hello\\nworld)" | 允许交错输出world/hello的任意顺序 |
如果上述都不匹配,就需要自行编写一个能剔除易变文本的正则表达式。合理使用过滤器能保证 doctest 跨平台、跨 Julia 版本保持稳定。
⚠️ docstring 中的双重转义:在 docstring 内编写正则过滤器时,反斜杠必须双重转义。例如应写r"[\\d\\.]+"而不是r"[\d\.]+",因为 docstring 自身会先处理一次转义序列,之后正则才会被创建。
仓库源码中有大量实践样例,例如 base/abstractarray.jl 的jldoctest; filter = r"[01]",以及 base/abstractdict.jl 中对表格输出使用filter = r"^\\s+'\\S'.*\$"m等多行正则过滤的写法。
2.2 Setup 与 teardown:前置与清理代码
简短的前置表达式可用内联的setup =选项:
```jldoctest; setup = :(using InteractiveUtils) ... ```如果 setup 代码较长,或多个代码块需要相同环境,应使用DocTestSetup元块(meta block):
```@meta DocTestSetup = :(import Random; Random.seed!(1234)) ```并在使用完毕后通过DocTestSetup = nothing关闭:
```@meta DocTestSetup = nothing ```需要清理代码时(例如删除临时文件、恢复当前目录),可使用teardown =选项:
```jldoctest; setup = :(oldpath = pwd(); cd(mktempdir())), teardown = :(cd(oldpath)) ... ```2.3 标签(label):在代码块之间维持状态
相关的 doctest 块可以通过jldoctest关键字后的同名标签共享状态。Julia 手册就用这种模式演示可变(mutation)与重绑定(rebinding)的区别:
```jldoctest mutation_vs_rebind julia> a = [1,2,3] ... ``````jldoctest mutation_vs_rebind julia> a[1] = 42 ... ```同名代码块在 doctest 运行时按顺序执行,第一个块中创建的变量在后续块中仍然可用。当某个片段的计算结果需要在后续示例中继续使用时,给它们加上相同标签即可,这样既避免重复 setup 代码,也更贴近真实 REPL 会话的行为。
2.4 语法版本化(syntax =)
当文档涉及新 Julia 语法时,可用syntax =选项为 doctest 指定所需语法版本:
```jldoctest; syntax = v"1.14" julia> result = @label myblock begin for i in 1:10 i > 5 && break myblock i * 2 end 0 end 12 ```这保证代码块按指定语法版本解析,即使文档是用更老的默认语法构建的,新语法特性的 doctest 也能通过。对于以新语法为主的模块,可在元块中设置全局默认值:
```@meta DocTestSyntax = v"1.14" ```单块的syntax =设置会覆盖全局DocTestSyntax。注意:doctest 的语法版本化需要 Julia 1.14 及以上;在更老的 Julia 上运行时,syntax = v"1.14"及以上的代码块会被跳过并给出警告。仓库的构建脚本正是通过meta = Dict(:DocTestSyntax => VERSION)将当前版本注入文档构建的(见 doc/make.jl)。
三、验证流程:每次 doctest 改动必须执行
技能文档强调:只要修改了任何jldoctest块,就必须验证。验证步骤如下。
第 1 步:回顾最佳实践
再次阅读 doc/src/devdocs/contributing/jldoctests.md,特别确认改动的 doctest 是否需要 filters、labels 或 setup code。
第 2 步:运行 doctest
提供两种运行方式,按偏好选择:
使用预构建的 juliaup 版本运行:
make -C doc doctest=true revise=true JULIA_EXECUTABLE=$HOME/.juliaup/bin/julia使用仓库内构建的 Julia 运行(推荐):
make -C doc doctest=true revise=true这是首选方式,且不要传递其他任何选项。
⏱️ 重要提醒:doctest 运行可能需要长达15 分钟。在完成之前不要终止doctest,也不要为其设置超时(例如在使用 ChatGPT 这类工具时,可能需要调大yield_timeout_ms)。
3.1 命令背后的构建机制
上述命令的含义可以从 doc/Makefile 中得到印证:
@echo "To run doctests, use 'make <target> doctest=true'" @echo "To fix outdated doctests, use 'make <target> doctest=fix'" @echo "To run doctests using Revise (to test changes without rebuilding the sysimage), use 'make <target> doctest=true revise=true'"doctest=true:开启 doctest 检查模式;doctest=fix:自动修复过期的 doctest 输出(直接改写文档中与运行结果不符的代码块输出,需人工确认改动是否符合预期);revise=true:借助 Revise 机制在不重新构建 sysimage 的情况下测试对源码的修改,显著缩短迭代周期;JULIA_EXECUTABLE=...:指定实际执行 doctest 的 Julia 可执行文件,便于使用系统安装的 juliaup 版本。
这些开关在 doc/make.jl 中被转换为 Documenter.jl 的doctest参数:命令行含doctest=fix时为:fix,含doctest=only时为:only,含doctest=true时为true,否则为false。其中doctest=only模式仅运行 doctest 而不构建完整文档,适合快速回归。
四、仓库中的真实案例佐证
jldoctest并非孤立机制,而是深度嵌入 Julia 主仓库的文档体系:
- base/ 源码:几乎所有 docstring 都包含 doctest,例如 base/abstractarray.jl(47 处)、base/abstractdict.jl(12 处)等,覆盖了过滤器、标签、多行正则等各类写法,是学习
jldoctest语法的最佳活教材。 - 文档章节:完整规范收录在 doc/src/devdocs/contributing/jldoctests.md,并作为 "Contributor's Guide" 的一部分被 doc/make.jl 注册到文档导航中。
- Agent 技能体系:doctests 技能与 test-changes、c-static-analysis、external-deps 等技能共同构成 Julia 的 Agentic Devdocs 体系,见 doc/src/devdocs/agents/README.md 与 doc/src/devdocs/agents/skills/doctests/SKILL.md。
五、实操清单
在提交任何涉及 doctest 的改动前,对照以下清单自查:
- 编写阶段:新写或修改
jldoctest前,复习 jldoctests.md; - 判断需求:输出是否可能变化(内存未初始化、随机数、计时、路径)?是否需要
filter =?docstring 内的正则是否已双重转义? - 组织代码:是否需要
setup =/DocTestSetup/teardown =?多个块需要共享状态时是否已使用相同标签?是否涉及新语法(syntax =)? - 执行验证:使用推荐命令
make -C doc doctest=true revise=true(仓库内 Julia)运行 doctest; - 耐心等待:最多等待 15 分钟,期间不终止、不设超时;若输出过期,可改用
doctest=fix自动修复后人工核对。
遵循这套流程,既能保证文档示例长期可运行,也能让 doctest 成为 Julia 仓库中值得信赖的“活文档”。
【免费下载链接】juliaThe Julia Programming Language项目地址: https://gitcode.com/gh_mirrors/ju/julia
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考