Дилемма проклятий Python - PullRequest
10 голосов
/ 24 марта 2012

Я немного поиграюсь с 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 ]

47 голосов
/ 17 февраля 2013

Я полагаю, что вы ищете 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)
8 голосов
/ 24 марта 2012

Вы можете сделать это:

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" )
1 голос
/ 26 августа 2012

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

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

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

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

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

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

0 голосов
/ 24 марта 2012

Вы можете:

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

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

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

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

0 голосов
/ 24 марта 2012

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

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

...