Есть ли способ предотвратить перехват исключительной ситуации 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 + '.')