2

在较旧的(我相信是 5.0 之前的)IPython 版本中,如果我正在处理一行/块,并且突然发现我需要调查其他内容来完成它,我的方法是按 Ctrl-C,这会留下不完整的行/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(似乎已从“CLI 支持框架”切换readline到)中,Ctrl-C 的行为有所不同;prompt_toolkit现在,它没有给我一个换行符,而是重置当前行,丢弃我输入的所有内容并将光标返回到行首。

# Before:
In [1]: def foo():
   ...:     stuff

# After Ctrl-C:
In [1]:   # Hey, where'd everything go?

这非常烦人,因为在我完成任何导致需要新提示的副任务之后,我再也看不到或复制/粘贴我正在处理的代码以恢复我的工作。

我的问题是:有没有办法恢复旧的 IPython 行为,其中 Ctrl-C 执行以下操作:

  1. 不执行到目前为止键入的行/块
  2. 留在屏幕上
  3. 能够选择(在配置时很好)是否添加到历史记录(这将是个人喜好;您想要历史记录中的半成品,还是只是在终端上进行复制/粘贴?)
  4. 在到目前为止键入的文本下方为我提供了一个全新的提示

我到处搜索,发现最多的是一个错误报告评论,其中提到了这种新行为“......从早期版本的 IPython 的变化,但它是故意的。”

我找不到任何关于修改 IPython 或文档中的行为的prompt_toolkit文档;我发现了很多这些处理程序的安装位置,但是尝试通过猴子修补来改变当前行为的尝试失败了(坦率地说,猴子修补未记录的代码意味着我冒着破坏每次升级的风险,所以我想找到对此进行了一些半支持的修复;如果失败了,hacky Monkey-patching 是可以接受的)。

4

1 回答 1

6

经过更多研究,我发现似乎是一种受支持的方法,它依赖于IPython 键盘快捷键文档(对于5.x6.x的记录略有不同)。

~/.ipython/profile_default/startup解决方案是在(任何名称,以.pyor结尾ipy都可以,例如)中创建一个文件fixctrlc.py,并添加以下内容:

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

def on_ctrlc(event):
    # Move cursor to end of line and redraw (so whole block is preserved)
    event.cli.current_buffer.cursor_position = len(event.cli.current_buffer.text)
    event.cli._redraw(**redraw_args)

    # (Optional) Put non-empty partial commands in history
    if event.cli.current_buffer.text.strip():
        event.cli.current_buffer.append_to_history()

    if doprint:
        print()                       # Skip to next line past cursor
    event.cli.reset()                 # Reset/redraw prompt
    event.cli.current_buffer.reset()  # Clear buffer so new line is fresh (empty)

ip = get_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+; 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:
    pass  # Old IPython doesn't need special handler
else:
    registry.add_binding(Keys.ControlC,
        filter=(HasFocus(DEFAULT_BUFFER) & (ViInsertMode() | EmacsInsertMode()))
        )(on_ctrlc)

如果您找到更好的解决方案,请随时贡献。

于 2017-08-09T21:25:51.197 回答