python: Como sei que tipo de exceção ocorreu?


230

Eu tenho uma função chamada pelo programa principal:

try:
    someFunction()
except:
    print "exception happened!"

mas, no meio da execução da função, gera exceção, e pula para a exceptparte.

Como posso ver exatamente o que aconteceu no someFunction()que causou a exceção?


9
Nunca, jamais, use o bare except:(sem o bare raise), exceto talvez uma vez por programa e, de preferência, não o faça.
21412 Mike Graham

Se você usar várias exceptcláusulas, não será necessário verificar o tipo de exceção, é o que geralmente é feito para agir de acordo com um tipo de exceção específico.
Rik Poggi

3
Se você se importa com o tipo de exceção, é porque você já considerou que tipos de exceção podem ocorrer logicamente.
22412 Karl Knechtel

3
exceptBloco interno: a exceção está disponível através da sys.exc_info()função - Esta função retorna uma tupla de três valores que fornecem informações sobre a exceção que está sendo tratada no momento.
Piotr Dobrogost

Respostas:


384

As outras respostas apontam que você não deve capturar exceções genéricas, mas ninguém parece querer dizer o porquê, o que é essencial para entender quando você pode violar a "regra". Aqui está uma explicação. Basicamente, é para você não esconder:

Portanto, desde que você tome cuidado para não fazer nada disso, não há problema em capturar a exceção genérica. Por exemplo, você pode fornecer informações sobre a exceção ao usuário de outra maneira, como:

  • Apresentar exceções como caixas de diálogo em uma GUI
  • Transferir exceções de um processo ou segmento de trabalho para o processo ou segmento de controle em um aplicativo de multithreading ou multiprocessing

Então, como capturar a exceção genérica? Existem várias maneiras. Se você deseja apenas o objeto de exceção, faça o seguinte:

try:
    someFunction()
except Exception as ex:
    template = "An exception of type {0} occurred. Arguments:\n{1!r}"
    message = template.format(type(ex).__name__, ex.args)
    print message

Certifique- se message de chamar a atenção do usuário de uma maneira difícil de perder! A impressão, como mostrado acima, pode não ser suficiente se a mensagem estiver oculta em muitas outras mensagens. Não chamar a atenção dos usuários é o mesmo que engolir todas as exceções, e se houver uma impressão que você deva ter depois de ler as respostas nesta página, é que isso não é uma coisa boa . Encerrar o bloco de exceção com uma raiseinstrução solucionará o problema, re-levantando de maneira transparente a exceção que foi capturada.

A diferença entre o exposto e o uso except:sem argumento é duplo:

  • Um nu except:não fornece o objeto de exceção para inspecionar
  • As exceções SystemExit, KeyboardInterrupte GeneratorExitnão são abrangidas pelo código acima, que é geralmente o que você quer. Veja a hierarquia de exceções .

Se você também quiser o mesmo rastreamento de pilha que obtém se não capturar a exceção, poderá obtê-lo assim (ainda dentro da cláusula exceto):

import traceback
print traceback.format_exc()

Se você usar o loggingmódulo, poderá imprimir a exceção no log (junto com uma mensagem) assim:

import logging
log = logging.getLogger()
log.exception("Message for you, sir!")

Se você quiser aprofundar e examinar a pilha, observe as variáveis ​​etc., use a post_mortemfunção do pdbmódulo dentro do bloco de exceção:

import pdb
pdb.post_mortem()

Eu achei esse último método inestimável ao caçar bugs.


1
traceback.print_exc () faria o mesmo que o seu "" .join mais complicado, eu acho.
Gurgeh 22/03/12

1
@ Gurgeh Sim, mas não sei se ele deseja imprimi-lo ou salvá-lo em um arquivo ou registrá-lo ou fazer outra coisa com ele.
Lauritz V. Thaulow 22/03/12

Não diminuí a votação, mas eu diria que é porque você deveria ter colocado uma grande quantidade de gordura no começo dizendo que não precisava de nada disso, mas aqui está como isso pode ser feito . E talvez porque você sugira capturar a exceção genérica.
Rik Poggi

10
@ Rick Eu acho que você pode muito bem precisar de tudo isso. Por exemplo, se você possui um programa com uma GUI e um back-end e deseja apresentar todas as exceções do back-end como mensagens da GUI, em vez de encerrar o programa com um rastreamento de pilha. Nesse caso, você deve capturar a exceção genérica , criar um texto de retorno para a caixa de diálogo, registrar a exceção também e, no modo de depuração, inserir post-mortem.
Lauritz V. Thaulow 22/03/12

18
@RikPoggi: Pensamento ingênuo. Existem muitas circunstâncias razoáveis ​​quando você precisa capturar exceções do código de outra pessoa e não sabe quais exceções serão geradas.
stackoverflowuser2010

63

Obtenha o nome da classe à qual o objeto de exceção pertence:

e.__class__.__name__

e usar a função print_exc () também imprimirá o rastreamento da pilha, que é uma informação essencial para qualquer mensagem de erro.

Como isso:

from traceback import print_exc

