Traitez les exceptions.
Quand vous trouvez pour la première fois des exceptions qui affichent des tracebacks longs dans la sortie, vous pouvez être tenté d’intercepter chaque erreur pour éviter cela.
Si vous participez à une mission sur Mars, que pouvez-vous faire si un texte sur le système de navigation indique « une erreur s’est produite » ? Imaginez qu’il n’y a pas d’autre information ni de contexte : juste un indicateur lumineux rouge clignotant avec le texte d’erreur. En tant que développeur, il est utile de vous placer de l’autre côté du programme : que peut faire un utilisateur en cas d’erreur ?
Bien que ce module explique comment gérer les exceptions en les interceptant, il n’est pas nécessaire d’intercepter les exceptions en permanence. Parfois, il est utile de laisser les exceptions être levées afin que d’autres appelants puissent traiter les erreurs.
Blocs try et except
Utilisons l’exemple de navigateur pour créer du code qui ouvre des fichiers de configuration pour la mission sur mars. Les fichiers de configuration peuvent avoir toutes sortes de problèmes : il est donc essentiel de signaler les problèmes avec précision quand ils se produisent. Nous savons que si un fichier ou un répertoire n’existe pas, l’exception FileNotFoundError
est levée. Si nous voulons gérer cette exception, nous pouvons le faire avec un bloc try
et except
:
try:
open('config.txt')
except FileNotFoundError:
print("Couldn't find the config.txt file!")
Couldn't find the config.txt file!
Après le mot clé try
, vous ajoutez du code qui peut potentiellement provoquer une exception. Ensuite, vous ajoutez le mot clé except
avec l’exception possible, suivi du code qui doit s’exécuter quand cette condition se produit. Comme config.txt n’existe pas dans le système, Python indique que le fichier de configuration n’est pas présent. Le bloc try
et except
ainsi qu’un message utile, évitent un traceback et informent néanmoins l’utilisateur au sujet du problème.
Bien que l’inexistence d’un fichier soit courante, ce n’est pas la seule erreur que vous pourriez trouver. Des autorisations de fichier non valides peuvent empêcher la lecture d’un fichier, même si le fichier existe. Créons un fichier Python appelé config.py dans Visual Studio Code. Ajoutez le code suivant au fichier, qui recherche et lit le fichier de configuration du système de navigation :
def main():
try:
configuration = open('config.txt')
except FileNotFoundError:
print("Couldn't find the config.txt file!")
if __name__ == '__main__':
main()
Ensuite, créez un répertoire appelé config.txt. Essayez d’appeler le fichier config.py pour voir une nouvelle erreur qui devrait être similaire à celle-ci :
python3 config.py
Traceback (most recent call last):
File "/tmp/config.py", line 9, in <module>
main()
File "/tmp/config.py", line 3, in main
configuration = open('config.txt')
IsADirectoryError: [Errno 21] Is a directory: 'config.txt'
Une façon inutile de gérer cette erreur serait d’intercepter toutes les exceptions possibles pour éviter un traceback. Pour comprendre pourquoi intercepter toutes les exceptions est problématique, essayez de le faire en mettant à jour la fonction main()
dans le fichier config.py nouvellement créé :
def main():
try:
configuration = open('config.txt')
except Exception:
print("Couldn't find the config.txt file!")
À présent, réexécutez le code au même emplacement que celui où se trouve le répertoire config.txt avec des autorisations incorrectes :
python3 config.py
Couldn't find the config.txt file!
Le problème est maintenant que le message d’erreur est incorrect. Le répertoire existe, mais il a des autorisations différentes et Python ne peut pas le lire. Quand vous traitez des erreurs logicielles, il peut être frustrant de rencontrer des erreurs qui :
- N’indiquent pas ce qu’est le problème réel.
- Produisent une sortie qui ne correspond pas au problème réel.
- Ne donnent pas d’indication sur ce que vous pouvez faire pour résoudre le problème.
Corrigeons ce morceau de code de façon répondre à toutes ces frustrations. Rétablissez l’interception de FileNotFoundError
, puis ajoutez un autre bloc except
pour intercepter PermissionError
:
def main():
try:
configuration = open('config.txt')
except FileNotFoundError:
print("Couldn't find the config.txt file!")
except IsADirectoryError:
print("Found config.txt but it is a directory, couldn't read it")
À présent, réexécutez le code au même emplacement que celui où se trouve le répertoire config.txt :
python3 config.py
Found config.txt but couldn't read it
Supprimez maintenant le répertoire config.txt pour faire en sorte que le premier bloc except
soit atteint à la place :
rm -f config.txt
python3 config.py
Couldn't find the config.txt file!
Quand des erreurs sont de nature similaire et qu’il n’est pas nécessaire de les gérer individuellement, vous pouvez regrouper les exceptions en une seule en utilisant des parenthèses dans la ligne except
. Par exemple, si le système de navigation est soumis à des charges lourdes et que le système de fichiers devient trop occupé, il est logique d’intercepter ensemble BlockingIOError
et TimeOutError
:
def main():
try:
configuration = open('config.txt')
except FileNotFoundError:
print("Couldn't find the config.txt file!")
except IsADirectoryError:
print("Found config.txt but it is a directory, couldn't read it")
except (BlockingIOError, TimeoutError):
print("Filesystem under heavy load, can't complete reading configuration file")
Conseil
Même si vous pouvez regrouper des exceptions, faites-le seulement quand vous n’avez pas besoin de les gérer individuellement. Évitez de regrouper de nombreuses exceptions pour fournir un message d’erreur généralisé.
Si vous avez besoin d’accéder à l’erreur associée à l’exception, vous devez mettre à jour la ligne except
pour y inclure le mot clé as
. Cette technique est pratique si une exception est trop générique et que le message d’erreur peut être utile :
try:
open("mars.jpg")
except FileNotFoundError as err:
print("Got a problem trying to read the file:", err)
Got a problem trying to read the file: [Errno 2] No such file or directory: 'mars.jpg'
Dans ce cas, as err
signifie que err
devient une variable avec l’objet exception comme valeur. Il utilise ensuite cette valeur pour afficher le message d’erreur associé à l’exception. Une autre raison d’utiliser cette technique est d’accéder directement aux attributs de l’erreur. Par exemple, si vous interceptez une exception OSError
plus générique, qui est l’exception parente à la fois de FilenotFoundError
et de PermissionError
, vous pouvez les distinguer par l’attribut .errno
:
try:
open("config.txt")
except OSError as err:
if err.errno == 2:
print("Couldn't find the config.txt file!")
elif err.errno == 13:
print("Found config.txt but couldn't read it")
Couldn't find the config.txt file!
Essayez toujours d’utiliser la technique qui offre la meilleure lisibilité pour le code et qui contribue à le maintenir dans le futur. Il est parfois nécessaire d’utiliser du code moins lisible pour offrir une meilleure expérience utilisateur quand une erreur se produit.