python: ¿Cómo sé qué tipo de excepción ocurrió?

230

Tengo una función llamada por el programa principal:

try:
    someFunction()
except:
    print "exception happened!"

pero en medio de la ejecución de la función genera una excepción, por lo que salta a la exceptparte.

¿Cómo puedo ver exactamente qué sucedió en el someFunction()que causó la excepción?

Shang Wang
fuente
99
Nunca use nunca bare except:(sin bare raise), excepto tal vez una vez por programa, y ​​preferiblemente no entonces.
Mike Graham
Si usa varias exceptcláusulas, no necesitará verificar el tipo de excepción, eso es lo que generalmente se hace para actuar de acuerdo con un tipo de excepción específico.
Rik Poggi
3
Si le importa el tipo de excepción, es porque ya ha considerado qué tipos de excepción podrían ocurrir lógicamente.
Karl Knechtel
3
Dentro del exceptbloque, la excepción está disponible a través de la sys.exc_info()función: esta función devuelve una tupla de tres valores que proporcionan información sobre la excepción que se está manejando actualmente.
Piotr Dobrogost
Relacionado: ¿Cómo imprimir un error en Python?
Stevoisiak

Respuestas:

384

Todas las otras respuestas señalan que no debe atrapar excepciones genéricas, pero nadie parece querer decirle por qué, lo cual es esencial para comprender cuándo puede romper la "regla". Aquí hay una explicación. Básicamente, es para que no te escondas:

Entonces, siempre que tenga cuidado de no hacer ninguna de esas cosas, está bien detectar la excepción genérica. Por ejemplo, podría proporcionar información sobre la excepción al usuario de otra manera, como:

  • Presentar excepciones como cuadros de diálogo en una GUI
  • Transfiera excepciones de un subproceso o proceso de trabajo al subproceso o proceso de control en una aplicación multiproceso o multiprocesamiento

Entonces, ¿cómo atrapar la excepción genérica? Hay varias formas Si solo desea el objeto de excepción, hágalo así:

try:
    someFunction()
except Exception as ex:
    template = "An exception of type {0} occurred. Arguments:\n{1!r}"
    message = template.format(type(ex).__name__, ex.args)
    print message

¡ Asegúrese message de llamar la atención del usuario de una manera difícil de perder! Imprimirlo, como se muestra arriba, puede no ser suficiente si el mensaje está enterrado en muchos otros mensajes. No captar la atención de los usuarios equivale a tragarse todas las excepciones, y si hay una impresión que debería haber salido después de leer las respuestas en esta página, es que esto no es algo bueno . Al finalizar el bloque except con una raisedeclaración se solucionará el problema volviendo a plantear de forma transparente la excepción que se detectó.

La diferencia entre lo anterior y el uso except:sin ningún argumento es doble:

  • Un simple except:no te da el objeto de excepción para inspeccionar
  • Las excepciones SystemExit, KeyboardInterrupty GeneratorExitno están atrapados por el código anterior, que generalmente es lo que quieres. Ver la jerarquía de excepciones .

Si también desea el mismo stacktrace que obtiene si no captura la excepción, puede obtenerlo así (aún dentro de la cláusula except):

import traceback
print traceback.format_exc()

Si usa el loggingmódulo, puede imprimir la excepción en el registro (junto con un mensaje) como este:

import logging
log = logging.getLogger()
log.exception("Message for you, sir!")

Si desea profundizar y examinar la pila, ver variables, etc., use la post_mortemfunción del pdbmódulo dentro del bloque except:

import pdb
pdb.post_mortem()

He encontrado que este último método es invaluable al buscar errores.

