Come salvare i valori di traceback / sys.exc_info () in una variabile?


127

Voglio salvare il nome dell'errore e i dettagli del traceback in una variabile. Ecco il mio tentativo.

import sys
try:
    try:
        print x
    except Exception, ex:
        raise NameError
except Exception, er:
    print "0", sys.exc_info()[0]
    print "1", sys.exc_info()[1]
    print "2", sys.exc_info()[2]

Produzione:

0 <type 'exceptions.NameError'>
1 
2 <traceback object at 0xbd5fc8>

Uscita desiderata:

0 NameError
1
2 Traceback (most recent call last):
  File "exception.py", line 6, in <module>
    raise NameError

PS So che questo può essere fatto facilmente usando il modulo traceback, ma voglio sapere qui come usare l'oggetto sys.exc_info () [2].


Hai provato a stampare sys.exc_info () [x] .__ str __ ()?
zmbq,

3
Potresti aver frainteso ciò che sta succedendo nel tuo programma: ciò che ti riferisci come "oggetto sys.exc_info () [2]" è un'istanza dell'oggetto traceback (= stai già utilizzando il modulo traceback). Ora puoi manipolare quell'oggetto senza usare le funzioni di aiuto nel modulo traceback, ma ciò non cambia il fatto che lo stai ancora usando. :)
mac,

1
Quindi @mac, per favore, aiutami a utilizzare l'accesso al valore da questo oggetto con o senza la funzione helper.
codersofthedark,

1
@dragosrsupercool - Come ho detto nella mia risposta di seguito, dovresti consultare la documentazione di traceback . Ho fornito un esempio di come recuperare i dati testualmente, ma ci sono altri metodi dell'oggetto che ti permettono di estrarre il nome dell'eccezione, la riga del codice, ecc ... quello giusto dipende davvero da come vuoi manipolare il valore dopo ...
mac

1
La mia risposta a un'altra domanda può aiutare a illustrare i dettagli - con i collegamenti! Per stringhe fisse, il modulo traceback della libreria standard sembra a posto. Se vuoi ottenere i dettagli, leggi la fonte ( <python install path>/Lib/traceback.py) per maggiori informazioni.
pythonlarry,

Risposte:


180

Ecco come lo faccio:

>>> import traceback
>>> try:
...   int('k')
... except:
...   var = traceback.format_exc()
... 
>>> print var
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ValueError: invalid literal for int() with base 10: 'k'

Dovresti comunque dare un'occhiata alla documentazione di traceback , in quanto potresti trovare metodi più adatti, a seconda di come vuoi successivamente elaborare la tua variabile ...


1
Stavo cercando un metodo senza usare il modulo traceback. Esiste un modo in cui possiamo semplicemente stampare i dettagli di ritorno da questo riferimento all'oggetto? sys.exc_info () [2]
codersofthedark il

Bene, ecco perché ho pensato che potessimo fare qualcosa come sys.exc_info () [2] .format_exc (), ma questo non funziona .. Quindi, mi chiedo come posso estrarre valore dall'oggetto trace-back sys.exc_info () [2]. Qualche idea?
codersofthedark,

1
sys.exc_info () [2] .tb_text dà il seguente errore -> AttributeError: l'oggetto 'traceback' non ha attributo 'tb_text'
codersofthedark

4
@dragosrsupercool - sys.exc_info()[2].tb_frame.f_code.co_names[3], ma non ha alcun senso ... Se esiste un modulo chiamato tracebacknella libreria standard, c'è un motivo per farlo ... :)
mac

2
@codersofthedark traceback.format_exception(*sys.exc_info())è il modo di farlo. Ma questo è funzionalmente equivalente a traceback.format_exc().
wizzwizz4,

25

sys.exc_info () restituisce una tupla con tre valori (tipo, valore, traceback).

  1. Qui type ottiene il tipo di eccezione dell'eccezione gestita
  2. valore sono gli argomenti che vengono passati al costruttore della classe di eccezione
  3. traceback contiene le informazioni sullo stack come dove si è verificata l'eccezione ecc.

Ad esempio, nel seguente programma

try:

    a = 1/0

except Exception,e:

    exc_tuple = sys.exc_info()

