Estoy escribiendo un módulo y quiero tener una jerarquía de excepción unificada para las excepciones que puede generar (por ejemplo, heredar de una FooError
clase abstracta para todas las foo
excepciones específicas del módulo). Esto permite a los usuarios del módulo capturar esas excepciones particulares y manejarlas de manera distinta, si es necesario. Pero muchas de las excepciones generadas desde el módulo se generan debido a alguna otra excepción; por ejemplo, fallar en alguna tarea debido a un error OSEr en un archivo.
Lo que necesito es "envolver" la excepción detectada de manera que tenga un tipo y mensaje diferente , de modo que la información esté disponible más arriba en la jerarquía de propagación por lo que detecte la excepción. Pero no quiero perder el tipo existente, el mensaje y el seguimiento de la pila; Esa es toda la información útil para alguien que intenta depurar el problema. Un controlador de excepciones de nivel superior no es bueno, ya que estoy tratando de decorar la excepción antes de que suba por la pila de propagación, y el controlador de nivel superior es demasiado tarde.
Esto se resuelve en parte derivando los foo
tipos de excepción específicos de mi módulo del tipo existente (por ejemplo class FooPermissionError(OSError, FooError)
), pero eso no hace que sea más fácil envolver la instancia de excepción existente en un nuevo tipo, ni modificar el mensaje.
El PEP 3134 de Python "Encadenamiento de excepciones y seguimientos integrados" analiza un cambio aceptado en Python 3.0 para "encadenar" objetos de excepción, para indicar que se produjo una nueva excepción durante el manejo de una excepción existente.
Lo que intento hacer está relacionado: también necesito que funcione en versiones anteriores de Python, y no lo necesito para encadenar, sino solo para el polimorfismo. ¿Cuál es la forma correcta de hacer esto?
fuente
except Exception as e
->raise type(e), type(e)(e.message + custom_message), sys.exc_info()[2]
-> esta solución es de otra pregunta SO . Esto no es bonito sino funcional.Respuestas:
Python 3 introdujo el encadenamiento de excepciones (como se describe en PEP 3134 ). Esto permite, al generar una excepción, citar una excepción existente como la "causa":
La excepción capturada se convierte en parte de (es la "causa de") la nueva excepción, y está disponible para cualquier código que capture la nueva excepción.
Al usar esta función,
__cause__
se establece el atributo. El controlador de excepciones incorporado también sabe cómo informar la "causa" y el "contexto" de la excepción junto con el rastreo.En Python 2 , parece que este caso de uso no tiene una buena respuesta (como lo describen Ian Bicking y Ned Batchelder ). Gorrón.
fuente
try: return 2 / 0 except ZeroDivisionError as e: raise ValueError(e)
Puede usar sys.exc_info () para obtener el rastreo y generar su nueva excepción con dicho rastreo (como menciona el PEP). Si desea conservar el tipo y el mensaje antiguos, puede hacerlo con la excepción, pero eso solo es útil si lo que detecta su excepción lo busca.
Por ejemplo
Por supuesto, esto realmente no es tan útil. Si lo fuera, no necesitaríamos esa PEP. No recomendaría hacerlo.
fuente
sys.exc_info()
@Devin. Dice: "Asignar el valor de retorno de rastreo a una variable local en una función que maneja una excepción causará una referencia circular". Sin embargo, una nota siguiente dice que desde Python 2.2, el ciclo se puede limpiar, pero es más eficiente simplemente evitarlo.Puede crear su propio tipo de excepción que se extiende a cualquier excepción que haya detectado.
Pero la mayoría de las veces, creo que sería más sencillo detectar la excepción, manejarla y
raise
la excepción original (y preservar el rastreo) oraise NewException()
. Si estuviera llamando a su código, y recibí una de sus excepciones personalizadas, esperaría que su código ya haya manejado cualquier excepción que haya tenido que atrapar. Por lo tanto, no necesito acceder a mí mismo.Editar: Encontré este análisis de formas de lanzar su propia excepción y mantener la excepción original. No hay soluciones bonitas.
fuente
También descubrí que muchas veces necesito un "ajuste" a los errores generados.
Esto incluía tanto el alcance de una función como, a veces, solo algunas líneas dentro de una función.
Creado un envoltorio que se utilizará una
decorator
ycontext manager
:Implementación
Ejemplos de uso
decorador
cuando llame al
do
método, no se preocupeIndexError
, soloMyError
gestor de contexto
adentro
do2
, en elcontext manager
, siIndexError
se eleva, se envolverá y se elevaráMyError
fuente
La solución más sencilla para sus necesidades debería ser esta:
De esta forma, luego puede imprimir su mensaje y el error específico arrojado por la función de carga
fuente