7

Как перехватить SIGINT в Python?

1

Я работаю над скриптом на Python, который запускает несколько процессов и соединений с базой данных. Периодически мне требуется завершить работу скрипта с помощью сигнала Ctrl+C, и я хотел бы выполнить некоторую очистку перед этим.

В Perl я бы сделал это следующим образом:

$SIG{'INT'} = 'exit_gracefully';

sub exit_gracefully {
    print "Caught ^C \n";
    exit (0);
}

Как реализовать аналогичную функциональность в Python?

5 ответ(ов)

2

Вы можете обрабатывать это как исключение (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, выводится сообщение "До свидания", и программа завершает свою работу.

0

Данный код представляет собой реализацию менеджера контекста для обработки прерываний (например, нажатия 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

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

Если у вас есть вопросы по данному коду или его использованию, не стесняйтесь спрашивать!

0

Вы можете обработать "плюс", перехватив исключение KeyboardInterrupt. В обработчике исключений вы можете реализовать любой код для очистки ресурсов.

0

Конечно! Вот перевод на русский в стиле ответа на StackOverflow:


Этот фрагмент кода представляет собой стандартную конструкцию для запуска вашей основной функции main() и обеспечения корректного завершения программы с помощью функции exit_gracefully().

Объяснение:

  • if __name__ == '__main__': — это условие проверяет, что текущий файл является основным модулем, который запускается. Если скрипт запускается непосредственно (а не импортируется как модуль), условие истинно, и код внутри него выполняется.

  • try: — внутри блока try мы вызываем нашу основную функцию main().

  • except KeyboardInterrupt: — здесь мы обрабатываем исключение KeyboardInterrupt, которое возникает, когда вы прерываете выполнение программы, например, с помощью комбинации клавиш Ctrl + C. Мы просто «гасим» это исключение с помощью оператора pass. Это значит, что программа завершится без ошибок.

  • finally: — блок finally будет выполнен в любом случае — после выполнения try или except. Это идеально подходит для вызова функции exit_gracefully(), которая, вероятно, выполняет какие-то финальные действия перед завершением программы, такие как освобождение ресурсов или сохранение состояния.

Пример использования:

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


Если у вас есть дополнительные вопросы или нужна помощь в понимании, не стесняйтесь спрашивать!

0

Вы адаптировали код от @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>). Таким образом, вы можете корректно завершать выполнение вашего приложения при получении этих сигналов.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь