traceback — Excepciones y rastreos de pila

Propósito:Extraer, formatear e imprimir excepciones y rastreos de pila.

El módulo traceback funciona con la pila de llamadas para generar mensajes de error. Un traceback es un seguimiento de la pila desde el punto de un controlador de excepciones en la cadena de llamadas hasta el punto donde se generó la excepción. También se puede acceder a los rastreos desde la pila de llamadas actual desde el punto de una llamada (y sin el contexto de un error), lo cual es útil para descubrir las rutas que se siguen en una función.

La interfaz de programación de alto nivel en traceback usa instancias StackSummary y FrameSummary para mantener la representación de la pila. Estas clases pueden construirse a partir de un rastreo o la pila de ejecución actual, y luego procesarse de la misma manera.

Las funciones de bajo nivel en traceback se dividen en varias categorías comunes. Hay funciones para extraer rastreos sin procesar del entorno de tiempo de ejecución actual (ya sea un controlador de excepción para un rastreo o la pila normal). El rastreo de pila extraída es una secuencia de tuplas que contiene el nombre de archivo, el número de línea, el nombre de la función y el texto de la línea de origen.

Una vez extraído, el rastreo de la pila se puede formatear usando funciones como format_exception(), format_stack(), etc. Las funciones de formato devuelven una lista de cadenas con mensajes formateados para ser impresos. También hay funciones abreviadas para imprimir los valores formateados.

Aunque las funciones en traceback imitan el comportamiento del intérprete interactivo de forma predeterminada, también son útiles para manejar excepciones en situaciones en las que no es deseable descargar el rastreo completo de la pila en la consola. Por ejemplo, una aplicación web puede necesitar formatear el rastreo para que se vea bien en HTML y un IDE puede convertir los elementos del rastreo de la pila en una lista en la que se puede hacer clic para que el usuario explore la fuente.

Funciones de apoyo

Los ejemplos en esta sección usan el módulo traceback_example.py.

traceback_example.py
import traceback
import sys


def produce_exception(recursion_level=2):
    sys.stdout.flush()
    if recursion_level:
        produce_exception(recursion_level - 1)
    else:
        raise RuntimeError()


def call_function(f, recursion_level=2):
    if recursion_level:
        return call_function(f, recursion_level - 1)
    else:
        return f()

Examinar la pila

Para examinar la pila actual, construye un StackSummary a partir de walk_stack().

traceback_stacksummary.py
import traceback
import sys

from traceback_example import call_function


def f():
    summary = traceback.StackSummary.extract(
        traceback.walk_stack(None)
    )
    print(''.join(summary.format()))


print('Calling f() directly:')
f()

print()
print('Calling f() from 3 levels deep:')
call_function(f)

El método format() produce una secuencia de cadenas formateadas listas para imprimir.

$ python3 traceback_stacksummary.py

Calling f() directly:
  File "traceback_stacksummary.py", line 18, in f
    traceback.walk_stack(None)
  File "traceback_stacksummary.py", line 24, in <module>
    f()


Calling f() from 3 levels deep:
  File "traceback_stacksummary.py", line 18, in f
    traceback.walk_stack(None)
  File ".../traceback_example.py", line 26, in call_function
    return f()
  File ".../traceback_example.py", line 24, in call_function
    return call_function(f, recursion_level - 1)
  File ".../traceback_example.py", line 24, in call_function
    return call_function(f, recursion_level - 1)
  File "traceback_stacksummary.py", line 28, in <module>
    call_function(f)

El StackSummary es un contenedor iterable que contiene instancias de FrameSummary.

traceback_framesummary.py
import traceback
import sys

from traceback_example import call_function

template = (
    '{fs.filename:<26}:{fs.lineno}:{fs.name}:\n'
    '    {fs.line}'
)


def f():
    summary = traceback.StackSummary.extract(
        traceback.walk_stack(None)
    )
    for fs in summary:
        print(template.format(fs=fs))


print('Calling f() directly:')
f()

print()
print('Calling f() from 3 levels deep:')
call_function(f)

Cada FrameSummary describe un marco de la pila, que incluye información sobre dónde está el contexto de ejecución en los archivos fuente del programa.

$ python3 traceback_framesummary.py

Calling f() directly:
traceback_framesummary.py :23:f:
    traceback.walk_stack(None)
traceback_framesummary.py :30:<module>:
    f()

Calling f() from 3 levels deep:
traceback_framesummary.py :23:f:
    traceback.walk_stack(None)
.../traceback_example.py:26:call_function:
    return f()
.../traceback_example.py:24:call_function:
    return call_function(f, recursion_level - 1)
.../traceback_example.py:24:call_function:
    return call_function(f, recursion_level - 1)
traceback_framesummary.py :34:<module>:
    call_function(f)

TracebackException

La clase TracebackException es una interfaz de alto nivel para construir un StackSummary mientras se procesa un rastreo.

