Sé que si quiero volver a generar una excepción, lo uso raisesin argumentos en el exceptbloque respectivo . Pero dada una expresión anidada como
try:
something()
except SomeError as e:
try:
plan_B()
except AlsoFailsError:
raise e # I'd like to raise the SomeError as if plan_B()
# didn't raise the AlsoFailsError
¿Cómo puedo volver a subir el SomeErrorsin romper el rastro de la pila? raisesolo en este caso volvería a plantear el más reciente AlsoFailsError. ¿O cómo podría refactorizar mi código para evitar este problema?

plan_Botra función que regreseTruecon éxito yFalsecon excepción? Entonces elexceptbloque exterior podría serif not try_plan_B(): raisearge intentaría llamar, loarg.plan_B()que podría generar unAttributeErrorargplan_Bgenerar excepcionesRespuestas:
A partir de Python 3, el rastreo se almacena en la excepción, por lo que un simple
raise ehará lo correcto (en su mayoría):El rastreo producido incluirá un aviso adicional que
SomeErrorocurrió durante la manipulaciónAlsoFailsError(porraise eestar dentroexcept AlsoFailsError). Esto es engañoso porque lo que realmente sucedió es al revés: lo encontramosAlsoFailsErrory lo manejamos mientras intentamos recuperarnosSomeError. Para obtener un rastreo que no incluyeAlsoFailsError, reemplaceraise econraise e from None.En Python 2, almacenaría el tipo de excepción, el valor y el rastreo en variables locales y usaría la forma de tres argumentos de
raise:fuente
raise self.exc_info[1], None, self.exc_info[2]despuésself.exc_info = sys.exc_info()- poner[1]a la primera posición por alguna razónraise t, None, tbperderá el valor de la excepción y obligaráraisea volver a crear una instancia del tipo, dándole un valor de excepción menos específico (o simplemente incorrecto). Por ejemplo, si la excepción generada esKeyError("some-key"), simplemente volverá a generarKeyError()y omitirá la clave exacta que falta en el rastreo.raise v.with_traceback(tb). (Su comentario incluso dice lo mismo, excepto que propone volver a crear una instancia del valor.)sys.exc_info()en una variable local tenía sentido antes de Python 2.0 (lanzado hace 13 años), pero hoy roza el ridículo. El Python moderno sería casi inútil sin el recopilador de ciclos, ya que cada biblioteca de Python no trivial crea ciclos sin pausa y depende de su correcta limpieza.Incluso si la solución aceptada es correcta, es bueno señalar la biblioteca Six que tiene una solución Python 2 + 3, usando
six.reraise.Entonces, puedes escribir:
fuente
six.raise_fromsi desea incluir información queplan_B()también falló.six.raise_fromcuando creas una nueva excepción que está vinculada a una anterior, no vuelves a subir , por lo que el rastreo es diferente.reraiseda la impresión de que solosomething()arrojóSomeError, siraise_fromtambién sabes que esto causóplan_B()que se ejecutara, pero arrojando elAlsoFailsError. Entonces depende del caso de uso. Creoraise_fromque facilitará la depuraciónSegún la sugerencia de Drew McGowen , pero teniendo en cuenta un caso general (donde hay un valor de retorno
s), aquí hay una alternativa a la respuesta del usuario4815162342 :fuente
raise from, por lo que el seguimiento de la pila también me permitiría ver que el plan B falló. Que se puede emular en Python 2 por cierto.Python 3.5+ adjunta la información de rastreo al error de todos modos, por lo que ya no es necesario guardarlo por separado.
fuente
except. Pero tienes razón, cuando reemplazoerr = epor, digamos,raise AttributeErrorobtienes primero elSyntaxErrorseguimiento de la pila, seguido de ay el seguimiento deDuring handling of the above exception, another exception occurred:laAttributeErrorpila. Es bueno saberlo, aunque desafortunadamente no se puede confiar en que se instale 3.5+. PD: ff verstehen nicht-Deutsche vermutlich nicht;)