Как перехватить SIGINT в Python?
Я работаю над скриптом на Python, который запускает несколько процессов и соединений с базой данных. Периодически мне требуется завершить работу скрипта с помощью сигнала Ctrl+C, и я хотел бы выполнить некоторую очистку перед этим.
В Perl я бы сделал это следующим образом:
$SIG{'INT'} = 'exit_gracefully';
sub exit_gracefully {
print "Caught ^C \n";
exit (0);
}
Как реализовать аналогичную функциональность в Python?
5 ответ(ов)
Вы можете обрабатывать это как исключение (KeyboardInterrupt
), так же как и любое другое. Создайте новый файл и выполните его из командной строки с следующим содержимым, чтобы понять, о чем я говорю:
import time
import sys
x = 1
while True:
try:
print(x)
time.sleep(0.3)
x += 1
except KeyboardInterrupt:
print("До свидания")
sys.exit()
Этот код будет печатать числа каждную третью секунду, и вы сможете прервать его с помощью комбинации Ctrl+C
. После этого обрабатывается исключение KeyboardInterrupt
, выводится сообщение "До свидания", и программа завершает свою работу.
Данный код представляет собой реализацию менеджера контекста для обработки прерываний (например, нажатия Ctrl+C) с помощью модуля signal
. Он позволяет аккуратно завершать выполнение программы, не теряя данных или не оставляя висячих потоков.
Вот короткое объяснение ключевых компонентов данной реализации:
Класс GracefulInterruptHandler
Инициализация: В конструкторе класса мы задаем сигнал, который будем обрабатывать (по умолчанию это
SIGINT
).Метод
__enter__
: Этот метод вызывается при входе в блокwith
. Здесь мы сохраняем оригинальный обработчик сигнала, устанавливаем новый обработчик и инициализируем флагinterrupted
.Метод
__exit__
: Вызывается при выходе из блокаwith
, он просто вызывает методrelease
, чтобы восстановить оригинальный обработчик сигнала.Метод
release
: Восстанавливает оригинальный обработчик сигнала и обновляет состояние флагаreleased
. Это предотвращает повторное восстановление оригинального обработчика.
Пример использования
Вы можете использовать этот менеджер контекста для обработки прерываний следующим образом:
with GracefulInterruptHandler() as h:
for i in range(1000):
print("...")
time.sleep(1)
if h.interrupted:
print("interrupted!")
time.sleep(2)
break
Вложенные обработчики
Класс также поддерживает вложенные менеджеры, что позволяет обрабатывать прерывания на нескольких уровнях вложенности:
with GracefulInterruptHandler() as h1:
while True:
print("(1)...")
time.sleep(1)
with GracefulInterruptHandler() as h2:
while True:
print("\t(2)...")
time.sleep(1)
if h2.interrupted:
print("\t(2) interrupted!")
time.sleep(2)
break
if h1.interrupted:
print("(1) interrupted!")
time.sleep(2)
break
В этой конструкции, если произойдет прерывание в внутреннем блоке, внешний блок также будет иметь возможность обрабатывать его, обеспечивая при этом корректную инициализацию и освобождение ресурсов.
Если у вас есть вопросы по данному коду или его использованию, не стесняйтесь спрашивать!
Вы можете обработать "плюс", перехватив исключение KeyboardInterrupt
. В обработчике исключений вы можете реализовать любой код для очистки ресурсов.
Конечно! Вот перевод на русский в стиле ответа на StackOverflow:
Этот фрагмент кода представляет собой стандартную конструкцию для запуска вашей основной функции main()
и обеспечения корректного завершения программы с помощью функции exit_gracefully()
.
Объяснение:
if __name__ == '__main__':
— это условие проверяет, что текущий файл является основным модулем, который запускается. Если скрипт запускается непосредственно (а не импортируется как модуль), условие истинно, и код внутри него выполняется.try:
— внутри блокаtry
мы вызываем нашу основную функциюmain()
.except KeyboardInterrupt:
— здесь мы обрабатываем исключениеKeyboardInterrupt
, которое возникает, когда вы прерываете выполнение программы, например, с помощью комбинации клавиш Ctrl + C. Мы просто «гасим» это исключение с помощью оператораpass
. Это значит, что программа завершится без ошибок.finally:
— блокfinally
будет выполнен в любом случае — после выполненияtry
илиexcept
. Это идеально подходит для вызова функцииexit_gracefully()
, которая, вероятно, выполняет какие-то финальные действия перед завершением программы, такие как освобождение ресурсов или сохранение состояния.
Пример использования:
Таким образом, ваш код гарантирует, что основная логика приложения выполняется, и при прерывании выполнения (например, пользователем) вы сможете без проблем завершить работу, вызывая exit_gracefully()
. Это полезно в любой программе, где необходимо гарантировать корректное завершение работы, даже в случае ошибок.
Если у вас есть дополнительные вопросы или нужна помощь в понимании, не стесняйтесь спрашивать!
Вы адаптировали код от @udi для поддержки нескольких сигналов (ничего сложного), вот такой пример:
import signal
class GracefulInterruptHandler(object):
def __init__(self, signals=(signal.SIGINT, signal.SIGTERM)):
self.signals = signals
self.original_handlers = {}
def __enter__(self):
self.interrupted = False
self.released = False
for sig in self.signals:
self.original_handlers[sig] = signal.getsignal(sig)
signal.signal(sig, self.handler)
return self
def handler(self, signum, frame):
self.release()
self.interrupted = True
def __exit__(self, type, value, tb):
self.release()
def release(self):
if self.released:
return False
for sig in self.signals:
signal.signal(sig, self.original_handlers[sig])
self.released = True
return True
Этот код поддерживает обработку клавиатурного прерывания (SIGINT
) и сигнала SIGTERM
(например, при выполнении kill <process>
). Таким образом, вы можете корректно завершать выполнение вашего приложения при получении этих сигналов.
Как изменить порядок столбцов в DataFrame?
Получение текущей даты в формате YYYY-MM-DD в Python
Как проверить тип NoneType в Python?
Как удалить пакеты, установленные с помощью easy_install в Python?
Использование @property против геттеров и сеттеров