• 8 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 11/03/2022

Gérez les exceptions avec Python

Découvrez la gestion des exceptions avec Python

Comme vu dans le chapitre Apprenez à comprendre ce que Python vous dit de la partie 2, les exceptions sont des bugs qui apparaissent quand votre programme fait des choses que Python n’aiment pas. Soit Python ne comprend pas ce que vous lui demandez, soit vous lui demandez de faire quelque chose d’impossible.

Quoi qu’il en soit, la conséquence est une sentence irrévocable : le programme s’arrête. Irrévocable ? Pas si vous savez les gérer.

Pour gérer une exception en Python, on utilise les instructions try et  except  comme ceci :

try:
    numerateur = input("Indiquez le numérateur")
    denominateur = input("Indiquez le dénomirateur")
    print(numerateur/denominateur)
except:
    print(“Il y a quelque chose qui cloche avec cette opération ”)

Que se passe-t-il ici ? Notre instruction susceptible de générer une erreur se trouve dans le try. Si elle ne génère pas d'erreur, le programme continue et tout va pour le mieux dans le meilleur des mondes.

Mais si elle génère une exception, l’instruction qui se trouve dans except sera lancée, et ce quelle que soit l’exception, puisque nous n’avons rien indiqué après except. Il s’agit d’un message assez vague qui ne donne pas d’indications particulières sur la cause de l’erreur.

D’ailleurs, quelles sont les exceptions qui pourraient apparaître dans notre opération ? J’en vois au moins 3 :

  • ValueError : L’utilisateur a indiqué autre chose que des chiffres avec des .. Par exemple a$, ou 10,5. En effet, en programmation on utilise le symbole . pour les décimales.

  • ZeroDivisionError : Erreur à cause d’une division par 0, ce qui est mathématiquement impossible. 

  • TypeError : On essaie d’effectuer une opération entre un chiffre et une chaîne de caractère.

Voilà comment nous aurions pu gérer ces cas de figures :

try :
    numerateur = input("Indiquez le numérateur")
    denominateur = input("Indiquez le dénominateur")
    print(int(numerateur)/int(denominateur)) 
except (ZeroDivisionError, ValueError):
    print("Y a un truc qui ne va pas dans votre code")

Comme vous le constatez, cette fois-ci nous avons capturé les exceptions de typesZeroDivisionError et ValueError  en indiquant un message d’erreur. Néanmoins, nous aurions pu indiquer des messages un peu plus détaillés, non ? Remédions à ça :

try :
    numerateur = input("Indiquez le numérateur")
    denominateur = input("Indiquez le dénomirateur")
    print(int(numerateur)/int(denominateur)) 
except ValueError:
    print("ValueError : Vous devez avoir écrit autre chose que des chiffres avec ou sans .")
except ZeroDivisionError:
    print("ZeroDivisionError : Une division par zéro, vous êtes sûr de vous ?")
except :
    print("Alors là, je ne peux pas vous aider")
finally:
    print("L'instruction 'try except' a bien été exécutée !")

Voilà qui est déjà mieux. Nous avons maintenant un message par erreur. La dernière instruction except s'affiche pour toutes les erreurs autres queValueError et ZeroDivisionError. L’instruction finally est lancée à la fin du bloc d’exception quelle que soit l’exception levée. C’est un peu comme le mot de la fin, qu’importe ce qui s’est passé avant.

Mais nous aurions pu faire encore mieux. En effet, pour l’instant nous avons seulement capturé l’exception, mais nous ne l’avons pas gérée pour autant. Pourquoi ne pas le faire pour permettre à notre programme de continuer sa route sereinement ? On y va ?

try :
    numerateur = input("Indiquez le numérateur")
    denominateur = input("Indiquez le dénomirateur")
    print(int(numerateur)/int(denominateur)) 
