Python SIGINT SIGTERM не выходит мгновенно из цикла while

def do_stuff_before_python_terminates():
    save_variables_in_mysql()
    do_this_and_that()...

def main():
    do stuff
    while loops ect...

def sigterm(x, y):
    raise Exception()
def sigint(signal, frame):
    raise Exception()





signal.signal(signal.SIGINT, sigint)
signal.signal(signal.SIGTERM, sigterm)  
try:
    while True:
        main()
except Exception as e:
    logger.error("Exception")
    do_stuff_before_python_terminates()
    logger.log("sys.exit")
    sys.exit(0)

Я использую Python в контейнере Docker.

Когда я делаю ctrl+c через терминал tty или останавливаю изображение, Python останавливается и делает это не всегда успешно "do_stuff_before_python_terminates()".

Причина в том, что когда python случайно находится в цикле while, мне не повезло, он не выходит, он остается в цикле while и выполняет другие действия и не завершается успешно.

Docker ждет только 10 секунд, прежде чем убьет контейнер, и, вуаля, он не «do_stuff_before_python_terminates()»

что я здесь делаю неправильно, как решить проблему, что даже когда он находится в цикле while, он мгновенно выходит и "do_stuff_before_python_terminates()"

Обновленное объяснение:

если python выдает ошибку

он успешно переходит к

except Exception as e:
    logger.error("Exception")
    do_stuff_before_python_terminates()
    logger.log("sys.exit")
    sys.exit(0)

если я остановлю контейнер или использую ctrl+c, а python НЕ находится в цикле while

он успешно переходит к

sigterm() or sigint() -> then raise an Exception() ->  then jumps to
except Exception as e:
    logger.error("Exception")
    do_stuff_before_python_terminates()
    logger.log("sys.exit")
    sys.exit(0)

если я остановлю контейнер или использую ctrl+c, а python находится в цикле

it does stay in the loop 
do stuff
do stuff
do stuff
after nearly 20-30seconds
sigterm() or sigint() -> then raise an Exception() ->  then jumps to
except Exception as e:
    logger.error("Exception")
    do_stuff_before_python_terminates()
    logger.log("sys.exit")
    sys.exit(0)

что мне нужно, так это то, что он мгновенно выпрыгивает из цикла while всякий раз, когда я останавливаю контейнер или нажимаю ctrl+c (sigterm + sigint)

sigterm() or sigint() -> then raise an Exception() ->  then jumps to
except Exception as e:
    logger.error("Exception")
    do_stuff_before_python_terminates()
    logger.log("sys.exit")
    sys.exit(0)

Docker убивает контейнер через 10 секунд, поэтому у python есть только 10 секунд для выхода, эффект заключается в том, что он никогда не делает do_stuff_before_python_terminates(), когда python находится в цикле


