Есть ли способ предотвратить исключение исключения SystemExit из sys.exit()?

Документы говорят, что вызов sys.exit() вызывает исключение SystemExit, которое может быть обнаружено на внешних уровнях. У меня есть ситуации, в которой я хочу, чтобы окончательно и бесспорно выйти из внутри теста, однако UnitTest модуль ловит SystemExit и препятствует выходу. Обычно это здорово, но конкретная ситуация, которую я пытаюсь обработать, - это та, где наша тестовая среда обнаружила, что она настроена так, чтобы указывать на нетестовую базу данных. В этом случае я хочу выйти и предотвратить запуск каких-либо дополнительных тестов. Конечно, поскольку unittest ловушки SystemExit и продолжает счастливо на этом пути, это мешает мне.

Единственный вариант, о котором я думал до сих пор, - это использовать ctypes или что-то подобное вызову exit (3), но это кажется довольно уродливым взломом для чего-то, что должно быть действительно просто.

Ответ 1

Вы можете вызвать os._exit() для прямого выхода без исключения:

  import os
  os._exit(1)

Это обходит всю логику выключения python, такую ​​как модуль atexit, и не будет работать через логику обработки исключений, которую вы пытаетесь избежать в этой ситуации. Аргумент - это код выхода, который будет возвращен процессом.

Ответ 2

Как сказал Иеруб, os._exit(1) - ваш ответ. Но, учитывая, что он обходит все процедуры очистки, в том числе finally: блоки, закрывающие файлы и т.д., И их следует действительно избегать любой ценой, могу ли я представить "безопасный" способ его использования?

Если у вас проблема с SystemExit, находящейся на внешних уровнях (т.е. unittest), тогда будет внешним уровнем! Оберните свой основной код в блок try/except, поймите SystemExit и вызовите os._exit и только! Таким образом, вы можете называть sys.exit обычно в любом месте кода, позволять ему выходить на верхний уровень, грациозно закрывать все файлы и запускать все очистки, а затем вызывать os._exit.

Вы даже можете выбрать, какие выходы являются "аварийными". Приведенный ниже код является примером такого подхода:

import sys, os

EMERGENCY = 255  # can be any number actually

try:
    # wrap your whole code here ...
    # ... some code
    if x: sys.exit()
    # ... some more code
    if y: sys.exit(EMERGENCY)  # use only for emergency exits
    # ...
except SystemExit as e:
    if e.code != EMERGENCY:
        raise  # normal exit, let unittest catch it
    else:
        os._exit(EMERGENCY)  # try to stop *that*, sucker!