Sous-sections


8. Erreurs et Exceptions

Jusqu'ici nous avons à peine mentionné les messages d'erreur, mais si vous avez essayé les exemples vous en avez certainement croisé. Il y a (au moins) deux types distincts d'erreurs: les erreurs de syntaxe et les exceptions.


8.1 Erreurs de Syntaxe

Les erreurs de syntaxe, ou erreurs d'interprétation, sont peut-être les formes de messages d'erreur les plus courantes que vous rencontrerez pendant votre apprentissage de Python:

>>> while 1 print 'Bonjour'
  File "<stdin>", line 1
    while 1 print 'Bonjour'
                ^
SyntaxError: invalid syntax

L'interpréteur affiche la ligne où l'erreur a eu lieu, et une petite `flèche' qui marque le premier point de la ligne où l'erreur a été détectée. L'erreur est causée par le (ou du moins détectée au) lexème qui précède la flèche: dans l'exemple, l'erreur est détectée au mot-clé print, vu qu'il manque un deux-points (":") juste avant. Un nom et un numéro de ligne de fichier sont aussi donnés pour que vous sachiez où aller regarder si les commandes proviennent d'un script.


8.2 Exceptions

Même lorsqu'une instruction ou une expression est syntaxiquement correcte, elle peut provoquer une erreur lorsqu'on essaye de l'exécuter. Les erreurs détectées à l'exécution sont appelées exceptions et ne sont pas fatales: vous allez bientôt apprendre à les gérer dans des programmes Python. Néanmoins, la plupart des exceptions n'est pas gérée par un programme et entraîne des messages d'erreur comme ci-dessous:

>>> 10 * (1/0)
Traceback (innermost last):
  File "<stdin>", line 1
ZeroDivisionError: integer division or modulo
>>> 4 + spam*3
Traceback (innermost last):
  File "<stdin>", line 1
NameError: spam
>>> '2' + 2
Traceback (innermost last):
  File "<stdin>", line 1
TypeError: illegal argument type for built-in operation

La dernière ligne du message d'erreur indique ce qui s'est passé. Les exceptions peuvent être de plusieurs types, le type est écrit dans le message: dans l'exemple, les types sont ZeroDivisionError (erreur de division par zéro), NameError (erreur de nom) et TypeError (erreur de type). La chaîne de caractères affichée comme type de l'exception est le nom intégré pour l'exception qui a eu lieu. C'est le cas pour toutes les exceptions intégrées, mais pas forcément pour les exceptions définies par l'utilisateur (même si c'est une convention utile). Les noms des exceptions standard sont des identifiants intégrés (et non des mots-clés réservés).

Le reste de la ligne contient des détails, dont l'interprétation dépend du type de l'exception.

La partie précédant le message d'erreur affiche le contexte dans lequel l'exception a eu lieu, sous forme de trace de la pile. En général, elle contient une trace de pile avec des lignes de code source; toutefois, les lignes lues depuis l'entrée standard ne seront pas affichées.

La Library Reference donne la liste des exceptions intégrées et leur signification.


8.3 Gestion des Exceptions

Il est possible d'écrire des programmes qui prennent en charge des exceptions spécifiques. Regardez l'exemple suivant, qui affiche une table des inverses de quelques nombres à virgule flottante:

>>> while 1:
...     try:
...         x = int(raw_input("Veuillez entrer un nombre: "))
...         break
...     except ValueError:
...         print "Aille! Ce n'était pas un nombre valide.  Essayez encore..."
...

L'instruction try fonctionne ainsi:

Une instruction try peut avoir plus d'une clause d'exception, de façon à définir des gestionnaires d'exception différents pour des exceptions différentes. Au plus une clause d'exception sera exécutée. Dans une clause d'exception ne seront gérées que les exceptions survenant dans la clause d'essai correspondante, et non pas celles provenant d'autres clauses d'exception. Une clause d'exception peut nommer plusieurs exceptions dans une liste parenthésée, par exemple:

... except (RuntimeError, TypeError, NameError):
...     pass

La dernière clause d'exception peut omettre le(s) nom(s) d'exception, et sert alors de passe-partout. Utilisez ceci avec une précaution extrême: il est facile de cacher de cette façon une vraie erreur de programmation!

import string, sys

try:
    f = open('monfichier.txt')
    c = f.readline()
    i = int(string.strip(c))
except IOError, (errno, strerror):
    print "Erreur(s) E/S: c"  (errno, strerror)
except ValueError:
    print "N'a pas pu convertir la donnée en entier."
except:
    print "Erreur non prévue:", sys.exc_info()[0]
    raise

L'instruction try...except admet une clause par défaut (clause else) qui doit suivre toutes les clauses d'exception. Elle est utile pour placer le code qui doit être exécuté si la clause d'essai ne déclenche pas d'exception. Par exemple:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'impossible d'ouvrir', arg
    else:
        print arg, 'comporte', len(f.readlines()), 'lignes'
        f.close()

L'utilisation de la clause else est meilleure que l'ajout d'un code supplémentaire à la clause try parce qu'elle évite d'intercepter de façon accidentelle une exception qui n'a pas été déclenchée par le code qui est protégé par l'instruction try ... except.

Quand une exception survient, elle peut avoir une valeur associée, appelée aussi l'argument de l'exception. La présence et le type de l'argument dépendent du type de l'exception. Pour les types d'exception qui ont un argument, la clause d'exception peut spécifier une variable après le nom (ou la liste) d'exception(s) qui recevra la valeur de l'argument, de la manière suivante:

>>> try:
...     spam()
... except NameError, x:
...     print 'nom', x, 'non défini'
... 
nom spam non défini

Si une exception a un argument, celui-ci sera affiché dans la dernière partie (`détail') du message pour une exception non gérée.

