Cadena de excepciones de Python

¿Hay una forma estándar de usar cadenas de excepción en Python? ¿Te gusta la excepción de Java ‘causada por’?

Aquí hay algunos antecedentes.

Tengo un módulo con una clase de excepción principal DSError :

  class DSError(Exception): pass 

En algún lugar dentro de este módulo habrá:

 try: v = my_dict[k] something(v) except KeyError as e: raise DSError("no key %s found for %s" % (k, self)) except ValueError as e: raise DSError("Bad Value %s found for %s" % (v, self)) except DSError as e: raise DSError("%s raised in %s" % (e, self)) 

Básicamente, este fragmento debe lanzar solo DSError y decirme qué sucedió y por qué. La cosa es que el bloque try podría lanzar muchas otras excepciones, así que preferiría si puedo hacer algo como:

 try: v = my_dict[k] something(v) except Exception as e: raise DSError(self, v, e) # Exception chained... 

¿Es esta forma pythonica estándar? No vi cadenas de excepción en otros módulos, ¿cómo se hace en Python?

El encadenamiento de excepciones solo está disponible en Python 3, donde puede escribir:

 try: v = {}['a'] except KeyError as e: raise ValueError('failed') from e 

lo que produce una salida como

 Traceback (most recent call last): File "t.py", line 2, in  v = {}['a'] KeyError: 'a' The above exception was the direct cause of the following exception: Traceback (most recent call last): File "t.py", line 4, in  raise ValueError('failed') from e ValueError: failed 

En la mayoría de los casos, ni siquiera necesita el from ; Python 3 mostrará por defecto todas las excepciones que ocurrieron durante el manejo de excepciones, como esto:

 Traceback (most recent call last): File "t.py", line 2, in  v = {}['a'] KeyError: 'a' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "t.py", line 4, in  raise ValueError('failed') ValueError: failed 

Lo que puede hacer en Python 2 es agregar atributos personalizados a su clase de excepción, como:

 class MyError(Exception): def __init__(self, message, cause): super(MyError, self).__init__(message + u', caused by ' + repr(cause)) self.cause = cause try: v = {}['a'] except KeyError as e: raise MyError('failed', e) 

¿Es esto lo que estás pidiendo?

 class MyError(Exception): def __init__(self, other): super(MyError, self).__init__(other.message) >>> try: ... 1/0 ... except Exception, e: ... raise MyError(e) Traceback (most recent call last): File "", line 4, in  raise MyError(e) MyError: division by zero 

Si desea almacenar el objeto de excepción original, puede hacerlo en __init__ su propia clase de __init__ . Es posible que desee almacenar el rastreo ya que el objeto de excepción en sí mismo no proporciona mucha información útil sobre dónde ocurrió la excepción:

 class MyError(Exception): def __init__(self, other): self.traceback = sys.exc_info() super(MyError, self).__init__(other.message) 

Después de esto, puede acceder al atributo de traceback de su excepción para obtener información sobre la excepción original. (Python 3 ya proporciona esto como el atributo __traceback__ de un objeto de excepción).