class CustomException(Exception): pass

try:
    raise CustomException("hi")
except Exception, e:
    print 'type is:', e.__class__.__name__
    print_exc()
    # print "exception happened!"

Você obterá uma saída como esta:

type is: CustomException
Traceback (most recent call last):
  File "exc.py", line 7, in <module>
    raise CustomException("hi")
CustomException: hi

E após a impressão e análise, o código pode decidir não manipular exceções e apenas executar raise:

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        raise
    print "handling exception"

Resultado:

special case of CustomException not interfering

E o intérprete imprime a exceção:

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    calculate()
  File "test.py", line 6, in calculate
    raise CustomException("hi")
__main__.CustomException: hi

Após a raiseexceção original, continua a se propagar ainda mais na pilha de chamadas. ( Cuidado com a possível armadilha ) Se você criar uma nova exceção, ela carrega um novo rastreamento (mais curto) da pilha.

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        #raise CustomException(e.message)
        raise e
    print "handling exception"

Resultado:

special case of CustomException not interfering
Traceback (most recent call last):
  File "test.py", line 13, in <module>
    raise CustomException(e.message)
__main__.CustomException: hi    

Observe como o traceback não inclui a calculate()função da linha 9que é a origem da exceção original e.


Se você deseja armazenar o rastreamento como uma string, você pode usar traceback.format_exc()bem
Stevoisiak

1
e.__class__.__name__é o mesmo que type(e).__name__, conforme sugerido pela resposta acima?
Information_interchange

