Cómo obtener un seguimiento completo de la pila de excepciones en Python


El siguiente fragmento:

import traceback

def a():
    b()

def b():
    try:
        c()
    except:
        traceback.print_exc()

def c():
    assert False

a()

Produce esta salida:

Traceback (most recent call last):
  File "test.py", line 8, in b
    c()
  File "test.py", line 13, in c
    assert False
AssertionError

¿Qué debo usar si quiero el seguimiento completo de la pila incluyendo la llamada a a?

Si importa tengo Python 2.6.6

Editar: Lo que me gustaría obtener es la misma información que obtendría si dejo la excepción de prueba y dejo que la excepción se propague al nivel superior. Este fragmento, por ejemplo:

def a():
    b()

def b():
    c()

def c():
    assert False

a()

Produce esta salida:

Traceback (most recent call last):
  File "test.py", line 10, in <module>
    a()
  File "test.py", line 2, in a
    b()
  File "test.py", line 5, in b
    c()
  File "test.py", line 8, in c
    assert False
AssertionError
Author: Gordon Wrigley, 2011-05-22

4 answers

No se si hay una mejor manera, pero esto es lo que hice:

import traceback
import sys

def format_exception(e):
    exception_list = traceback.format_stack()
    exception_list = exception_list[:-2]
    exception_list.extend(traceback.format_tb(sys.exc_info()[2]))
    exception_list.extend(traceback.format_exception_only(sys.exc_info()[0], sys.exc_info()[1]))

    exception_str = "Traceback (most recent call last):\n"
    exception_str += "".join(exception_list)
    # Removing the last \n
    exception_str = exception_str[:-1]

    return exception_str

def main1():
    main2()

def main2():
    try:
        main3()
    except Exception as e:
        print "Printing only the traceback above the current stack frame"
        print "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))
        print
        print "Printing the full traceback as if we had not caught it here..."
        print format_exception(e)

def main3():
    raise Exception()

if __name__ == '__main__':
    main1()

Y aquí está la salida que obtengo:

Printing only the traceback above the current stack frame
Traceback (most recent call last):
  File "exc.py", line 22, in main2
    main3()
  File "exc.py", line 31, in main3
    raise Exception()
Exception


Printing the full traceback as if we had not caught it here...
Traceback (most recent call last):
  File "exc.py", line 34, in <module>
    main1()
  File "exc.py", line 18, in main1
    main2()
  File "exc.py", line 22, in main2
    main3()
  File "exc.py", line 31, in main3
    raise Exception()
Exception
 19
Author: John Wernicke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-21 23:21:35

Aquí hay una función basada en esta respuesta. También funcionará cuando no haya ninguna excepción:

def full_stack():
    import traceback, sys
    exc = sys.exc_info()[0]
    stack = traceback.extract_stack()[:-1]  # last one would be full_stack()
    if not exc is None:  # i.e. if an exception is present
        del stack[-1]       # remove call of full_stack, the printed exception
                            # will contain the caught exception caller instead
    trc = 'Traceback (most recent call last):\n'
    stackstr = trc + ''.join(traceback.format_list(stack))
    if not exc is None:
         stackstr += '  ' + traceback.format_exc().lstrip(trc)
    return stackstr

print full_stack() imprimirá el seguimiento completo de la pila hasta la parte superior, incluyendo, por ejemplo, las llamadas de IPython interactiveshell.py, ya que no hay (que yo sepa) forma de saber quién capturaría las excepciones. Probablemente no valga la pena averiguarlo de todos modos...

Si print full_stack() se llama desde dentro de un bloque except, full_stack incluirá el seguimiento de la pila hasta el raise. En el intérprete estándar de Python, esto será idéntico al mensaje que recibas cuando no captes la excepción (por lo que del stack[-1] está ahí, no te importa el bloque except sino el bloque try).

 21
Author: Tobias Kienzler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:08:59

Use

 traceback.print_stack()

Http://docs.python.org/library/traceback.html#traceback.print_stack

suxmac2 $ python out.py 
  File "out.py", line 16, in <module>
    a()
  File "out.py", line 5, in a
    b()
  File "out.py", line 11, in b
    traceback.print_stack()
 7
Author: Andreas Jung,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-22 09:16:49


Aquí hay una variante un poco mejor de respuesta de Tobias Kienzler . Funciona igual, pero se puede llamar no directamente en el bloque except, sino en algún lugar más profundo. En otras palabras, esta variante imprimirá las mismas pilas, cuando se llame como

try:
   ...
except Exception:
    print full_stack()

O

def print_full_stack():
    print full_stack()

try:
   ...
except Exception:
    print_full_stack()

Aquí está el código:

def full_stack():
    import traceback, sys
    exc = sys.exc_info()[0]
    if exc is not None:
        f = sys.exc_info()[-1].tb_frame.f_back
        stack = traceback.extract_stack(f)
    else:
        stack = traceback.extract_stack()[:-1]  # last one would be full_stack()
    trc = 'Traceback (most recent call last):\n'
    stackstr = trc + ''.join(traceback.format_list(stack))
    if exc is not None:
        stackstr += '  ' + traceback.format_exc().lstrip(trc)
    return stackstr
 1
Author: Skipor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-12 09:14:35