person Human Khoo    schedule 13.07.2018    source источник
comment
Два возможных объяснения: код, вызванный из main(), устанавливает свой собственный обработчик сигналов, или код, вызванный из main, перехватывает Exception.   -  person VPfB    schedule 13.07.2018
comment
извините, не знаю, что вы имеете в виду, я исправил структуру, чтобы она была более читаемой, возможно, именно поэтому она выглядела так, как будто сигналы находятся в основной функции, можете ли вы предоставить решение   -  person Human Khoo    schedule 13.07.2018
comment
Я имею в виду, что некоторые из вызываемых функций могут мешать механизму генерации и перехвата исключений, на который вы полагаетесь. Может быть, это не так, я не знаю, но вы можете проверить и исключить это, минимизировав код.   -  person VPfB    schedule 13.07.2018
comment
Встречается ли текст except: или except Exception где-либо еще в вашем коде? Любой из них может перехватить сгенерированное сигналом исключение до того, как оно достигнет обработчика, выполняющего do_stuff_before_python_terminates().   -  person jasonharper    schedule 13.07.2018
comment
@jasonharper, вы имеете в виду, что когда python находится в цикле while, в цикле while есть еще одна попытка: ../except: часть, которая может поймать исключение? я мельком взглянул, я получил 1k строк кода, и да, действительно, может быть, что в цикле while есть какие-то другие попытки: кроме: частей, поэтому я должен изменить эти части и что именно? я должен добавить ко всем исключениям в моем коде что-то вроде if sigterm или sigint -> поднять исключение? какое именно исключение ловить? мне нужно поймать и sigint, и sigterm. Правильно ли я понял, что остановка или использование ctrl+c вызывает исключение?   -  person Human Khoo    schedule 13.07.2018
comment
Да - если ваш код в настоящее время выполняется внутри другого try/except, у него есть первый шанс поймать Exception. Все ваши except должны использовать максимально конкретное имя исключения, чтобы они не перехватывали то, что не должны.   -  person jasonharper    schedule 13.07.2018
comment
так что способ не в том, чтобы добавить исключение для перехвата sigint или sigterm, а в том, что я не использую только исключение: я должен использовать конкретное, кроме одного и единственного исключения, которое может быть там? это непросто, потому что я даже не знаю, какие исключения могут быть выброшены там в каком сценарии... невозможно ли добавить конкретные, кроме sigint, потому что я могу просто вызвать исключение, которое переходит к do_stuff_before_python_terminates (), если это решение моей проблемы   -  person Human Khoo    schedule 13.07.2018


Ответы (3)


Ваш do_stuff_before_python_terminates() не вызывается должным образом (по крайней мере, из моего опыта работы с сигналами и python).

Сделайте вызов do_stuff_before_python_terminates() в вашей функции sigterm и/или sigint.

def do_stuff_before_python_terminates():
    save_variables_in_mysql()
    do_this_and_that()...

def sigterm(x, y):
    do_stuff_before_python_terminates()
    raise Exception()
def sigint(signal, frame):
    do_stuff_before_python_terminates()
    raise Exception()

Обязательно поместите функцию do_stuff над функциями sigterm и sigint.

person J0hn    schedule 13.07.2018
comment
Это может быть правильным способом написания обработчиков сигналов, но я хотел бы понять, что не так с исходным кодом. - person VPfB; 13.07.2018
comment
я добавил больше информации, спасибо за ваш ответ, я ценю это, но ваш ответ не помогает - person Human Khoo; 13.07.2018

  1. Зарегистрируйте сигнал в main(). Например:

        def signal_handler(signal, frame):
                print('\n')
                sys.exit(0)
    
        def main():
            signal.signal(signal.SIGINT, signal_handler)
            do_stuff_before_python_terminates()
    
  2. Сигналы не будут срабатывать, пока низкоуровневый код C не завершится; из документации: «Длительные вычисления, реализованные исключительно на C (например, сопоставление регулярных выражений с большим текстом), могут выполняться непрерывно в течение произвольного периода времени, независимо от любых полученных сигналов. Будут вызываться обработчики сигналов Python когда расчет закончится».

person gregory    schedule 13.07.2018
comment
какая разница, если я зарегистрирую их глобально до того, как мой код перейдет в main ()? у меня проблема только с выходом из цикла while - person Human Khoo; 13.07.2018
comment
нет глобального - если у вас есть main(), сценарий начинается с него и оттуда продолжается. - person gregory; 13.07.2018
comment
мой код работает сверху вниз, а затем переходит к основному в конце моего кода, где я ему говорю, за пределами любой функции, в которой я регистрирую обработчик sigint и sigterm, как в моем примере кода - person Human Khoo; 13.07.2018

Из Выполнение обработчиков сигналов Python :

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

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

Если вам действительно нужно немедленно поймать сигнал, вам может понадобиться написать оболочку для вашей программы на языке низкого уровня (например, C), который вызывает ваш код Python.

person David Cullen    schedule 13.07.2018
comment
спасибо, но это просто невозможно для меня. я не могу кодировать на C, и мне определенно нужен запуск python do_stuff_before_python_terminates() для каждого sigterm или sigint (выключение или ctr+l). - person Human Khoo; 13.07.2018