try print general exceptions error custom catch and all python exception error-handling

print - "Excepción interna"(con traceback) en Python?



python try except print error (8)

Mi experiencia está en C # y recientemente comencé a programar en Python. Cuando se lanza una excepción, normalmente quiero envolverla en otra excepción que agregue más información, sin dejar de mostrar el seguimiento completo de la pila. Es bastante fácil en C #, pero ¿cómo lo hago en Python?

P.ej. en C # Haría algo como esto:

try { ProcessFile(filePath); } catch (Exception ex) { throw new ApplicationException("Failed to process file " + filePath, ex); }

En Python puedo hacer algo similar:

try: ProcessFile(filePath) except Exception as e: raise Exception(''Failed to process file '' + filePath, e)

... pero esto pierde el rastreo de la excepción interna!

Editar: me gustaría ver los mensajes de excepción y ambos rastreos de pila y correlacionar los dos. Es decir, quiero ver en el resultado que la excepción X ocurrió aquí y luego la excepción Y, igual que en C #. ¿Es esto posible en Python 2.6? Parece que lo mejor que puedo hacer hasta ahora (basado en la respuesta de Glenn Maynard) es:

try: ProcessFile(filePath) except Exception as e: raise Exception(''Failed to process file'' + filePath, e), None, sys.exc_info()[2]

Esto incluye los mensajes y los registros de seguimiento, pero no muestra qué excepción ocurrió en el rastreo.


¿Tal vez podrías tomar la información relevante y pasarla? Estoy pensando algo así como:

import traceback import sys import StringIO class ApplicationError: def __init__(self, value, e): s = StringIO.StringIO() traceback.print_exc(file=s) self.value = (value, s.getvalue()) def __str__(self): return repr(self.value) try: try: a = 1/0 except Exception, e: raise ApplicationError("Failed to process file", e) except Exception, e: print e


Asumiendo:

  • necesitas una solución que funcione para Python 2 (para Python 3 puro ver raise ... from solución)
  • solo quiero enriquecer el mensaje de error, por ejemplo, proporcionando un contexto adicional
  • necesita el seguimiento completo de la pila

puede usar una solución simple de los doc. https://docs.python.org/3/tutorial/errors.html#raising-exceptions :

try: raise NameError(''HiThere'') except NameError: print ''An exception flew by!'' # print or log, provide details about context raise # reraise the original exception, keeping full stack trace

La salida:

An exception flew by! Traceback (most recent call last): File "<stdin>", line 2, in ? NameError: HiThere

Parece que la pieza clave es la palabra clave simplificada ''subir'' que se encuentra sola. Eso volverá a subir la excepción en el bloque excepto.


En Python 3.x :

raise Exception(''Failed to process file '' + filePath).with_traceback(e.__traceback__)

o simplemente

except Exception: raise MyException()

que propagará MyException pero imprimirá ambas excepciones si no se maneja.

En Python 2.x :

raise Exception, ''Failed to process file '' + filePath, e

