Chapitre 6. Traitement des exceptions et utilisation de fichiers

Dans ce chapitre vous plongerez dans les exceptions, les objets-fichiers, les boucles for et les modules os et sys. Si vous avez utilisé les exceptions dans un autre langage de programmation, vous pouvez survoler la première section pour avoir une idée de la syntaxe de Python. Assurez-vous de reprendre une lecture détaillée pour l'utilisation des fichiers.

6.1. Traitement des exceptions

Comme beaucoup de langages orientés objet, Python gère les exception à l'aide de blocs try...except.

NOTE
Python utilise try...except pour gérer les exceptions et raise pour les générer. Java et C++ utilisent try...catch pour gérer les exceptions et throw pour les générer.

Les exceptions sont partout en Python, pratiquement chaque module de la librairie standard Python les utilise et Python lui-même en déclenchera dans de nombreuses circonstances différentes. Vous les avez déjà vu à plusieurs reprises tout au long de ce livre.

Dans chacun de ces cas, nous ne faisions qu'expérimenter à l'aide de l'IDE Python : une erreur se produisait, l'exception était affichée (éventuellement, en fonction de votre IDE, dans un rouge détonnant) et c'était tout. C'est ce que l'on appelle une exception non-gérée, lorsque l'exception a été déclenchée, il n'y avait pas de code pour la prendre en charge explicitement, elle est donc remontée jusqu'à Python qui l'a traité selon la méthode par défaut, qui est d'afficher une information de débogage et d'abandonner. Dans l'IDE ce n'est pas un problème, mais si cela arrivait pendant le déroulement d'un de vos programmes Python réels, le programme dans son ensemble serait arrété.

Cependant, une exception ne doit pas forcément entrainer le plantage complet d'un programme. Les exceptions, lorsqu'elles sont déclenchées, peuvent être gérées. Parfois une exception se produit parcequ'il y a réellement un bogue dans votre code (comme tenter d'accéder à une variable qui n'existe pas), mais souvent, une exception est un évènement que vous pouvez prévoir. Si vous ouvrez un fichier, il peut ne pas exister, si vous vous connectez à une base de données, elle peut être indisponible, ou peut-être n'avez-vous pas les droits nécéssaires pour y accéder. Si vous savez qu'une ligne de code est susceptible de déclencher un exception, vous devriez gérer l'exception avec un bloc try...except.

Exemple 6.1. Ouverture d'un fichier inexistant

>>> fsock = open("/notthere", "r")      1
	Traceback (innermost last):
  File "<interactive input>", line 1, in ?
	IOError: [Errno 2] No such file or directory: '/notthere'
	>>> try:
	...     fsock = open("/notthere")       2
	... except IOError:                     3
	...     print "The file does not exist, exiting gracefully"
	... print "This line will always print" 4
	The file does not exist, exiting gracefully
	This line will always print
1 En utilisant la fonction prédéfinie open, nous pouvons ouvrir un fichier en lecture (nous verrons open plus en détail dans la section suivante). Mais le fichier n'existe pas, ce qui déclenche une exception IOError. Comme nous n'avons pas fourni de gestionnaire pour l'exception IOError, Python se contente d'afficher des informations de débogage et abandonne.
2 Nous allons essayer d'ouvrir le même fichier non-existant, mais cette fois à l'intérieur d'un bloc try...except.
3 Quand la méthode open déclenche une exception IOError, nous sommes prêts. La ligne except IOError: intercepte l'exception et exécute notre propre bloc de code, qui en l'occurence ne fait qu'afficher un message d'erreur plus agréable.
4 Une fois qu'une exception a été traitée, le traitement continue normalement à la première ligne après le bloc try...except. Notez que cette ligne sera toujours affichée, qu'une exception se produise ou pas. Si vous aviez vraiment un fichier appelé notthere dans votre répertoire racine, l'appel a open réussirait, la clause except serait ignorée, mais cette ligne serait quand même exécutée.

Les exceptions peuvent sembler hostiles (après tout, si vous ne les interceptez pas, votre programme plante), mais réflechissez à l'alternative. Voudriez-vous plutôt un objet-fichier inutilisable pointant vers un fichier non-existant ? De toute manière, vous auriez quand même à vérifier sa validité, sinon votre programme produirait des erreurs bizarres plus loin dont vous auriez à retrouver la source. Je suis sûr que vous avez déjà fait cela, ce n'est pas drôle. Avec les exceptions, les erreurs se produisent immédiatement et vous pouvez les gérer de manière standardisée à la source du problème.

