Дилемма проклятий 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?