except ValueError:
    print("ValueError : Vous devez avoir écrit autre chose que des chiffres avec ou sans .\n
    Je vous propose de recommencer.")
    while True:
        numerateur = input("Indiquez le numérateur. Uniquement des chiffres svp.")
        denominateur = input("Indiquez le dénominateur. Uniquement des chiffres svp.")
        if (numerateur.isdigit() and denominateur.isdigit() and int(denominateur) != 0):
            break
    print(int(numerateur)/int(denominateur)) 
except ZeroDivisionError:
    print("ZeroDivisionError : Une division par zéro, vous êtes sûr de vous ?")
except :
    print("Alors là, je ne peux pas vous aider")
finally:
    print("L'instruction 'try except' a bien été exécutée !")

Le code ci-dessus capture non seulement l’exception ValueError, mais en plus il y remédie pour permettre au programme de continuer.

Par exemple, si votre numérateur ou votre dénominateur contiennent autre chose que des chiffres, une ValueError est levée. Vous rentrez ensuite dans une boucle infinie qui vous demande d’indiquer un nouveau numérateur et dénominateur. Tant que ceux-ci ne sont pas uniquement numériques, vous ne sortez pas de la boucle. On vérifie par la même occasion que le dénominateur n’est pas égal à 0. Voilà, exception capturée et gérée.

Une dernière chose. Il est possible également de lever des exceptions manuellement. Pour quoi faire, vous demandez-vous peut-être ? Cela peut être utile si vous voulez appliquer une règle spécifique à votre programme pour interdire un certain fonctionnement.

Imaginons par exemple que vous créez un programme qui vérifie le mot de passe que donne une personne pour l’autoriser ou non à entrer dans le logiciel. Vous pourrez écrire ceci :

mot_de_passe = input("C'est quoi le mot de passe ?")
if mot_de_passe != "1234":
    raise Exception("Intrus !")
else:
    print("Bienvenue au club !")

Grâce au mot clé raise, nous avons manuellement levé une exception.

Nous aurions également pu en choisir une parmi celles qui sont natives en Python comme ceci :

mot_de_passe = input("C'est quoi le mot de passe ?")
if mot_de_passe != "1234":
    raise TypeError("Le mot de passe est une suite de chiffres normalement. Intrus !")
else:
    print("Bienvenue au club !")

Gérez des exceptions dans nos projets logiciel et web

Dans le screencast suivant, nous allons gérer plusieurs exceptions de notre projet de jeu « Pierre Feuille Ciseaux ». Nous allons le faire pour la partie logiciel et pour la version web. Commençons par le projet logiciel :

Dans ce screencast, nous avons capturé les différentes exceptions que notre programme a générées. Pour cela, nous avons tout simplement enfermé l’ensemble des lignes susceptibles de les générer dans des blocs bloctry et except. Il s’agissait de  RuntimeError,  NameError et     .

L’exception _tkinter.TclError était un cas particulier. En effet, elle n’appartient pas à la bibliothèque standard de Python et ne peut donc pas être utilisée après l’instruction except, sauf si on l’importe depuis le module tkinter. Une autre solution pour la capturer est tout simplement de ne rien indiquer après except, ce qui aura pour effet de capturer toutes les exceptions générées dans le try.

Passons à la version web avec Flask !

Dans ce screencast, nous avons vu comment utiliser la fonction abort pour associer à une route spécifique une exception. Nous avons également vu comment associer à une fonction le décorateur @app.errorhandler() pour rediriger une page générant une erreur HTTP vers un template spécifique. 

Enfin, passons maintenant à la version Django :

Comme vu dans ce screencast, la gestion des erreurs HTTP avec Django est très simple. Il suffit notamment de créer un fichier HTML qui a pour nom le code HTTP que vous ciblez. Ainsi, lorsqu'une erreur HTTP ayant ce code sera générée, l’utilisateur sera redirigé vers le template du fichier HTML.

Exercez-vous

Un exercice pour la fin. Le code ci-dessous permet tout simplement d’afficher un message qui indique l’âge de la personne. Vous devez capturer l’ensemble des exceptions qui pourraient se présenter et les gérer pour que le programme se poursuive.

Voici le code de l’exercice :

from datetime import *

date_de_naissance = int(input("Quelle est votre date de naissance ?"))
age = int(datetime.now().strftime("%Y")) - date_de_naissance
print(f"Vous avez donc {age} ans.")

Je vous laisse essayer. Vous pourrez comparer votre travail avec le mien, dans le repository Github du cours.

En résumé

  • Une exception se gère dans un bloc try et except.

  • Le bloc try contient l’instruction susceptible de lever une exception.

  • Le bloc except contient les instructions à exécuter si l’exception est levée.

  • On peut faire suivre l’instruction except du nom d’une exception ou d’un groupe d’exception. Ne rien indiquer après except signifie « quelle que soit l’exception qui est levée ».

  • L’instruction finally se lance à la fin d’un bloc try et except quel que soit le résultat précédent. 

Maintenant que nous savons comment gérer nos exceptions, il est grand temps d’éliminer les bugs, vous ne trouvez pas ? Dans ce cas, rendez-vous au prochain chapitre.

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