Puede evitar la impresión de ambas excepciones __context__ atributo __context__ . Aquí escribo un administrador de contexto que usa eso para atrapar y cambiar su excepción sobre la marcha: (vea http://docs.python.org/3.1/library/stdtypes.html para ver cómo funcionan)

try: # Wrap the whole program into the block that will kill __context__. class Catcher(Exception): ''''''This context manager reraises an exception under a different name.'''''' def __init__(self, name): super().__init__(''Failed to process code in {!r}''.format(name)) def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is not None: self.__traceback__ = exc_tb raise self ... with Catcher(''class definition''): class a: def spam(self): # not really pass, but you get the idea pass lut = [1, 3, 17, [12,34], 5, _spam] assert a().lut[-1] == a.spam ... except Catcher as e: e.__context__ = None raise


En python 3 puedes hacer lo siguiente:

try: raise MyExceptionToBeWrapped("I have twisted my ankle") except MyExceptionToBeWrapped as e: raise MyWrapperException("I''m not in a good shape") from e

Esto producirá algo como esto:

Traceback (most recent call last): ... MyExceptionToBeWrapped: ("I have twisted my ankle") The above exception was the direct cause of the following exception: Traceback (most recent call last): ... MyWrapperException: ("I''m not in a good shape")


Es sencillo; pase el traceback como el tercer argumento para subir.

import sys class MyException(Exception): pass try: raise TypeError("test") except TypeError, e: raise MyException(), None, sys.exc_info()[2]

Siempre haga esto cuando capture una excepción y vuelva a subir otra.


No creo que puedas hacer esto en Python 2.x, pero algo similar a esta funcionalidad es parte de Python 3. De PEP 3134 :

En la implementación actual de Python, las excepciones se componen de tres partes: el tipo, el valor y el rastreo. El módulo ''sys'' expone la excepción actual en tres variables paralelas, exc_type, exc_value y exc_traceback, la función sys.exc_info () devuelve una tupla de estas tres partes, y la instrucción ''raise'' tiene una aceptación de tres argumentos estas tres partes La manipulación de excepciones a menudo requiere pasar estas tres cosas en paralelo, lo que puede ser tedioso y propenso a errores. Además, la instrucción ''except'' solo puede proporcionar acceso al valor, no a la traceback. Agregar el atributo '' traceback '' a los valores de excepción hace que toda la información de excepción sea accesible desde un solo lugar.

Comparación con C #:

Las excepciones en C # contienen una propiedad de ''InnerException'' de solo lectura que puede apuntar a otra excepción. Su documentación [10] dice que "cuando se lanza una excepción X como resultado directo de una excepción previa Y, la propiedad InnerException de X debe contener una referencia a Y." La VM no establece esta propiedad automáticamente; más bien, todos los constructores de excepciones toman un argumento opcional ''innerException'' para establecerlo explícitamente. El atributo '' causa '' cumple el mismo propósito que InnerException, pero este PEP propone una nueva forma de ''aumentar'' en lugar de extender los constructores de todas las excepciones. C # también proporciona un método GetBaseException que salta directamente al final de la cadena InnerException; este PEP no propone ningún análogo.

Tenga en cuenta también que Java, Ruby y Perl 5 tampoco son compatibles con este tipo de cosas. Citando nuevamente:

En cuanto a otros lenguajes, Java y Ruby descartan la excepción original cuando se produce otra excepción en una cláusula ''catch'' / ''rescue'' o ''finally'' / ''ensure''. Perl 5 carece de manejo estructurado de excepciones. Para Perl 6, RFC número 88 [9] propone un mecanismo de excepción que retiene implícitamente las excepciones encadenadas en una matriz llamada @@.


Puede usar mi clase CausedException para encadenar excepciones en Python 2.x (e incluso en Python 3 puede ser útil en caso de que quiera dar más de una excepción atrapada como causa de una excepción recientemente planteada). Puede que esto te ayude.


Python 3 tiene el raise ... from cláusula a la cadena de excepciones. La respuesta de Glenn es excelente para Python 2.7, pero solo utiliza el rastreo de la excepción original y descarta el mensaje de error y otros detalles. Aquí hay algunos ejemplos en Python 2.7 que agregan información de contexto del alcance actual al mensaje de error de la excepción original, pero mantienen intactos otros detalles.

Tipo de excepción conocida

try: sock_common = xmlrpclib.ServerProxy(rpc_url+''/common'') self.user_id = sock_common.login(self.dbname, username, self.pwd) except IOError: _, ex, traceback = sys.exc_info() message = "Connecting to ''%s'': %s." % (config[''connection''], ex.strerror) raise IOError, (ex.errno, message), traceback

Ese sabor de la instrucción raise toma el tipo de excepción como la primera expresión, los argumentos del constructor de la clase de excepción en una tupla como la segunda expresión y la traceback como la tercera expresión. Si está ejecutando antes que Python 2.2, consulte las advertencias en sys.exc_info() .

Cualquier tipo de excepción

Aquí hay otro ejemplo que es más general si no sabes qué tipo de excepciones podría tener tu código para atrapar. La desventaja es que pierde el tipo de excepción y solo aumenta un RuntimeError. Tienes que importar el módulo de traceback .

except Exception: extype, ex, tb = sys.exc_info() formatted = traceback.format_exception_only(extype, ex)[-1] message = "Importing row %d, %s" % (rownum, formatted) raise RuntimeError, message, tb

Modificar el mensaje

Aquí hay otra opción si el tipo de excepción le permitirá agregarle contexto. Puede modificar el mensaje de la excepción y luego volver a publicarlo.

import subprocess try: final_args = [''lsx'', ''/home''] s = subprocess.check_output(final_args) except OSError as ex: ex.strerror += '' for command {}''.format(final_args) raise

Eso genera la siguiente traza de pila:

Traceback (most recent call last): File "/mnt/data/don/workspace/scratch/scratch.py", line 5, in <module> s = subprocess.check_output(final_args) File "/usr/lib/python2.7/subprocess.py", line 566, in check_output process = Popen(stdout=PIPE, *popenargs, **kwargs) File "/usr/lib/python2.7/subprocess.py", line 710, in __init__ errread, errwrite) File "/usr/lib/python2.7/subprocess.py", line 1327, in _execute_child raise child_exception OSError: [Errno 2] No such file or directory for command [''lsx'', ''/home'']

Puede ver que muestra la línea donde se llamó a check_output() , pero el mensaje de excepción ahora incluye la línea de comando.