Minha formação é em C # e eu recentemente comecei a programar em Python. Quando uma exceção é lançada, normalmente quero agrupá-la em outra exceção que adiciona mais informações, enquanto ainda mostra o rastreamento completo da pilha. É muito fácil em C #, mas como faço em Python?
Por exemplo. em C #, eu faria algo assim:
try
{
ProcessFile(filePath);
}
catch (Exception ex)
{
throw new ApplicationException("Failed to process file " + filePath, ex);
}
No Python, posso fazer algo semelhante:
try:
ProcessFile(filePath)
except Exception as e:
raise Exception('Failed to process file ' + filePath, e)
... mas isso perde o rastro da exceção interna!
Editar: eu gostaria de ver as mensagens de exceção e os rastreamentos de pilha e correlacionar os dois. Ou seja, quero ver na saída que a exceção X ocorreu aqui e depois a exceção Y lá - o mesmo que faria em C #. Isso é possível no Python 2.6? Parece que o melhor que posso fazer até agora (com base na resposta de Glenn Maynard) é:
try:
ProcessFile(filePath)
except Exception as e:
raise Exception('Failed to process file' + filePath, e), None, sys.exc_info()[2]
Isso inclui as mensagens e os tracebacks, mas não mostra qual exceção ocorreu no traceback.
Respostas:
Python 2
É simples; passar o retorno como o terceiro argumento a ser levantado.
Sempre faça isso ao capturar uma exceção e aumentar novamente outra.
fonte
raise MyException(str(e)), ...
, etcraise E() from tb
e.with_traceback(...)
raise
é o valor a ser passado para a exceção (caso o primeiro argumento seja uma classe de exceção e não uma instância). Então, se você quer exceções de swap, em vez de fazerraise MyException(str(e)), None, sys.exc_info()[2]
, é melhor usar este:raise MyException, e.args, sys.exc_info()[2]
.from future.utils import raise_
eraise_(ValueError, None, sys.exc_info()[2])
.Python 3
No python 3, você pode fazer o seguinte:
Isso produzirá algo como isto:
fonte
raise ... from ...
é realmente a maneira correta de fazer isso no Python 3. Isso precisa de mais votos.Nakedible
Eu acho que é porque, infelizmente, a maioria das pessoas ainda não está usando Python 3.future
pacote para fazer isso: python-future.org/compatible_idioms.html#raising-exceptions Por exemplo,from future.utils import raise_
eraise_(ValueError, None, sys.exc_info()[2])
.O Python 3 possui a cláusula
raise
...from
para encadear exceções. A resposta de Glenn é ótima para o Python 2.7, mas ele usa apenas o rastreio da exceção original e joga fora a mensagem de erro e outros detalhes. Aqui estão alguns exemplos no Python 2.7 que adicionam informações de contexto do escopo atual à mensagem de erro da exceção original, mas mantêm outros detalhes intactos.Tipo de exceção conhecido
Esse tipo de
raise
instrução usa o tipo de exceção como a primeira expressão, os argumentos do construtor da classe de exceção em uma tupla como a segunda expressão e o retorno como a terceira expressão. Se você estiver executando antes do Python 2.2, consulte os avisos emsys.exc_info()
.Qualquer tipo de exceção
Aqui está outro exemplo de propósito mais geral, se você não souber que tipo de exceção seu código pode ter que capturar. A desvantagem é que ele perde o tipo de exceção e apenas gera um RuntimeError. Você precisa importar o
traceback
módulo.Modifique a mensagem
Aqui está outra opção se o tipo de exceção permitir que você adicione contexto a ele. Você pode modificar a mensagem da exceção e, em seguida, aumentá-la.
Isso gera o seguinte rastreamento de pilha:
Você pode ver que ele mostra a linha onde
check_output()
foi chamada, mas a mensagem de exceção agora inclui a linha de comando.fonte
ex.strerror
vem isso? Não consigo encontrar nenhum resultado relevante nos documentos do Python. Não deveria serstr(ex)
?IOError
é derivado deEnvironmentError
, @hheimbuerger, que proporciona oerrorno
estrerror
atributos.Error
, por exemplo, ValueError, em umaRuntimeError
capturaException
? Se eu reproduzir sua resposta para este caso, o rastreamento de pilha será perdido.No Python 3.x :
ou simplesmente
que será propagado,
MyException
mas imprimirá as duas exceções, se não for tratado.No Python 2.x :
Você pode impedir a impressão das duas exceções, matando o
__context__
atributo. Aqui, escrevo um gerenciador de contexto usando isso para capturar e alterar sua exceção rapidamente : (consulte http://docs.python.org/3.1/library/stdtypes.html para saber como eles funcionam)fonte
e.__traceback__
atributo!Eu não acho que você possa fazer isso no Python 2.x, mas algo semelhante a essa funcionalidade faz parte do Python 3. No PEP 3134 :
Comparação com C #:
Observe também que Java, Ruby e Perl 5 também não suportam esse tipo de coisa. Citando novamente:
fonte
Para máxima compatibilidade entre Python 2 e 3, você pode usar
raise_from
nasix
biblioteca. https://six.readthedocs.io/#six.raise_from . Aqui está o seu exemplo (ligeiramente modificado para maior clareza):fonte
Você pode usar minha classe CausedException para encadear exceções no Python 2.x (e mesmo no Python 3 pode ser útil caso você queira fornecer mais de uma exceção capturada como causa de uma exceção recém-criada). Talvez possa ajudá-lo.
fonte
Talvez você possa pegar as informações relevantes e passá-las? Estou pensando em algo como:
fonte
Assumindo:
raise ... from
solução)você pode usar uma solução simples no https://docs.python.org/3/tutorial/errors.html#raising-exceptions dos documentos :
A saída:
Parece que a peça principal é a palavra-chave simplificada 'raise' que fica sozinha. Isso aumentará novamente a exceção no bloco de exceção.
fonte