Sé que si quiero volver a generar una excepción, lo uso raise
sin argumentos en el except
bloque 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 SomeError
sin romper el rastro de la pila? raise
solo 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_B
otra función que regreseTrue
con éxito yFalse
con excepción? Entonces elexcept
bloque exterior podría serif not try_plan_B(): raise
arg
e intentaría llamar, loarg.plan_B()
que podría generar unAttributeError
arg
plan_B
generar excepcionesRespuestas:
A partir de Python 3, el rastreo se almacena en la excepción, por lo que un simple
raise e
hará lo correcto (en su mayoría):El rastreo producido incluirá un aviso adicional que
SomeError
ocurrió durante la manipulaciónAlsoFailsError
(porraise e
estar dentroexcept AlsoFailsError
). Esto es engañoso porque lo que realmente sucedió es al revés: lo encontramosAlsoFailsError
y lo manejamos mientras intentamos recuperarnosSomeError
. Para obtener un rastreo que no incluyeAlsoFailsError
, reemplaceraise e
conraise 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, tb
perderá el valor de la excepción y obligaráraise
a 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_from
si desea incluir información queplan_B()
también falló.six.raise_from
cuando creas una nueva excepción que está vinculada a una anterior, no vuelves a subir , por lo que el rastreo es diferente.reraise
da la impresión de que solosomething()
arrojóSomeError
, siraise_from
también sabes que esto causóplan_B()
que se ejecutara, pero arrojando elAlsoFailsError
. Entonces depende del caso de uso. Creoraise_from
que 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 = e
por, digamos,raise AttributeError
obtienes primero elSyntaxError
seguimiento de la pila, seguido de ay el seguimiento deDuring handling of the above exception, another exception occurred:
laAttributeError
pila. Es bueno saberlo, aunque desafortunadamente no se puede confiar en que se instale 3.5+. PD: ff verstehen nicht-Deutsche vermutlich nicht;)