Можно ли задать и код завершения, и текст вывода в stderr в исключении python?

Рейтинг: 0Ответов: 2Опубликовано: 17.07.2023

Исключение SystemExit позволяет задавать код завершения. Также оно позволяет задавать текст вывода в stderr (который как и ожидается будет напечатан только если исключение не отловлено). Но при этом, код завершения может быть равен только 1. Т.е. указать и то и другое нельзя, что как по мне не удобно, может потребоваться и то и другое. За примером далеко ходить не надо. В argparse если передать некорректные аргументы из терминала, метод parse_args() вбрасывает SystemExit. В stderr пишется текст, код завершения 2. Казалось бы всё отлично, но нет. Если отловить исключение, в stderr всё равно будет тот же вывод. Чего по идее не должно быть, если исключение отловлено. Получается в модуле написано что-то вроде этого:

print(text, file=sys.stderr)
exit(2)

Как по мне выглядит коряво. Отсюда вопрос: можно ли задать и код завершения, и текст вывода в stderr в исключении python?

Ответы

▲ 1

Вариант с наследованием ошибки, мне кажется вполне нормальным. В качестве альтернативы с наследованием возможно подойдет следующий вариант, не требующий создания дополнительных сущностей, основанный на tuple в сообщении об ошибке:

import sys

def test():
  sys.exit((2,'Ошибка'))

if __name__ == '__main__':
  exit_code, exit_message = 0, None
  try:
    test()  
  except SystemExit as e:
    exit_code, exit_message = e.args
    sys.exit(exit_code) # инициализируем ошибку только с кодом
  finally:
    if (exit_message):
      sys.stderr.write(f'ERROR: {exit_message}\n')

Проверка:

$ gdb --args python3 test.py 
(gdb) r 
...
ERROR: Ошибка
[Inferior 1 (process 9645) exited with code 02]
▲ 0

Можно имитировать нужное поведение:

import sys


class SystemQuit(SystemExit):
    def __init__(self, message, exit_code):
        super().__init__()
        self.__message = message
        self.__exit_code = exit_code

    def process(self):
        if self.__message is not None:
            print(self.__message, file=sys.stderr)
        sys.exit(self.__exit_code)


def quit(message, exit_code):
    raise SystemQuit(message, exit_code)


def main():
    try:
        print('before')
        quit('something went wrong', 2)
        print('after')
    except SystemQuit as e:
        e.process()


main()
$ python quit.py
before
something went wrong

$ echo $?
2