Как правильно получить сообщение об исключении в Python

97

Как лучше всего получать сообщения об исключениях из компонентов стандартной библиотеки в Python?

Я заметил, что в некоторых случаях вы можете получить его через messageтакое поле:

try:
  pass
except Exception as ex:
  print(ex.message)

но в некоторых случаях (например, в случае ошибок сокета) вам нужно сделать что-то вроде этого:

try:
  pass
except socket.error as ex:
  print(ex)

Мне было интересно, есть ли какой-нибудь стандартный способ охватить большинство этих ситуаций?

Ледяное сердце
источник
1
Вы объединяете две разные вещи - except Foo as bar:то же самое except Foo, bar:(за исключением того, что первое новее и будет продолжать работать в 3.x), независимо от того, идет ли ошибка с messageатрибутом или нет, является отдельным.
jonrsharpe
1
@FrozenHeart Вы спрашиваете, является ли доступ .messageк ошибке стандартным или нет?
Ананд С Кумар
ваш второй пример print(msg)- это просто ярлык дляprint(str(msg))
Сало
@Anand S Kumar Ага. Будет ли работать в любом случае?
FrozenHeart
4
Я считаю, что доступ messageустарел.
Джонатон Рейнхарт,

Ответы:

94

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

Примечательно, что EnvironmentError(с подклассами IOErrorи OSError) имеет первый аргумент errno, второй из strerror. Нет message... strerrorпримерно аналогично тому, что обычно было бы message.

В более общем плане подклассы Exceptionмогут делать все, что захотят. Они могут иметь или не иметь messageатрибут. Будущие встроенные Exceptionобъекты могут не иметь messageатрибута. Любой Exceptionподкласс, импортированный из сторонних библиотек или пользовательского кода, может не иметь messageатрибута.

Я думаю, что правильный способ справиться с этим - определить конкретные Exceptionподклассы, которые вы хотите поймать, а затем поймать только те, а не все с помощью except Exception, а затем использовать любые атрибуты, которые определяет конкретный подкласс, как вы хотите.

Если вам printчто-то нужно , я думаю, что печать пойманного Exception, скорее всего, сделает то, что вы хотите, независимо от того, есть ли у него messageатрибут или нет.

Вы также можете проверить атрибут сообщения, если хотите, например, вот так, но я бы не стал предлагать это, поскольку это просто кажется беспорядочным:

try:
    pass
except Exception as e:
    # Just print(e) is cleaner and more likely what you want,
    # but if you insist on printing message specifically whenever possible...
    if hasattr(e, 'message'):
        print(e.message)
    else:
        print(e)
Искусство Войны
источник
Спасибо за ответ. Есть ли какая-то особая причина использовать str(ex)вместо просто ex?
FrozenHeart
3
@FrozenHeart - print()автоматически звонит str()вам. Нет причин вызывать его вручную, хотя это безвредно, поскольку вызов str()строки просто вернет саму строку.
ArtOfWarfare
1
@ArtOfWarfare Я думаю, что могут возникнуть проблемы с str()типом сообщения unicode.
Кратенко
34

Чтобы улучшить ответ, предоставленный @artofwarfare , вот что я считаю более изящным способом проверить messageатрибут и распечатать его или распечатать Exceptionобъект в качестве запасного варианта.

try:
    pass 
except Exception as e:
    print getattr(e, 'message', repr(e))

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


Обновление №1:

Следуя комментарию @MadPhysicist , вот доказательство того, почему вызов reprможет быть необходим. Попробуйте запустить в интерпретаторе следующий код:

try:
    raise Exception 
except Exception as e:
    print(getattr(e, 'message', repr(e)))
    print(getattr(e, 'message', str(e)))

Обновление №2:

Вот демонстрация с особенностями Python 2.7 и 3.5: https://gist.github.com/takwas/3b7a6edddef783f2abddffda1439f533

Тосин Маш
источник
1
getattrгенерирует исключение, если переданный объект не имеет запрошенного атрибута. Если вы хотите сделать что - то подобное, я думаю , вы должны использовать необязательный третий аргумент в пользу getattr, например: print getattr(e, 'message', e). Сомнительно лучше, чем то, что я сделал. Другой вариант был бы print(e.message if hasattr(e, 'message') else e).
ArtOfWarfare
2
Вы почти наверняка захотите strвместо repr.
Безумный физик
2
По сравнению с str, reprпросто добавляет имя класса. Вместо использования reprпредлагаю использовать print('{e.__class__.__name__}: {e}'.format(e=e)). Вывод на печать чище и соответствует выводу самого повышения.
kadee
1
Основываясь на вышеизложенном, я нашел наиболее полезным для себя'{e.__class__.__module__}.{e.__class__.__name__}: {e}'
shadi
1
Благодарность!! repr(e)на данный момент было единственным решением, которое помогло мне напечатать хотя бы минимальный объем информации об ошибке, которую я улавливаю в определенных обстоятельствах. repr(e)дает KeyError(0,)(что и является ошибкой), в то время как str(e)или e.messageдает только 0или вообще ничего, соответственно.
FlorianH
0

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

try:
    pass
    log.info('Success')
except:
    log.exception('Failed')
Билл Z
источник
1
Что есть log? Я только что попробовал import logPython 2.7.13 и получил сообщение о том, что модуля с таким именем нет. Это что-то, что вы добавили через, pipили это было добавлено в более новую версию python (я знаю, я знаю, мне нужно обновить до Python 3 ... Я сделаю это, как только CentOS перестанет поставляться с Python 2 по умолчанию ...)
ArtOfWarfare
6
Он / она, вероятно, использует модуль ведения журнала из Python, а затем создает экземпляр регистратора в качестве переменной журнала. import logging\ nlog = logging.getLogger(__name__)
Josepas
0

У меня тоже была такая же проблема. Вникнув в это, я обнаружил, что класс Exception имеет argsатрибут, который фиксирует аргументы, которые использовались для создания исключения. Если вы сузите исключения, которые, кроме того, будут улавливаться, до подмножества, вы сможете определить, как они были созданы, и, следовательно, какой аргумент содержит сообщение.

try:
   # do something that may raise an AuthException
except AuthException as ex:
   if ex.args[0] == "Authentication Timeout.":
      # handle timeout
   else:
      # generic handling
user2501097
источник