Lauritz V. Thaulow
fuente
1
traceback.print_exc () haría lo mismo que tu "" .join-thing más complicado, creo.
Gurgeh
1
@Gurgeh Sí, pero no sé si quiere imprimirlo o guardarlo en un archivo o registrarlo o hacer algo más con él.
Lauritz V. Thaulow
No voté en contra, pero diría que es porque deberías haber puesto un gran trabajo al principio diciendo que no deberías necesitar nada de esto, pero así es como se podría hacer . Y tal vez porque sugieres atrapar la excepción genérica.
Rik Poggi
10
@Rik Creo que es muy posible que necesites todo esto. Por ejemplo, si tiene un programa con una GUI y un back-end, y desea presentar todas las excepciones del back-end como mensajes de la GUI en lugar de que su programa se cierre con un seguimiento de la pila. En tal caso, debe detectar la excepción genérica , crear un texto de rastreo para el cuadro de diálogo, registrar la excepción también y, si está en modo de depuración, ingresar post-mortem.
Lauritz V. Thaulow
18
@RikPoggi: Pensamiento ingenuo. Existen muchas circunstancias razonables en las que necesita detectar excepciones del código de otra persona y no sabe qué excepciones se generarán.
stackoverflowuser2010
63

Obtenga el nombre de la clase a la que pertenece el objeto de excepción:

e.__class__.__name__

y el uso de la función print_exc () también imprimirá el seguimiento de la pila, que es información esencial para cualquier mensaje de error.

Me gusta esto:

from traceback import print_exc

class CustomException(Exception): pass

try:
    raise CustomException("hi")
except Exception, e:
    print 'type is:', e.__class__.__name__
    print_exc()
    # print "exception happened!"

Obtendrá una salida como esta:

type is: CustomException
Traceback (most recent call last):
  File "exc.py", line 7, in <module>
    raise CustomException("hi")
CustomException: hi

Y después de la impresión y el análisis, el código puede decidir no manejar excepciones y simplemente ejecutar raise:

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        raise
    print "handling exception"

Salida:

special case of CustomException not interfering

Y el intérprete imprime la excepción:

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    calculate()
  File "test.py", line 6, in calculate
    raise CustomException("hi")
__main__.CustomException: hi

Después de que la raiseexcepción original continúa propagándose más arriba en la pila de llamadas. ( Tenga cuidado con las posibles dificultades ) Si genera una nueva excepción, conlleva un nuevo seguimiento de pila (más corto).

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        #raise CustomException(e.message)
        raise e
    print "handling exception"

Salida:

special case of CustomException not interfering
Traceback (most recent call last):
  File "test.py", line 13, in <module>
    raise CustomException(e.message)
__main__.CustomException: hi    

Observe cómo el rastreo no incluye la calculate()función de la línea, 9que es el origen de la excepción original e.

