Cómo obtener el mensaje de excepción en Python correctamente

97

¿Cuál es la mejor manera de obtener mensajes de excepciones de los componentes de la biblioteca estándar en Python?

Noté que en algunos casos puedes obtenerlo a través de un messagecampo como este:

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

pero en algunos casos (por ejemplo, en caso de errores de socket) tienes que hacer algo como esto:

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

Me preguntaba si existe alguna forma estándar de cubrir la mayoría de estas situaciones.

Corazón congelado
fuente
1
Estás combinando dos cosas diferentes: except Foo as bar:es lo mismo que except Foo, bar:(excepto que el primero es más nuevo y continuará funcionando en 3.x), ya sea que el error venga con un messageatributo o no sea separado.
jonrsharpe
1
@FrozenHeart ¿Se pregunta si el acceso .messagepor error es una forma estándar o no?
Anand S Kumar
su segundo ejemplo con print(msg)es solo un atajo paraprint(str(msg))
Salo
@Anand S Kumar Sí. ¿Funcionará en cualquier caso?
FrozenHeart
4
Creo que el acceso messageestá en desuso.
Jonathon Reinhart

Respuestas:

94

Si observa la documentación de los errores incorporados , verá que la mayoría de las Exceptionclases asignan su primer argumento como messageatributo. Sin embargo, no todos lo hacen.

En particular, EnvironmentError(con subclases IOErrory OSError) tiene un primer argumento de errno, segundo de strerror. No hay message... strerrores aproximadamente análogo a lo que normalmente sería un message.

De manera más general, las subclases de Exceptionpueden hacer lo que quieran. Pueden tener o no un messageatributo. Es posible que los mensajes de correo electrónico integrados futuros Exceptionno tengan un messageatributo. Es posible que cualquier Exceptionsubclase importada de bibliotecas de terceros o código de usuario no tenga un messageatributo.

Creo que la forma correcta de manejar esto es identificar las Exceptionsubclases específicas que desea capturar, y luego capturar solo aquellas en lugar de todo con un except Exception, luego utilizar los atributos que esa subclase específica defina como desee.

Si debe printalgo, creo que Exceptiones más probable que la impresión de la captura en sí haga lo que desee, tenga messageo no un atributo.

También puede verificar el atributo de mensaje si lo desea, así, pero realmente no lo sugeriría ya que parece desordenado:

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
fuente
Gracias por la respuesta. ¿Hay alguna razón en particular para usar en str(ex)lugar de solo ex?
FrozenHeart
3
@FrozenHeart: te print()llama automáticamente str(). No hay razón para llamarlo manualmente usted mismo, aunque es inofensivo ya que llamar str()a una cadena simplemente devolverá la cadena en sí.
ArtOfWarfare
1
@ArtOfWarfare Creo que podría haber problemas str()si el mensaje es de tipo unicode.
Kratenko
34

Para mejorar la respuesta proporcionada por @artofwarfare , aquí está lo que considero una forma más ordenada de verificar el messageatributo e imprimirlo o imprimir el Exceptionobjeto como respaldo.

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

La llamada a repres opcional, pero la encuentro necesaria en algunos casos de uso.


Actualización n. ° 1:

Siguiendo el comentario de @MadPhysicist , aquí hay una prueba de por qué la llamada a reprpodría ser necesaria. Intente ejecutar el siguiente código en su intérprete:

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

Actualización n. ° 2:

Aquí hay una demostración con detalles para Python 2.7 y 3.5: https://gist.github.com/takwas/3b7a6edddef783f2abddffda1439f533

Tosin Mash
fuente
1
getattrlanza una excepción si el objeto pasado no tiene el atributo solicitado. Si quería hacer algo así, creo que se debe utilizar el tercer argumento opcional para getattr, de esta manera: print getattr(e, 'message', e). Probablemente mejor que lo que hice. Otra opción sería print(e.message if hasattr(e, 'message') else e).
ArtOfWarfare
2
Es casi seguro que querrías en strlugar de repr.
Mad Physicist
2
En comparación con str, reprsolo agrega el nombre de la clase. En lugar de usar repr, propongo usar print('{e.__class__.__name__}: {e}'.format(e=e)). La salida de impresión es más limpia y se adhiere a la salida del propio aumento.
kadee
1
Basado en lo anterior, encontré el más útil para mí'{e.__class__.__module__}.{e.__class__.__name__}: {e}'
shadi
1
¡¡Gracias!! repr(e)fue por ahora la única solución que me ayudó a imprimir al menos una cantidad mínima de información sobre un error que detecto en alguna circunstancia particular. repr(e)produce KeyError(0,)(que es el error), mientras que str(e)o e.messagecedió solo 0o nada respectivamente.
FlorianH
0

Yo tuve el mismo problema. Creo que la mejor solución es usar log.exception, que imprimirá automáticamente el seguimiento de la pila y el mensaje de error, como:

try:
    pass
    log.info('Success')
except:
    log.exception('Failed')
Bill Z
fuente
1
¿Qué es log? Acabo de probar import logPython 2.7.13 y recibí un mensaje de que no hay ningún módulo con ese nombre. ¿Es algo que agregó a través de pipo se agregó en una versión más nueva de python (lo sé, lo sé, necesito actualizar a Python 3 ... Lo haré tan pronto como CentOS deje de enviarse con Python 2 de forma predeterminada ...)
ArtOfWarfare
6
Probablemente esté usando el módulo de registro de Python y luego instanciando un registrador como una variable de registro. import logging\ nlog = logging.getLogger(__name__)
Josepas
0

Yo también tuve el mismo problema. Profundizando en esto, encontré que la clase Exception tiene un argsatributo, que captura los argumentos que se usaron para crear la excepción. Si reduce las excepciones que excepto se detectarán a un subconjunto, debería poder determinar cómo se construyeron y, por lo tanto, qué argumento contiene el mensaje.

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