1
@information_interchange yes. O conteúdo da pergunta e a resposta aceita mudaram completamente ao longo do tempo. É vergonha outros participantes não são notificados por máquinas SO :(
Alex

14

Você geralmente não deve capturar todas as exceções possíveis, try: ... exceptpois isso é muito amplo. Basta pegar aqueles que se espera que aconteçam por qualquer motivo. Se você realmente precisar, por exemplo, se quiser descobrir mais sobre algum problema durante a depuração, faça

try:
    ...
except Exception as ex:
    print ex # do whatever you want for debugging.
    raise    # re-raise exception.

17
O uso da palavra "nunca" aqui nunca foi tão errado. Eu uso try: ... except Exception:muitas coisas, por exemplo, o uso de bibliotecas dependentes da rede ou um massagista de dados que pode receber coisas estranhas enviadas a ela. Naturalmente, também tenho registro adequado. Isso é crucial para permitir que o programa continue operando no caso de um único erro nos dados de entrada.
Thnee

3
Já tentou capturar todas as exceções que poderiam ser levantadas ao enviar um email usando smtplib?
linusg

1
Pode haver alguns casos especiais em que a captura de todas as exceções é necessária, mas em geral você deve capturar o que espera, para não ocultar acidentalmente erros que não previa. Um bom registro também é uma boa ideia, é claro.
hochl

1
É perfeitamente razoável capturar todas as exceções. Se você estiver chamando uma biblioteca de terceiros, talvez não saiba quais exceções serão geradas nessa biblioteca. Nesse caso, o único recurso é capturar todas as exceções, por exemplo, registrá-las em um arquivo.
stackoverflowuser2010

Ok ok, você está certo, vou reformular minha resposta para deixar claro que existem casos de uso válidos para uma captura geral.
hochl

10

A menos que somefunctionseja uma função legada codificada muito ruim, você não deve precisar do que está pedindo.

Use várias exceptcláusulas para manipular de diferentes maneiras diferentes exceções:

try:
    someFunction()
except ValueError:
    # do something
except ZeroDivision:
    # do something else

O ponto principal é que você não deve capturar exceções genéricas, mas apenas as que você precisa. Tenho certeza de que você não deseja ocultar erros ou bugs inesperados.


8
Se você estiver usando uma biblioteca de terceiros, talvez não saiba quais exceções serão geradas dentro dela. Como você pode capturar todos eles individualmente?
stackoverflowuser2010

8

A maioria das respostas aponta para a except (…) as (…):sintaxe (com razão), mas ao mesmo tempo ninguém quer falar sobre um elefante na sala, onde o elefante está sys.exc_info()funcionando. A partir da documentação do módulo sys (ênfase minha):

Essa função retorna uma tupla de três valores que fornecem informações sobre a exceção que está sendo manipulada no momento.
(…)
Se nenhuma exceção estiver sendo manipulada em qualquer lugar da pilha, uma tupla contendo três valores Nenhum será retornada. Caso contrário, os valores retornados são (tipo, valor, retorno). Seu significado é: type obtém o tipo da exceção que está sendo tratada (uma subclasse de BaseException); value obtém a instância de exceção (uma instância do tipo de exceção); O traceback obtém um objeto traceback (consulte o Manual de referência) que encapsula a pilha de chamadas no ponto em que a exceção ocorreu originalmente.

Eu acho que isso sys.exc_info()poderia ser tratado como a resposta mais direta à pergunta original de Como sei que tipo de exceção ocorreu?


1
Essa é a resposta correta para mim, pois resolve o problema de qual exceção está ocorrendo; portanto, o que devo colocar em vez de descobrir except. Apenas por uma questão de exaustividade, exctype, value = sys.exc_info()[:2]mostrará o tipo de exceção que pode ser usado no except.
Ondrej Burkert

5

try: someFunction () exceto Exception, exc:

#this is how you get the type
excType = exc.__class__.__name__

#here we are printing out information about the Exception
print 'exception type', excType
print 'exception msg', str(exc)

#It's easy to reraise an exception with more information added to it
msg = 'there was a problem with someFunction'
raise Exception(msg + 'because of %s: %s' % (excType, exc))

-1 como o uso exc.__class__.__name__já havia sido sugerido na resposta de Alex - stackoverflow.com/a/9824060/95735
Piotr Dobrogost

3

Essas respostas são adequadas para depuração, mas para testar programaticamente a exceção, isinstance(e, SomeException)pode ser útil, pois também testa subclasses SomeException, para que você possa criar funcionalidades que se apliquem às hierarquias de exceções.


1

Veja como estou lidando com minhas exceções. A idéia é tentar resolver o problema se isso for fácil e, posteriormente, adicionar uma solução mais desejável, se possível. Não resolva o problema no código que gera a exceção, ou esse código perde o controle do algoritmo original, que deve ser escrito diretamente. No entanto, passe os dados necessários para resolver o problema e retorne um lambda para o caso de você não conseguir resolver o problema fora do código que o gera.

path = 'app.p'

def load():
    if os.path.exists(path):
        try:
            with open(path, 'rb') as file:
                data = file.read()
                inst = pickle.load(data)
        except Exception as e:
            inst = solve(e, 'load app data', easy=lambda: App(), path=path)()
    else:
        inst = App()
    inst.loadWidgets()

# e.g. A solver could search for app data if desc='load app data'
def solve(e, during, easy, **kwargs):
    class_name = e.__class__.__name__
    print(class_name + ': ' + str(e))
    print('\t during: ' + during)
    return easy

Por enquanto, como não quero pensar tangencialmente no objetivo do meu aplicativo, não adicionei nenhuma solução complicada. Porém, no futuro, quando eu souber mais sobre possíveis soluções (já que o aplicativo foi projetado mais), eu poderia adicionar um dicionário de soluções indexadas por during.

No exemplo mostrado, uma solução pode ser procurar dados de aplicativos armazenados em outro lugar, digamos se o arquivo 'app.p' foi excluído por engano.

Por enquanto, como escrever o manipulador de exceções não é uma ideia inteligente (ainda não sabemos as melhores maneiras de resolvê-lo, porque o design do aplicativo evoluirá), simplesmente retornamos a solução fácil que é agir como se estivéssemos executando o aplicativo pela primeira vez (neste caso).


0

Para adicionar à resposta de Lauritz, criei um decorador / wrapper para manipulação de exceções e os logs de wrapper cujo tipo de exceção ocorreu.

class general_function_handler(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, obj, type=None):
        return self.__class__(self.func.__get__(obj, type))
    def __call__(self, *args, **kwargs):
        try:
            retval = self.func(*args, **kwargs)
        except Exception, e :
            logging.warning('Exception in %s' % self.func)
            template = "An exception of type {0} occured. Arguments:\n{1!r}"
            message = template.format(type(e).__name__, e.args)
            logging.exception(message)
            sys.exit(1) # exit on all exceptions for now
        return retval

Isso pode ser chamado em um método de classe ou em uma função autônoma com o decorador:

@general_function_handler

Veja meu blog sobre para obter o exemplo completo: http://ryaneirwin.wordpress.com/2014/05/31/python-decorators-and-exception-handling/


0

Você pode começar como Lauritz recomendou, com:

except Exception as ex:

e então apenas para print exgostar assim:

try:
    #your try code here
except Exception as ex:
    print ex

Você pode elaborar um pouco para que sua resposta fique sozinha?
GHC

1
Certifique-se: você pode imprimir a exceção capturada assim: try: #your código tentativa aqui, exceto Exceção como ex: print ex agora o erro será impressa
Gura

-2

A exceção real pode ser capturada da seguinte maneira:

try:
    i = 1/0
except Exception as e:
    print e

Você pode aprender mais sobre exceções no Tutorial do Python .


-2

Sua pergunta é: "Como posso ver exatamente o que aconteceu no someFunction () que causou a exceção?"

Parece-me que você não está perguntando sobre como lidar com exceções imprevistas no código de produção (como muitas respostas assumidas), mas como descobrir o que está causando uma exceção específica durante o desenvolvimento.

A maneira mais fácil é usar um depurador que possa parar onde ocorre a exceção não capturada, de preferência não sair, para que você possa inspecionar as variáveis. Por exemplo, o PyDev no IDE de código aberto do Eclipse pode fazer isso. Para ativar isso no Eclipse, abra a perspectiva Debug, selecione Manage Python Exception Breakpointsno Runmenu e marque Suspend on uncaught exceptions.


-4

Evite capturar a exceção e o retorno que o Python imprime lhe dirá qual exceção ocorreu.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.