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_tracebackmétodo de excepciones:
raise Exception("foo occurred").with_traceback(tracebackobj)
Estas características se describen mínimamente como parte de la raisedocumentació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 exceptclá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 exceptcláusula, puede usar la salida de sys.exc_info()junto con el tracebackmó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_inforegresa (None, None, None)cuando no se maneja ninguna excepción. Otros sysatributos relacionados tampoco ayudan. sys.exc_tracebackestá en desuso y no está definido cuando no se maneja ninguna excepción; sys.last_tracebackparece 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 inspectuna 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_infojunto con el enfoque de devolución de llamada que sugiero en su otra pregunta.Desde Python 3.0 [PEP 3109], la clase incorporada
Exceptiontiene 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
raisedice 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?__fooes 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_tbrespuestas 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
stufflos globales de parche para que cuando crea que está detectando, en realidad esté detectandoExceptionun tipo especializado y la excepción se propague a ti como llamador:fuente
e.__traceback__.exceptbloque, según PEP 3110 .