A resposta a esta pergunta depende da versão do Python que você está usando.
Em Python 3
É simples: as exceções vêm equipadas com um __traceback__
atributo que contém o traceback. Este atributo também é gravável e pode ser convenientemente configurado usando o with_traceback
método de exceções:
raise Exception("foo occurred").with_traceback(tracebackobj)
Esses recursos são minimamente descritos como parte da raise
documentação.
Todo o crédito por esta parte da resposta deve ir para Vyctor, que primeiro postou esta informação . Estou incluindo aqui apenas porque essa resposta está presa no topo e o Python 3 está se tornando mais comum.
Em Python 2
É irritantemente complexo. O problema com tracebacks é que eles têm referências a stack frames, e stack frames têm referências a tracebacks que têm referências a stack frames que têm referências a ... você entendeu. Isso causa problemas para o coletor de lixo. (Obrigado a ecatmur por primeiro apontar isso.)
A boa maneira de resolver isso seria quebrar cirurgicamente o ciclo depois de deixar a except
cláusula, que é o que o Python 3 faz. A solução Python 2 é muito mais feia: você recebe uma função ad-hoc sys.exc_info()
, que só funciona dentro da except
cláusula . Ele retorna uma tupla contendo a exceção, o tipo de exceção e o rastreamento para qualquer exceção que esteja sendo tratada no momento.
Portanto, se você estiver dentro da except
cláusula, poderá usar a saída de sys.exc_info()
junto com o traceback
módulo para fazer várias coisas úteis:
>>> import sys, traceback
>>> def raise_exception():
... try:
... raise Exception
... except Exception:
... ex_type, ex, tb = sys.exc_info()
... traceback.print_tb(tb)
... finally:
... del tb
...
>>> raise_exception()
File "<stdin>", line 3, in raise_exception
Mas, como indica sua edição, você está tentando obter o rastreamento que teria sido impresso se sua exceção não tivesse sido tratada, depois de já ter sido tratada. Essa é uma pergunta muito mais difícil. Infelizmente, sys.exc_info
retorna (None, None, None)
quando nenhuma exceção está sendo tratada. Outros sys
atributos relacionados também não ajudam. sys.exc_traceback
é preterido e indefinido quando nenhuma exceção está sendo tratada; sys.last_traceback
parece perfeito, mas parece ser definido apenas durante as sessões interativas.
Se você puder controlar como a exceção é gerada, você poderá usar inspect
uma exceção personalizada para armazenar algumas das informações. Mas não tenho certeza de como isso funcionaria.
Para dizer a verdade, capturar e retornar uma exceção é algo incomum. Isso pode ser um sinal de que você precisa refatorar de qualquer maneira.
sys.exc_info
em conjunto com a abordagem de retorno de chamada que sugiro em sua outra pergunta.Desde Python 3.0 [PEP 3109], a classe incorporada
Exception
tem um__traceback__
atributo que contém umtraceback object
(com Python 3.2.3):O problema é que depois de pesquisar
__traceback__
por um tempo no Google encontrei apenas alguns artigos, mas nenhum deles descreve se ou por que você deve (não) usar__traceback__
.No entanto, a documentação
raise
do Python 3 para diz que:Portanto, presumo que seja para ser usado.
fonte
__
no nome indicando que é um detalhe de implementação, não uma propriedade pública?__foo
é um método privado, mas__foo__
(com sublinhados também) é um método "mágico" (e não privado).__traceback__
atributo é 100% seguro para usar como quiser, sem implicações de GC. É difícil dizer isso pela documentação, mas ecatmur encontrou evidências concretas .Uma maneira de obter traceback como uma string de um objeto de exceção no Python 3:
traceback.format_tb(...)
retorna uma lista de strings.''.join(...)
os junta. Para obter mais referências, visite: https://docs.python.org/3/library/traceback.html#traceback.format_tbfonte
Como um aparte, se você deseja realmente obter o rastreamento completo como você o veria impresso em seu terminal, você deseja isto:
Se você usar
format_tb
as respostas acima, sugere que obterá menos informações:fonte
etype=type(exc)
pode ser omitido agora btw: "Alterado na versão 3.5: O argumento etype é ignorado e inferido do tipo de valor." docs.python.org/3.7/library/… Testado em Python 3.7.3.Há uma boa razão para o traceback não ser armazenado na exceção; porque o traceback contém referências aos locais de sua pilha, isso resultaria em uma referência circular e vazamento de memória (temporário) até que o GC circular seja acionado. (É por isso que você nunca deve armazenar o traceback em uma variável local .)
Praticamente a única coisa em que posso pensar seria que você fizesse
stuff
um monkeypatch para os globais de modo que, quando ele pensa que está capturandoException
, está na verdade capturando um tipo especializado e a exceção se propaga para você como o chamador:fonte
e.__traceback__
.except
bloco, de acordo com PEP 3110 .