traceback_tracebackexception.py
import traceback
import sys

from traceback_example import produce_exception

print('with no exception:')
exc_type, exc_value, exc_tb = sys.exc_info()
tbe = traceback.TracebackException(exc_type, exc_value, exc_tb)
print(''.join(tbe.format()))

print('\nwith exception:')
try:
    produce_exception()
except Exception as err:
    exc_type, exc_value, exc_tb = sys.exc_info()
    tbe = traceback.TracebackException(
        exc_type, exc_value, exc_tb,
    )
    print(''.join(tbe.format()))

    print('\nexception only:')
    print(''.join(tbe.format_exception_only()))

El método format() produce una versión formateada del rastreo completo, mientras que format_exception_only() muestra solo el mensaje de excepción.

$ python3 traceback_tracebackexception.py

with no exception:
None: None


with exception:
Traceback (most recent call last):
  File "traceback_tracebackexception.py", line 22, in <module>
    produce_exception()
  File ".../traceback_example.py", line 17, in produce_exception
    produce_exception(recursion_level - 1)
  File ".../traceback_example.py", line 17, in produce_exception
    produce_exception(recursion_level - 1)
  File ".../traceback_example.py", line 19, in produce_exception
    raise RuntimeError()
RuntimeError


exception only:
RuntimeError

Interfaz de programación de excepción de bajo nivel

Otra forma de manejar los informes de excepción es con print_exc(). Utiliza sys.exc_info() para obtener la información de excepción para el hilo actual, formatea los resultados e imprime el texto en un identificador de archivo (sys.stderr, por defecto).

traceback_print_exc.py
import traceback
import sys

from traceback_example import produce_exception

print('print_exc() with no exception:')
traceback.print_exc(file=sys.stdout)
print()

try:
    produce_exception()
except Exception as err:
    print('print_exc():')
    traceback.print_exc(file=sys.stdout)
    print()
    print('print_exc(1):')
    traceback.print_exc(limit=1, file=sys.stdout)

En este ejemplo, el identificador de archivo para sys.stdout se sustituye para que los mensajes informativos y de rastreo se mezclen correctamente:

$ python3 traceback_print_exc.py

print_exc() with no exception:
NoneType: None

print_exc():
Traceback (most recent call last):
  File "traceback_print_exc.py", line 20, in <module>
    produce_exception()
  File ".../traceback_example.py", line 17, in produce_exception
    produce_exception(recursion_level - 1)
  File ".../traceback_example.py", line 17, in produce_exception
    produce_exception(recursion_level - 1)
  File ".../traceback_example.py", line 19, in produce_exception
    raise RuntimeError()
RuntimeError

print_exc(1):
Traceback (most recent call last):
  File "traceback_print_exc.py", line 20, in <module>
    produce_exception()
RuntimeError

print_exc() es solo un atajo para print_exception(), que requiere argumentos explícitos.

traceback_print_exception.py
import traceback
import sys

from traceback_example import produce_exception

try:
    produce_exception()
except Exception as err:
    print('print_exception():')
    exc_type, exc_value, exc_tb = sys.exc_info()
    traceback.print_exception(exc_type, exc_value, exc_tb)

Los argumentos de print_exception() son producidos por sys.exc_info().

$ python3 traceback_print_exception.py

Traceback (most recent call last):
  File "traceback_print_exception.py", line 16, in <module>
    produce_exception()
  File ".../traceback_example.py", line 17, in produce_exception
    produce_exception(recursion_level - 1)
  File ".../traceback_example.py", line 17, in produce_exception
    produce_exception(recursion_level - 1)
  File ".../traceback_example.py", line 19, in produce_exception
    raise RuntimeError()
RuntimeError
print_exception():

print_exception() usa format_exception() para preparar el texto.

traceback_format_exception.py
import traceback
import sys
from pprint import pprint

from traceback_example import produce_exception

try:
    produce_exception()
except Exception as err:
    print('format_exception():')
    exc_type, exc_value, exc_tb = sys.exc_info()
    pprint(
        traceback.format_exception(exc_type, exc_value, exc_tb),
        width=65,
    )

Los mismos tres argumentos, tipo de excepción, valor de excepción y rastreo, se usan con format_exception().

$ python3 traceback_format_exception.py

format_exception():
['Traceback (most recent call last):\n',
 '  File "traceback_format_exception.py", line 17, in
<module>\n'
 '    produce_exception()\n',
 '  File '
 '".../traceback_example.py", '
 'line 17, in produce_exception\n'
 '    produce_exception(recursion_level - 1)\n',
 '  File '
 '".../traceback_example.py", '
 'line 17, in produce_exception\n'
 '    produce_exception(recursion_level - 1)\n',
 '  File '
 '".../traceback_example.py", '
 'line 19, in produce_exception\n'
 '    raise RuntimeError()\n',
 'RuntimeError\n']

