Eu sei que se eu quiser relançar uma exceção, eu simplesmente uso raise
sem argumentos no respectivo except
bloco. Mas dada uma expressão aninhada como
try:
something()
except SomeError as e:
try:
plan_B()
except AlsoFailsError:
raise e # I'd like to raise the SomeError as if plan_B()
# didn't raise the AlsoFailsError
como posso aumentar novamente o SomeError
sem quebrar o rastreamento de pilha? raise
sozinho, neste caso, ressuscitaria o mais recente AlsoFailsError
. Ou como eu poderia refatorar meu código para evitar esse problema?
plan_B
outra função que retornaTrue
em caso de sucesso eFalse
em exceção? Então, oexcept
bloco externo poderia ser apenasif not try_plan_B(): raise
arg
e eu tentaria chamar oarg.plan_B()
que pode gerar umAttributeError
devido aarg
não fornecer um plano Bplan_B
levantar exceçõesRespostas:
A partir do Python 3, o traceback é armazenado na exceção, portanto, um simples
raise e
fará a (principalmente) coisa certa:O traceback produzido incluirá um aviso adicional que
SomeError
ocorreu durante o manuseioAlsoFailsError
(porraise e
estar dentroexcept AlsoFailsError
). Isso é enganoso porque o que realmente aconteceu é o contrário - encontramosAlsoFailsError
e lidamos com isso, enquanto tentávamos nos recuperarSomeError
. Para obter um traceback que não incluaAlsoFailsError
, substituaraise e
porraise e from None
.No Python 2 você armazenaria o tipo de exceção, valor e traceback em variáveis locais e usaria a forma de três argumentos de
raise
:fonte
raise self.exc_info[1], None, self.exc_info[2]
depois deself.exc_info = sys.exc_info()
- colocar[1]
a primeira posição por algum motivoraise t, None, tb
perderá o valor da exceção e forçaráraise
uma nova instância do tipo, fornecendo a você um valor de exceção menos específico (ou simplesmente incorreto). Por exemplo, se a exceção levantada forKeyError("some-key")
, ele apenas aumentará novamenteKeyError()
e omitirá a chave ausente exata do traceback.raise v.with_traceback(tb)
. (Seu comentário até diz isso, exceto que propõesys.exc_info()
em uma variável local fazia sentido antes do Python 2.0 (lançado 13 anos atrás), mas beira o ridículo hoje. O Python moderno seria quase inútil sem o coletor de ciclo, já que toda biblioteca Python não trivial cria ciclos sem pausa e depende de sua limpeza correta.Mesmo que a solução aceita esteja certa, é bom apontar para a biblioteca Six que possui uma solução Python 2 + 3, usando
six.reraise
.Então, você pode escrever:
fonte
six.raise_from
se quiser incluir informações queplan_B()
também falharam.six.raise_from
você cria uma nova exceção que está ligada a uma anterior, você não levanta novamente , então o rastreamento de volta é diferente.reraise
tem a impressão apenassomething()
jogouSomeError
, seraise_from
você também sabe que isso causouplan_B()
ser executado, mas jogando oAlsoFailsError
. Portanto, depende do caso de uso. Acho queraise_from
tornará a depuração mais fácilConforme a sugestão de Drew McGowen , mas cuidando de um caso geral (onde um valor de retorno
s
está presente), aqui está uma alternativa para a resposta do usuário 4815162342 :fonte
raise from
, o rastreamento de pilha também me permitiria se o plano B falhou. Que pode ser emulado no Python 2 por sinal.Python 3.5+ anexa as informações de traceback ao erro de qualquer maneira, então não é mais necessário salvá-lo separadamente.
fonte
except
. Mas você está certo, quando eu substituoerr = e
por, digamos,,raise AttributeError
você obtém primeiro oSyntaxError
rastreamento da pilha, seguido por ae o rastreamentoDuring handling of the above exception, another exception occurred:
daAttributeError
pilha. É bom saber, embora, infelizmente, não se possa contar com a instalação do 3.5+. PS: ff verstehen nicht-Deutsche vermutlich nicht;)