将 IPython 笔记本置于版本控制之下的好策略是什么?
Notebook 格式非常适合版本控制:如果想要对 Notebook 和输出进行版本控制,那么这非常有效。当人们只想对输入进行版本控制时,烦恼就来了,不包括可能是大二进制 blob 的单元输出(又名“构建产品”),尤其是对于电影和情节。特别是,我试图找到一个好的工作流程:
允许我在包含或排除输出之间进行选择,
如果我不想要它,可以防止我意外提交输出,
允许我将输出保留在本地版本中,
允许我使用我的版本控制系统查看输入何时发生更改(即,如果我只对输入进行版本控制但我的本地文件有输出,那么我希望能够查看输入是否已更改(需要提交). 使用版本控制状态命令将始终记录差异,因为本地文件有输出。)
允许我从更新的干净笔记本更新我的工作笔记本(包含输出)。 (更新)
如前所述,如果我选择包含输出(例如,在使用 nbviewer 时这是可取的),那么一切都很好。问题是当我不想想要对输出进行版本控制时。有一些工具和脚本可以剥离笔记本的输出,但我经常遇到以下问题:
我不小心提交了带有输出的版本,从而污染了我的存储库。我清除输出以使用版本控制,但实际上宁愿将输出保留在我的本地副本中(例如,有时需要一段时间才能重现)。与 Cell/All Output/Clear 菜单选项相比,一些去除输出的脚本会稍微改变格式,从而在差异中产生不需要的噪音。这可以通过一些答案来解决。在将更改拉到文件的干净版本时,我需要找到某种方法将这些更改合并到我的工作笔记本中,而无需重新运行所有内容。 (更新)
我已经考虑了几个我将在下面讨论的选项,但还没有找到一个好的综合解决方案。一个完整的解决方案可能需要对 IPython 进行一些更改,或者可能依赖于一些简单的外部脚本。我目前使用 mercurial,但想要一个也适用于 git 的解决方案:理想的解决方案是与版本控制无关。
这个问题已经讨论过很多次了,但是从用户的角度来看,并没有明确的或明确的解决方案。这个问题的答案应该提供明确的策略。如果它需要 IPython 的最新(甚至是开发)版本或易于安装的扩展程序,那很好。
更新:我一直在玩 my modified notebook 版本,它可以选择在每次保存时使用 Gregory Crosswhite's suggestions 保存一个 .clean
版本。这满足了我的大部分限制,但未解决以下问题:
这还不是标准解决方案(需要修改 ipython 源。有没有办法通过简单的扩展来实现这种行为?需要某种 on-save 挂钩。我当前工作流程的一个问题是拉动更改。这些将进入 .clean 文件,然后需要以某种方式集成到我的工作版本中。(当然,我总是可以重新执行笔记本,但这可能会很痛苦,特别是如果某些结果依赖于长计算,并行计算等)我还没有一个好主意如何解决这个问题。也许涉及像 ipycache 这样的扩展的工作流可能会起作用,但这似乎有点太复杂了。
笔记
移除(剥离)输出
当笔记本运行时,可以使用单元格/所有输出/清除菜单选项来删除输出。
有一些用于删除输出的脚本,例如脚本 nbstripout.py 会删除输出,但不会产生与使用笔记本界面相同的输出。这最终被包含在 ipython/nbconvert repo 中,但是已经关闭,说明这些更改现在包含在 ipython/ipython 中,但似乎还没有包含相应的功能。 (更新)话虽如此,Gregory Crosswhite 的解决方案表明这很容易做到,即使没有调用 ipython/nbconvert,所以如果它可以正确连接,这种方法可能是可行的。(但是将它附加到每个版本控制系统,似乎不是一个好主意——这应该以某种方式与笔记本机制挂钩。)
新闻组
关于版本控制的笔记本格式的思考。
问题
977:笔记本功能请求(打开)。
1280:清除所有保存选项(打开)。 (来自这个讨论。)
3295:自动导出的笔记本:仅导出明确标记的单元格(已关闭)。由扩展解决 11 添加 writeandexecute 魔术(合并)。
拉取请求
1621:清除“清除所有输出”(合并)上的 In[] 提示编号。 (另见 2519(合并)。)
1563:clear_output 改进(合并)。
3065:笔记本的差异能力(已关闭)。
3291:添加保存时跳过输出单元格的选项。 (关闭)。这似乎非常相关,但是由于建议使用“清洁/涂抹”过滤器而被关闭。一个相关的问题,如果你想在运行 git diff 之前去掉输出,你可以使用什么?似乎没有得到答复。
3312:WIP:笔记本保存挂钩(已关闭)。
3747: ipynb -> ipynb 变压器(关闭)。这是在 4175 中重新设置的。
4175:nbconvert:Jinjaless 出口商基地(合并)。
142:如果没有给出输入,则在 nbstripout 中使用 STDIN(打开)。
--script
选项,但该选项已被删除。我正在等待实现保存后挂钩 (which are planned),此时我认为我将能够提供一个结合多种技术的可接受的解决方案。
这是我使用 git 的解决方案。它允许您像往常一样添加和提交(和差异):这些操作不会改变您的工作树,同时(重新)运行笔记本不会改变您的 git 历史记录。
尽管这可能适用于其他 VCS,但我知道它不能满足您的要求(至少与 VSC 无关)。尽管如此,它对我来说还是很完美的,虽然它没有什么特别出色的地方,而且很多人可能已经在使用它,但我没有找到关于如何通过谷歌搜索来实现它的明确说明。所以它可能对其他人有用。
将包含此内容的文件保存在某处(对于以下内容,让我们假设 ~/bin/ipynb_output_filter.py) 使其可执行(chmod +x ~/bin/ipynb_output_filter.py) 创建文件 ~/.gitattributes,具有以下内容*.ipynb filter=dropoutput_ipynb 运行以下命令: git config --global core.attributesfile ~/.gitattributes git config --global filter.dropoutput_ipynb.clean ~/bin/ipynb_output_filter.py git config --global filter.dropoutput_ipynb.smudge猫
完毕!
限制:
它仅适用于 git
在 git 中,如果您在分支 somebranch 中,并且执行 git checkout otherbranch; git checkout somebranch,您通常希望工作树保持不变。相反,您将丢失两个分支之间来源不同的笔记本的输出和单元格编号。
更一般地说,输出根本没有版本控制,就像 Gregory 的解决方案一样。为了不只是在每次您执行任何涉及结帐的操作时都将其丢弃,可以通过将其存储在单独的文件中来更改方法(但请注意,在运行上述代码时,提交 ID 是未知的!),并可能对它们进行版本控制(但请注意,这需要的不仅仅是 git commit notebook_file.ipynb,尽管它至少可以让 git diff notebook_file.ipynb 免受 base64 垃圾的影响)。
也就是说,顺便说一句,如果您确实提取了包含一些输出的代码(即由其他人不使用这种方法提交),则输出将被正常检出。只有本地生产的输出会丢失。
我的解决方案反映了一个事实,即我个人不喜欢对生成的内容进行版本控制 - 请注意,进行涉及输出的合并几乎可以保证使输出或您的生产力或两者都无效。
编辑:
如果您确实采用了我建议的解决方案 - 也就是说,全球范围内 - 如果您想要对某些 git repo 进行版本输出,您将遇到麻烦。因此,如果您想禁用特定 git 存储库的输出过滤,只需在其中创建一个文件 .git/info/attributes,使用 **.ipynb filter=
作为内容。显然,以同样的方式可以做相反的事情:仅对特定存储库启用过滤。
代码现在保存在自己的 git repo 中
如果上述说明导致 ImportErrors,请尝试在脚本路径前添加“ipython”: git config --global filter.dropoutput_ipynb.clean ipython ~/bin/ipynb_output_filter.py
编辑:2016 年 5 月(2017 年 2 月更新):我的脚本有几种替代方案 - 为了完整起见,这里列出了我知道的那些:nbstripout (other variants),{ 4},jq。
我们有一个合作项目,产品是 Jupyter Notebooks,过去六个月我们使用了一种效果很好的方法:我们激活自动保存 .py
文件并跟踪 .ipynb
文件和 .py
文件。
这样,如果有人想查看/下载最新的笔记本,他们可以通过 github 或 nbviewer 进行,如果有人想查看笔记本代码的变化,他们只需查看对 .py
文件的更改。
对于 Jupyter
个笔记本服务器,这可以通过添加以下行来完成
import os
from subprocess import check_call
def post_save(model, os_path, contents_manager):
"""post-save hook for converting notebooks to .py scripts"""
if model['type'] != 'notebook':
return # only do this for notebooks
d, fname = os.path.split(os_path)
check_call(['jupyter', 'nbconvert', '--to', 'script', fname], cwd=d)
c.FileContentsManager.post_save_hook = post_save
到 jupyter_notebook_config.py
文件并重新启动笔记本服务器。
如果您不确定在哪个目录中可以找到您的 jupyter_notebook_config.py
文件,您可以输入 jupyter --config-dir
,如果您没有在该目录中找到该文件,您可以通过输入 jupyter notebook --generate-config
创建它。
对于 Ipython 3
个笔记本服务器,这可以通过添加以下行来完成
import os
from subprocess import check_call
def post_save(model, os_path, contents_manager):
"""post-save hook for converting notebooks to .py scripts"""
if model['type'] != 'notebook':
return # only do this for notebooks
d, fname = os.path.split(os_path)
check_call(['ipython', 'nbconvert', '--to', 'script', fname], cwd=d)
c.FileContentsManager.post_save_hook = post_save
到 ipython_notebook_config.py
文件并重新启动笔记本服务器。这些行来自 github 问题答案 @minrk provided,@dror 也将它们包含在他的 SO 答案中。
对于 Ipython 2
笔记本服务器,这可以通过使用以下命令启动服务器来完成:
ipython notebook --script
或通过添加行
c.FileNotebookManager.save_script = True
到 ipython_notebook_config.py
文件并重新启动笔记本服务器。
如果您不确定在哪个目录中可以找到您的 ipython_notebook_config.py
文件,您可以输入 ipython locate profile default
,如果您没有在该目录中找到该文件,您可以通过输入 ipython profile create
创建它。
这里是 our project on github that is using this approach:这里是 github example of exploring recent changes to a notebook。
我们对此非常满意。
--script
在实践中有效的补充证据。这样做的问题是,如果保留图像,实际的笔记本可能会很大。这种方式的理想解决方案可能会使用 git-annex 之类的东西来仅跟踪最新的完整笔记本。
--script
已弃用。 ipython.org/ipython-doc/3/whatsnew/version3.html
jupyter notebook --generate-config
创建配置文件。命令 jupyter --config-dir
找出哪个目录包含配置文件。 @Rich 给出的代码片段应该添加到名为 jupyter_notebook_config.py
的文件中。其余的和以前一样工作。
check_call(['ipython'
替换为 check_call(['jupyter'
,否则您将收到警告 ipython nbconvert
已被弃用,您应该改用 jupyter nbconvert
。 (Jupyter v4.1.0,iPython v4.1.2)
.py
文件保存到当前目录以外的其他目录,请将 '--output-dir', 'your_dir'
添加到 check_call
。例如,check_call(['jupyter', 'nbconvert', '--to', 'script', fname, '--output-dir', './src'], cwd=d)
我基于 MinRKs gist 创建了 nbstripout
,它同时支持 Git 和 Mercurial(感谢 mforbes)。它既可以在命令行上独立使用,也可以作为过滤器使用,可以通过 nbstripout install
/ nbstripout uninstall
轻松(卸载)安装在当前存储库中。
从 PyPI 或简单地获取它
pip install nbstripout
nbstripout
并不容易支持此用例,因为它依赖于 Notebook 的 JSON 格式。您最好编写一个专门针对您的用例的脚本。
nbstripout
是否可以选择在给定文件夹上递归工作(我说的是可执行文件本身)?
find
或其他一些递归查找要操作的文件的标准方法。
由于存在很多策略和工具来处理笔记本的版本控制,因此我尝试创建一个流程图来选择合适的策略(创建于 2019 年 4 月)
https://i.stack.imgur.com/hyHAg.png
在删除笔记本中的输出几年后,我试图提出一个更好的解决方案。我现在使用 Jupytext,这是我为 Jupyter Notebook 和 Jupyter Lab 设计的扩展。
Jupytext 可以将 Jupyter 笔记本转换为各种文本格式(脚本、Markdown 和 R Markdown)。反之亦然。它还提供将笔记本配对与其中一种格式的选项,并自动同步笔记本的两种表示形式(.ipynb
和 .md/.py/.R
文件)。
让我解释一下 Jupytext 如何回答上述问题:
允许我在包含或排除输出之间进行选择,
.md/.py/.R
文件仅包含输入单元格。您应该始终跟踪此文件。仅当您要跟踪输出时才对 .ipynb
文件进行版本控制。
如果我不想要它,可以防止我意外提交输出,
将 *.ipynb
添加到 .gitignore
允许我将输出保留在本地版本中,
输出保存在(本地).ipynb
文件中
允许我使用我的版本控制系统查看输入何时发生更改(即,如果我只对输入进行版本控制但我的本地文件有输出,那么我希望能够查看输入是否已更改(需要提交). 使用版本控制状态命令将始终记录差异,因为本地文件有输出。)
.py/.R
或 .md
文件上的差异是您要查找的内容
允许我从更新的干净笔记本更新我的工作笔记本(包含输出)。 (更新)
拉取 .py/.R
或 .md
文件的最新版本并在 Jupyter (Ctrl+R) 中刷新您的笔记本。您将从文本文件中获得最新的输入单元格,并从 .ipynb
文件中获得匹配的输出。内核不受影响,这意味着您的局部变量被保留 - 您可以继续在离开它的地方工作。
我喜欢 Jupytext 的地方在于,可以在您喜欢的 IDE 中编辑笔记本(以 .py/.R
或 .md
文件的形式)。使用这种方法,重构笔记本变得容易。完成后,您只需在 Jupyter 中刷新笔记本即可。
如果您想尝试一下:使用 pip install jupytext
安装 Jupytext 并重新启动您的 Jupyter Notebook 或 Lab 编辑器。打开您要进行版本控制的笔记本,并使用 Jupyter 笔记本中的 Jupytext Menu(或 Jupyter Lab 中的 Jupytext commands)将其与 Markdown 文件(或脚本)配对。保存您的笔记本,您将获得两个文件:原始的 .ipynb
,加上承诺的笔记本文本表示,非常适合版本控制!
对于那些可能感兴趣的人:Jupytext 也可以在 command line 上找到。
与 2019 年更好的方法相比,上面非常流行的 2016 年答案是不一致的黑客攻击。
存在多种选择,最能回答问题的是 Jupytext。
抓住Towards Data Science article on Jupytext
它与版本控制一起工作的方式是将 .py 和 .ipynb 文件都放在版本控制中。如果您想要输入差异,请查看 .py,如果您想要最新的渲染输出,请查看 .ipynb。
值得注意的提及:VS studio、nbconvert、nbdime、hydrogen
我认为通过更多的工作,VS studio 和/或hydrogen(或类似的)将成为这个工作流程解决方案中的主导者。
更新:现在您可以直接在 Visual Studio Code 中编辑 Jupyter Notebook 文件。您可以选择编辑 notebook 或转换后的 python 文件。
我终于找到了一种高效且简单的方法来让 Jupyter 和 Git 很好地协同工作。我仍处于第一步,但我已经认为它比所有其他复杂的解决方案要好得多。
Visual Studio Code 是来自 Microsoft 的一款酷炫的开源代码编辑器。它有一个出色的 Python 扩展,现在允许您将 import a Jupyter Notebook 作为 Python 代码。现在您也可以直接edit Jupyter Notebooks。
将 notebook 导入 python 文件后,所有代码和 markdown 都将放在一个普通的 python 文件中,并在注释中带有特殊标记。您可以在下图中看到:
https://i.stack.imgur.com/gUZTO.png
您的 python 文件只有笔记本输入单元格的内容。输出将在拆分窗口中生成。您在笔记本中有纯代码,它不会在您执行时更改。没有与您的代码混合的输出。没有奇怪的 JSON 难以理解的格式来分析您的差异。
只是纯 Python 代码,您可以在其中轻松识别每个差异。
我什至不再需要对我的 .ipynb
文件进行版本控制。我可以在 .gitignore
中添加一个 *.ipynb
行。
需要生成笔记本以发布或与他人共享?没问题,在交互式 python 窗口中只需 click the export button
https://i.stack.imgur.com/TVp1S.png
https://i.stack.imgur.com/hxCRj.png
这是 Visual Studio Code 中笔记本的屏幕截图:
https://i.stack.imgur.com/Tr5it.png
我只用了一天,但终于可以愉快地将 Jupyter 与 Git 一起使用。
PS:VSCode 代码补全比 Jupyter 好很多。
(2017-02)
策略
on_commit(): 剥离输出 > name.ipynb (nbstripout, ) 剥离输出 > name.clean.ipynb (nbstripout,) 总是 nbconvert 到 python: name.ipynb.py (nbconvert) 总是转换成 markdown: name.ipynb。 md (nbconvert, ipymd)
剥离输出 > name.ipynb (nbstripout, )
剥离输出 > name.clean.ipynb (nbstripout,)
总是 nbconvert 到 python:name.ipynb.py (nbconvert)
始终转换为降价:name.ipynb.md (nbconvert, ipymd)
vcs.configure():git difftool,mergetool:来自 nbdime 的 nbdiff 和 nbmerge
git difftool、mergetool:来自 nbdime 的 nbdiff 和 nbmerge
工具
nbstripout:从笔记本中剥离输出 src:https://gist.github.com/minrk/6176788 src:https://github.com/kynan/nbstripout pip install nbstripout; nbstripout 安装
来源:https://gist.github.com/minrk/6176788
src:https://github.com/kynan/nbstripout pip install nbstripout; nbstripout 安装
点安装 nbstripout; nbstripout 安装
ipynb_output_filter:从笔记本 src 中剥离输出:https://github.com/toobaz/ipynb_output_filter/blob/master/ipynb_output_filter.py
源代码:https://github.com/toobaz/ipynb_output_filter/blob/master/ipynb_output_filter.py
ipymd:在 {Jupyter、Markdown、O'Reilly Atlas Markdown、OpenDocument、.py} 之间转换 src:https://github.com/rossant/ipymd
源代码:https://github.com/rossant/ipymd
nbdime:“用于区分和合并 Jupyter 笔记本的工具。” (2015) src:https://github.com/jupyter/nbdime 文档:http://nbdime.readthedocs.io/ nbdiff:以终端友好的方式比较笔记本 nbdime nbdiff 用作 git diff 工具:https:/ /nbdime.readthedocs.io/en/latest/#git-integration-quickstart nbmerge:具有自动冲突解决的笔记本的三向合并 nbdime nbmerge 用作 git 合并工具 nbdiff-web:向您显示笔记本的丰富渲染差异 nbmerge -web:为您提供基于 Web 的笔记本三向合并工具 nbshow:以终端友好的方式呈现单个笔记本
源代码:https://github.com/jupyter/nbdime
文档:http://nbdime.readthedocs.io/ nbdiff:以终端友好的方式比较笔记本 nbdime nbdiff 用作 git diff 工具:https://nbdime.readthedocs.io/en/latest/#git-integration-快速入门 nbmerge:具有自动冲突解决的笔记本的三向合并 nbdime nbmerge 用作 git 合并工具 nbdiff-web:向您显示笔记本的丰富渲染差异 nbmerge-web:为您提供基于 Web 的笔记本三向合并工具nbshow:以终端友好的方式呈现单个笔记本
nbdiff:以终端友好的方式比较笔记本 nbdime nbdiff 用作 git diff 工具:https://nbdime.readthedocs.io/en/latest/#git-integration-quickstart
nbdime nbdiff 用作 git diff 工具:https://nbdime.readthedocs.io/en/latest/#git-integration-quickstart
nbmerge:具有自动冲突解决功能的笔记本的三向合并 nbdime nbmerge 用作 git 合并工具
nbdime nbmerge 用作 git 合并工具
nbdiff-web:向您展示笔记本的丰富渲染差异
nbmerge-web:为您提供基于 Web 的笔记本三向合并工具
nbshow:以终端友好的方式呈现单个笔记本
这是 Cyrille Rossant 为 IPython 3.0 提供的一个新解决方案,它保留在 markdown 文件而不是基于 json 的 ipymd 文件中:
https://github.com/rossant/ipymd
刚刚遇到看起来像一个完美解决方案的“jupytext”。它从笔记本生成一个 .py 文件,然后保持两者同步。您可以通过 .py 文件进行版本控制、差异化和合并输入,而不会丢失输出。当您打开笔记本时,它使用 .py 输入单元格和 .ipynb 输出单元格。如果你想在 git 中包含输出,那么你可以添加 ipynb。
https://github.com/mwouts/jupytext
正如所指出的,--script
在 3.x
中已弃用。可以通过应用保存后挂钩来使用此方法。特别是,将以下内容添加到 ipython_notebook_config.py
:
import os
from subprocess import check_call
def post_save(model, os_path, contents_manager):
"""post-save hook for converting notebooks to .py scripts"""
if model['type'] != 'notebook':
return # only do this for notebooks
d, fname = os.path.split(os_path)
check_call(['ipython', 'nbconvert', '--to', 'script', fname], cwd=d)
c.FileContentsManager.post_save_hook = post_save
代码取自 #8009。
.py
文件返回到笔记本是有问题的,所以很遗憾,这不是一个完整的解决方案。 (我有点希望这是因为比较 .py
文件而不是笔记本非常好。也许新的 notebook diff 功能会很有用。
--script
行为,而不管版本控制如何。起初我遇到了一些问题,所以以防万一我可以节省一些时间:1) 如果配置文件文件夹中缺少 ipython_notebook_config.py
,请运行 ipython profile create
来生成它。 2) 如果似乎忽略了 post-save-hook,请使用 --debug
运行 ipython 来诊断问题。 3) 如果脚本因错误 ImportError: No module named mistune
而失败 - 简单安装 minstue: pip install mistune
。
我使用非常务实的方法;这适用于几个笔记本,在几个方面。它甚至使我能够“转移”笔记本。它适用于 Windows 和 Unix/MacOS。艾尔觉得很简单,就是解决上面的问题……
概念
基本上,不跟踪 .ipnyb
文件,只跟踪相应的 .py
文件。
通过使用 --script
选项启动 notebook-server ,保存笔记本时会自动创建/保存该文件。
这些 .py
文件确实包含所有输入;非代码保存到注释中,单元格边框也是如此。可以将这些文件读取/导入(并拖动)到笔记本服务器中以(重新)创建笔记本。只有输出消失了;直到重新运行。
我个人使用 mercurial 对 .py
文件进行版本跟踪;并使用普通(命令行)命令添加,签入(等)。大多数其他 (D)VCS 将允许这样做。
现在追踪历史很简单; .py
很小,有文字且易于区分。有时,我们需要一个克隆(只是分支;在那里启动第二个笔记本服务器)或旧版本(签出并导入笔记本服务器)等。
提示与技巧
将 *.ipynb 添加到 '.hgignore',以便 Mercurial 知道它可以忽略这些文件
创建一个(bash)脚本来启动服务器(使用 --script 选项)并对其进行版本跟踪
保存笔记本确实会保存 .py 文件,但不会将其签入。这是一个缺点:人们可能会忘记它也是一个功能:无需对存储库历史进行集群即可保存笔记本(并稍后继续)。
这是一个缺点:人们可能会忘记
这也是一个功能:可以保存笔记本(并稍后继续),而无需对存储库历史进行集群。
愿望
在笔记本仪表板中有一个用于签入/添加/等的按钮会很好
结帐(通过示例) file@date+rev.py) 应该会有所帮助添加它会做很多工作;也许我会这样做一次。直到现在,我只是手动完成。
.py
文件返回到笔记本?我喜欢这种方法,但因为 .ipynb
-> .py
-> .ipynb
可能有损,我没有认真考虑这一点。
.py
到 .ipynb
格式的转换中完全保留数据。有一个 issue about this - 所以也许这将构成一个完整解决方案的基础。
.py
文件转换为 .ipynb
文件时遇到了一些困难。 nbconvert
似乎还不支持这一点,而且我没有笔记本仪表板,因为我手动运行 ipython notebook
。您对如何实现这种向后转换有任何一般性建议吗?
.py
到笔记本的转换并不是为了往返。所以这并不是一个通用的解决方案,尽管它很适合你。
不幸的是,我对 Mercurial 知之甚少,但我可以为您提供与 Git 一起使用的可能解决方案,希望您能够将我的 Git 命令翻译成与 Mercurial 等效的命令。
作为后台,在 Git 中,add
命令将对文件所做的更改存储到暂存区域。完成此操作后,Git 将忽略对文件的任何后续更改,除非您告诉它也将它们暂存。因此,对于每个给定文件,以下脚本会剥离所有 outputs
和 prompt_number sections
,暂存剥离的文件,然后恢复原始文件:
注意:如果运行此程序会收到类似 ImportError: No module named IPython.nbformat
的错误消息,请使用 ipython
而不是 python
来运行脚本。
from IPython.nbformat import current
import io
from os import remove, rename
from shutil import copyfile
from subprocess import Popen
from sys import argv
for filename in argv[1:]:
# Backup the current file
backup_filename = filename + ".backup"
copyfile(filename,backup_filename)
try:
# Read in the notebook
with io.open(filename,'r',encoding='utf-8') as f:
notebook = current.reads(f.read(),format="ipynb")
# Strip out all of the output and prompt_number sections
for worksheet in notebook["worksheets"]:
for cell in worksheet["cells"]:
cell.outputs = []
if "prompt_number" in cell:
del cell["prompt_number"]
# Write the stripped file
with io.open(filename, 'w', encoding='utf-8') as f:
current.write(notebook,f,format='ipynb')
# Run git add to stage the non-output changes
print("git add",filename)
Popen(["git","add",filename]).wait()
finally:
# Restore the original file; remove is needed in case
# we are running in windows.
remove(filename)
rename(backup_filename,filename)
在您要提交更改的文件上运行脚本后,只需运行 git commit
。
.clean
扩展名的干净版本。不幸的是,如果没有 directly modifying IPython,我无法看到如何做到这一点(尽管这种变化非常微不足道)。我会玩一段时间,看看它是否适合我的所有需求。
我已经构建了解决这个问题的 python 包
https://github.com/brookisme/gitnb
它提供了一个带有受 git 启发的语法的 CLI 来跟踪/更新/区分你的 git 存储库中的笔记本。
这是一个例子
# add a notebook to be tracked
gitnb add SomeNotebook.ipynb
# check the changes before commiting
gitnb diff SomeNotebook.ipynb
# commit your changes (to your git repo)
gitnb commit -am "I fixed a bug"
请注意,我使用“gitnb commit”的最后一步是提交到您的 git 存储库。它本质上是一个包装器
# get the latest changes from your python notebooks
gitnb update
# commit your changes ** this time with the native git commit **
git commit -am "I fixed a bug"
还有其他几种方法,并且可以配置为在每个阶段都需要或多或少的用户输入,但这就是一般的想法。
我还将向其他人添加 https://nbdev.fast.ai/,它是最先进的“文学编程环境,正如 Donald Knuth 在 1983 年所设想的那样!”。
它还有一些 git 钩子可以帮助一些 https://nbdev.fast.ai/#Avoiding-and-handling-git-conflicts 和其他命令,例如:
nbdev_read_nbs
nbdev_clean_nbs
nbdev_diff_nbs
nbdev_test_nbs
因此,您还可以在编写库时随时随地创建文档,例如其中一些:
https://dev.fast.ai/
https://ohmeow.github.io/blurr/
https://rbracco.github.io/fastai2_audio/
除了第一个链接之外,您还可以在此处nbdev tutorial观看视频。
tangling
,但从该文件生成的“真实”python 文件是 fastai2/callback/schedule.py,我添加了一个我没看过的 youtube 视频。
要跟进 Pietro Battiston 的出色脚本,如果遇到这样的 Unicode 解析错误:
Traceback (most recent call last):
File "/Users/kwisatz/bin/ipynb_output_filter.py", line 33, in <module>
write(json_in, sys.stdout, NO_CONVERT)
File "/Users/kwisatz/anaconda/lib/python2.7/site-packages/IPython/nbformat/__init__.py", line 161, in write
fp.write(s)
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2014' in position 11549: ordinal not in range(128)
您可以在脚本的开头添加:
reload(sys)
sys.setdefaultencoding('utf8')
挖了一圈,终于找到了this relatively simple pre-save hook on the Jupyter docs。它剥离单元格输出数据。您必须将其粘贴到 jupyter_notebook_config.py
文件中(请参阅下面的说明)。
def scrub_output_pre_save(model, **kwargs):
"""scrub output before saving notebooks"""
# only run on notebooks
if model['type'] != 'notebook':
return
# only run on nbformat v4
if model['content']['nbformat'] != 4:
return
for cell in model['content']['cells']:
if cell['cell_type'] != 'code':
continue
cell['outputs'] = []
cell['execution_count'] = None
# Added by binaryfunt:
if 'collapsed' in cell['metadata']:
cell['metadata'].pop('collapsed', 0)
c.FileContentsManager.pre_save_hook = scrub_output_pre_save
如果您不确定在哪个目录中可以找到 jupyter_notebook_config.py 文件,您可以键入 jupyter --config-dir [进入命令提示符/终端],如果您在那里找不到该文件,您可以通过输入 jupyter notebook --generate-config。
这是 2020 年 4 月,有很多用于 Jupyter notebook 版本控制的策略和工具。以下是您可以使用的所有工具的快速概览,
nbdime - 非常适合笔记本的本地差异和合并
nbstripout - 一个 git 过滤器,用于在每次提交之前自动删除笔记本输出
jupytext - 将 .py 配套文件同步到每个笔记本。你只提交 .py 文件
nbconvert - 将笔记本转换为 python 脚本或 HTML(或两者)并提交这些备用文件类型
ReviewNB - 显示 GitHub 上任何提交或拉取请求的笔记本差异(连同输出)。还可以在笔记本单元格上写评论以讨论更改(下面的屏幕截图)。
https://i.stack.imgur.com/3xE1O.png
免责声明:我建立了 ReviewNB。
我做了 Albert & Rich did - 不要版本化 .ipynb 文件(因为这些文件可能包含图像,这会变得混乱)。相反,始终运行 ipython notebook --script
或将 c.FileNotebookManager.save_script = True
放入您的配置文件中,以便在您保存笔记本时始终创建一个(可版本化的).py
文件。
为了重新生成笔记本(在签出 repo 或切换分支之后),我将脚本 py_file_to_notebooks.py 放在我存储笔记本的目录中。
现在,签出 repo 后,只需运行 python py_file_to_notebooks.py
即可生成 ipynb 文件。切换分支后,您可能需要运行 python py_file_to_notebooks.py -ov
来覆盖现有的 ipynb 文件。
为了安全起见,最好将 *.ipynb
添加到您的 .gitignore
文件中。
编辑:我不再这样做,因为(A)每次签出分支时都必须从 py 文件重新生成笔记本,并且(B)笔记本中还有其他东西,比如你丢失的降价。我改为使用 git 过滤器从笔记本中剥离输出。关于如何做到这一点的讨论是here。
.py
文件转换回 .ipynb
是有问题的,尤其是对于尚无转换器的版本 4 笔记本。目前需要使用 v3 导入器,然后转换为 v4,我有点担心这个复杂的行程。此外,如果笔记本主要是 Julia 代码,则 .py
文件不是一个很好的选择!最后,--script
已被弃用,所以我认为钩子是要走的路。
好的,根据讨论 here,看起来当前的最佳解决方案是制作一个 git 过滤器,以便在提交时自动去除 ipynb 文件的输出。
这是我为使其正常工作所做的工作(从该讨论中复制):
当您无法导入最新的 IPython 时,我稍微修改了 cfriedline 的 nbstripout 文件以提供信息性错误:https://github.com/petered/plato/blob/fb2f4e252f50c79768920d0e47b870a8d799e92b/notebooks/config/strip_notebook_output 并将其添加到我的存储库中,让我们在 ./relative/path/to/strip_notebook_output
中说
还将文件 .gitattributes 文件添加到 repo 的根目录,其中包含:
*.ipynb filter=stripoutput
并创建了一个 setup_git_filters.sh
包含
git config filter.stripoutput.clean "$(git rev-parse --show-toplevel)/relative/path/to/strip_notebook_output"
git config filter.stripoutput.smudge cat
git config filter.stripoutput.required true
并运行 source setup_git_filters.sh
。花哨的 $(git rev-parse...) 事情是在任何(Unix)机器上找到你的仓库的本地路径。
你可以使用这个 jupyter 扩展。它将使您能够直接将您的 ipython 笔记本上传到 github。
https://github.com/sat28/githubcommit
我还制作了一个演示这些步骤的视频 - youtube link
下面的帖子中讨论的想法怎么样,笔记本的输出应该保存在哪里,并认为生成它可能需要很长时间,而且它很方便,因为 GitHub 现在可以渲染笔记本。添加了用于导出 .py 文件的自动保存挂钩,用于 diff 和 .html 用于与不使用笔记本或 git 的团队成员共享。
https://towardsdatascience.com/version-control-for-jupyter-notebook-3e6cef13392d
ImportError
,我对上述内容进行了更改以使用 ipython 运行:git config --global filter.dropoutput_ipynb.clean ipython ~/bin/ipynb_output_filter.py
~/.gitattributes
,其他人具有相同的过滤器我这样做 2) 我将正则表达式定义为workdir/**/*.ipynb filter=dropoutput_ipynb
,并将我的大部分笔记本放在 workdir/ =>如果我仍然想推送一个带有输出的笔记本并享受 github 中的可书签渲染,我只需将它放在该文件夹之外。