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

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

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

Ответы [ 2 ]

71 голосов
/ 06 октября 2008

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

import os
os._exit(1)

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

46 голосов
/ 05 декабря 2012

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

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