Définition
Vous lancez un script qui commence par un simple import, et il s'arrête net avant sa première ligne utile. C'est ModuleNotFoundError qui vient de se déclencher : Python a parcouru tous les endroits où il sait chercher un module, sans y trouver ni fichier ni paquet portant ce nom.
# pip install requests a pourtant été lancé avant ceci
import requests
# ModuleNotFoundError: No module named 'requests'Cette erreur porte un nom à part depuis Python 3.6 seulement ; avant, elle partageait une seule exception avec un cas voisin, ImportError, sans distinguer un module absent d'un module qui existe mais refuse de se charger. La distinction compte pour le code de récupération : intercepter ModuleNotFoundError ne rattrape que l'absence pure, intercepter ImportError rattrape les deux, puisque la première en hérite.
Où Python cherche, exactement
Dire que Python n'a rien trouvé « nulle part » ne veut rien dire tant qu'on ignore ce que ce mot recouvre : il consulte en réalité une liste précise de dossiers, nommée sys.path, construite au lancement du programme et toujours parcourue dans le même ordre.
| Rang | Ce qui est fouillé |
|---|---|
| 1 | Le dossier du script lancé |
| 2 | Les chemins déclarés dans la variable PYTHONPATH |
| 3 | La bibliothèque standard de l'interpréteur en cours |
| 4 | Les paquets installés pour ce même interpréteur |
Deux mots de ce tableau expliquent une bonne moitié des cas rencontrés en pratique. « Lancé », au premier rang, désigne le dossier du fichier exécuté, pas celui où l'on se trouve dans un terminal : un module maison qui s'importe très bien depuis la racine d'un projet peut devenir introuvable depuis un sous-dossier, la racine ayant quitté la liste.
« En cours », aux rangs trois et quatre, désigne l'interpréteur qui exécute le code à cet instant, pas celui qui a servi à installer quoi que ce soit. C'est la cause la plus fréquente, celle qui déroute presque tout le monde une fois : une installation réussie, suivie d'un import qui échoue quand même.
Les quatre causes, par fréquence
La première rejoint l'interpréteur « en cours » : le paquet a été installé par un interpréteur différent de celui qui exécute le code, par exemple un environnement virtuel oublié au lancement. Deux lignes suffisent pour trancher.
import sys
print(sys.executable) # le chemin de l'interpréteur qui exécute ce scriptSi le chemin affiché ne correspond pas à l'environnement où l'installation a eu lieu, la cause est trouvée : relancez le script avec le bon interpréteur, ou réinstallez le paquet dans celui réellement utilisé.
Un éditeur qui a gardé en mémoire un ancien interpréteur reproduit ce piège sans prévenir : l'installation affiche un succès, l'import échoue juste après, et rien ne dit qu'ils n'ont pas parlé au même Python.
La deuxième cause tient au nom lui-même : le nom du paquet installé et le nom du module importé sont deux chaînes indépendantes, choisies séparément par les auteurs, sans qu'elles se ressemblent forcément.
| Ce qui s'installe | Ce qui s'importe |
|---|---|
| pillow | PIL |
| beautifulsoup4 | bs4 |
| python-dateutil | dateutil |
| scikit-learn | sklearn |
La troisième cause vient d'un fichier qui se prend, sans le vouloir, pour une bibliothèque : un random.py posé à côté du script masque le module standard du même nom, puisque le dossier courant occupe le premier rang du tableau vu plus haut. L'erreur remonte alors depuis une importation interne, à un endroit du traceback sans rapport apparent avec le fichier fautif.
print(nom_module.__file__) affiche le chemin exact du fichier chargé sous ce nom : s'il pointe vers votre propre dossier plutôt que vers la bibliothèque standard, le nom est en collision.
La quatrième cause tient au dossier de lancement : importer un module maison suppose de partir de la racine du projet, ou de passer par l'option -m. Depuis un sous-dossier, un script perd cette racine, et tous ses imports internes échouent d'un coup.
La distinguer de ses voisines
Trois erreurs se ressemblent au premier coup d'oeil, et pourtant elles pointent vers des corrections opposées.
| Erreur | Ce qu'elle signale | Où chercher |
|---|---|---|
ModuleNotFoundError | Aucun module de ce nom | Environnement, installation |
ImportError | Le module existe, le nom réclamé après from n'y est pas | Version du paquet |
| NameError | Le nom n'a jamais été défini | Un import oublié |
La dernière ligne mérite qu'on s'y arrête : oublier l'import produit une erreur de nom, pas une erreur d'import, au moment où la variable est lue plus loin. Savoir laquelle des trois s'affiche dit s'il faut installer quelque chose ou ajouter une ligne en haut du fichier.
Questions fréquentes
L'installation a réussi, pourquoi l'import échoue-t-il quand même ?
Parce que l'installation et l'exécution n'ont pas visé le même interpréteur, ce qui arrive dès qu'un environnement virtuel entre en jeu. Comparez le chemin de sys.executable avec celui de l'environnement utilisé pour installer : neuf fois sur dix, les deux diffèrent.
Faut-il rattraper cette erreur ?
Seulement pour une dépendance réellement facultative, celle dont le programme peut se passer. Un try suivi d'un except permet alors de basculer sur une solution de repli ou d'afficher un message clair. Pour une dépendance indispensable, laissez l'exception remonter telle quelle : elle est déjà plus explicite que n'importe quel contournement écrit à la main.
Mon module est juste à côté, pourquoi reste-t-il introuvable ?
Parce que « à côté du fichier en cours » et « dans le dossier de lancement » désignent deux choses différentes, et c'est le second qui compte pour Python. Lancez le programme depuis la racine du projet plutôt que depuis un sous-dossier, ou déclarez ce dossier comme un paquet, et vérifiez qu'aucun nom de fichier n'entre en collision avec une bibliothèque déjà installée.