Mis à jour le jeudi 31 octobre 2013
  • Facile
Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Introduction du cours

Si vous codez en Python, vous avez sûrement déjà été confronté(e) à des erreurs diverses, provenant de l'utilisateur, d'une bibliothèque, etc.
Par exemple, vous souhaitez que l'utilisateur entre un nombre et que votre programme affiche le triple de ce nombre.
Si vous utilisez input() , mais que l'utilisateur entre une chaîne de caractères (sans l'entourer de guillemets), le programme plante lamentablement.
Si vous utilisez raw_input() , vous n'avez pas de problème lors de la saisie, mais vous vous retrouvez avec une chaîne. Or, vous avez besoin d'un nombre.
Si vous utilisez int() pour transformer la chaîne obtenue en nombre, le programme plantera aussi dans le cas où l'utilisateur n'a pas entré un nombre.
Alors, comment remédier à un tel problème ?

Ce tutoriel va vous expliquer comment gérer les erreurs en Python.
Une première partie vous enseignera comment utiliser les mots-clé try , except , finally et else , tandis que la seconde vous présentera une liste des exceptions et des exemples les expliquant. Enfin, la troisième vous expliquera comment créer vos propres exceptions.

Les mots-clés try, except, else et finally

Quatre mots-clés vont servir à gérer les erreurs.

try permet « d'essayer » un bout de code. Si une erreur est rencontrée, on cesse d'interpréter le code et on passe aux except , qui permettent d'agir en fonction de l'erreur qui s'est produite. C'est pourquoi try doit toujours être suivi d'au moins un except ou d'un finally .
Nous allons, pour comprendre leur fonctionnement, analyser le code suivant.

try:
    fichier = open("foo.txt", "r")
    print int("abc")
except IOError:
    print "Erreur lors de l'ouverture du fichier !"
except ValueError:
    print "Erreur lors de la conversion !"

Les deux premières lignes, dans le try: , sont tout d'abord interprétées. La première tente d'ouvrir un fichier appelé foo.txt. Si ce fichier existe dans le répertoire du script, remplacez "foo.txt" par autre chose, de sorte que le fichier que le script tente d'ouvrir n'existe pas.
La seconde essaie d'afficher à l'écran la chaîne "abc" convertie en nombre.
La troisième ligne, qui contient le mot-clé except , est exécutée en cas d'IOError . IOError est l'exception lancée lorsqu'il s'avère impossible d'ouvrir un fichier (entre autres). Dans ce cas, on affiche une erreur. La cinquième ligne agit comme la troisième, en cas de ValueError , qui est lancé quand on passe un argument avec le bon type (ici, "abc" est bien une chaîne), mais que la valeur n'est pas correcte ("abc" ne saurait être transformée en nombre, vous en conviendrez).
Lorsque nous lançons ce code, comme foo.txt n'existe pas, l'ouverture du fichier provoque une IOError . Si nous le créons (touch foo.txt sous UNIX), alors la ligne print int("abc") sera interprétée et lancera une ValueError . Si, enfin, nous remplaçons "abc" par "22" (ou toute autre chaîne qui peut être convertie en nombre), alors aucune exception ne sera lancée et seul le bloc try: aura été exécuté.

On peut également mettre un tuple d'exceptions, pour les cas où le même code doit être interprété pour des erreurs différentes :

except (IOError, ValueError):

Il est possible de créer une instance de l'exception, afin d'avoir plus d'informations sur une erreur :

try:
    fichier = open('foo.txt')
except IOError, e:
    print e
[Errno 2] No such file or directory: 'foo.txt'

Le mot-clé else permet d'interpréter du code lorsqu'aucune exception n'a été lancée dans le try: . Ainsi, on pourrait rajouter une lecture du fichier dans le code précédent :

try:
    fichier = open("foo.txt", "r")
    print int("22")
except IOError:
    print "Erreur lors de l'ouverture du fichier !"
except ValueError:
    print "Erreur lors de la conversion !"
else:
    print fichier.read()

Ainsi, si le fichier s'est ouvert sans problème, et le reste du code (ici symbolisé par le int("22") ) aussi, le fichier sera lu.

Enfin, le mot-clé finally permet d'exécuter du code après l'ensemble try: except: , quoi qu'il arrive (qu'une exception ait été lancée ou non, qu'un return ou un break soit rencontré, le bloc finally: sera interprété).

Liste des exceptions built-in

