在外部编辑器中编辑 IPython 个单元格
Edit IPython cell in an external editor
如果在 IPython 笔记本中有一个键盘快捷键就太好了,这样可以在外部编辑器(例如 gvim)中编辑当前单元格的内容。也许只是将当前单元格的内容复制到一个临时文件中,在其上启动 gvim,并在每次保存文件时更新当前单元格(并在退出 gvim 时删除临时文件) .此外,如果从浏览器编辑单元格,可能会更新临时文件,以便 gvim 知道文件已更改。
我知道 vim-ipython 和 ipython-vimception 等项目,但它们不符合我的需求。我认为浏览器对于简单的事情已经足够了,但是当需要更强大的编辑时就没有必要重新发明轮子了。
您知道 IPython 笔记本中是否已经存在这样的功能吗?
谢谢。
这是我想到的。我添加了 2 个快捷方式:
- 'g' 使用当前单元格的内容启动 gvim(您可以用您喜欢的任何文本编辑器替换 gvim)。
- 'u' 用 gvim 保存的内容更新当前单元格的内容。
因此,当您想使用首选编辑器编辑单元格时,点击 'g',对单元格进行所需的更改,在编辑器中保存文件(并退出),然后点击 'u'.
只需执行此单元格即可启用这些功能:
%%javascript
IPython.keyboard_manager.command_shortcuts.add_shortcut('g', {
handler : function (event) {
var input = IPython.notebook.get_selected_cell().get_text();
var cmd = "f = open('.toto.py', 'w');f.close()";
if (input != "") {
cmd = '%%writefile .toto.py\n' + input;
}
IPython.notebook.kernel.execute(cmd);
cmd = "import os;os.system('gvim .toto.py')";
IPython.notebook.kernel.execute(cmd);
return false;
}}
);
IPython.keyboard_manager.command_shortcuts.add_shortcut('u', {
handler : function (event) {
function handle_output(msg) {
var ret = msg.content.text;
IPython.notebook.get_selected_cell().set_text(ret);
}
var callback = {'output': handle_output};
var cmd = "f = open('.toto.py', 'r');print(f.read())";
IPython.notebook.kernel.execute(cmd, {iopub: callback}, {silent: false});
return false;
}}
);
基于@david-brocart 接受的答案,我已经获取了他的代码并将其包装成一个魔术函数,所以现在我只需要 运行 行魔术 %gvim
在笔记本中,以便能够通过 Gvim 为整个笔记本编辑任何单元格的内容(我可以在我系统上的任何其他笔记本 运行ning 中重复使用相同的行魔术)。
如果您想做类似的事情,只需在 ipython 启动文件夹(您的 ipython 启动路径可能类似于 ~/.ipython/profile_default/startup
),然后将以下代码放入该文件(并保存):
import IPython.core.magic as ipym
from IPython import get_ipython
@ipym.magics_class
class MareBearMagics(ipym.Magics):
@ipym.line_magic
def gvim(self, line):
cell_text = """
IPython.keyboard_manager.command_shortcuts.add_shortcut('g', {
handler : function (event) {
var input = IPython.notebook.get_selected_cell().get_text();
var cmd = "f = open('.toto.py', 'w');f.close()";
if (input != "") {
cmd = '%%writefile .toto.py\n' + input;
}
IPython.notebook.kernel.execute(cmd);
cmd = "import os;os.system('gvim .toto.py')";
IPython.notebook.kernel.execute(cmd);
return false;
}}
);
IPython.keyboard_manager.command_shortcuts.add_shortcut('u', {
handler : function (event) {
function handle_output(msg) {
var ret = msg.content.text;
IPython.notebook.get_selected_cell().set_text(ret);
}
var callback = {'output': handle_output};
var cmd = "f = open('.toto.py', 'r');print(f.read())";
IPython.notebook.kernel.execute(cmd, {iopub: callback}, {silent: false});
return false;
}}
);
"""
ipython = get_ipython()
ipython.run_cell_magic(
magic_name='javascript', line=None, cell=cell_text)
print("Cell contents can now be edited via Gvim. From command mode "
"use 'g' to open current cell contents in Gvim. After ':wq' "
"from Gvim, use 'u' in command mode to update cell contents.")
if __name__ == '__main__':
get_ipython().register_magics(MareBearMagics)
现在启动你的 Jupyter notebook 内核,你应该可以在单元格中输入 %gvim%
(你可以使用自动完成功能找到新的魔法命令)然后 运行单元格以启用在 Gvim 中编辑笔记本的单元格内容。您将收到一条输出消息,让您知道魔术命令已生效:
Cell contents can now be edited via Gvim. From command mode use 'g' to open current cell contents in Gvim. After ':wq' from Gvim, use 'u' in command mode to update cell contents.
感谢 Whosebug 问题中的人们以及他们为我提供了将这些整合在一起的要素。 :-)
- How to run an IPython magic from a script (or timing a Python script)
上面@david-brocart 的代码片段是一个很好的 hack,但它有几个缺点:
- 数据很容易丢失,例如不小心按错了单元格 'u'。
- Python 内核在编辑文件时被阻塞。
- 内核的全局命名空间被污染。
- 无法并行编辑多个单元格。
- 剩余的“.toto.py”文件保留在磁盘上。
- 文件扩展名与单元格类型无关。
这是一个改进版本,解决了上述所有问题。它仍然是一种 hack(例如,当内核繁忙时不可能开始编辑单元格),但它在实践中工作得很好。它仍然滥用计算内核来读取和写入文件并启动编辑器,但这样做的方式会导致尽可能少的副作用。
要使用此代码段,它必须在 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;
}}
);
对于使用 IPython terminal 应用程序发现此问题的人,有一个内置的键盘快捷键可以启动 $EDITOR
和内容当前单元格的。保存并退出编辑器会将单元格的内容替换(但尚未执行)为已保存文件的内容。
默认的键盘快捷键是 F2 键。这对应于 IPython 设置 IPython.terminal.shortcuts.open_input_in_editor
.
如果在 IPython 笔记本中有一个键盘快捷键就太好了,这样可以在外部编辑器(例如 gvim)中编辑当前单元格的内容。也许只是将当前单元格的内容复制到一个临时文件中,在其上启动 gvim,并在每次保存文件时更新当前单元格(并在退出 gvim 时删除临时文件) .此外,如果从浏览器编辑单元格,可能会更新临时文件,以便 gvim 知道文件已更改。
我知道 vim-ipython 和 ipython-vimception 等项目,但它们不符合我的需求。我认为浏览器对于简单的事情已经足够了,但是当需要更强大的编辑时就没有必要重新发明轮子了。
您知道 IPython 笔记本中是否已经存在这样的功能吗?
谢谢。
这是我想到的。我添加了 2 个快捷方式:
- 'g' 使用当前单元格的内容启动 gvim(您可以用您喜欢的任何文本编辑器替换 gvim)。
- 'u' 用 gvim 保存的内容更新当前单元格的内容。
因此,当您想使用首选编辑器编辑单元格时,点击 'g',对单元格进行所需的更改,在编辑器中保存文件(并退出),然后点击 'u'.
只需执行此单元格即可启用这些功能:
%%javascript
IPython.keyboard_manager.command_shortcuts.add_shortcut('g', {
handler : function (event) {
var input = IPython.notebook.get_selected_cell().get_text();
var cmd = "f = open('.toto.py', 'w');f.close()";
if (input != "") {
cmd = '%%writefile .toto.py\n' + input;
}
IPython.notebook.kernel.execute(cmd);
cmd = "import os;os.system('gvim .toto.py')";
IPython.notebook.kernel.execute(cmd);
return false;
}}
);
IPython.keyboard_manager.command_shortcuts.add_shortcut('u', {
handler : function (event) {
function handle_output(msg) {
var ret = msg.content.text;
IPython.notebook.get_selected_cell().set_text(ret);
}
var callback = {'output': handle_output};
var cmd = "f = open('.toto.py', 'r');print(f.read())";
IPython.notebook.kernel.execute(cmd, {iopub: callback}, {silent: false});
return false;
}}
);
基于@david-brocart 接受的答案,我已经获取了他的代码并将其包装成一个魔术函数,所以现在我只需要 运行 行魔术 %gvim
在笔记本中,以便能够通过 Gvim 为整个笔记本编辑任何单元格的内容(我可以在我系统上的任何其他笔记本 运行ning 中重复使用相同的行魔术)。
如果您想做类似的事情,只需在 ipython 启动文件夹(您的 ipython 启动路径可能类似于 ~/.ipython/profile_default/startup
),然后将以下代码放入该文件(并保存):
import IPython.core.magic as ipym
from IPython import get_ipython
@ipym.magics_class
class MareBearMagics(ipym.Magics):
@ipym.line_magic
def gvim(self, line):
cell_text = """
IPython.keyboard_manager.command_shortcuts.add_shortcut('g', {
handler : function (event) {
var input = IPython.notebook.get_selected_cell().get_text();
var cmd = "f = open('.toto.py', 'w');f.close()";
if (input != "") {
cmd = '%%writefile .toto.py\n' + input;
}
IPython.notebook.kernel.execute(cmd);
cmd = "import os;os.system('gvim .toto.py')";
IPython.notebook.kernel.execute(cmd);
return false;
}}
);
IPython.keyboard_manager.command_shortcuts.add_shortcut('u', {
handler : function (event) {
function handle_output(msg) {
var ret = msg.content.text;
IPython.notebook.get_selected_cell().set_text(ret);
}
var callback = {'output': handle_output};
var cmd = "f = open('.toto.py', 'r');print(f.read())";
IPython.notebook.kernel.execute(cmd, {iopub: callback}, {silent: false});
return false;
}}
);
"""
ipython = get_ipython()
ipython.run_cell_magic(
magic_name='javascript', line=None, cell=cell_text)
print("Cell contents can now be edited via Gvim. From command mode "
"use 'g' to open current cell contents in Gvim. After ':wq' "
"from Gvim, use 'u' in command mode to update cell contents.")
if __name__ == '__main__':
get_ipython().register_magics(MareBearMagics)
现在启动你的 Jupyter notebook 内核,你应该可以在单元格中输入 %gvim%
(你可以使用自动完成功能找到新的魔法命令)然后 运行单元格以启用在 Gvim 中编辑笔记本的单元格内容。您将收到一条输出消息,让您知道魔术命令已生效:
Cell contents can now be edited via Gvim. From command mode use 'g' to open current cell contents in Gvim. After ':wq' from Gvim, use 'u' in command mode to update cell contents.
感谢
- How to run an IPython magic from a script (or timing a Python script)
上面@david-brocart 的代码片段是一个很好的 hack,但它有几个缺点:
- 数据很容易丢失,例如不小心按错了单元格 'u'。
- Python 内核在编辑文件时被阻塞。
- 内核的全局命名空间被污染。
- 无法并行编辑多个单元格。
- 剩余的“.toto.py”文件保留在磁盘上。
- 文件扩展名与单元格类型无关。
这是一个改进版本,解决了上述所有问题。它仍然是一种 hack(例如,当内核繁忙时不可能开始编辑单元格),但它在实践中工作得很好。它仍然滥用计算内核来读取和写入文件并启动编辑器,但这样做的方式会导致尽可能少的副作用。
要使用此代码段,它必须在 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;
}}
);
对于使用 IPython terminal 应用程序发现此问题的人,有一个内置的键盘快捷键可以启动 $EDITOR
和内容当前单元格的。保存并退出编辑器会将单元格的内容替换(但尚未执行)为已保存文件的内容。
默认的键盘快捷键是 F2 键。这对应于 IPython 设置 IPython.terminal.shortcuts.open_input_in_editor
.