Existe uma maneira de saber (no momento da codificação) quais exceções esperar ao executar o código Python? Eu acabo pegando a classe Exception base 90% do tempo, pois não sei qual tipo de exceção pode ser lançada (e não me diga para ler a documentação. Muitas vezes uma exceção pode ser propagada do fundo. E muitas vezes a documentação não é atualizada ou correta). Existe algum tipo de ferramenta para verificar isso? (como lendo o código python e libs)?
87
raise
usar strings, não apenasBaseException
subclasses. Portanto, se você estiver chamando um código de biblioteca que está fora de seu controle, nem mesmoexcept Exception
é suficiente, pois não detectará exceções de string. Como outros apontaram, você está latindo para a árvore errada aqui.except Exception
funciona bem para capturar exceções de string no Python 2.6 e posterior.Respostas:
Acho que uma solução só pode ser imprecisa por causa da falta de regras de digitação estática.
Não estou ciente de alguma ferramenta que verifica exceções, mas você pode criar sua própria ferramenta que atenda às suas necessidades (uma boa chance de brincar um pouco com a análise estática).
Como uma primeira tentativa, você pode escrever uma função que constrói um AST, encontra todos os
Raise
nós e, em seguida, tenta descobrir padrões comuns de levantamento de exceções (por exemplo, chamar um construtor diretamente)Seja
x
o seguinte programa:x = '''\ if f(x): raise IOError(errno.ENOENT, 'not found') else: e = g(x) raise e '''
Crie o AST usando o
compiler
pacote:Em seguida, defina uma
Raise
classe de visitante:class RaiseVisitor(object): def __init__(self): self.nodes = [] def visitRaise(self, n): self.nodes.append(n)
E percorra os
Raise
nós de coleta de AST :v = RaiseVisitor() compiler.walk(tree, v) >>> print v.nodes [ Raise( CallFunc( Name('IOError'), [Getattr(Name('errno'), 'ENOENT'), Const('not found')], None, None), None, None), Raise(Name('e'), None, None), ]
Você pode continuar resolvendo símbolos usando tabelas de símbolos do compilador, analisando dependências de dados, etc. Ou você pode apenas deduzir que
CallFunc(Name('IOError'), ...)
"deve definitivamente significar aumentoIOError
", o que é bastante bom para resultados práticos rápidos :)fonte
v.nodes
valor acima, você não pode realmente dizer o que éName('IOError')
ouName('e')
. Você não sabe que valor (es) essesIOError
ee
podem apontar, pois são as chamadas variáveis livres. Mesmo se seu contexto de ligação fosse conhecido (aqui as tabelas de símbolos entram em ação), você deve realizar algum tipo de análise de dependência de dados para inferir seus valores exatos (isso deve ser difícil em Python).['IOError(errno.ENOENT, "not found")', 'e']
exibidos para o usuário é o suficiente. Mas você não pode inferir classes reais de valores de variáveis representadas por strings :) (desculpe por repostagem)exc_class = raw_input(); exec "raise " + exc_class
. A questão é que esse tipo de análise estática não é realmente possível em uma linguagem dinâmica como o Python.find /path/to/library -name '*.py' | grep 'raise '
obter apenas resultados semelhantes :)Você só deve capturar exceções com as quais irá lidar.
Capturar todas as exceções por seus tipos concretos é um absurdo. Você deve capturar exceções específicas que você pode e vai segurar. Para outras exceções, você pode escrever uma captura genérica que captura "Exceção de base", registra-a (usa a
str()
função) e termina seu programa (ou faz algo mais apropriado em uma situação de falha).Se você realmente vai lidar com todas as exceções e tem certeza de que nenhuma delas é fatal (por exemplo, se você está executando o código em algum tipo de ambiente em área restrita), então sua abordagem de capturar BaseException genérica se ajusta aos seus objetivos.
Você também pode estar interessado em referência de exceção de linguagem , não uma referência para a biblioteca que você está usando.
Se a referência da biblioteca for realmente pobre e não relançar suas próprias exceções ao capturar as do sistema, a única abordagem útil é executar testes (talvez adicioná-lo ao conjunto de testes, porque se algo não estiver documentado, pode mudar!) . Exclua um arquivo crucial para seu código e verifique qual exceção está sendo lançada. Forneça muitos dados e verifique o erro que isso produz.
Você terá que executar testes de qualquer maneira, já que, mesmo se o método de obter as exceções pelo código-fonte existisse, ele não daria a você nenhuma ideia de como você deve lidar com qualquer um deles . Talvez você devesse exibir a mensagem de erro "Arquivo needful.txt não encontrado!" quando você pega
IndexError
? Apenas o teste pode dizer.fonte
A ferramenta correta para resolver este problema são os testes de unidade. Se você está tendo exceções levantadas por código real que os testes de unidade não geram, então você precisa de mais testes de unidade.
Considere isto
def f(duck): try: duck.quack() except ??? could be anything
pato pode ser qualquer objeto
Obviamente, você pode ter um
AttributeError
pato se não tem charlatão, umTypeError
pato se tem um charlatão, mas não pode ser chamado. Você não tem ideia do queduck.quack()
pode aumentar, talvez até mesmo umDuckError
ou algoAgora, suponha que você tenha um código como este
Se surgir um,
IndexError
você não saberá se veio de arr [i] ou de dentro da função de banco de dados. geralmente não importa muito onde ocorreu a exceção, mas sim que algo deu errado e o que você queria que acontecesse não aconteceu.Uma técnica útil é capturar e talvez aumentar novamente a exceção como esta
except Exception as e #inspect e, decide what to do raise
fonte
Ninguém explicou até agora porque você não pode ter uma lista de exceções completa e 100% correta, então achei que vale a pena comentar. Um dos motivos é uma função de primeira classe. Digamos que você tenha uma função como esta:
def apl(f,arg): return f(arg)
Agora
apl
pode gerar qualquer exceção quef
surgir. Embora não existam muitas funções como essa na biblioteca central, qualquer coisa que use a compreensão de lista com filtros personalizados, mapear, reduzir, etc. é afetada.A documentação e os analisadores de origem são as únicas fontes "sérias" de informações aqui. Basta ter em mente o que eles não podem fazer.
fonte
Eu encontrei isso ao usar o socket, eu queria descobrir todas as condições de erro que eu encontraria (então, em vez de tentar criar erros e descobrir qual socket eu queria apenas uma lista concisa). No final, acabei pedindo "/usr/lib64/python2.4/test/test_socket.py" para "aumentar":
$ grep raise test_socket.py Any exceptions raised by the clients during their tests raise TypeError, "test_func must be a callable function" raise NotImplementedError, "clientSetUp must be implemented." def raise_error(*args, **kwargs): raise socket.error def raise_herror(*args, **kwargs): raise socket.herror def raise_gaierror(*args, **kwargs): raise socket.gaierror self.failUnlessRaises(socket.error, raise_error, self.failUnlessRaises(socket.error, raise_herror, self.failUnlessRaises(socket.error, raise_gaierror, raise socket.error # Check that setting it to an invalid value raises ValueError # Check that setting it to an invalid type raises TypeError def raise_timeout(*args, **kwargs): self.failUnlessRaises(socket.timeout, raise_timeout, def raise_timeout(*args, **kwargs): self.failUnlessRaises(socket.timeout, raise_timeout,
Que é uma lista bastante concisa de erros. É claro que isso só funciona caso a caso e depende da precisão dos testes (o que geralmente é). Caso contrário, você precisa praticamente capturar todas as exceções, registrá-las e dissecá-las e descobrir como lidar com elas (o que com o teste de unidade não seria tão difícil).
fonte
Existem duas maneiras que considero informativas. O primeiro, execute o código em iPython, que exibirá o tipo de exceção.
n = 2 str = 'me ' str + 2 TypeError: unsupported operand type(s) for +: 'int' and 'str'
Na segunda maneira, nós nos contentamos em pegar muito e melhoramos com o tempo. Inclua uma
try
expressão em seu código e pegueexcept Exception as err
. Imprima dados suficientes para saber qual exceção foi lançada. À medida que exceções são lançadas, melhore seu código adicionando umaexcept
cláusula mais precisa . Quando você sentir que detectou todas as exceções relevantes, remova aquela que inclui tudo. De qualquer forma, é uma boa coisa, porque engole erros de programação.try: so something except Exception as err: print "Some message" print err.__class__ print err exit(1)
fonte
normalmente, você só precisa capturar a exceção em torno de algumas linhas de código. Você não gostaria de colocar toda a sua
main
função natry except
cláusula. para cada poucas linhas, você sempre deve agora (ou pode facilmente verificar) que tipo de exceção pode ser levantada.documentos têm uma lista exaustiva de exceções integradas . não tente exceto aquelas exceções que você não está esperando, elas podem ser tratadas / esperadas no código de chamada.
editar : o que pode ser lançado depende obviamente do que você está fazendo! acessar o elemento aleatório de uma sequência
IndexError
:, elemento aleatório de um dicionárioKeyError
, etc.Apenas tente executar essas poucas linhas no IDLE e causar uma exceção. Mas o teste unitário seria uma solução melhor, naturalmente.
fonte