支持 Git 的 Jupyter

如何使用 nbdev 钩子实现对 Git 友好的 Jupyter 笔记本

版本控制对于软件开发至关重要,然而 Jupyter 笔记本默认不支持版本控制。nbdev 解决了这个问题!它提供了一系列钩子,可以在任何 Git 仓库中启用对 Git 友好的 Jupyter 笔记本,包括那些不使用更广泛的 nbdev 系统的仓库。

要开始,安装 nbdev

pip install -U nbdev
conda install -c fastai nbdev

然后安装钩子

nbdev_install_hooks

就这样!如果您遇到困难或想了解更多关于 nbdev 钩子及其定制方法,请继续阅读。如果您对这个功能的开发过程及其底层工作原理感到好奇,请查看我们相关的博文

注意

clean 钩子目前仅支持 Jupyter Notebook 和 Jupyter Lab。如果您使用其他笔记本编辑器,如 VSCode 或 PyCharm,您可能需要使用nbdev 的 pre-commit 钩子

快速入门:为一个仓库安装 nbdev 钩子

首先,切换到您当前项目的目录并再次确认。不用担心奇怪的路径,那是因为我们为本教程使用了临时目录。

pwd
/private/var/folders/ft/0gnvc3ts5jz4ddqtttp6tjvm0000gn/T/tmpez8nec5v/repo

安装 nbdev

pip install -Uqq nbdev

安装 nbdev 钩子

nbdev_install_hooks
Not in a git repository, git hooks cannot be installed.

如果您不在 Git 仓库中,您会看到上面的错误。如果是这样,请初始化一个 Git 仓库。

git init
Initialized empty Git repository in /private/var/folders/ft/0gnvc3ts5jz4ddqtttp6tjvm0000gn/T/tmpez8nec5v/repo/.git/

然后再次尝试安装 nbdev 钩子。

nbdev_install_hooks
Hooks are installed.

如果您已经在 Jupyter 配置文件中设置了 pre-save 钩子,我们将无法安全地自动安装新的钩子。相反,您将遇到错误,并需要按照其说明进行手动安装。

Jupyter 钩子现在将安装在您的用户 Jupyter 配置目录中,并默认适用于所有仓库。Git 钩子将仅安装在当前仓库中;您需要为您的每个 Git 仓库重新运行nbdev_install_hooks。如果您想自定义钩子行为,例如在某些仓库中选择退出钩子,请参阅配置 nbdev 钩子

什么是 nbdev 钩子?

nbdev 提供了三个钩子来简化 Jupyter 与 Git 的集成。

使用 Git 合并笔记本时的nbdev_merge

使用 Jupyter 时最大的抱怨之一是合并冲突会破坏笔记本。这在有许多协作者的项目中尤为突出。

Jupyter notebook error: 'Error loading notebook: Unreadable Notebook...'

当打开有合并冲突的笔记本时,Jupyter notebook 会显示以上错误。

通常这些冲突是关于我们并不关心的元数据,例如单元格执行次数。nbdev 提供了一个自定义的 Git 合并驱动程序,它可以自动修复冲突的输出和元数据,并将剩余的冲突保留在仍然适用于 Jupyter 的状态。它适用于所有底层使用合并的 Git 命令,包括 mergepullrebasestash

以下是使用 nbdev 合并驱动程序时,冲突在 Jupyter 中的样子

Jupyter-friendly git conflict with patches in individual cells separated by conflict markers

在 Jupyter 中保存笔记本时的nbdev_clean

Jupyter 笔记本存储了各种元数据(包括执行次数和笔记本扩展信息),这些元数据不利于 Git 等协作版本控制系统。它们污染了拉取请求和 Git 历史记录中的差异(这会使调试更加困难),并且容易导致合并冲突。例如

  {
   "cell_type": "code",
-  "execution_count": 1,
+  "execution_count": 2,
   "metadata": {
     "hide_input": false
  }

Python 的默认 repr 是另一个例子,因为它包含了我们通常不感兴趣的内存地址

-<matplotlib.axes._subplots.AxesSubplot at 0x7fbc11508950>
+<matplotlib.axes._subplots.AxesSubplot at 0x7fbc113dbe90>

nbdev 安装了一个 Jupyter 钩子,它运行nbdev_clean来自动清除笔记本中不需要的元数据和输出,包括默认 Python reprs 中的 ids!使用 nbdev 钩子后,上面的例子会变成

{
  "cell_type": "code",
  "execution_count": null,
  "metadata": {}
}

<matplotlib.axes._subplots.AxesSubplot>

使用 Git 合并笔记本后的nbdev_trust

Jupyter 安全模型的一个副作用是,小部件在协作仓库中无法工作,除非您在每次 git pull 后手动“信任”笔记本。这背后有很好的原因:由于 Jupyter 笔记本包含 HTML 和 JavaScript,信任系统可以避免在您打开笔记本时运行恶意代码,并且您没有明确运行任何单元格。有关更多信息,请参阅官方文档

每次手动信任笔记本很麻烦。一个更自然的流程是一次性信任一个仓库,然后信任之后的所有笔记本和更改。nbdev 包含一个 Git post-merge 钩子,它在您的仓库中运行nbdev_trust来做到这一点。

配置 nbdev 钩子

nbdev 设置的最新参考位于nbdev_create_config文档中。此外,本节将指导您完成一些常见配置。

控制是否运行 Jupyter 钩子

  • 全局启用 Jupyter 钩子:在用户设置中设置 jupyter_hooks = True
  • 全局禁用 Jupyter 钩子:在用户设置(位于 ~/.config/nbdev/settings.ini)中设置 jupyter_hooks = False
  • 仅为选定的仓库启用 Jupyter 钩子:在用户设置中设置 jupyter_hooks = False,并在选定的仓库设置中设置 jupyter_hooks = True

使用以下设置自定义笔记本清理

  • 清理所有输出和元数据:clear_all
  • 按键保留某些元数据:allowed_metadata_keysallowed_cell_metadata_keys
  • 清理默认 Python repr 中的 idclean_ids

以上所有设置都可以按用户和按仓库进行自定义。

控制是否运行 Git 钩子

由于 Git 钩子是按仓库安装的,它们只会运行在您手动运行nbdev_install_hooks的仓库中。如果您后来改变主意,可以按照仓库中创建的 .gitconfig 文件中的说明卸载 Git 钩子。