La respuesta a esta pregunta depende de la versión de Python que esté usando.
En Python 3
Es simple: las excepciones vienen equipadas con un __traceback__
atributo que contiene el rastreo. Este atributo también se puede escribir y se puede configurar convenientemente utilizando el with_traceback
método de excepciones:
raise Exception("foo occurred").with_traceback(tracebackobj)
Estas características se describen mínimamente como parte de la raise
documentación.
Todo el crédito por esta parte de la respuesta debe ir a Vyctor, quien primero publicó esta información . Lo incluyo aquí solo porque esta respuesta está atascada en la parte superior y Python 3 se está volviendo más común.
En Python 2
Es fastidiosamente complejo. El problema con los tracebacks es que tienen referencias a marcos de pila, y los marcos de pila tienen referencias a los tracebacks que tienen referencias a marcos de pila que tienen referencias a ... ya se hace una idea. Esto causa problemas al recolector de basura. (Gracias a ecatmur por señalar esto primero).
La buena forma de resolver esto sería romper quirúrgicamente el ciclo después de salir de la except
cláusula, que es lo que hace Python 3. La solución Python 2 es mucho más fea: se le proporciona una función ad-hoc sys.exc_info()
, que solo funciona dentro de la except
cláusula . Devuelve una tupla que contiene la excepción, el tipo de excepción y el rastreo de cualquier excepción que se esté manejando actualmente.
Entonces, si está dentro de la except
cláusula, puede usar la salida de sys.exc_info()
junto con el traceback
módulo para hacer varias cosas útiles:
>>> import sys, traceback
>>> def raise_exception():
... try:
... raise Exception
... except Exception:
... ex_type, ex, tb = sys.exc_info()
... traceback.print_tb(tb)
... finally:
... del tb
...
>>> raise_exception()
File "<stdin>", line 3, in raise_exception
Pero como indica su edición, está intentando obtener el rastreo que se habría impreso si su excepción no se hubiera manejado, después de que ya se haya manejado. Esa es una pregunta mucho más difícil. Desafortunadamente, sys.exc_info
regresa (None, None, None)
cuando no se maneja ninguna excepción. Otros sys
atributos relacionados tampoco ayudan. sys.exc_traceback
está en desuso y no está definido cuando no se maneja ninguna excepción; sys.last_traceback
parece perfecto, pero parece que solo se define durante las sesiones interactivas.
Si puede controlar cómo se genera la excepción, es posible que pueda usar inspect
una excepción personalizada para almacenar parte de la información. Pero no estoy del todo seguro de cómo funcionaría.
A decir verdad, detectar y devolver una excepción es algo inusual. Esto podría ser una señal de que necesita refactorizar de todos modos.
sys.exc_info
junto con el enfoque de devolución de llamada que sugiero en su otra pregunta.Desde Python 3.0 [PEP 3109], la clase incorporada
Exception
tiene un__traceback__
atributo que contiene untraceback object
(con Python 3.2.3):El problema es que después de buscar en Google
__traceback__
por un tiempo, encontré solo algunos artículos, pero ninguno de ellos describe si debería (no) usarlos o por qué__traceback__
.Sin embargo, la documentación de Python 3
raise
dice que:Así que supongo que está destinado a ser utilizado.
fuente
__
nombre indica que es un detalle de implementación, no una propiedad pública?__foo
es un método privado pero__foo__
(con guiones bajos al final también) es un método "mágico" (y no privado).__traceback__
atributo es 100% seguro de usar como desee, sin implicaciones de GC. Es difícil saberlo por la documentación, pero ecatmur encontró pruebas contundentes .Una forma de obtener el rastreo como una cadena de un objeto de excepción en Python 3:
traceback.format_tb(...)
devuelve una lista de cadenas.''.join(...)
los une. Para obtener más referencias, visite: https://docs.python.org/3/library/traceback.html#traceback.format_tbfuente
Por otro lado, si realmente desea obtener el rastreo completo como lo vería impreso en su terminal, desea esto:
Si usa las
format_tb
respuestas anteriores sugieren que obtendrá menos información:fuente
etype=type(exc)
se puede omitir ahora por cierto: "Cambiado en la versión 3.5: El argumento etype se ignora y se infiere del tipo de valor". docs.python.org/3.7/library/… Probado en Python 3.7.3.Hay una muy buena razón por la que el rastreo no se almacena en la excepción; Debido a que el rastreo contiene referencias a los locales de su pila, esto daría como resultado una referencia circular y una fuga de memoria (temporal) hasta que el GC circular entre en funcionamiento (es por eso que nunca debe almacenar el rastreo en una variable local ).
Lo único en lo que puedo pensar es que montes
stuff
los globales de parche para que cuando crea que está detectando, en realidad esté detectandoException
un tipo especializado y la excepción se propague a ti como llamador:fuente
e.__traceback__
.except
bloque, según PEP 3110 .