Ловіть переривання клавіатури в Python під час вимкнення програми


81

Я пишу утиліту командного рядка в Python, яка, оскільки це виробничий код, повинна мати можливість чистого вимкнення, не скидаючи купу речей (коди помилок, сліди стека тощо) на екран. Це означає, що мені потрібно вловлювати переривання клавіатури.

Я спробував використовувати обидва блоки try catch, як:

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print 'Interrupted'
        sys.exit(0)

і вловлювання самого сигналу (як у цьому пості ):

import signal
import sys

def sigint_handler(signal, frame):
    print 'Interrupted'
    sys.exit(0)
signal.signal(signal.SIGINT, sigint_handler)

Здається, обидва методи працюють досить добре під час нормальної роботи. Однак, якщо переривання виникають під час очищення коду в кінці програми, Python, здається, завжди друкує щось на екран. Ловля переривання дає

^CInterrupted
Exception KeyboardInterrupt in <bound method MyClass.__del__ of <path.to.MyClass object at 0x802852b90>> ignored

тоді як обробка сигналу дає будь-яке

^CInterrupted
Exception SystemExit: 0 in <Finalize object, dead> ignored

або

^CInterrupted
Exception SystemExit: 0 in <bound method MyClass.__del__ of <path.to.MyClass object at 0x802854a90>> ignored

Ці помилки не тільки некрасиві, вони не дуже корисні (особливо для кінцевого користувача без вихідного коду)!

Код очищення для цього додатку досить великий, тому є пристойна ймовірність, що ця проблема потрапить до реальних користувачів. Чи є спосіб зачепити або заблокувати цей результат, чи це просто щось, з чим мені доведеться мати справу?


2
Чому б вам не замінити sys.stdout/ sys.stderr? Подобається sys.stderr = open(os.devnull, 'w')? Якщо ви насправді не піклуєтесь про кінцевий результат, це здається очевидним рішенням.
Бакуріу

1
Є, os._exitале це схоже на носових демонів для мене. Де ваш код очищення, чи використовуєте ви для цього модуль atexit ?
wim

1
@Bakuriu: Хоча перенаправлення stderr заспокоює вихід, воно також видавлює законні помилки, щодо яких користувач може щось зробити, наприклад, файл не знайдений або недоступний для хосту.
Ден

4
@Dan Це не повинно бути /dev/null. Ви можете написати власний файлоподібний об’єкт, який приховує лише повідомлення із заданим форматом.
Бакуріу

2
@Bakuriu: Мені все ще здається досить удачним. Я зроблю це, якщо мені доведеться, але я відчуваю, що це те, що слід вбудувати в мову.
Ден

Відповіді:


115

Отримайте цей потік , він містить деяку корисну інформацію про вихід та зворотне відстеження.

Якщо ви більше зацікавлені просто вбити програму, спробуйте щось подібне (це також виведе ноги з-під коду очищення):

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print('Interrupted')
        try:
            sys.exit(0)
        except SystemExit:
            os._exit(0)

6
Я б рекомендував виняти as, а потім захопити і повторно використати код виходу.
wizzwizz4

5
Так. Однозначно не слід виходити за допомогою 0KeyboardInterrupt. Якщо хтось використовує ваш скрипт у сценарії, конвеєрі чи будь-якому іншому, він вважатиме, що ваш код виконується як зазвичай.
user3342816

4
Linux зазвичай виходить із 130: 128 + 2 tldp.org/LDP/abs/html/exitcodes.html#EXITCODESREF , Не вдається знайти жодну функцію коду виходу з різних платформ на python. Але 1це остаточно краще тоді0
user3342816

2
Якщо користувач викликає програму python із скрипта bash, і вони використовують set -eу сценарії (як слід), вам слід перервати весь сценарій bash після того, як користувач натисне CTRL + C. Для цього потрібно буде повернути ненульовий код виходу.
максимум

1
@Bersan: Дякую! Я читав на grammarly.com/blog/than-then , тепер мені потрібно лише це запам'ятати: D
user3342816

6

Ви можете проігнорувати SIGINT після запуску вимкнення, зателефонувавши signal.signal(signal.SIGINT, signal.SIG_IGN)перед тим, як розпочати код очищення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.