Сообщить об ошибке.

Функции excepthook() и exc_info() модуля sys в Python.

Информация об исключении и обработка необработанных исключений.

Содержание:

sys.excepthook(type, value, traceback):

Функция sys.excepthook() выводит заданную трассировку и исключение в sys.stderr.

Когда возникает исключение и не обрабатывается, то интерпретатор вызывает sys.excepthook() с тремя аргументами: классом исключения type, экземпляром исключения value и объектом трассировки traceback. В интерактивном сеансе это происходит непосредственно перед тем, как управление возвращается к приглашению ввода. В программе Python это происходит непосредственно перед выходом из программы. Обработка исключений верхнего уровня может быть настроена путем назначения другой функции с тремя аргументами для sys.excepthook.

Вызывает событие аудита sys.excepthook с аргументами hook, type, value, traceback. Если хук/ловушка не была установлена, то аргумент hook может быть None.

Если какой-либо хук вызывает исключение от RuntimeError и его производных, то вызов хука/ловушки будет подавлен. В противном случае исключение перехвата будет отмечено как невыполненное и будет вызван sys.excepthook().

Смотрим пример:

import sys

def my_excepthook(type, value, traceback):
    print('Unhandled error:', type, value)

sys.excepthook = my_excepthook
print('Before exception')
raise RuntimeError('This is the error message')
print('After exception')

# Before exception
# Unhandled error: <class 'RuntimeError'> This is the error message

Так как блока try/except нет вокруг строки, в которой возбуждается исключение, то последний вызов print() не выполняется, хотя хук/ловушка для исключения установлена.


sys.exc_info():

Функция sys.exc_info() возвращает кортеж из трех значений, которые предоставляют информацию об исключении, которое в данный момент обрабатывается. Возвращаемая информация относится как к текущему потоку, так и к текущему кадру стека. Если текущий кадр стека не обрабатывает исключение, информация берется из вызывающего кадра стека или его вызывающего и т. д. до тех пор, пока не будет найден кадр стека, который обрабатывает исключение. Здесь "обработка исключения" определяется как "выполнение условия исключения". Для любого стекового фрейма доступна только информация об исключении, которое обрабатывается в данный момент.

Если нигде в стеке исключение не обрабатывается, возвращается кортеж, содержащий три значения None. В противном случае возвращаются значения (type, value, traceback).

Их значение:

  • type получает тип обрабатываемого исключения - подкласс BaseException;
  • value получает экземпляр исключения - экземпляр типа исключения;
  • traceback получает объект traceback, который инкапсулирует стек вызовов в точке, где первоначально произошло исключение.

sys.unraisablehook(unraisable, /):

Функция sys.unraisablehook() обрабатывает невыполнимое исключение. (Новое в Python 3.8)

Вызывается, когда возникает исключение, но Python не может его обработать. Например, когда вызывает исключение деструктор или во время сборки мусора.

Аргумент unraisable имеет следующие атрибуты:

  • exc_type: Тип исключения.
  • exc_value: значение исключения, может быть None.
  • exc_traceback: Отслеживание исключения, может быть Нет.
  • err_msg: Сообщение об ошибке, может быть Нет.
  • object: Объект, вызвавший исключение, может быть None.

По этот хук форматирует err_msg и object как: f '{err_msg}: {object! R}'. Используйте сообщение об ошибке "Exception ignored in", если err_msg - Нет.

Функцией sys.unraisablehook() можно переопределить поведение невыполнимых исключений.

Сохранение exc_value с использованием пользовательского хука может создать ссылочный цикл. Его следует явно очистить, чтобы прервать ссылочный цикл, когда исключение больше не требуется.

Сохранение object с помощью настраиваемого хука может воскресить его, если он установлен на объект, который завершается. Избегайте сохранения объекта object после завершения кастомного хука, чтобы избежать воскрешения объектов.

Смотрите также функцию sys.excepthook(), которая обрабатывает неперехваченные исключения.

Вызывает событие аудита sys.unraisablehook с аргументами hook, unraisable, когда происходит исключение, которое не может быть обработано. Объект unraisable - это то же самое, что и то, что будет передано хуку. Если хук не был установлен, то аргумент hook может быть None.

Практические примеры использования функций sys.excepthook и sys.exc_info.

Использование sys.exc_info() для вывода сообщения об ошибке.

В представленном коде ниже, последняя инструкция except может опустить имя исключения. Используйте такую конструкцию с крайней осторожностью, таким образом можно легко замаскировать реальную ошибку! Здесь можно использовать sys.exc_info() для вывода сообщения об ошибке, а затем повторно вызвать исключение, что позволяет вызывающей стороне также обработать исключение:

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error: {0}".format(err))
except ValueError:
    print("Не удалось преобразовать данные в целое число.")
except:
    print("Непредвиденная ошибка:", sys.exc_info()[0])
    raise

Обработчик, которому не нужно явно передавать исключение.

Есть моменты, когда предпочтителен явный обработчик исключений, либо для ясности кода, либо во избежание конфликтов с библиотеками, которые пытаются установить свои собственные sys.excepthook. В этих случаях может быть создана общая функция-обработчик, которой не нужно явно передавать объект исключения, вызывая exc_info() для извлечения текущего исключения для потока.

import sys
import threading
import time


def with_exception():
    exc_type, exc_value = sys.exc_info()[:2]
    print(f'{exc_type.__name__} => {exc_value} in {threading.current_thread().name}')


def cause_exception(delay):
    time.sleep(delay)
    raise RuntimeError('Error message')


def thread_target(delay):
    try:
        cause_exception(delay)
    except RuntimeError:
        with_exception()


threads = [
    threading.Thread(target=thread_target, args=(0.3,)),
    threading.Thread(target=thread_target, args=(0.1,)),
]

for t in threads:
    t.start()
for t in threads:
    t.join()


# RuntimeError => Error message in Thread-2
# RuntimeError => Error message in Thread-1

В этом примере избегается введение циклической ссылки между объектом трассировки и локальной переменной в текущем кадре, игнорируя эту часть возвращаемого значения из sys.exc_info(). Если требуется трассировка, например чтобы ее можно было зарегистрировать явным образом удалите локальную переменную используя del, чтобы избежать циклов.

Перехват нажатия клавиш Ctrl+C.

Пример перехвата исключения KeyboardInterrupt, которое возникает, когда пользователь нажимает клавишу прерывания программы, обычно это Ctrl+C или Delete.

Например, если запустить код, представленный ниже, то по нажатию Ctrl+C можно увидеть трассировку исключения KeyboardInterrupt.

>>> import time
>>> while True:
...     time.sleep(5)
... 
# ^CTraceback (most recent call last):
#   File "<stdin>", line 2, in <module>
# KeyboardInterrupt

А что делать, если не нужна трассировка, если программу прервал пользователь? Эти сведения пользователю не о чем не скажут, а в некоторых случаях могут испугать. Напишем функцию перехвата этого исключения, в которой вместо ошибок будем выводить "Программа завершена по нажатию CTRL+C".

>>> import time, sys
# функция перехвата исключения 'KeyboardInterrupt'
>>> def my_except_hook(exctype, value, traceback):
...     if exctype == KeyboardInterrupt:
...         print("Программа завершена по нажатию CTRL+C")
...     else:
...         sys.__excepthook__(exctype, value, traceback)
... 
# применим функцию перехвата `my_except_hook`
>>> sys.excepthook = my_except_hook
>>> while True:
...     time.sleep(5)
... 
# ^CПрограмма завершена по нажатию CTRL+C

Теперь по нажатию Ctrl+C выводится "Программа завершена по нажатию CTRL+C"