Cette partie propose une liste des exceptions built-in en Python, accompagnées pour la plupart d'un exemple.
Certaines exceptions en contiennent d'autres : par exemple, Exception regroupe toutes les exceptions, exceptés KeyboardInterrupt , SystemExit , et bien sûr, BaseException , qui regroupe l'ensemble des exceptions. La hiérarchie est la suivante :

  • BaseException

    • SystemExit

    • KeyboardInterrupt

    • Exception

      • GeneratorExit

      • StopIteration

      • StandardError

        • ArithmeticError

          • FloatingPointError

          • OverflowError

          • ZeroDivisionError

        • AssertionError

        • AttributeError

        • EnvironmentError

          • IOError

          • OSError

            • VMSError

          • EOFError

          • ImportError

          • LookupError

            • IndexError

            • KeyError

          • MemoryError

          • NameError

            • UnboundLocalError

          • ReferenceError

          • RuntimeError

            • NotImplementedError

          • SyntaxError

            • IndentationError

              • TabError

          • SystemError

          • TypeError

          • ValueError

            • UnicodeError

              • UnicodeDecodeError

              • UnicodeEncodeError

              • UnicodeTranslateError

      • Warning

        • DeprecationWarning

        • PendingDeprecationWarning

        • RuntimeWarning

        • SyntaxWarning

        • UserWarning

        • FutureWarning

        • ImportWarning

        • UnicodeWarning

  • BaseException regroupe l'ensemble des exceptions built-in. Les exceptions définies par l'utilisateur (comme nous allons apprendre à le faire) ne doivent pas en hériter directement, mais hériter d'Exception .

  • SystemExit est l'exception lancée par sys.exit() . Si elle n'est pas interceptée par un except: , le programme s'arrête. Elle n'hérite pas d'Exception pour que, lorsque le développeur cherche à intercepter toutes les erreurs possibles, il n'empêche pas le programme de quitter.

  • KeyboardInterrupt est lancé lorsque l'utilisateur appuie sur les touches d'interruption (généralement Ctrl + C). Tout comme SystemExit, il hérite directement de BaseException , pour les mêmes raisons.

  • Exception regroupe toutes les exceptions qui ne se rapportent pas à la fermeture du programme. Vos propres exceptions doivent en dériver. On l'utilise généralement pour s'assurer que tout a bien fonctionné, et dans le cas contraire, pour intercepter puis afficher l'erreur qui s'est produite :

    try:
        # code
    except Exception, e:
        print "Erreur : %s", % e
    
  • GeneratorExit est lancé lorsqu'on utilise la méthode close() d'un générateur. Pour en savoir plus sur les générateurs, je vous conseille le tutoriel de Natim sur les pratiques avancées et méconnues en Python, qui fournit notamment un exemple d'utilisation de cette exception.

  • StopIteration est, lui aussi, lié aux générateurs. Le tutoriel précédemment cité en parle également.

  • StandardError regroupe toutes les exceptions, sauf les Warnings, GeneratorExit et StopIteration puisqu'il ne s'agit pas d'erreurs à proprement parler.

  • ArithmeticError est la classe de base pour les erreurs d'arithmétique.

  • FloatingPointError est lancé lorsqu'une erreur est détectée dans une opération à virgule flottante.

  • OverflowError est lancé lorsque le résultat d'une opération est trop grand pour être représenté.

  • ZeroDivisionError est l'exception lancée lorsque le programme tente de faire une division (ou un modulo) par zéro.

    try:
        print 5/0
    except ZeroDivisionError, e:
        print e
    
    try:
        print 5%0
    except ZeroDivisionError, e:
        print e
    
    integer division or modulo by zero
    integer division or modulo by zero
  • AssertionError est lancé lorsqu'un assert n'est pas vrai.

    try:
        assert 1 == 0
    except AssertionError:
        print "Erreur !"
    
    Erreur !
  • AttributeError est lancé lorsqu'une erreur se produit lors d'une référence ou d'un assignement à un attribut.

    class Foo:
        pass
    
    foo = Foo()
    try:
        print foo.bar
    except AttributeError, e:
        print e
    
    Foo instance has no attribute 'bar'
  • EnvironmentError est la classe de base des exceptions qui ne proviennent pas de Python (par exemple, un fichier introuvable).

  • IOError est une exception lancée lorsqu'une erreur I/O (pour Input/Output) se produit. Trois attributs sont intéressants : errno , qui indique le numéro de l'erreur ; filename , qui donne le nom du fichier concerné ; et strerror qui donne une description de l'erreur.

    try:
        fichier = open("foo.txt", "r")
    except IOError, e:
        print "Erreur", e.errno, ":", e.filename, "n'a pas pu être ouvert (", e.strerror, ")."
    
    Erreur 2 : foo.txt n'a pas pu être ouvert ( No such file or directory ).
  • OSError est l'exception utilisée par le module os .

  • EOFError est lancé lorsque les fonctions input() et raw_input() reçoivent le signal EOF (pour End Of File).

    try:
        input()
    except EOFError:
        print "Erreur !"
    

    Si vous envoyez le signal EOF pendant l'input() (généralement, c'est la combinaison de touches Ctrl + D) :

    Erreur !
  • ImportError est lancé lorsqu'une erreur se produit lors d'un import .

    try:
        import foo
    except ImportError, e:
        print e
    try:
        from os import bar
    except ImportError, e:
        print e
    
    No module named foo
    cannot import name bar
  • LookupError est la classe de base pour les exceptions IndexError et KeyError .

  • IndexError est lancé quand l'index d'un tableau n'est pas valide (mais reste un nombre ; autrement, c'est TypeError qui est utilisé).

    foo = [0,1,2]
    try:
        print foo[3]
    except IndexError, e:
        print e
    
    list index out of range
  • KeyError est l'équivalent d'IndexError pour les dictionnaires.

  • MemoryError est lancé lorsqu'un programme prend trop de mémoire.

  • NameError est lancé quand un nom local ou global est introuvable.

    try:
        print foo
    except NameError, e:
        print e
    
    name 'foo' is not defined
  • UnboundLocalError est lancé lorsqu'une référence à une variable à laquelle aucune valeur n'a été donnée est faite dans une fonction ou une méthode.

    def foo():
        bar += 1
    
    try:
        foo()
    except UnboundLocalError, e:
        print e
    
    local variable 'bar' referenced before assignment
  • ReferenceError est une exception spécifique au module weakref .

  • RuntimeError est utilisé pour toutes les erreurs ne rentrant pas dans les autres catégories.

  • NotImplementedError est utilisé dans les fonctions définies par l'utilisateur, pour informer (comme son nom l'indique) que ce qui est demandé n'est pas implémenté.

  • SyntaxError est lancé lorsque l'interpréteur trouve une erreur de syntaxe. Cela peut arriver lorsque vous importez du code, que vous utilisez exec , eval() , input() ou que le script ou la ligne que vous venez d'entrer dans l'interpréteur sont lus.

  • IndentationError est un dérivé de SyntaxError et, comme son nom l'indique, apparaît lorsqu'une erreur d'indentation est détectée.

  • SystemError indique une erreur interne de l'interpréteur.

  • TypeError est lancé quand une opération ou une fonction est faite sur un objet de type inapproprié.

    try:
        print int([0,1])
    except TypeError, e:
        print e
    
    int() argument must be a string or a number
  • ValueError indique qu'une fonction ou une opération a reçu un argument du bon type, mais avec une valeur incorrecte. Par exemple, int("abc") produit une telle erreur : "abc" est bien une chaîne, mais n'est pas convertible en un nombre.

  • UnicodeError est lancé quand une erreur lors d'un encodage ou décodage Unicode se produit.

  • UnicodeDecodeError indique que l'erreur s'est produite lors du décodage.

  • UnicodeEncodeError indique que c'était durant l'encodage.

  • Enfin, UnicodeTranslateError indique que c'était durant une translation.

  • Warning est la classe de base pour les Warnings.

  • UserWarning est la classe de base pour les warnings définis par l'utilisateur.

