Оставьте неполную строку на экране при нажатии Ctrl-C в ipython 5.0+

В более старых (я полагаю, до 5.0) версиях IPython, если я работал над строкой / блоком и вдруг обнаружил, что мне нужно исследовать что-то еще, чтобы закончить его, мой подход заключался в нажатии Ctrl-C, что оставляло неполную строку / блок на экране, но не выполнен, и дал мне свежий запрос. То есть я бы увидел что-то вроде:

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"), поведение Ctrl-C отличается; теперь, вместо того, чтобы дать мне новую строку, он просто сбрасывает текущую, отбрасывая все, что я набрал, и возвращая курсор в начало строки.

# 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 документация; Я обнаружил, где установлены многие из этих обработчиков, но попытки обезьянок-патчей изменить текущее поведение потерпели неудачу (и, честно говоря, патчи-обезвреживания недокументированного кода означают, что я рискую нарушить каждое обновление, поэтому я хотел бы найти некоторые частично поддерживаемые исправления для этого; если это не так, то хакерские исправления для обезьян допустимы).

1 ответ

Решение

И после дальнейших исследований я обнаружил, что кажется поддерживаемым подходом, опираясь на документацию по сочетаниям клавиш IPython (документированную немного по-разному для 5.x и 6.x).

Решение состоит в том, чтобы создать файл в ~/.ipython/profile_default/startup (любое имя, заканчивающееся на .py или же 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)

Пожалуйста, не стесняйтесь вносить свой вклад, если вы найдете лучшее решение.

Другие вопросы по тегам