@david-brochart 的上述代码 sn-p 是一个不错的 hack,但它有几个缺点:
- 数据很容易丢失,例如不小心按错了单元格上的“u”。
- 在编辑文件时,Python 内核被阻塞。
- 内核的全局命名空间被污染。
- 无法同时编辑多个单元格。
- 剩余的“.toto.py”文件保留在磁盘上。
- 文件扩展名不依赖于单元格类型。
这是解决上述所有问题的改进版本。它仍然是一个 hack(例如,在内核忙碌时无法开始编辑单元格),但它在实践中运行良好。它仍然滥用计算内核来读取和写入文件并启动编辑器,但这样做的方式是尽可能减少副作用。
要使用这个 sn-p,它必须在 Jupyter 单元中执行。它也可以添加到
~/.jupyter/custom/custom.js。默认情况下会启动“emacsclient -c”,但这可以被任何其他编辑器替换。只有一个键(默认为“e”)可以将单元格交换为文件并启动编辑器,或者读取文件并将内容重新插入单元格。
%%javascript
Jupyter.keyboard_manager.command_shortcuts.add_shortcut('e', {
handler : function (event) {
function callback(msg) {
cell.set_text(msg.content.text);
}
var cell = Jupyter.notebook.get_selected_cell();
// Quote the cell text and *then* double any backslashes.
var cell_text = JSON.stringify(cell.get_text()).replace(/\\/g, "\\\\");
var cmd = `exec("""
cell_text = ${cell_text}
ext = "${cell.cell_type == 'code' ? 'py' : 'txt'}"
sep = "#-#-# under edit in file "
prefix, _, fname = cell_text.partition(sep)
if not fname or prefix:
# Create file and open editor, pass back placeholder.
import itertools, subprocess
for i in itertools.count():
fname = 'cell_{}.{}'.format(i, ext)
try:
with open(fname, 'x') as f:
f.write(cell_text)
except FileExistsError:
pass
else:
break
# Run editor in the background.
subprocess.Popen(['emacsclient', '-c', fname],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
print(sep, fname, sep='', end='')
else:
# Cell has been in edit: read it in and pass it back, and delete it.
import os
try:
with open(fname, 'r') as f:
cell_text = f.read()
except FileNotFoundError:
print("# File {} could not be inserted back.".format(fname), end='')
else:
if cell_text.endswith('\\\\n'):
cell_text = cell_text[:-1]
print(cell_text, end='')
os.remove(fname)
try:
os.remove(fname + '~')
except FileNotFoundError:
pass
""", None, {})`;
Jupyter.notebook.kernel.execute(cmd, {iopub: {output: callback}},
{silent: false});
return false;
}}
);