Как поднять KeyboardInterrupt когда внутри проклятия

Используя модуль curses в Windows (через этот PDCurses), я пытаюсь чтобы прервать KeyboardInterrupt исключение, но оно не возникает, когда я нажимаю ctrl + c.

Какой-то дистиллированный код:

from curses import wrapper
items = ['a', 'very', 'long', 'list', 'of', 'strings']

def main(screen):
    for item in items:
        screen.addstr(0, 0, item)
        screen.getch()
        screen.refresh()

wrapper(main)

Список items очень длинный, и сейчас я не могу остановить выполнение на полпути. Мне нужно просто нажимать клавиши несколько раз, пока я не дойду до конца. Не дай бог, я когда-нибудь попробую это в while True:!

Когда я нажимаю ctrl + c, исключение не возникает. Он передается моему getch() как 3. Поднимать ли СОП вручную, когда getch получает 3, или есть более правильный способ избежать проглатывания KeyboardInterrupt?


person mhlester    schedule 14.01.2014    source источник


Ответы (3)


По умолчанию curses используется режим raw, который отключает прерывание / выход / приостановить и т. д. из документации

В необработанном режиме обычная буферизация строки и обработка клавиш прерывания, выхода, приостановки и управления потоком отключены; символы вводятся в функции ввода curses один за другим

Из документации C's curses:

Разница между этими двумя функциями (raw и cbreak) заключается в том, как управляющие символы, такие как приостановка (CTRL-Z), прерывание и выход (CTRL-C), передаются программе. В режиме raw() эти символы напрямую передаются в программу без генерации сигнала.

Поскольку python выдает KeyboardInterrupt при отправке SIGINT, ожидается, что он не сгенерирован. 3, который вы видите действительно, представляет прерывание.

Поскольку это то, что обрабатывается библиотекой C, невозможно избежать этого «проглатывания» исключения. Однако вы можете использовать простую оболочку для getch, которая проверяет, когда она возвращает 3, и соответственно вызывает ошибку.

person Bakuriu    schedule 14.01.2014
comment
Спасибо; в этом есть смысл. Читая документацию, мне кажется, что мне, вероятно, нужна какая-то комбинация curses.noqiflush(), curses.noraw() или curses.cbreak(), но ни одна из них не меняет поведение. Думаю, я просто заверну свой getch, чтобы проверить против 3 или ctrl + z 26 - person mhlester; 15.01.2014
comment
PDCurses имитирует поведение проклятий на основе Unix, но для таких низкоуровневых вещей никогда не будет точно соответствовать. В Unix терминал всегда находится в необработанном режиме (инструкции вводят в заблуждение), а curses имитирует другие режимы. - person Thomas Dickey; 07.02.2021

Используйте UGETCHAR_ (реализовано ниже) вместо getch

def UGETCHAR_(scr):
    import curses
    h = scr.getch()
    if h == 3:
        raise KeyboardInterrupt
    if h == 26:
        raise EOFError
    return h

Позволь мне объяснить.

Итак, сначала, когда эта функция вызывается, она импортирует curses [import curses].

Затем он запускает getch (), который вы используете, и помещает результат в переменную с именем h. [h = scr.getch()]

Затем он вызывает KeyboardInterrupt [raise KeyboardInterrupt], если h равно 3 (^C) [if h == 3:], и EOFError [raise KeyboardInterrupt], если h равно 26 (^Z) [if h == 26:].

Наконец, он возвращает значение h [return h].

person Community    schedule 15.03.2020

Этот вопрос задавали давно, но я столкнулся с той же проблемой. Я хочу, чтобы программа Python, использующая curses, работала как в Windows, так и в Linux. KeyboardInterrupt работает точно так же, как ожидалось, в Linux, но не в Windows. Я перепробовал все функции настройки curses, но так и не смог заставить Ctrl + C прервать выполнение.

Код ниже, кажется, работает, но не идеален. Я пока не могу найти лучшего метода. Проблема с этим подходом в Windows заключается в том, что он не прерывает работу. Код будет выполнять ту работу, которую он выполняет в текущей итерации цикла, прежде чем проверять ввод. (Он по-прежнему отлично работает в Linux.)

import curses
import time

def Main(stdscr):
    stdscr.addstr(0, 0, "Main starting.  Ctrl+C to exit.")
    stdscr.refresh()
    try:
        i = 0
        while True:
            i = i + 1
            stdscr.addstr(1, 0, "Do work in loop. i=" + str(i))
            stdscr.refresh()
            time.sleep(1)

            stdscr.nodelay(1) # Don't block waiting for input.
            c = stdscr.getch() # Get char from input.  If none is available, will return -1.
            if c == 3:
                stdscr.addstr(2, 0, "getch() got Ctrl+C")
                stdscr.refresh()
                raise KeyboardInterrupt
            else:
                curses.flushinp() # Clear out buffer.  We only care about Ctrl+C.
        
    except KeyboardInterrupt:
        stdscr.addstr(3, 0, "Ctrl+C detected, Program Stopping")
        stdscr.refresh()
    
    finally:
        stdscr.addstr(4, 0, "Program cleanup")
        stdscr.refresh()
        time.sleep(3) # This delay just so we can see final screen output

curses.wrapper(Main)

Вывод в Linux:

Main starting.  Ctrl+C to exit.
Do work in loop. i=4

Ctrl+C detected, Program Stopping
Program cleanup

Вывод в Windows:

Main starting.  Ctrl+C to exit.
Do work in loop. i=6
getch() got Ctrl+C
Ctrl+C detected, Program Stopping
Program cleanup
person Dan S.    schedule 07.02.2021
comment
OP спрашивает о Windows, а не о Linux (ожидайте различий). - person Thomas Dickey; 07.02.2021
comment
Сравнение кажется совершенно актуальным для читателей, занимающихся кроссплатформенной разработкой. - person Dan S.; 10.02.2021