Existe-t-il un moyen de savoir (au moment du codage) à quelles exceptions s'attendre lors de l'exécution de code python? Je finis par attraper la classe d'exception de base 90% du temps car je ne sais pas quel type d'exception pourrait être levé (et ne me dites pas de lire la documentation. Plusieurs fois, une exception peut être propagée depuis le plus profond. fois la documentation n'est pas mise à jour ou correcte). Existe-t-il une sorte d'outil pour vérifier cela? (comme en lisant le code python et les bibliothèques)?
87
raise
utiliser des chaînes, pas seulement desBaseException
sous - classes. Donc, si vous appelez du code de bibliothèque hors de votre contrôle, ceexcept Exception
n'est même pas suffisant, car il n'attrapera pas les exceptions de chaîne. Comme d'autres l'ont souligné, vous aboyez ici le mauvais arbre.except Exception
fonctionne bien pour intercepter les exceptions de chaîne dans Python 2.6 et versions ultérieures.Réponses:
Je suppose qu'une solution pourrait être imprécise en raison du manque de règles de typage statiques.
Je ne connais pas d'outil qui vérifie les exceptions, mais vous pourriez proposer votre propre outil correspondant à vos besoins (une bonne chance de jouer un peu avec l'analyse statique).
Dans un premier temps, vous pouvez écrire une fonction qui construit un AST, trouve tous les
Raise
nœuds, puis essaie de trouver des modèles courants de levée d'exceptions (par exemple, appeler un constructeur directement)Soit
x
le programme suivant:x = '''\ if f(x): raise IOError(errno.ENOENT, 'not found') else: e = g(x) raise e '''
Construisez l'AST en utilisant le
compiler
package:Définissez ensuite une
Raise
classe de visiteurs:class RaiseVisitor(object): def __init__(self): self.nodes = [] def visitRaise(self, n): self.nodes.append(n)
Et parcourez les
Raise
nœuds de collecte 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), ]
Vous pouvez continuer en résolvant les symboles en utilisant les tables de symboles du compilateur, en analysant les dépendances de données, etc. Ou vous pouvez simplement en déduire que
CallFunc(Name('IOError'), ...)
"devrait certainement signifier augmenterIOError
", ce qui est tout à fait acceptable pour des résultats pratiques rapides :)la source
v.nodes
valeur ci-dessus, vous ne pouvez pas vraiment dire ce qu'est la choseName('IOError')
ouName('e')
. Vous ne savez pas sur quelle (s) valeur (s) ceuxIOError
-cie
peuvent pointer, car ce sont des variables dites libres. Même si leur contexte de liaison était connu (ici les tables de symboles entrent en jeu), vous devez effectuer une sorte d'analyse de dépendance des données pour déduire leurs valeurs exactes (cela devrait être difficile en Python).['IOError(errno.ENOENT, "not found")', 'e']
affichage à l'utilisateur est très bien. Mais vous ne pouvez pas déduire de classes réelles de valeurs de variables représentées par des chaînes :) (désolé pour le republication)exc_class = raw_input(); exec "raise " + exc_class
. Le fait est que ce type d'analyse statique n'est pas vraiment possible dans un langage dynamique comme Python.find /path/to/library -name '*.py' | grep 'raise '
obtenir des résultats similaires :)Vous ne devez attraper que les exceptions que vous allez gérer.
Attraper toutes les exceptions par leurs types concrets est un non-sens. Vous devez détecter les exceptions spécifiques que vous pouvez et que vous allez gérer. Pour les autres exceptions, vous pouvez écrire une capture générique qui attrape "l'exception de base", la consigne (utilisez la
str()
fonction) et termine votre programme (ou fait quelque chose d'autre qui est approprié dans une situation de crash).Si vous voulez vraiment gérer toutes les exceptions et êtes sûr qu'aucune d'entre elles n'est fatale (par exemple, si vous exécutez le code dans une sorte d'environnement en bac à sable), alors votre approche de capture de BaseException générique correspond à vos objectifs.
Vous pourriez également être intéressé par une référence d'exception de langue , et non par une référence pour la bibliothèque que vous utilisez.
Si la référence de la bibliothèque est vraiment médiocre et qu'elle ne renvoie pas ses propres exceptions lors de la capture de celles du système, la seule approche utile consiste à exécuter des tests (peut-être l'ajouter à la suite de tests, car si quelque chose n'est pas documenté, cela peut changer!) . Supprimez un fichier crucial pour votre code et vérifiez quelle exception est levée. Fournissez trop de données et vérifiez quelle erreur cela génère.
Vous devrez quand même exécuter des tests, car, même si la méthode d'obtention des exceptions par code source existait, cela ne vous donnerait aucune idée de la façon dont vous devriez gérer l'une d'entre elles . Peut-être que vous devriez afficher le message d'erreur "Le fichier nécessaire.txt est introuvable!" quand tu attrapes
IndexError
? Seul le test peut le dire.la source
Le bon outil pour résoudre ce problème est unittests. Si vous rencontrez des exceptions déclenchées par du code réel que les unittests ne déclenchent pas, vous avez besoin de plus d'unittests.
Considère ceci
def f(duck): try: duck.quack() except ??? could be anything
le canard peut être n'importe quel objet
Évidemment, vous pouvez avoir un
AttributeError
si canard n'a pas de charlatan, unTypeError
si canard a un charlatan mais il n'est pas appelable. Vous n'avez aucune idée de ce quiduck.quack()
pourrait augmenter, peut-être même unDuckError
ou quelque choseSupposons maintenant que vous ayez un code comme celui-ci
S'il lève un,
IndexError
vous ne savez pas s'il vient de arr [i] ou du plus profond de la fonction de base de données. généralement, peu importe où l'exception s'est produite, plutôt que quelque chose s'est mal passé et que ce que vous vouliez se produire ne s'est pas produit.Une technique pratique consiste à attraper et peut-être à relancer l'exception comme ceci
except Exception as e #inspect e, decide what to do raise
la source
Personne n'a expliqué jusqu'à présent, pourquoi vous ne pouvez pas avoir une liste d'exceptions complète et correcte à 100%, alors j'ai pensé que cela méritait d'être commenté. L'une des raisons est une fonction de première classe. Disons que vous avez une fonction comme celle-ci:
def apl(f,arg): return f(arg)
Maintenant
apl
peut soulever toute exception qui sef
lève. Bien qu'il n'y ait pas beaucoup de fonctions comme celle-là dans la bibliothèque principale, tout ce qui utilise la compréhension de liste avec des filtres personnalisés, mapper, réduire, etc. est affecté.La documentation et les analyseurs de sources sont ici les seules sources d'informations «sérieuses». Gardez simplement à l'esprit ce qu'ils ne peuvent pas faire.
la source
Je suis tombé sur cela lors de l'utilisation de socket, je voulais découvrir toutes les conditions d'erreur dans lesquelles je me heurterais (donc plutôt que d'essayer de créer des erreurs et de déterminer quelle socket je voulais juste une liste concise). Finalement, j'ai fini par grep'ing "/usr/lib64/python2.4/test/test_socket.py" pour "augmenter":
$ 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,
Ce qui est une liste d'erreurs assez concise. Maintenant, bien sûr, cela ne fonctionne qu'au cas par cas et dépend de l'exactitude des tests (ce qu'ils sont généralement). Sinon, vous devez à peu près attraper toutes les exceptions, les consigner, les disséquer et comprendre comment les gérer (ce qui avec les tests unitaires ne serait pas trop difficile).
la source
Il y a deux façons que j'ai trouvées informatives. Le premier, exécutez le code dans iPython, qui affichera le type d'exception.
n = 2 str = 'me ' str + 2 TypeError: unsupported operand type(s) for +: 'int' and 'str'
Dans un deuxième temps, nous nous contentons de trop attraper et nous l'améliorons avec le temps. Incluez une
try
expression dans votre code et attrapezexcept Exception as err
. Imprimez suffisamment de données pour savoir quelle exception a été levée. Au fur et à mesure que des exceptions sont levées, améliorez votre code en ajoutant uneexcept
clause plus précise . Lorsque vous sentez que vous avez détecté toutes les exceptions pertinentes, supprimez celle tout compris. Une bonne chose à faire quand même car ça avale les erreurs de programmation.try: so something except Exception as err: print "Some message" print err.__class__ print err exit(1)
la source
normalement, vous n'avez besoin d'attraper une exception que sur quelques lignes de code. Vous ne voudriez pas mettre toute votre
main
fonction dans l'try except
article. pour toutes les quelques lignes, vous devriez toujours maintenant (ou être en mesure de vérifier facilement) quel type d'exception pourrait être déclenché.docs ont une liste exhaustive d'exceptions intégrées . n'essayez pas d'exclure les exceptions auxquelles vous ne vous attendez pas, elles pourraient être gérées / attendues dans le code d'appel.
edit : ce qui pourrait être jeté dépend évidemment de ce que vous faites! accès à un élément aléatoire d'une séquence
IndexError
:, élément aléatoire d'un dict:,KeyError
etc.Essayez simplement d'exécuter ces quelques lignes dans IDLE et de provoquer une exception. Mais unittest serait une meilleure solution, naturellement.
la source