3.8. Erreurs et exceptions

3.8.1. Introduction

Les erreurs et exceptions ont été mentionnées de nombreuses fois dans ces pages, mais nous nous sommes toujours contenter de dire ce qui les déclenchait (et donc comment les éviter). Même si parfois, les exceptions indiquent un réel problème, elles ne devraient pas forcément interrompre l’exécution du programme.

L’on peut distinguer deux types d’erreurs :

  • les erreurs de syntaxe, qui indiquent une mauvaise syntaxe dans l’écriture du programme ;
  • les exceptions, qui surviennent lorsqu’un comportement non prévu à l’origine survient.

Dans tous les cas, une pile, appelée Traceback, est affichée. Elle permet de retracer les différentes étapes qui ont conduit à l’erreur, ainsi que des informations utiles pour la réparer (le type d’erreur, le ficher, la ligne…).

3.8.2. Lever une exception

Une exception est levée grâce au mot-clé raise : il suffit d’indiquer le nom de l’exception levée, suivie éventuellement d’arguments :

raise ExceptionName(args)

Voici un exemple simple :

>>> raise ValueError('Just a test.')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Just a test.

Si vous souhaitez déterminer si une erreur a été levée, mais sans la prendre en charge, il est possible de lever la même erreur dans le bloc except à l’aide de raise.

>>> try:
...     raise Exception('An exception')
... except Exception:
...     print('Here the exception is catched.')
...     raise
Here the exception is catched.
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Exception: An exception

3.8.3. Gérer les exceptions

3.8.3.1. Généralités

Les exceptions se gèrent grâce au couple try-except. Un bloc try doit être accompagné d’au moins un bloc except (il n’y a aucune limite maximale). Il existe trois manières d’écrire la ligne except :

  • en indiquant le nom de l’erreur concernée : except Error: ;
  • en indiquant un tuple contenant plusieurs erreurs : except (Error1, ..., Errorn): ;
  • en n’indiquant rien : except:.

Ainsi, un ensemble try-except s’écrira de la forme :

try:
    instructions
except Error1:
    instructions si Error1
except Error2:
    instructions si Error2
except (Error3, Error4):
    instructions si Error3 ou Error4
except:
    instructions si autre erreur

Prenons un exemple concret :

>>> try:
...     num1 = int(input('Enter a number: '))
...     num2 = int(input('Enter another number: '))
... except ValueError:
...     print('You must enter only numbers.')

Imaginons que nous souhaitions demander deux nombres à l’utilisateur, pour ensuite effectuer diverses opérations (par exemple). Comme la fonction input() retourne une chaine, nous devons la convertir en entier. Toutefois, si l’utilisateur n’a pas suivi la consigne, et qu’il entre une chaine, alors l’interpréteur ne parviendra pas à la convertir en nombre et lèvera une exception ValueError.

Si jamais une exception qui n’était pas attendue était levée, alors le Traceback normal serait affiché (ce qui n’est pas le cas ici).

Notons aussi que le try prend en charge toute erreur (du même type que celui spécifié) qui pourrait survenir dans une fonctions appelées dans le bloc try (et ce récursivement).

>>> def zerodiv():
...     1/0
>>> try:
...     zerodiv()
... except ZeroDivisionError:
...     print('Int division by zero.')
Int division by zero.

Note

Il est tout à fait possible de ne préciser aucune erreur après except, comme on l’a vu, mais il s’agit d’une pratique fortement déconseillé :

  • l’on perd en souplesse, puisque l’on ne peut gérer chaque erreur au cas par cas ;
  • cela masque toutes les erreurs qui surviennent, même celles que l’on imaginait pas, ce qui a pour conséquence de complexifier largement le débuggage.

3.8.3.2. Blocs évolués

Au même titre que pour les boucles, il est possible d’adjoindre un bloc else aux blocs de gestion d’exceptions : ce dernier ne sera exécuté que si aucune exception n’a été levée.

Il est aussi possible d’ajouter un bloc finally : les instructions de ce dernier bloc seront exécutées quoiqu’il arrive. Ce dernier peut être utilisé pour les actions de “nettoyage” (fermeture d’un fichier ouvert dans try, fermeture d’une base de données…).

Note

with peut parfois remplacer avantageusement finally.

Voici un exemple général (testez-le en entrant une chaine puis un nombre) :

>>> try:
...     num = int(input('Enter a number: '))
... except ValueError:
...     print('You must enter a number.')
... else:
...     print('You have enter a number.')
... finally:
...     print('This text is always printed.')

Note

Il est important de noter que le bloc finally sera toujours exécuté, même si un return, break ou continue intervient avant (dans le code).

>>> def func():
...     try:
...         return 1
...     except:
...         pass
...     finally:
...         print(2)
>>> func()
2
1

3.8.3.3. Variable de l’erreur

Il est aussi possible de stocker l’erreur dans une variable, afin de la manipuler et d’obtenir des informations plus précises : la variable spécifiée dans la ligne except est alors associée à l’exception qui a été levée. Il est alors possible d’accéder aux attributs et aux arguments de l’exception concernée.

>>> try:
...     raise Exception('First argument', 'Another argument')
... except Exception as error:
...     print(type(error))
...     print(error.args)
<class 'Exception'>
('First argument', 'Another argument')

3.8.4. Définition d’exceptions

Les exceptions, comme tout élément en Python, est un objet. Il est donc très simple de définir des exceptions personnalisées : il suffit de dériver une classe d’exception pré-existante.

>>> class MyError(Exception):
...     pass
>>> raise MyError
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
MyError

Note

Il aurait bien entendu été possible de redéfinir la méthode __init__(), ou encore d’ajouter d’autres méthodes et attributs. Toutefois, les classes d’exception sont souvent simplifiées autant que possible.

3.8.5. Exceptions built-in

Au même titre que pour les fonctions, Python contient tout un set d’exceptions built-in. Voici donc une liste des exceptions les plus courrantes :