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

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

Системная информация об исключении и необработанные исключения

Содержание:


sys.exc_info():

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

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

Их значение:

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

Изменено в Python 3.11: type и traceback поля теперь являются производными от value (экземпляр исключения), поэтому, когда исключение модифицируется во время его обработки, изменения отражаются в результатах последующих вызовов sys.exc_info().

Пояснения к изменению в Python 3.11. Эта функция возвращает представление обработанного исключения в старом стиле (как описано выше). Если в настоящее время обрабатывается исключение e (поэтому sys.exception() (добавлена в Python 3.11) возвращает e), sys.exc_info() возвращает кортеж (type(e), e, e.__traceback__). То есть кортеж, содержащий тип исключения (подкласс BaseException), само исключение и объект трассировки, который обычно инкапсулирует стек вызовов в точке, где в последний раз возникло исключение.


sys.exception():

Функция sys.exception() (добавлена в Python 3.11), вызываемая во время выполнения обработчика исключений (например, предложения except или except*), возвращает экземпляр исключения, который был перехвачен этим обработчиком. Когда обработчики исключений вложены друг в друга, то доступно только исключение, обработанное самым внутренним обработчиком.

Если обработчик исключений не выполняется, эта функция возвращает None.

Новое в Python 3.11.


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.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.

Новое в Python 3.8.

Практические примеры использования функций 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"