traceback - Eccezioni e Tracciature di Stack

Scopo: Estrae, formatta e stampa eccezioni e tracciature di stack

Il modulo traceback lavora con lo stack di chiamate per fornire messaggi di errore. Un traceback è una tracciatura dello stack dal punto di un gestore di eccezione scendendo nella catena di chiamate fino al punto dove l'eccezione è stata sollevata. I traceback possono essere indirizzati dallo stack di chiamata corrente risalendo fino al punto di una chiamata (senza il contesto di un errore), il che è utile per rilevare i percorsi seguiti da una funzione.

L'API di alto livello in traceback usa istanze di StackSummary e FrameSummary per mantenere una rappresentazione dello stack. Queste classi possono essere costruite da un traceback o dallo stack di esecuzione corrente, quindi elaborate allo stesso modo.

Le funzioni di basso livello in traceback sono suddivise in diverse comuni categorie. Ci sono funzioni per estrarre traceback grezzi dall'ambiente di esecuzione corrente (sia un gestore di eccezione per un traceback che lo stack normale). La tracciatura dello stack estratta è una sequenza di tuple che contengono il nome del file, il numero di riga, il nome della funzione e il testo della riga di sorgente.

Una volta estratta, la tracciatura dello stack può essere formattata usando funzioni come format_exception(), format_stack(), ecc.; le funzioni di formattazione ritornano una lista di stringhe con messaggi formattati per la stampa. Ci anche sono funzioni scorciatoia per stampare i valori formattati.

Sebbene le funzioni in traceback ricalchino il comportamento dell'interprete interattivo nella modalità predefinita, sono anche utili per gestire eccezioni in situazioni nelle quali scaricare l'intera tracciatura dello stack alla console non è consigliabile. Ad esempio, una applicazione web potrebbe aver bisogno di formattare il traceback in modo che sia leggibile in HTML, mentre un IDE potrebbe dover convertire gli elementi nella tracciatura dello stack in voci cliccabili che consentano all'utente di scorrere il sorgente.

Funzioni a Supporto

Gli esempi in questo articolo usano il modulo 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()

Esaminare lo Stack

Per esaminare lo stack corrente, si costruisca l'oggetto StackSummary da 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('Chiamata diretta di f():')
f()

print()
print('Chiamata di f() da 3 livelli più in basso:')
call_function(f)

IL metodo format() produce una sequenza di stringhe formattate pronte per la stampa.

$ python3 traceback_stacksummary.py

