- 开发工具
【免费下载链接】jupytext
Jupyter Notebooks as Markdown Documents, Julia, Python or R scripts
本文以 Jupytext 仓库中一个真实转换产物Notebook_with_more_R_magic_111.md为样本,系统讲解 Jupytext 的Pandoc Markdown 格式(md:pandoc):它如何用 Pandoc 的 fenced div 语法(::: {.cell .code})表达 notebook 的每个单元、如何处理%load_ext rpy2.ipython与%%R -i df这类 R cell magic、以及底层经由src/jupytext/pandoc.py调用 pandoc 完成 ipynb ↔ Markdown 往返的完整管线。读完你将掌握:该格式的语法结构、环境要求(pandoc ≥ 2.7.2)、CLI/配置用法、magic 保留机制及其与脚本类格式在 magic 转义行为上的本质差异。
样本文档是什么:一次真实的md:pandoc转换输出
仓库中tests/data/notebooks/outputs/ipynb_to_pandoc/Notebook_with_more_R_magic_111.md是 Jupytext 测试体系里"将 notebook 转为 Pandoc Markdown"这一类镜像测试(round-trip test)的产出物,其输入源文件是tests/data/notebooks/inputs/ipynb_py/Notebook_with_more_R_magic_111.ipynb。后者是一个标准的 nbformat 4(nbformat_minor: 2)notebook:内核为 Python 3,包含两个代码单元,第二个单元通过%%Rcell magic 调用 rpy2 与 ggplot2 绘制散点图,因此文件名中的 "more R magic" 指的就是同时出现行 magic(%load_ext)与cell magic(%%R)的情形。
该输出文档全文只有约 30 行,却完整覆盖了 Pandoc Markdown 格式的两大组成:YAML 头部(front matter)与fenced div 包裹的代码单元,是理解该格式最精炼的活样例。我们逐段拆解。
第一步:YAML 头部与 notebook 元数据
文档开头是一段 YAML front matter:
--- jupyter: kernelspec: display_name: Python 3 language: python name: python3 nbformat: 4 nbformat_minor: 2 ---jupyter:键下保存了 notebook 级元数据,其中kernelspec(display_name/language/name)与nbformat/nbformat_minor均直接取自源 ipynb 的metadata字段(见tests/data/notebooks/inputs/ipynb_py/Notebook_with_more_R_magic_111.ipynb的metadata段)。- 相比 Jupytext 自家的 Markdown 格式(
md),Pandoc 格式的头部不写入jupytext版本信息,因为 ipynb 与 Pandoc Markdown 的转换完全交由 pandoc 完成,Jupytext 无需记录自身text_representation细节。 - 这里只保留了经元数据过滤后支持的内核相关信息;
language_info(如pygments_lexer、version)这类执行细节不会出现在文本表示中——代码单元统一由 Pandoc 的 div 与围栏语法标注,语言信息写在代码围栏属性里。
完整的多单元示例可见demo/World population.pandoc.md,其头部还会带有jupytext.text_representation.format_name: pandoc与formats配对声明,说明该格式可以像其他格式一样参与 ipynb 配对同步。
第二步:代码单元如何用 Pandoc div 表达
转换产物中每个代码单元都被包在一个 Pandoc fenced div 里:
::: {.cell .code} ``` python %load_ext rpy2.ipython import pandas as pd df = pd.DataFrame( { "Letter": ["a", "a", "a", "b", "b", "b", "c", "c", "c"], "X": [4, 3, 5, 2, 1, 7, 7, 5, 9], "Y": [0, 4, 3, 6, 7, 10, 11, 9, 13], "Z": [1, 2, 3, 1, 2, 3, 1, 2, 3], } ):::
关键语法点: - **`::: {.cell .code}`** 是 Pandoc 的 fenced div 标记:`:::` 开合,属性 `{.cell .code}` 表明这是一个 code 类型的 notebook 单元。Markdown 单元则写作 `::: {.cell .markdown}`(参见 `demo/World population.pandoc.md` 中的大量示例)。 - 代码内容使用三重反引号围栏,语言标注 `python` 放在花括号式属性 `{...}` 中(即 ` ``` python ` 或 ` ``` {.python} `)。转换时,围栏的语言取自 notebook 的 `language_info.name` / kernelspec。 - **单元内代码保持原样**:`%load_ext rpy2.ipython`、`import pandas as pd` 与 DataFrame 构造均逐字保留,输出(outputs)与执行计数(execution_count)如预期被丢弃。 Pandoc 官方从 ipynb 直接读写 Markdown 时就采用这种 div + 围栏结构,因此整个格式"比 Jupytext Markdown 格式略为冗长"(官方文档表述,见 `website/src/content/docs/formats/markdown.md` 的 Pandoc Markdown 一节)——每个单元都要额外包一层 `:::`,这是其可读性上的主要代价,换来的则是与 Pandoc 生态的完全互通。 ## 第三步:R Magic 在 Pandoc 格式中原样保留 第二个单元是本样本的"题眼": ```markdown ::: {.cell .code} ``` python %%R -i df library("ggplot2") ggplot(data = df) + geom_point(aes(x = X, y = Y, color = Letter, size = Z)):::
注意 `%%R -i df` 与 `%load_ext` 一样**原样保留、未被注释转义**。这与脚本类格式形成鲜明对比:在 percent(`.pct.py`)、light(`.lgt.py`)等格式中,Jupytext 的 `src/jupytext/magics.py` 会在写出时把 magic 行改写成注释(如 `# %load_ext rpy2.ipython`),因为脚本要被 Python/R 解释器直接执行,magic 只是 IPython 的语法;而当读取脚本回 notebook 时,Jupytext 再根据 `_MAGIC_RE` 正则把这些注释还原为 magic。 而 Pandoc Markdown 的转换路径**不走 magics.py,也不经过 Jupytext 的 cell reader/writer**:`src/jupytext/jupytext.py` 中 `reads`/`writes` 对 `format_name == "pandoc"` 直接分派给 `md_to_notebook` / `notebook_to_md`(见 `src/jupytext/pandoc.py`),由 pandoc 本身在 ipynb JSON 与 Markdown 之间互转,magic 行只是单元源码的一部分,自然逐字往返。因此这份 `md:pandoc` 文档丢进任何 Pandoc 渲染管线都能还原出**可执行的含 magic 单元**——这是该格式对"多语言魔法"场景的一个重要优势,也是本样本被选为镜像测试输入的用意所在。 ## 底层实现:`src/jupytext/pandoc.py` 的转换管线 Jupytext 对 Pandoc 格式的实现非常薄,全部集中在 `src/jupytext/pandoc.py`: 1. **环境检查**:`raise_if_pandoc_is_not_available(min_version="2.7.2")` 会在转换前检测 `pandoc --version`。若 pandoc 未安装,抛出 `PandocError("The Pandoc Markdown format requires 'pandoc>=2.7.2', but pandoc was not found")`;版本低于要求则提示当前版本。`tests/external/simple_external_notebooks/test_read_simple_pandoc.py` 中的 `test_meaningfull_error_when_pandoc_is_missing` 专门验证了这条错误路径。 2. **notebook → Markdown**(`notebook_to_md`):把 notebook 用 nbformat 写成临时 ipynb 文件,再调用 `pandoc --from ipynb --to markdown -s --markdown-headings=atx --wrap=preserve --preserve-tabs`(pandoc ≥ 2.11.2 时用 `--markdown-headings=atx`;更早版本回退到 `--atx-headers`)。`--wrap=preserve` 与 `--preserve-tabs` 保证了换行与制表符不被 pandoc 重排。 3. **Markdown → notebook**(`md_to_notebook`):反向执行 `pandoc --from markdown --to ipynb -s`,再用 `nbformat.reads(..., as_version=4)` 读回 notebook 对象。 4. 两个方向都通过临时文件完成,用完即删;`notebook_to_md` 最后把 pandoc 输出按行拼接,统一行尾。 在 Jupytext 的分发层,`src/jupytext/formats.py` 注册了 `"pandoc": "md:pandoc"` 别名,因此 CLI、配置与配对声明里既可以写 `md:pandoc` 也可以写 `pandoc`;`formats.py` 还会在格式检测阶段调用 `is_pandoc_available()` 决定是否把 `pandoc` 列入可用格式(`src/jupytext/formats.py` 中 `if fmt.format_name == "pandoc" and not is_pandoc_available()` 的逻辑即为此服务)。 ## 第四步:如何安装并使用该格式 **安装 pandoc(必须)**:Pandoc Markdown 格式强依赖外部程序 pandoc,Jupytext 官方推荐 `conda install pandoc -c conda-forge`(详见 `website/src/content/docs/formats/markdown.md`)。测试环境中,pandoc ≥ 3.0 会打上 `requires_pandoc` 标记来启用相关用例(见 `tests/conftest.py` 中 `is_pandoc_available(min_version="3.0")` 的判定)。 **CLI 转换**: ```bash # ipynb -> Pandoc Markdown jupytext notebook.ipynb --to md:pandoc # Pandoc Markdown -> ipynb jupytext notebook.pandoc.md --to ipynb配对使用(关键操作,来自官方格式文档):将.ipynb与.pandoc.md配对,即可在 Jupyter 中编辑 ipynb、在 Markdown 编辑器中编辑 pandoc 文档并保持同步。配对声明示例(完整形态见demo/World population.pandoc.md头部):
jupyter: jupytext: formats: 'ipynb,md:pandoc' text_representation: format_name: pandoc format_version: '2.7.2'配置文件中也可直接声明formats = "ipynb,md:pandoc"。集成测试tests/external/contents_manager/test_contentsmanager_external.py的test_save_load_paired_md_pandoc_notebook验证了配对保存/加载后 notebook 内容与jupytext.formats元数据均保持一致。
第五步:边界与限制(从源码与测试确认的事实)
- 版本敏感:pandoc 未安装或低于 2.7.2 时转换直接抛
PandocError(src/jupytext/pandoc.py);2.11.2 前后用于 ATX 标题的 pandoc 参数名不同,Jupytext 已做分支兼容。 - Markdown 单元的 div 写法:pandoc 读回时既支持显式
::: {#cell_id .cell .markdown}(含 cell id,见test_pandoc_explicit),也支持隐式识别(无 div 的 Markdown 文本 + 围栏代码块,见test_pandoc_implicit);UTF-8 与 LaTeX 数学(如$\pi$)在md:pandoc往返中可无损保留(test_pandoc_utf8_in_md/test_pandoc_utf8_in_nb)。 - round-trip 验证:
tests/external/round_trip/test_mirror_external.py的test_ipynb_to_pandoc与tests/functional/cli/test_cli.py的test_sync_pandoc证明:ipynb → md:pandoc → ipynb与直接丢弃输出的镜像结果一致,即本样本文档具备可逆性——这正是它能作为测试 fixture 长期保留的原因。 - 与 Jupytext 自家 Markdown 的区别:
website/src/content/docs/formats/markdown.md明确指出 pandoc 格式"所有单元都用 div 标记,比 Jupytext Markdown 格式更冗长"。Jupytext Markdown(md)用围栏语言后接key=value的 JSON 元数据与<!-- #raw -->注释表达单元格,无需外部工具;而md:pandoc依赖 pandoc、语法更接近 Pandoc 通用文档生态,适合需要 pandoc 流水线(如学术写作、批量转换)的用户。
小结
Notebook_with_more_R_magic_111.md用 30 行代码展示了md:pandoc格式的全部核心要素:YAML 头部承载内核与 nbformat 元数据、::: {.cell .code}div 包裹每个代码单元、以及%load_ext/%%R -i df这类 R magic 在往返中被原样保留。结合src/jupytext/pandoc.py的薄封装实现、tests/external/simple_external_notebooks/test_read_simple_pandoc.py等测试以及官方格式文档,可以确认:只要安装 pandoc ≥ 2.7.2,即可通过jupytext --to md:pandoc或formats = "ipynb,md:pandoc"配对,让含 R magic 的多语言 notebook 在 Pandoc 生态与 Jupyter 之间无损往返。对于需要在 Pandoc 文档管线中处理 notebook 内容的场景,这是 Jupytext 给出的标准答案。
延伸阅读(仓库内路径):
- 转换样本:tests/data/notebooks/outputs/ipynb_to_pandoc/Notebook_with_more_R_magic_111.md
- 输入 ipynb:tests/data/notebooks/inputs/ipynb_py/Notebook_with_more_R_magic_111.ipynb
- 实现源码:src/jupytext/pandoc.py、分派逻辑在 src/jupytext/jupytext.py
- 格式别名与检测:src/jupytext/formats.py
- 测试用例:tests/external/simple_external_notebooks/test_read_simple_pandoc.py、tests/external/round_trip/test_mirror_external.py
- 官方格式说明:website/src/content/docs/formats/markdown.md
- 完整多单元示例:demo/World population.pandoc.md
- 开发工具
【免费下载链接】jupytext
Jupyter Notebooks as Markdown Documents, Julia, Python or R scripts
相关推荐
Jupytext 多格式配对实战:用 World population 示例解析 Jupyter Notebook 的 Markdown/脚本/MyST/R Markdown 表示
Jupytext 多格式配对实战:用 World population 示例解析 Jupyter Notebook 的 Markdown/脚本/MyST/R M
开发工具Jupytext 实战:用 MyST Markdown 表示 Jupyter Notebook——以 jupyter_again.ipynb 的转换产物为例
Jupytext 实战:用 MyST Markdown 表示 Jupyter Notebook——以 jupyter_again.ipynb 的转换产物为例 J
开发工具TorchTitan-NPU PR 测试静态审查报告模板:UT/ST 覆盖设计、独立 oracle 与合入判定实战指南
TorchTitan NPU PR 测试静态审查报告模板:UT/ST 覆盖设计、独立 oracle 与合入判定实战指南 TorchTitan NPU 的测试审查
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考