6.1.1. Utilisation d'exceptions pour d'autres cas que la gestion d'erreur

Il y a de nombreux autres usages pour les exceptions en dehors de la prise en compte de véritables conditions d'erreurs. Un des usages commun dans la bibliothèque standard Python est d'essayer d'importer un module, puis de vérifier si cela à marché. Importer un module qui n'existe pas déclenchera une exception ImportError. Vous pouvez utiliser cela pour définir des niveaux multiples de fonctionnalité basé sur la disponibilité des modules à l'exécution, ou pour supporter plusieurs plateformes (dans ce cas le code spécifique à chaque plateforme est séparé dans différents modules).

Vous pouvez aussi définir vos propre exceptions en créant un classe qui hérite de la classe prédéfinie Exception et déclencher vos exceptions avec l'instruction raise. Cela dépasse le champ de cette section, voyez la section «Pour en savoir plus» si vous êtes intéressé.

L'exemple suivant démontre l'utilisation d'une exception pour le support d'une fonctionnalité spécifique à une plate-forme. Ce code vient du module getpass, un module enveloppe pour obtenir un mot de passe de l'utilisateur. Obtenir un mot de passe est fait de manière différente sous UNIX, Windows et Mac OS, mais ce code encapsule toutes ces différences.

Exemple 6.2. Support de fonctionnalités propre à une plate-forme

	  # Bind the name getpass to the appropriate function
	  try:
	      import termios, TERMIOS                     1
	  except ImportError:
	      try:
		  import msvcrt                           2
	      except ImportError:
		  try:
		      from EasyDialogs import AskPassword 3
		  except ImportError:
		      getpass = default_getpass           4
		  else:                                   5
		      getpass = AskPassword
	      else:
		  getpass = win_getpass
	  else:
	      getpass = unix_getpass
1 termios est un module spécifique à UNIX qui fournit un contrôle de bas niveau sur le terminal d'entrée. Si ce module n'est pas disponible (parcequ'il n'est pas sur votre système ou que votre système ne le supporte pas), l'import échoue et Python déclenche une exception ImportError, que nous interceptons.
2 OK, nous n'avons pas termios, essayons donc msvcrt, qui est un module spécifique à Windows qui fournit une API pour de nombreuses fonctions utiles des services d'exécution de Microsoft Visual C++. Si l'import échoue, Python déclenche une exception ImportError, que nous interceptons.
3 Si les deux premiers n'ont pas marché, nous essayons d'importer une fonction de EasyDialogs, qui est un module spécifique à Mac OS qui fournit des fonctions pour afficher des boîtes de dialogue de différents types. Encore une fois, si cette import échoue, Python déclenche une exception ImportError, que nous interceptons.
4 Aucun de ces modules spécifiques n'est disponible (ce qui est possible puisque Python a été porté sur de nombreuses plateformes), nous devons donc nous replier sur la fonction de saisie de mot de passe par défaut (qui est définie ailleurs dans le module getpass). Remarquez ce que nous faison là : nous assignons la fonction default_getpass à la variable getpass. Si vous lisez la documentation officielle de getpass, elle vous dit que le module getpass définit une fonction getpass. C'est comme ça qu'il le fait, en assignant getpass à la bonne fonction pour votre plateforme. Quand vous appelez ensuite la fonction getpass, vous appelez en fait une fonction spécifique à la plateforme que ce code a mis en place pour vous. Vous n'avez pas à vous soucier de la plateforme sur laquelle votre code est exécuté, appelez getpass, qui fera ce qu'il faut.
5 Un bloc try...except peut avoir une clause else, comme une instruction if. Si aucune exception n'est déclenchée dans le bloc try, la clause else est exécutée à la suite. Dans ce cas, cela veut dire que l'import from EasyDialogs import AskPassword a fonctionné et donc nous assignons getpass à la fonction AskPassword. Chacun des autres blocs try...except a une clause else similaire pour assigner getpass à la bonne fonction lorsque nous trouvons un import qui marche.

Pour en savoir plus sur le traitement des exceptions