Como obter uma mensagem de exceção em Python corretamente

104

Qual é a melhor maneira de obter mensagens de exceções de componentes da biblioteca padrão em Python?

Percebi que em alguns casos você pode obtê-lo por meio de um messagecampo como este:

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

mas em alguns casos (por exemplo, no caso de erros de soquete), você deve fazer algo assim:

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

Eu me perguntei se existe alguma maneira padrão de cobrir a maioria dessas situações.

Coração congelado
fonte
1
Você está combinando duas coisas diferentes - except Foo as bar:é o mesmo que except Foo, bar:(exceto que a primeira é mais recente e continuará a funcionar na 3.x), independentemente de o erro vir com um messageatributo ou não é separado.
jonrsharpe
1
@FrozenHeart Você está perguntando se o acesso .messagepor erro é o método padrão ou não?
Anand S Kumar de
seu segundo exemplo com print(msg)é apenas um atalho paraprint(str(msg))
Salo
@Anand S Kumar Yep. Funcionará em qualquer caso?
FrozenHeart de
5
Eu acredito que o acesso messageestá obsoleto.
Jonathon Reinhart de

Respostas:

102

Se você olhar a documentação para os erros embutidos , verá que a maioria das Exceptionclasses atribui seu primeiro argumento como um messageatributo. Porém, nem todos eles o fazem.

Notavelmente, EnvironmentError(com subclasses IOErrore OSError) tem um primeiro argumento de errno, segundo de strerror. Não há message... strerroré aproximadamente análogo ao que normalmente seria um message.

De maneira mais geral, as subclasses de Exceptionpodem fazer o que quiserem. Eles podem ou não ter um messageatributo. Os futuros Exceptions integrados podem não ter um messageatributo. Qualquer Exceptionsubclasse importada de bibliotecas de terceiros ou código de usuário pode não ter um messageatributo.

Acho que a maneira adequada de lidar com isso é identificar as Exceptionsubclasses específicas que você deseja capturar e, em seguida, capturar apenas aquelas em vez de tudo com um e except Exception, em seguida, utilizar quaisquer atributos que essa subclasse específica defina como você quiser.

Se você precisar de printalguma coisa, acho que imprimir a captura em Exceptionsi provavelmente fará o que você quiser, tenha messageou não um atributo.

Você também pode verificar o atributo de mensagem se quiser, assim, mas eu não iria sugerir isso, pois parece confuso:

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)
ArtOfWarfare
fonte
Obrigado pela resposta. Existe algum motivo particular para usar em str(ex)vez de apenas ex?
FrozenHeart de
4
@FrozenHeart - print()liga automaticamente str()para você. Não há razão para chamá-lo manualmente, embora seja inofensivo, já que chamar str()uma string retornará apenas a própria string.
ArtOfWarfare
1
@ArtOfWarfare Acho que pode haver problemas str()se a mensagem for do tipo unicode.
Kratenko de
39

Para melhorar a resposta fornecida por @artofwarfare , aqui está o que considero uma maneira mais simples de verificar o messageatributo e imprimi-lo ou imprimir o Exceptionobjeto como um fallback.

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

A chamada para repré opcional, mas acho que é necessária em alguns casos de uso.


Atualização # 1:

Após o comentário de @MadPhysicist , aqui está uma prova de por que a chamada para reprpode ser necessária. Tente executar o seguinte código em seu interpretador:

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

Atualização # 2:

Aqui está uma demonstração com especificações para Python 2.7 e 3.5: https://gist.github.com/takwas/3b7a6edddef783f2abddffda1439f533

Tosin Mash
fonte
1
getattrlança uma exceção se o objeto passado não tiver o atributo solicitado. Se você queria fazer algo assim, eu acho que você deve usar o terceiro argumento opcional para getattr, como este: print getattr(e, 'message', e). Debativelmente melhor do que o que eu fiz. Outra opção seria print(e.message if hasattr(e, 'message') else e).
ArtOfWarfare
2
Você quase certamente desejaria em strvez de repr.
Mad Physicist
2
Em comparação com str, reprapenas adiciona o nome da classe. Em vez de usar repr, proponho usar print('{e.__class__.__name__}: {e}'.format(e=e)). A saída de impressão é mais limpa e adere à própria saída de aumento.
kadee 01 de
1
Com base no que precede, achei mais útil para mim ser'{e.__class__.__module__}.{e.__class__.__name__}: {e}'
shadi
1
Obrigado!! repr(e)por enquanto, foi a única solução que me ajudou a imprimir pelo menos uma quantidade mínima de informações sobre um erro que detectei em alguma circunstância específica. repr(e)produz KeyError(0,)(que é o erro), enquanto str(e)ou e.messageproduziu apenas 0ou nada, respectivamente.
FlorianH
0

Eu tive o mesmo problema. Acho que a melhor solução é usar log.exception, que imprimirá automaticamente o rastreamento de pilha e a mensagem de erro, como:

try:
    pass
    log.info('Success')
except:
    log.exception('Failed')
Bill Z
fonte
2
O que é log? Acabei de experimentar o import logPython 2.7.13 e recebi uma mensagem de que não há nenhum módulo com esse nome. É algo que você adicionou por meio de pipou foi adicionado em uma versão mais recente do python (eu sei, eu sei, eu preciso atualizar para o Python 3 ... farei isso assim que o CentOS parar de ser fornecido com o Python 2 por padrão ...)
ArtOfWarfare
7
Ele / ela provavelmente está usando o módulo de log do python e instanciando um logger como uma variável de log. import logging\ nlog = logging.getLogger(__name__)
Josepas
0

Eu também tive o mesmo problema. Investigando isso, descobri que a classe Exception tem um argsatributo, que captura os argumentos usados ​​para criar a exceção. Se você restringir as exceções que, exceto serão capturadas, a um subconjunto, poderá determinar como elas foram construídas e, portanto, qual argumento contém a mensagem.

try:
   # do something that may raise an AuthException
except AuthException as ex:
   if ex.args[0] == "Authentication Timeout.":
      # handle timeout
   else:
      # generic handling
user2501097
fonte