Блоги

Unexpected exception

Автор: 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()

Марина

Recent Posts

10 удалённых Python-вакансий

Если вы ищете работу на Python, на Talanto.work сейчас есть варианты для junior, middle, senior…

6 месяцев ago

Где искать Python-вакансии в 2026 году: зачем на Talanto.work появился отдельный сценарий поиска

Рынок Python-вакансий большой, но удобным его назвать сложно. Формально вакансий много, однако в реальном поиске…

6 месяцев ago

Срезы в Python со всеми подробностями

Срезы в Python — это больше, чем a[2:7]. В статье — быстрый разбор полезных приёмов:…

8 месяцев ago

Возведение в степень в Python: полное руководство

В этой статье мы рассмотрим возведение в степень в Python с помощью различных методов, от…

9 месяцев ago

Как удалить дубликаты из списка Python?

Дубликаты в списке Python — частая проблема. В статье показано, как быстро убрать повторы, в…

9 месяцев ago

Виртуальный (shared) хостинг от SpaceWeb: технический обзор

Услуга виртуального хостинга на SpaceWeb позволяет арендовать часть сервера (shared-сервера) и разместить на нём свой…

11 месяцев ago