Les clauses d'exception ne prennent pas en charge uniquement les exceptions qui surviennent dans la clause d'essai, mais aussi celles qui surviennent dans les fonctions appelées (même de façon indirecte) dans la clause d'essai. Par exemple:

>>> def ceci_ne_marche_pas():
...     x = 1/0
... 
>>> try:
...     ceci_ne_marche_pas()
... except ZeroDivisionError, detail:
...     print 'Gestion d'erreur à l'exécution:', detail
... 
Gestion d'erreur à l'exécution: integer division or modulo


8.4 Déclencher des Exceptions

L'instruction raise permet au programmeur de déclencher une exception. Par exemple:

>>> raise NameError, 'Coucou'
Traceback (innermost last):
  File "<stdin>", line 1
NameError: Coucou

Le premier argument de raise nomme l'exception qui doit être déclenchée. Le second argument (optionnel) spécifie l'argument de l'exception.


8.5 Exceptions Définies par l'Utilisateur

Les programmes peuvent nommer leurs propres exceptions en affectant une chaîne de caractères à une variable. Par exemple.

>>> mon_exc = 'mon_exc'
>>> try:
...     raise mon_exc, 2*2
... except mon_exc, val:
...     print 'Mon exception a été déclenchée, valeur:', val
... 
Mon exception a été déclenchée, valeur: 4
>>> raise mon_exc, 1
Traceback (innermost last):
  File "<stdin>", line 1
mon_exc: 1

Plusieurs modules standard s'en servent pour rapporter les erreurs qui peuvent survenir dans les fonctions qu'ils définissent.

Plus d'informations au sujet des classes sont données dans le chapitre 9, ``Classes.''


8.6 Définir les Actions de Nettoyage

L'instruction try admet une autre clause optionnelle qui permet de définir les actions de nettoyage qui doivent être exécutées impérativement. Par exemple:

>>> try:
...     raise KeyboardInterrupt
... finally:
...     print 'Adieu, monde!'
... 
Adieu, monde!
Traceback (innermost last):
  File "<stdin>", line 2
KeyboardInterrupt

Une clause de finalisation (clause finally) est exécutée qu'une exception ait eu lieu ou non dans la clause d'essai. Si une exception a été déclenchée, elle est déclenchée à nouveau après l'exécution de la clause de finalisation. La clause de finalisation est aussi exécutée ``en sortant'' lorsque l'instruction try est interrompue par les instructions break ou return.

Une instruction try doit avoir ou bien une ou plusieurs clauses d'exception, ou bien une clause de finalisation, mais pas les deux.


See About this document... for information on suggesting changes.