Para procesar el rastreo de alguna otra manera, como formatearlo de manera diferente, usa extract_tb() para obtener los datos en una forma utilizable.

traceback_extract_tb.py
import traceback
import sys
import os
from traceback_example import produce_exception

template = '{filename:<23}:{linenum}:{funcname}:\n    {source}'

try:
    produce_exception()
except Exception as err:
    print('format_exception():')
    exc_type, exc_value, exc_tb = sys.exc_info()
    for tb_info in traceback.extract_tb(exc_tb):
        filename, linenum, funcname, source = tb_info
        if funcname != '<module>':
            funcname = funcname + '()'
        print(template.format(
            filename=os.path.basename(filename),
            linenum=linenum,
            source=source,
            funcname=funcname)
        )

El valor de retorno es una lista de entradas de cada nivel de la pila representada por el rastreo. Cada entrada es una tupla con cuatro partes: el nombre del archivo fuente, el número de línea en ese archivo, el nombre de la función y el texto fuente de esa línea con espacios en blanco despojados (si la fuente está disponible).

$ python3 traceback_extract_tb.py

format_exception():
traceback_extract_tb.py:18:<module>:
    produce_exception()
traceback_example.py   :17:produce_exception():
    produce_exception(recursion_level - 1)
traceback_example.py   :17:produce_exception():
    produce_exception(recursion_level - 1)
traceback_example.py   :19:produce_exception():
    raise RuntimeError()

Interfaz de programación de pila de bajo nivel

Hay un conjunto similar de funciones para realizar las mismas operaciones con la pila de llamadas actual en lugar de un rastreo. print_stack() imprime la pila actual, sin generar una excepción.

traceback_print_stack.py
import traceback
import sys

from traceback_example import call_function


def f():
    traceback.print_stack(file=sys.stdout)


print('Calling f() directly:')
f()

print()
print('Calling f() from 3 levels deep:')
call_function(f)

El resultado parece un rastreo sin un mensaje de error.

$ python3 traceback_print_stack.py

Calling f() directly:
  File "traceback_print_stack.py", line 21, in <module>
    f()
  File "traceback_print_stack.py", line 17, in f
    traceback.print_stack(file=sys.stdout)

Calling f() from 3 levels deep:
  File "traceback_print_stack.py", line 25, in <module>
    call_function(f)
  File ".../traceback_example.py", line 24, in call_function
    return call_function(f, recursion_level - 1)
  File ".../traceback_example.py", line 24, in call_function
    return call_function(f, recursion_level - 1)
  File ".../traceback_example.py", line 26, in call_function
    return f()
  File "traceback_print_stack.py", line 17, in f
    traceback.print_stack(file=sys.stdout)

format_stack() prepara el seguimiento de la pila de la misma manera que format_exception() prepara la excepción.

traceback_format_stack.py
import traceback
import sys
from pprint import pprint

from traceback_example import call_function


def f():
    return traceback.format_stack()


formatted_stack = call_function(f)
pprint(formatted_stack)

Devuelve una lista de cadenas, cada una de las cuales constituye una línea de la salida.

$ python3 traceback_format_stack.py

['  File "traceback_format_stack.py", line 21, in <module>\n'
 '    formatted_stack = call_function(f)\n',
 '  File '
 '".../traceback_example.py", '
 'line 24, in call_function\n'
 '    return call_function(f, recursion_level - 1)\n',
 '  File '
 '".../traceback_example.py", '
 'line 24, in call_function\n'
 '    return call_function(f, recursion_level - 1)\n',
 '  File '
 '".../traceback_example.py", '
 'line 26, in call_function\n'
 '    return f()\n',
 '  File "traceback_format_stack.py", line 18, in f\n'
 '    return traceback.format_stack()\n']

La función extract_stack() funciona como extract_tb().

traceback_extract_stack.py
import traceback
import sys
import os

from traceback_example import call_function

template = '{filename:<26}:{linenum}:{funcname}:\n    {source}'


def f():
    return traceback.extract_stack()


stack = call_function(f)
for filename, linenum, funcname, source in stack:
    if funcname != '<module>':
        funcname = funcname + '()'
    print(template.format(
        filename=os.path.basename(filename),
        linenum=linenum,
        source=source,
        funcname=funcname)
    )

También acepta argumentos, que no se muestran aquí, para comenzar desde un lugar alternativo en el marco de la pila o para limitar la profundidad del recorrido.

$ python3 traceback_extract_stack.py

traceback_extract_stack.py:23:<module>:
    stack = call_function(f)
traceback_example.py      :24:call_function():
    return call_function(f, recursion_level - 1)
traceback_example.py      :24:call_function():
    return call_function(f, recursion_level - 1)
traceback_example.py      :26:call_function():
    return f()
traceback_extract_stack.py:20:f():
    return traceback.extract_stack()

Ver también