Alex
fuente
Si desea almacenar el rastreo como una cadena, puede utilizar traceback.format_exc()así
Stevoisiak
1
e.__class__.__name__¿Es esto lo mismo que type(e).__name__, como sugiere la respuesta anterior?
information_interchange
1
@information_interchange sí. El contenido de la pregunta y la respuesta aceptada cambió completamente con el tiempo. Es una pena que otros participantes no sean notificados por la maquinaria SO :(
Alex
14

Por lo general, no debe detectar todas las posibles excepciones, try: ... exceptya que esto es demasiado amplio. Simplemente atrape los que se espera que sucedan por cualquier razón. Si realmente debe hacerlo, por ejemplo, si desea obtener más información sobre algún problema durante la depuración, debe hacerlo

try:
    ...
except Exception as ex:
    print ex # do whatever you want for debugging.
    raise    # re-raise exception.
hochl
fuente
17
El uso de la palabra "nunca" aquí nunca ha sido tan incorrecto. Utilizo try: ... except Exception:muchas cosas, por ejemplo, el uso de bibliotecas dependientes de la red, o una masajista de datos que puede enviarle cosas extrañas. Naturalmente, también tengo un registro adecuado. Esto es crucial para permitir que el programa continúe operando en caso de un solo error en los datos de entrada.
thnee
3
¿Alguna vez trató de detectar todas las excepciones que se pueden generar al enviar un correo electrónico usando smtplib?
linusg
1
Puede haber algunos casos especiales en los que sea necesario capturar todas las excepciones, pero a nivel general solo debe capturar lo que espera para no ocultar accidentalmente errores que no anticipó. Un buen registro también es una buena idea, por supuesto.
hochl
1
Es perfectamente razonable detectar todas las excepciones. Si llama a una biblioteca de terceros, es posible que no sepa qué excepciones se generarán en esa biblioteca. En tal caso, el único recurso es capturar todas las excepciones, por ejemplo, registrarlas en un archivo.
stackoverflowuser2010
Ok ok tienes razón, reformularé mi respuesta para dejar en claro que hay casos de uso válidos para un catch all.
hochl
10

A menos que somefunctionsea ​​una función heredada codificada muy mal, no debería necesitar lo que está pidiendo.

Use múltiples exceptcláusulas para manejar de diferentes maneras diferentes excepciones:

try:
    someFunction()
except ValueError:
    # do something
except ZeroDivision:
    # do something else

El punto principal es que no debe atrapar una excepción genérica, sino solo las que necesita. Estoy seguro de que no desea ocultar errores o errores inesperados.

Rik Poggi
fuente
8
Si está utilizando una biblioteca de terceros, es posible que no sepa qué excepciones se generarán dentro de ella. ¿Cómo puedes atraparlos a todos individualmente?
stackoverflowuser2010
8

La mayoría de las respuestas apuntan a la except (…) as (…):sintaxis (con razón), pero al mismo tiempo nadie quiere hablar de un elefante en la habitación, donde el elefante está en sys.exc_info()funcionamiento. De la documentación del módulo sys (énfasis mío):

Esta función devuelve una tupla de tres valores que brindan información sobre la excepción que se está manejando actualmente.
(...)
Si no se maneja ninguna excepción en ninguna parte de la pila, se devuelve una tupla que contiene tres valores Ninguno. De lo contrario, los valores devueltos son (tipo, valor, rastreo). Su significado es: tipo obtiene el tipo de excepción que se maneja (una subclase de BaseException); el valor obtiene la instancia de excepción (una instancia del tipo de excepción); traceback obtiene un objeto traceback (consulte el Manual de referencia) que encapsula la pila de llamadas en el punto donde se produjo la excepción originalmente.

Creo que sys.exc_info()podría tratarse como la respuesta más directa a la pregunta original de ¿Cómo sé qué tipo de excepción ocurrió?

Piotr Dobrogost
fuente
1
Esa es la respuesta correcta para mí, ya que resuelve el problema de qué excepción está ocurriendo, así que qué debo poner en lugar de desnudo except. Solo por completo, exctype, value = sys.exc_info()[:2]le indicaremos el tipo de excepción que luego se puede utilizar en el except.
Ondrej Burkert
5

try: someFunction () excepto Exception, exc:

#this is how you get the type
excType = exc.__class__.__name__

#here we are printing out information about the Exception
print 'exception type', excType
print 'exception msg', str(exc)

#It's easy to reraise an exception with more information added to it
msg = 'there was a problem with someFunction'
raise Exception(msg + 'because of %s: %s' % (excType, exc))
wp-overwatch.com
fuente
-1 como el uso exc.__class__.__name__ya se había sugerido en la respuesta de Alex - stackoverflow.com/a/9824060/95735
Piotr Dobrogost
3

Estas respuestas están bien para la depuración, pero para probar mediante programación la excepción, isinstance(e, SomeException)puede ser útil, ya que también prueba las subclases SomeException, para que pueda crear una funcionalidad que se aplique a las jerarquías de excepciones.

Chris
fuente
1

Así es como estoy manejando mis excepciones. La idea es intentar resolver el problema si eso es fácil, y luego agregar una solución más deseable si es posible. No resuelva el problema en el código que genera la excepción, o ese código pierde el rastro del algoritmo original, que debe escribirse en el punto. Sin embargo, pase los datos necesarios para resolver el problema y devuelva un lambda en caso de que no pueda resolver el problema fuera del código que lo genera.

path = 'app.p'

def load():
    if os.path.exists(path):
        try:
            with open(path, 'rb') as file:
                data = file.read()
                inst = pickle.load(data)
        except Exception as e:
            inst = solve(e, 'load app data', easy=lambda: App(), path=path)()
    else:
        inst = App()
    inst.loadWidgets()

# e.g. A solver could search for app data if desc='load app data'
def solve(e, during, easy, **kwargs):
    class_name = e.__class__.__name__
    print(class_name + ': ' + str(e))
    print('\t during: ' + during)
    return easy

Por ahora, dado que no quiero pensar tangencialmente para el propósito de mi aplicación, no he agregado ninguna solución complicada. Pero en el futuro, cuando sepa más sobre las posibles soluciones (dado que la aplicación está diseñada más), podría agregar un diccionario de soluciones indexadas por during.

En el ejemplo que se muestra, una solución podría ser buscar datos de aplicaciones almacenados en otro lugar, por ejemplo, si el archivo 'app.p' se eliminó por error.

Por ahora, dado que escribir el controlador de excepciones no es una idea inteligente (todavía no conocemos las mejores formas de resolverlo, porque el diseño de la aplicación evolucionará), simplemente devolvemos la solución fácil que es actuar como si estuviéramos corriendo la aplicación por primera vez (en este caso).

ResumenÁlgebraAprendiz
fuente
0

Para agregar a la respuesta de Lauritz, creé un decorador / contenedor para el manejo de excepciones y el contenedor registra qué tipo de excepción ocurrió.

class general_function_handler(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, obj, type=None):
        return self.__class__(self.func.__get__(obj, type))
    def __call__(self, *args, **kwargs):
        try:
            retval = self.func(*args, **kwargs)
        except Exception, e :
            logging.warning('Exception in %s' % self.func)
            template = "An exception of type {0} occured. Arguments:\n{1!r}"
            message = template.format(type(e).__name__, e.args)
            logging.exception(message)
            sys.exit(1) # exit on all exceptions for now
        return retval

Esto se puede invocar en un método de clase o una función independiente con el decorador:

@general_function_handler

Consulte mi blog para ver el ejemplo completo: http://ryaneirwin.wordpress.com/2014/05/31/python-decorators-and-exception-handling/

rirwin
fuente
0

Puede comenzar como Lauritz recomienda, con:

except Exception as ex:

y luego solo para que me print exguste:

try:
    #your try code here
except Exception as ex:
    print ex
Gura
fuente
¿Puedes elaborar un poco para que tu respuesta sea única?
GHC
1
seguro: puede imprimir la excepción capturada de esta manera: intente: # su código de prueba aquí, excepto Excepción como ex: imprima ex ahora, el error se imprimirá
Gura
-2

La excepción real se puede capturar de la siguiente manera:

try:
    i = 1/0
except Exception as e:
    print e

Puede obtener más información sobre las excepciones en The Python Tutorial .

Kevin Coffey
fuente
-2

Su pregunta es: "¿Cómo puedo ver exactamente qué sucedió en someFunction () que causó la excepción?"

Me parece que no está preguntando cómo manejar excepciones imprevistas en el código de producción (como se suponía en muchas respuestas), sino cómo averiguar qué está causando una excepción particular durante el desarrollo.

La forma más fácil es utilizar un depurador que pueda detenerse donde se produce la excepción no detectada, preferiblemente sin salir, para que pueda inspeccionar las variables. Por ejemplo, PyDev en el IDE de código abierto de Eclipse puede hacer eso. Para habilitar eso en Eclipse, abra la perspectiva Depuración, seleccione Manage Python Exception Breakpointsen el Runmenú y verifique Suspend on uncaught exceptions.


fuente
-4

Simplemente abstenerse de atrapar la excepción y el rastreo que Python imprime le dirá qué excepción ocurrió.

Mike Graham
fuente