Есть ли способ предотвратить перехват исключительной ситуации SystemExit, вызванной sys.exit()?

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

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

2 ответа

Решение

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

import os
os._exit(1)

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

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

Если у вас проблема SystemExit будучи пойманным на внешних уровнях (т. е. на юнит-тестах), станьте самим внешним уровнем! Оберните ваш основной код в блок try/ исключением, перехватите 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!

Вы также можете использовать quit, см. Пример ниже:

while True:
print('Type exit to exit.')
response = input()
if response == 'exit':
    quit(0)
print('You typed ' + response + '.')
Другие вопросы по тегам