Ora se stampiamo la tupla i valori saranno questi.

  1. Il valore exc_tuple [0] sarà " ZeroDivisionError "
  2. Il valore di exc_tuple [1] sarà " divisione intera o modulo per zero " (stringa passata come parametro alla classe di eccezione)
  3. exc_tuple [2] sarà " trackback object at (alcuni indirizzi di memoria) "

I dettagli di cui sopra possono anche essere recuperati semplicemente stampando l'eccezione in formato stringa.

print str(e)

Per Python3, exc_tuple [1] (aka valore) è l' istanza dell'eccezione, non la "Stringa passata come parametro". Vedi: docs.python.org/3/library/sys.html#sys.exc_info
Jinghao Shi

Non dovrebbe essere "tranne eccezione come e:"?
Henrik,

20

Utilizzare traceback.extract_stack()se si desidera un comodo accesso ai nomi dei moduli e delle funzioni e ai numeri di riga.

Utilizzare ''.join(traceback.format_stack())se si desidera solo una stringa simile traceback.print_stack()all'output.

Nota che anche con ''.join()te otterrai una stringa multilinea, poiché gli elementi di format_stack()contengono \n. Vedi l'output di seguito.

Ricordati di import traceback.

Ecco l'output di traceback.extract_stack(). Formattazione aggiunta per leggibilità.

>>> traceback.extract_stack()
[
   ('<string>', 1, '<module>', None),
   ('C:\\Python\\lib\\idlelib\\run.py', 126, 'main', 'ret = method(*args, **kwargs)'),
   ('C:\\Python\\lib\\idlelib\\run.py', 353, 'runcode', 'exec(code, self.locals)'),
   ('<pyshell#1>', 1, '<module>', None)
]

Ecco l'output di ''.join(traceback.format_stack()). Formattazione aggiunta per leggibilità.

>>> ''.join(traceback.format_stack())
'  File "<string>", line 1, in <module>\n
   File "C:\\Python\\lib\\idlelib\\run.py", line 126, in main\n
       ret = method(*args, **kwargs)\n
   File "C:\\Python\\lib\\idlelib\\run.py", line 353, in runcode\n
       exec(code, self.locals)\n  File "<pyshell#2>", line 1, in <module>\n'

4

Fare attenzione quando si rimuove l'oggetto di eccezione o l'oggetto traceback dal gestore di eccezioni, poiché ciò causa riferimenti circolari e gc.collect()la raccolta non riuscirà. Questo sembra essere un problema particolare nell'ambiente notebook ipython / jupyter in cui l'oggetto traceback non viene cancellato al momento giusto e anche una chiamata esplicita gc.collect()nella finallysezione non fa nulla. E questo è un grosso problema se hai alcuni oggetti enormi che non ottengono la loro memoria recuperata a causa di ciò (es. CUDA esaurisce le eccezioni di memoria che senza questa soluzione richiedono un riavvio completo del kernel per il ripristino).

In generale, se si desidera salvare l'oggetto traceback, è necessario cancellarlo dai riferimenti a locals(), in questo modo:

import sys, traceback, gc
type, val, tb = None, None, None
try:
    myfunc()
except:
    type, val, tb = sys.exc_info()
    traceback.clear_frames(tb)
# some cleanup code
gc.collect()
# and then use the tb:
if tb:
    raise type(val).with_traceback(tb)

Nel caso del notebook jupyter, devi farlo almeno all'interno del gestore delle eccezioni:

try:
    myfunc()
except:
    type, val, tb = sys.exc_info()
    traceback.clear_frames(tb)
    raise type(val).with_traceback(tb)
finally:
    # cleanup code in here
    gc.collect()

Testato con Python 3.7.

ps il problema con ipython o jupyter notebook env è che ha una %tbmagia che salva il traceback e lo rende disponibile in qualsiasi momento in seguito. Di conseguenza, locals()tutti i frame che partecipano al traceback non verranno liberati fino a quando il notebook non verrà chiuso o un'altra eccezione sovrascriverà il backtrace precedentemente memorizzato. Questo è molto problematico. Non dovrebbe memorizzare il traceback senza pulire i suoi frame. Correzione inviata qui .


3

L'oggetto può essere utilizzato come parametro nella Exception.with_traceback()funzione:

except Exception as e:
    tb = sys.exc_info()
    print(e.with_traceback(tb[2]))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.