Chiamata diretta di f():
  File "traceback_stacksummary.py", line 10, in f
    summary = traceback.StackSummary.extract(
  File "traceback_stacksummary.py", line 17, in <module>
    f()


Chiamata di f() da 3 livelli più in basso:
  File "traceback_stacksummary.py", line 10, in f
    summary = traceback.StackSummary.extract(
  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", line 19, in call_function
    return f()
  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", line 17, in call_function
    return call_function(f, recursion_level - 1)
  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", line 17, in call_function
    return call_function(f, recursion_level - 1)
  File "traceback_stacksummary.py", line 21, in <module>
    call_function(f)

StackSummary è un contenitore iterabile che contiene istanze di 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('Chiamata diretta di f():')
f()

print()
print('Chiamata di f() da tre livelli più in basso:')
call_function(f)

Ogni FrameSummary descrive una struttura dello stack, incluse informazioni circa la locazione del contesto di esecuzione nei file sorgente del programma.

$ python3 traceback_framesummary.py

Chiamata diretta di f():
traceback_framesummary.py :15:f:
    summary = traceback.StackSummary.extract(
traceback_framesummary.py :23:<module>:
    f()

Chiamata di f() da tre livelli più in basso:
traceback_framesummary.py :15:f:
    summary = traceback.StackSummary.extract(
/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py:19:call_function:
    return f()
/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py:17:call_function:
    return call_function(f, recursion_level - 1)
/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py:17:call_function:
    return call_function(f, recursion_level - 1)
traceback_framesummary.py :27:<module>:
    call_function(f)

TracebackException

La classe TracebackException è una interfaccia di alto livello per costruire un oggetto StackSummary mentre si esamina un traceback.

# traceback_tracebackexception.py

import traceback
import sys

from traceback_example import produce_exception

print('senza eccezioni:')
exc_type, exc_value, exc_tb = sys.exc_info()
tbe = traceback.TracebackException(exc_type, exc_value, exc_tb)
print(''.join(tbe.format()))

print('\ncon eccezioni:')
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('\nsolo eccezioni:')
    print(''.join(tbe.format_exception_only()))

Il metodo format() produce una versione formattata dell'intero traceback, mentre format_exception_only() mostra solo il messaggio di eccezione.

$ python3 traceback_tracebackexception.py

senza eccezioni:
None: None


con eccezioni:
Traceback (most recent call last):
  File "traceback_tracebackexception.py", line 15, in <module>
    produce_exception()
  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", line 10, in produce_exception
    produce_exception(recursion_level - 1)
  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", line 10, in produce_exception
    produce_exception(recursion_level - 1)
  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", line 12, in produce_exception
    raise RuntimeError()
RuntimeError


solo eccezioni:
RuntimeError

API di Eccezione di Basso Livello

Un altro modo di gestire la segnalazione di eccezioni è tramite print_exec(). Utilizza sys.exc_info() per ottenere le informazioni dell'eccezione per il thread, formatta il risultato e stampa il testo verso sys.stderr nella modalità predefinita.

# traceback_print_exc.py

import traceback
import sys

from traceback_example import produce_exception

print('print_exc() senza eccezioni:')
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)

In questo esempio, il file handle per sys.stdout è modificato in modo che i messaggi informativi e di traceback siano integrati correttamente.

$ python3 traceback_print_exc.py

print_exc() senza eccezioni:
NoneType: None

print_exc():
Traceback (most recent call last):
  File "traceback_print_exc.py", line 13, in <module>
    produce_exception()
  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", line 10, in produce_exception
    produce_exception(recursion_level - 1)
  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", line 10, in produce_exception
    produce_exception(recursion_level - 1)
  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", line 12, in produce_exception
    raise RuntimeError()
RuntimeError

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

print_exc() è semplicemente una scorciatoia per print_exception(), che richiede argomenti espliciti.

# 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)

Gli argomenti per print_exception() sono forniti da sys.exc_info().

$ python3 traceback_print_exception.py

print_exception():
Traceback (most recent call last):
  File "traceback_print_exception.py", line 9, in <module>
    produce_exception()
  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", line 10, in produce_exception
    produce_exception(recursion_level - 1)
  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", line 10, in produce_exception
    produce_exception(recursion_level - 1)
  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", line 12, in produce_exception
    raise RuntimeError()
RuntimeError

print_exception() usa format_exception() per preparare il testo.

# 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,
    )

Gli stessi tre argomenti, tipo eccezione, valore eccezione e traceback, sono usati con format_exception().

$ python3 traceback_format_exception.py

format_exception():
['Traceback (most recent call last):\n',
 '  File "traceback_format_exception.py", line 10, in <module>\n'
 '    produce_exception()\n',
 '  File '
 '"/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", '
 'line 10, in produce_exception\n'
 '    produce_exception(recursion_level - 1)\n',
 '  File '
 '"/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", '
 'line 10, in produce_exception\n'
 '    produce_exception(recursion_level - 1)\n',
 '  File '
 '"/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", '
 'line 12, in produce_exception\n'
 '    raise RuntimeError()\n',
 'RuntimeError\n']

Per elaborare il traceback in qualche altro modo, tipo formattarlo diversamente, si usi extract_tb() per ottenere i dati una forma utilizzabile.

# 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)
        )

Il valore ritornato è una lista di voci da ciascun livello dello stack rappresentato da traceback. Ogni voce è una tupla con quattro parti: il nome del file sorgente, il numero di riga in quel file, il nome della funzione e il testo sorgente da quelle riga, dedotti spazi e altri caratteri di spaziatura orizzontale o verticale (se la sorgente è disponibile).

$ python3 traceback_extract_tb.py

format_exception():
traceback_extract_tb.py:11:<module>:
    produce_exception()
traceback_example.py   :10:produce_exception():
    produce_exception(recursion_level - 1)
traceback_example.py   :10:produce_exception():
    produce_exception(recursion_level - 1)
traceback_example.py   :12:produce_exception():
    raise RuntimeError()

API di Basso Livello dello Stack

Ci sono simili insiemi di funzioni per eseguire le stesse operazioni con lo stack di chiamata corrente invece che un traceback. print_stack() stampa lo stack corrente, senza generare una eccezione.

# traceback_print_stack.py

import traceback
import sys

from traceback_example import call_function


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


print('Chiamata diretta di f():')
f()

print()
print('Chiamata diretta di f() da tre livelli più in basso:')
call_function(f)

Il risultato assomiglia a un traceback senza messaggio di errore.

$ python3 traceback_print_stack.py

Chiamata diretta di f():
  File "traceback_print_stack.py", line 14, in <module>
    f()
  File "traceback_print_stack.py", line 10, in f
    traceback.print_stack(file=sys.stdout)

Chiamata diretta di f() da tre livelli più in basso:
  File "traceback_print_stack.py", line 18, in <module>
    call_function(f)
  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", line 17, in call_function
    return call_function(f, recursion_level - 1)
  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", line 17, in call_function
    return call_function(f, recursion_level - 1)
  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", line 19, in call_function
    return f()
  File "traceback_print_stack.py", line 10, in f
    traceback.print_stack(file=sys.stdout)

format_stack() prepara la tracciatura dello stack nello stesso modo nel quale format_exception() prepara il traceback.

# 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)

Restituisce una lista di stringhe, ognuna delle quali compone una riga del risultato.

$ python3 traceback_format_stack.py

['  File "traceback_format_stack.py", line 14, in <module>\n'
 '    formatted_stack = call_function(f)\n',
 '  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", '
 'line 17, in call_function\n'
 '    return call_function(f, recursion_level - 1)\n',
 '  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", '
 'line 17, in call_function\n'
 '    return call_function(f, recursion_level - 1)\n',
 '  File "/dati/dev/python/pymotw3restyling/dumpscripts/traceback_example.py", '
 'line 19, in call_function\n'
 '    return f()\n',
 '  File "traceback_format_stack.py", line 11, in f\n'
 '    return traceback.format_stack()\n']

La funzione extract_stack() funziona come 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)
    )

Accetta anche argomenti, non mostrati qui, per partire da un punto alternativo in una struttura dello stack oppure per limitare il livello di profondità di attraversamento.

$ python3 traceback_extract_stack.py

traceback_extract_stack.py:16:<module>:
    stack = call_function(f)
traceback_example.py      :17:call_function():
    return call_function(f, recursion_level - 1)
traceback_example.py      :17:call_function():
    return call_function(f, recursion_level - 1)
traceback_example.py      :19:call_function():
    return f()
traceback_extract_stack.py:13:f():
    return traceback.extract_stack()

Vedere anche:

traceback
La documentazione della libreria standard per questo modulo.
inspect
Il modulo inspect include altre funzioni per sondare stutture nello stack
cgitb
Un altro modulo per formattare gradevolmente i traceback