
Le mot FileNotFoundError s'étale en rouge sur tout l'écran, et le fichier .csv de mon budget, lui, n'a pas bougé d'un millimètre dans son nouveau dossier. Ça, c'est le genre de moment qui remet une débutante en formation python à sa place : on croit avoir dompté son petit script de gestion de fichiers, et il suffit d'un dossier déplacé par réflexe de rangement pour que tout s'écroule devant la table en formica.
Pour éviter ce genre de plantage, il y a en réalité deux réflexes à prendre, pas un seul. Le premier consiste à vérifier la présence du fichier avant d'agir, pour afficher un message clair plutôt qu'un mur de texte rouge. Le second consiste à protéger l'ouverture du fichier elle-même, parce que vérifier avant ne garantit rien si le fichier disparaît entre les deux étapes. Un script propre fait les deux, pas l'un ou l'autre.
Mon fichier de budget a changé de dossier, et mon script a paniqué
J'avais déplacé mon fichier de dépenses dans un autre dossier par souci de rangement, sans penser une seconde que mon script allait s'en offusquer. Le message d'erreur qui a suivi ne ressemblait à rien de ce que j'avais vu jusque-là : une explosion de texte rouge, et au milieu, ce nom, FileNotFoundError, qui sonnait comme un verdict.
Un copain développeur a proposé de m'aider par message. Il m'a tout expliqué d'un bloc, à une vitesse de code qui m'a perdue avant la fin de la troisième phrase, et j'ai refermé la conversation sans être plus avancée. Ce qui a fini par payer, c'est plus bête : rester devant ce message rouge, agacée mais pas prête à fermer l'ordinateur, jusqu'à avoir repéré la ligne exacte qui plantait. J'avais déjà galéré pour comprendre comment faire la somme d'une liste python pour ses premiers calculs, et voir un simple fichier manquant réduire cet effort à néant avait quelque chose d'injuste.

os.path contre pathlib : le vieux sage et le petit nouveau
Deux outils permettent de répondre à la même question, et ils ne viennent pas de la même époque. Le module os existe depuis toujours, un peu comme une vieille recette de famille qu'on continue d'utiliser parce qu'elle marche. On écrit os.path.exists('mon_fichier.csv'), et Python répond par oui ou par non. Le nom vient du fin fond des systèmes d'exploitation, d'où os pour Operating System.
pathlib, de son côté, est arrivé plus tard. Il a été porté par une proposition d'évolution du langage, la PEP 428, intégrée à partir de la version 3.4 et présente dans la bibliothèque standard depuis 2014. Au lieu de manipuler des chaînes de caractères pour désigner un chemin, on manipule un objet chemin, ce qui évite pas mal de confusion entre les slashs et les anti-slashs de Windows. La syntaxe change, Path('mon_fichier.csv').exists() plutôt que os.path.exists(...), mais la question posée à Python reste la même.
Demander la permission avant d'ouvrir un fichier
Vérifier avant d'agir semble être le réflexe le plus logique : on regarde si le fichier existe, puis on l'ouvre si la réponse est oui. C'est ce qu'on appelle le style LBYL, Look Before You Leap, regarder avant de sauter. Sur le papier, rien à redire.
Le problème, c'est le délai entre les deux étapes. Vérifier que le bol à pâte est libre ne m'empêche pas que, le temps d'aller chercher la farine, quelqu'un d'autre dans la cuisine s'en soit déjà servi. Sur un ordinateur, entre le moment où le script vérifie qu'un fichier est là et celui où il tente de l'ouvrir, ce fichier peut avoir été supprimé, déplacé ou renommé par un programme, ou par une utilisatrice maladroite comme moi.

L'autre style, EAFP, Easier to Ask Forgiveness than Permission, plus facile de demander pardon que la permission, renverse la logique : on tente directement l'ouverture, et on gère l'échec s'il survient. C'est la philosophie derrière le bloc try et except, qui mériterait un chapitre à lui seul tant il revient dans presque tous les scripts un peu robustes.
Gestion de fichiers : le bloc qui évite le crash
Dans la pratique, les deux styles ne s'excluent pas. Path('budget.csv').exists() sert très bien à afficher un message d'accueil ou à décider si on propose de créer le fichier. Mais pour l'ouverture réelle, celle qui compte, le filet de sécurité reste le bloc try, avec un except FileNotFoundError qui attrape l'erreur au lieu de laisser le script s'effondrer. La règle que je retiens est simple : .exists() pour informer, try/except pour agir, parce que lui seul protège contre la mauvaise surprise entre la vérification et l'ouverture.
Cassandre, une amie qui appelle toujours Python « le truc ordinateur », m'a demandé un soir pourquoi un fichier manquant faisait autant de bruit sur mon écran. Essayer de lui répondre avec des mots simples m'a forcée à vérifier que je comprenais vraiment ce que je pensais déjà avoir digéré, bien plus que n'importe quelle explication technique. Ce FileNotFoundError, d'ailleurs, appartient à toute une famille de messages d'erreur qui ont chacun leur grammaire propre, un sujet qui mériterait sa propre page plutôt qu'un paragraphe ici.
Pour que ce filet reste lisible, je l'ai glissé dans une petite fonction que j'appelle avant chaque lecture de fichier, plutôt que de répéter le même bloc partout dans le script, et ce réflexe d'emballer une vérification dans une fonction mériterait lui aussi sa propre explication complète ailleurs. Quand on cherche le temps d'apprendre le langage python après une journée chargée, on n'a clairement pas envie de perdre une soirée entière à déboguer un crash qui aurait pu être évité par trois lignes de code.

Mon code propre a encore un angle mort
Depuis que ce filet est en place, lancer le script ne provoque plus ce mur de texte rouge. Un fichier de test renommé exprès pour vérifier que tout tient produit juste une phrase posée : le fichier est introuvable, direction le bon dossier. Rien de spectaculaire, juste un script qui ne panique plus devant un dossier qui a bougé.
Reste une zone que je n'ai pas encore réglée : que faire quand le fichier existe bel et bien, mais que le système refuse de l'ouvrir pour une question de droits d'accès. Ce PermissionError-là n'est pas couvert par le except FileNotFoundError d'aujourd'hui, et je n'ai pas encore de réponse propre à lui apporter. Pour l'instant, mon script de budget tourne sans s'effondrer au premier dossier déplacé, et ça, à ma table de cuisine, ça compte déjà pour une victoire.