在 ipython 5.0+ 中按 Ctrl-C 时在屏幕上留下不完整的行
Leave incomplete line on screen when hitting Ctrl-C in ipython 5.0+
在 IPython 的旧版本(我相信是 5.0 之前)中,如果我正在处理 line/block,突然发现我需要调查其他事情来完成它,我的方法是按 Ctrl-C,它在屏幕上留下了不完整的 line/block,但未执行,并给了我一个新的提示。也就是说,我会看到类似这样的内容:
In [1]: def foo():
...: stuff^C # <-- Just realized I needed to check something on stuff usage
In [2]: # <-- cursor goes to new line, but old stuff still on terminal
在较新的 IPython 中(似乎已经从 readline
切换到 prompt_toolkit
作为 "CLI support framework"),Ctrl-C 的行为不同;现在,它不再给我换行符,而是重置当前换行符,丢弃我输入的所有内容并将光标返回到行首。
# Before:
In [1]: def foo():
...: stuff
# After Ctrl-C:
In [1]: # Hey, where'd everything go?
这非常烦人,因为我无法再看到或copy/paste我正在处理的代码,以便在我完成任何需要新提示的副任务后恢复我的工作。
我的问题是:有什么方法可以恢复旧的 IPython 行为,其中 Ctrl-C 执行以下操作:
- 不执行目前输入的line/block
- 留在屏幕上
- 能够选择(在配置时很好)是否添加到历史记录中(这将是个人偏好;你想要历史中的半成型的东西,还是只在终端上 copy/paste ?)
- 在目前输入的文本下方为我提供了一个新的提示
我到处搜索,我发现的最多的是 a bug report comment,它提到了这个新行为,作为“......与早期版本 IPython 的变化,但它是故意的。"
我无法在 IPython 或 prompt_toolkit
文档中找到任何关于修改行为的文档;我已经找到了很多这些处理程序的安装位置,但是试图通过猴子修补来改变当前行为的尝试失败了(坦率地说,猴子修补未记录的代码意味着我冒着破坏每次升级的风险,所以我想找到对此有一些半支持的修复;如果失败,hacky monkey-patching 是可以接受的)。
经过更多研究,我发现似乎是一种受支持的方法,它依赖于 IPython keyboard shortcuts documentation (documented slightly differently for 5.x and 6.x)。
解决方案是在~/.ipython/profile_default/startup
中创建一个文件(任何名称,以.py
或ipy
结尾都可以,例如fixctrlc.py
),并添加以下内容:
def fix_ctrlc():
'''Set up bindings so IPython 5.0+ responds to Ctrl-C as in pre-5.0
Specific behavior is to have Ctrl-C leave existing typed command on
screen and preserved in history. Easily modified to not put in history.
Since this is run as a startup script, all work, including imports,
is done in this function to avoid polluting global namespace.
Updates made as needed at
'''
from IPython import get_ipython
from prompt_toolkit.enums import DEFAULT_BUFFER
from prompt_toolkit.keys import Keys
from prompt_toolkit.filters import HasFocus, ViInsertMode, EmacsInsertMode
ip = get_ipython()
# Determine if we're on a version of IPython that needs a fix,
# acquire the key bindings registry from the appropriate location,
# and establish closure state appropriate to that version of IPython
try:
try:
# IPython 5-6; render_as_done doesn't exist, but manual print works
registry = ip.pt_cli.application.key_bindings_registry
redraw_args = {}
doprint = True
except AttributeError:
# IPython 7+ (tested through 8.0.1)
# render_as_done necessary, and removes need for print
registry = ip.pt_app.key_bindings
redraw_args = {'render_as_done': True}
doprint = False
except AttributeError:
# On an old version of IPython that doesn't need the fix, or
# a new version that changed the registry location. Nothing to do.
return
def on_ctrlc(event):
text = event.cli.current_buffer.text.rstrip()
if text:
# Update cursor position to last non-space char in buffer (so Ctrl-C
# with cursor in middle of block doesn't lose text typed after cursor)
event.cli.current_buffer.cursor_position = len(text)
event.cli.current_buffer.text = text
# Redraw so cursor in correct position before print
event.cli._redraw(**redraw_args)
# (Optional) Put non-empty partial commands in history, not just left on screen
# Delete to leave them on screen, but not in history
event.cli.current_buffer.append_to_history()
# Print a newline to move us past currently typed text so it's not
# replaced on redraw
if doprint:
print()
# Reset/redraw prompt
event.cli.reset()
# Clear active buffer, leaving you with fresh, empty prompt
event.cli.current_buffer.reset()
registry.add_binding(
Keys.ControlC,
filter=(HasFocus(DEFAULT_BUFFER) & (ViInsertMode() | EmacsInsertMode()))
)(on_ctrlc)
fix_ctrlc()
del fix_ctrlc # Avoid polluting global namespace
如果您找到更好的解决方案,请随时贡献力量。
在 IPython 的旧版本(我相信是 5.0 之前)中,如果我正在处理 line/block,突然发现我需要调查其他事情来完成它,我的方法是按 Ctrl-C,它在屏幕上留下了不完整的 line/block,但未执行,并给了我一个新的提示。也就是说,我会看到类似这样的内容:
In [1]: def foo():
...: stuff^C # <-- Just realized I needed to check something on stuff usage
In [2]: # <-- cursor goes to new line, but old stuff still on terminal
在较新的 IPython 中(似乎已经从 readline
切换到 prompt_toolkit
作为 "CLI support framework"),Ctrl-C 的行为不同;现在,它不再给我换行符,而是重置当前换行符,丢弃我输入的所有内容并将光标返回到行首。
# Before:
In [1]: def foo():
...: stuff
# After Ctrl-C:
In [1]: # Hey, where'd everything go?
这非常烦人,因为我无法再看到或copy/paste我正在处理的代码,以便在我完成任何需要新提示的副任务后恢复我的工作。
我的问题是:有什么方法可以恢复旧的 IPython 行为,其中 Ctrl-C 执行以下操作:
- 不执行目前输入的line/block
- 留在屏幕上
- 能够选择(在配置时很好)是否添加到历史记录中(这将是个人偏好;你想要历史中的半成型的东西,还是只在终端上 copy/paste ?)
- 在目前输入的文本下方为我提供了一个新的提示
我到处搜索,我发现的最多的是 a bug report comment,它提到了这个新行为,作为“......与早期版本 IPython 的变化,但它是故意的。"
我无法在 IPython 或 prompt_toolkit
文档中找到任何关于修改行为的文档;我已经找到了很多这些处理程序的安装位置,但是试图通过猴子修补来改变当前行为的尝试失败了(坦率地说,猴子修补未记录的代码意味着我冒着破坏每次升级的风险,所以我想找到对此有一些半支持的修复;如果失败,hacky monkey-patching 是可以接受的)。
经过更多研究,我发现似乎是一种受支持的方法,它依赖于 IPython keyboard shortcuts documentation (documented slightly differently for 5.x and 6.x)。
解决方案是在~/.ipython/profile_default/startup
中创建一个文件(任何名称,以.py
或ipy
结尾都可以,例如fixctrlc.py
),并添加以下内容:
def fix_ctrlc():
'''Set up bindings so IPython 5.0+ responds to Ctrl-C as in pre-5.0
Specific behavior is to have Ctrl-C leave existing typed command on
screen and preserved in history. Easily modified to not put in history.
Since this is run as a startup script, all work, including imports,
is done in this function to avoid polluting global namespace.
Updates made as needed at
'''
from IPython import get_ipython
from prompt_toolkit.enums import DEFAULT_BUFFER
from prompt_toolkit.keys import Keys
from prompt_toolkit.filters import HasFocus, ViInsertMode, EmacsInsertMode
ip = get_ipython()
# Determine if we're on a version of IPython that needs a fix,
# acquire the key bindings registry from the appropriate location,
# and establish closure state appropriate to that version of IPython
try:
try:
# IPython 5-6; render_as_done doesn't exist, but manual print works
registry = ip.pt_cli.application.key_bindings_registry
redraw_args = {}
doprint = True
except AttributeError:
# IPython 7+ (tested through 8.0.1)
# render_as_done necessary, and removes need for print
registry = ip.pt_app.key_bindings
redraw_args = {'render_as_done': True}
doprint = False
except AttributeError:
# On an old version of IPython that doesn't need the fix, or
# a new version that changed the registry location. Nothing to do.
return
def on_ctrlc(event):
text = event.cli.current_buffer.text.rstrip()
if text:
# Update cursor position to last non-space char in buffer (so Ctrl-C
# with cursor in middle of block doesn't lose text typed after cursor)
event.cli.current_buffer.cursor_position = len(text)
event.cli.current_buffer.text = text
# Redraw so cursor in correct position before print
event.cli._redraw(**redraw_args)
# (Optional) Put non-empty partial commands in history, not just left on screen
# Delete to leave them on screen, but not in history
event.cli.current_buffer.append_to_history()
# Print a newline to move us past currently typed text so it's not
# replaced on redraw
if doprint:
print()
# Reset/redraw prompt
event.cli.reset()
# Clear active buffer, leaving you with fresh, empty prompt
event.cli.current_buffer.reset()
registry.add_binding(
Keys.ControlC,
filter=(HasFocus(DEFAULT_BUFFER) & (ViInsertMode() | EmacsInsertMode()))
)(on_ctrlc)
fix_ctrlc()
del fix_ctrlc # Avoid polluting global namespace
如果您找到更好的解决方案,请随时贡献力量。