Дилемма проклятий Python

Я немного поиграюсь с Python и ругательствами.

Когда я бегу

import time
import curses

def main():
    curses.initscr()
    curses.cbreak()
    for i in range(3):
        time.sleep(1)
        curses.flash()
        pass
    print( "Hello World" )
    curses.endwin()

if __name__ == '__main__':
    main()

если я буду ждать до конца, curses.endwin() вызывается, так что все работает хорошо. Однако, если я обрежу его с помощью Ctrl-C, curses.endwin() никогда не вызывается, поэтому он портит мою терминальную сессию.

Как правильно справиться с этой ситуацией? Как я могу убедиться, что независимо от того, как я пытаюсь завершить / прервать программу (например, Ctrl-C, Ctrl-Z), она не испортит терминал?

5 ответов

Решение

Вы могли бы сделать это:

def main():
    curses.initscr()

    try:
        curses.cbreak()
        for i in range(3):
            time.sleep(1)
            curses.flash()
            pass
        print( "Hello World" )
    finally:
        curses.endwin()

Или, что еще приятнее, создайте контекстную оболочку:

class CursesWindow(object):
    def __enter__(self):
        curses.initscr()

    def __exit__(self):
        curses.endwin()

def main():
    with CursesWindow():
        curses.cbreak()
        for i in range(3):
            time.sleep(1)
            curses.flash()
            pass
        print( "Hello World" )

Я думаю, что вы ищете curses.wrapper. См. Http://docs.python.org/dev/library/curses.html#curses.wrapper.

Он будет делать curses.cbreak(), curses.noecho() и curses_screen.keypad(1) при инициализации и отменять их при выходе, даже если выход был исключением.

Ваша программа переходит как функция в обертку, например:

def main(screen):
    """screen is a curses screen passed from the wrapper"""
    ...

if __name__ == '__main__':
    curses.wrapper(main)

Мой совет: в целях тестирования, вызовите ваш скрипт, используя простой скрипт оболочки-оболочки; заставить сценарий оболочки выполнить reset Команда для возврата настроек терминала в рабочее состояние:

#!/bin/sh
eval "$@"
stty -sane
reset

... называть это как run.sh и будь счастлив. Это должно выполнить вашу команду почти точно так же, как ваша оболочка, если вы ввели аргументы как команду (точнее, если вы заключите аргументы в жесткие кавычки).

Чтобы гарантировать, что ваша программа выйдет из терминала в устойчивом состоянии, перед лицом необработанных исключений и ненормальных завершений... либо используйте curses.wrapper() метод для вызова вашей точки входа верхнего уровня (вероятно, main() или что угодно main_curses_ui() вы решили реализовать) или обернуть свой код в вашей собственной последовательности curses.* методы, чтобы восстановить видимость курсора, восстановить режим "cbreak" (канонический / готовый ввод), восстановить нормальные настройки "echo" и все остальное, что вы, возможно, испортили.

Вы также можете использовать обработчики Python: atexit для регистрации всех ваших действий по очистке. Но все же могут быть случаи, когда ваш код не вызывается - некоторые виды неуловимых сигналов и любая ситуация, когда вызывается os._exit().

Моя маленькая оболочка сценария оболочки должна быть достаточно надежной даже в этих случаях.

Вы можете:

  • оберните ваш код в try / finally блок, который вызывает curses.endwin()
  • захватить сигнал прерывания конкретно через signal библиотека
  • использовать atexit библиотека.

Первый вариант, вероятно, самый простой для базового случая (если вы не выполняете много кода).

Второй вариант наиболее специфичен, если вы хотите сделать что-то особенное для Ctrl+C.

Последний вариант является наиболее надежным, если вы всегда хотите выполнить определенные действия по завершению работы независимо от того, как заканчивается ваша программа.

Вам нужно захватить сигнал и запустить endwin() во время захвата.

Информацию об этом смотрите в этом SO-ответе: Как мне записать SIGINT в Python?

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