Como salvar os valores traceback / sys.exc_info () em uma variável?

127

Quero salvar o nome do erro e os detalhes do rastreamento em uma variável. Aqui está a minha tentativa.

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]

Resultado:

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

Saída desejada:

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

PS: Sei que isso pode ser feito facilmente usando o módulo traceback, mas quero saber o uso do objeto sys.exc_info () [2] aqui.

codersofthedark
fonte
Você tentou imprimir sys.exc_info () [x] .__ str __ ()?
zmbq
3
Você pode ter entendido mal o que está acontecendo no seu programa: o que você chama de "sys.exc_info () [2] object" é uma instância do objeto traceback (= você já está usando o módulo traceback). Agora, você pode manipular esse objeto sem usar as funções auxiliares no módulo traceback, mas isso não muda o fato de você ainda o estar usando. :)
mac
1
Então, @mac, por favor me ajude a acessar o valor desse objeto com ou sem a função auxiliar.
Codsofthedark
1
@dragosrsupercool - Como mencionei na minha resposta abaixo, você deve consultar a documentação do traceback . Forneci um exemplo de como recuperar os dados textualmente, mas existem outros métodos do objeto que permitem extrair o nome da exceção, a linha do código, etc ... o correto depende realmente de como você deseja manipular o valor depois ...
mac
1
Minha resposta para outra pergunta pode ajudar a ilustrar os detalhes - com links! Para seqüências de caracteres fixas, o módulo de rastreamento de biblioteca padrão parece bom. Se você deseja obter os detalhes, leia a fonte ( <python install path>/Lib/traceback.py) para obter mais informações.
Pythonlarry

Respostas:

180

É assim que eu faço:

>>> 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'

No entanto, você deve dar uma olhada na documentação do traceback , pois poderá encontrar métodos mais adequados, dependendo de como deseja processar sua variável posteriormente ...

Mac
fonte
1
Eu estava procurando por um método sem usar o módulo traceback. Existe alguma maneira de simplesmente imprimir detalhes de rastreamento a partir desta referência de objeto? sys.exc_info () [2]
codersofthedark 23/11
Certo, é por isso que pensei que podemos fazer algo como sys.exc_info () [2] .format_exc (), mas isso não funciona. Assim, eu me pergunto como posso extrair valor do objeto de retorno sys.exc_info () [2] Qualquer ideia?
Codsofthedark 23/11
1
sys.exc_info () [2] .tb_text dá erro follow -> AttributeError: objeto 'traceback' tem nenhum atributo 'tb_text'
codersofthedark
4
@dragosrsupercool - sys.exc_info()[2].tb_frame.f_code.co_names[3], mas faz qualquer sentido ... Se houver um módulo chamado tracebackna biblioteca padrão, há uma razão para isso ... :)
mac
2
@codersofthedark traceback.format_exception(*sys.exc_info())é a maneira de fazê-lo. Mas isso é funcionalmente equivalente a traceback.format_exc().
wizzwizz4
25

sys.exc_info () retorna uma tupla com três valores (tipo, valor, retorno).

  1. Aqui type obtém o tipo de exceção da exceção que está sendo manipulada
  2. value são os argumentos que estão sendo passados ​​para o construtor da classe de exceção
  3. traceback contém as informações da pilha como onde ocorreu a exceção etc.

Por exemplo, no programa a seguir

try:

    a = 1/0

except Exception,e:

    exc_tuple = sys.exc_info()

Agora Se imprimirmos a tupla, os valores serão esses.

  1. O valor exc_tuple [0] será " ZeroDivisionError "
  2. O valor exc_tuple [1] será " divisão inteira ou módulo por zero " (String passada como parâmetro para a classe de exceção)
  3. O valor exc_tuple [2] será " objeto de trackback em (algum endereço de memória) "

Os detalhes acima também podem ser buscados simplesmente imprimindo a exceção no formato de sequência.

print str(e)
keya
fonte
Para Python3, exc_tuple [1] (também conhecido como valor) é a instância da exceção, não a "String passada como parâmetro". Veja: docs.python.org/3/library/sys.html#sys.exc_info
Jinghao Shi
Não deveria ser "exceto Exceção como e:"?
Henrik
20

Use traceback.extract_stack()se desejar acesso conveniente aos nomes dos módulos e das funções e aos números de linha.

Use ''.join(traceback.format_stack())se você quiser apenas uma sequência que se pareça com a traceback.print_stack()saída.

Observe que, mesmo com ''.join()você, obterá uma sequência de linhas múltiplas, pois os elementos de format_stack()contêm \n. Veja a saída abaixo.

Lembre-se de import traceback.

Aqui está a saída de traceback.extract_stack(). Formatação adicionada para facilitar a leitura.

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

Aqui está a saída de ''.join(traceback.format_stack()). Formatação adicionada para facilitar a leitura.

>>> ''.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'
Nathan
fonte
4

Tenha cuidado ao remover o objeto de exceção ou o objeto de retorno do manipulador de exceções, pois isso causa referências circulares e gc.collect()não será possível coletar. Isso parece ser um problema específico no ambiente de notebook ipython / jupyter, em que o objeto traceback não é limpo no momento certo e mesmo uma chamada explícita gc.collect()na finallyseção não faz nada. E isso é um problema enorme se você tiver alguns objetos enormes que não recuperam a memória por causa disso (por exemplo, exceções de falta de memória CUDA que sem esta solução exigem uma reinicialização completa do kernel para se recuperar).

Em geral, se você deseja salvar o objeto traceback, é necessário limpá-lo das referências para locals():

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)

No caso do notebook jupyter, você deve fazer isso pelo menos dentro do manipulador de exceções:

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

Testado com python 3.7.

O problema com o ambiente de notebook ipython ou jupyter é que ele possui %tbmagia que salva o traceback e o disponibiliza a qualquer momento posteriormente. E, como resultado locals(), todos os quadros que participam do traceback não serão liberados até que o notebook saia ou outra exceção substituirá o backtrace armazenado anteriormente. Isso é muito problemático. Não deve armazenar o rastreio sem limpar seus quadros. Correção enviada aqui .

Stason
fonte
3

O objeto pode ser usado como um parâmetro na Exception.with_traceback()função:

except Exception as e:
    tb = sys.exc_info()
    print(e.with_traceback(tb[2]))
Hansen D'Silva
fonte