Les autres Warnings portent des noms tout à fait explicites, si bien qu'il est inutile de radoter autour.

Vos propres exceptions

Pour des raisons pratiques, vous serez peut-être amené(e) un jour à faire vos propres exceptions.
Nous allons comprendre comment le faire en analysant ce code :

class MonException(Exception):
    def __init__(self,raison):
        self.raison = raison
    
    def __str__(self):
        return self.raison

def multiplier_par_5(n):
    if n > 20:
        raise MonException("Le nombre est trop grand !")
    else:
        return n * 5

Nous créons ici une classe nommée MonException, avec deux méthodes. __init__ prend comme argument raison. La fonction __str__ est celle qui est appelée lorsqu'on tente d'afficher l'instance de l'exception que j'ai jusqu'ici appelée e. Ici, elle renvoie raison.
Il y a ensuite une fonction multiplier_par_5() , qui multiplie l'argument n par 5 à condition que celui-ci ne soit pas plus grand que 20. Dans le cas contraire, on utilise le mot-clé raise pour lancer l'exception. C'est aussi simple que cela.
Finalement, cette fonction utilisée pourrait donner ceci :

try:
    print multiplier_par_5(2)
    print multiplier_par_5(21)
except MonException, e:
    print e
10
Le nombre est trop grand !

Maintenant, vous pouvez ajouter des attributs (comme par exemple la taille du nombre envoyé, pour que __str__ puisse afficher « Le nombre 21 est trop grand ! » par exemple. De plus, l'utilisateur pourrait s'en servir pour afficher l'erreur soi-même, par exemple en faisant print e.n, "est plus grand que 20 ; erreur !" .

Vous voilà désormais capable de gérer tout type d'erreur en Python.
En effet, lorsque vous faites int() , avec une chaîne qui n'est pas transformable en un nombre, vous obtenez une ValueError . Vous n'avez qu'à faire une boucle, et ne pas la terminer tant que vous obtiendrez une ValueError . Voici mon code :

while 1:
    try:
        a = int(raw_input("Entrez un nombre : "))
        break
    except ValueError:
        print "Ce n'est pas un nombre !"
print a*3

Merci à #python pour les conseils qui m'ont été donnés, et à Klouguinette pour sa zCorrection incomparable. :')

Exemple de certificat de réussite
Exemple de certificat de réussite