Автор: CoolPython
Представьте, приходите вы на новый проект, заглядываете в логи, а там вперемешку с успешными запросами на сервер вот такие сообщения:
Unexpected exception:
Что-то постоянно ломается, но молча.
Если свести к минимальному примеру то, что происходило в коде приложения, с которым я недавно начала разбираться, то получится вот такая в целом не вызывающая подозрений конструкция:
try: raise KeyError except Exception as e: print(f"Unexpected exception: {e}")
Если выполнить этот пример, то и получится
Unexpected exception:
Дело в том, что есть четыре способа вывести сообщение пойманного исключения:
print(e) print(str(e)) print(e.message) print(repr(e))
Первые два варианта не очень информативны. Например, если попытаться обратиться к несуществующему ключу словаря (ошибка топ 1 в Python), то эти варианты выведут только название ключа.
my_dict = {} try: b = my_dict["bad"] except Exception as e: print(f"Unexpected exception: {e}") Unexpected exception: 'bad'
Это потому, что str(e)
и e
выводят сообщение исключения, но не его тип. Чаще всего этого достаточно, но исключения для того и нужны, чтобы сообщать о ситуациях, которых мы не ожидаем.
Иногда пишут print(e.message)
. Здесь проблемы целых две: во-первых, мы по-прежнему получаем только сообщение. А во-вторых, атрибут message
определен не у всех исключений. Если не снабдить наш print
условием, то мы получим только новую ошибку:
AttributeError: 'KeyError' object has no attribute 'message'
А вот магический метод repr
, который и нужен для того, чтобы давать максимально точное описание, все сделает отлично. Сравним:
try: raise KeyError except Exception as e: print(f"Unexpected exception: {repr(e)}") Unexpected exception: KeyError()
А в примере со словарем получилось бы
Unexpected exception: KeyError('bad')
что более явно, чем все три варианта выше.
После моего поста про исключения мне прислали крутой комментарий. Привожу целиком.
repr(e)
— это, конечно хорошо, но ведь есть ещё лучше, а именно:
from traceback import print_exc ... my_dict = {} try: b = my_dict["bad"] except Exception as e: print_exc()
что выведет:
Traceback (most recent call last): File "<pyshell#1>", line 2, in <module> KeyError: 'bad'
и при этом программа продолжит свою работу. А если ошибку надо выводить не в stdout, то можно сделать так:
from traceback import print_exc from io import StringIO ... try: b = my_dict["bad"] except Exception: buffer = StringIO() print_exc(file=buffer) out_var = buffer.getvalue()