Définition
Vous recopiez une ligne de la documentation, vous lancez le script, et Python répond que ce nom n'existe pas. La bibliothèque est pourtant installée, et elle vient de se charger sans broncher. Qui se trompe, la documentation ou la machine ?
Ni l'une ni l'autre : ImportError décrit une situation précise. Python a trouvé le module demandé, il l'a ouvert et exécuté, mais le nom écrit après import ne s'y trouve pas. Cette exception ne parle donc pas d'un module manquant, mais d'un contenu qui déçoit.
# Le module math existe et se charge sans le moindre problème...
from math import racine_carree
# ... mais aucun nom "racine_carree" ne se trouve dedans.
# ImportError: cannot import name 'racine_carree' from 'math'Le message se lit en deux moitiés, et la seconde est celle qu'on saute. La première nomme ce que vous réclamez ; la seconde, après from, nomme le module réellement ouvert pour le chercher. Quand la première paraît juste, le coupable est dans la seconde : le fichier chargé n'est pas celui que vous croyiez.
Autre repère : cette erreur naît de la forme from module import nom. Un import module seul ne réclame aucun nom précis, et quand il échoue, c'est plus tôt, faute d'avoir trouvé le module : Python lève alors une ModuleNotFoundError.
Deux erreurs, une seule famille
Ces deux noms se confondent d'autant plus que le langage les a rapprochés : depuis Python 3.6, ModuleNotFoundError est une sous-classe d'ImportError, et un seul except ImportError attrape les deux. Pratique dans le code, trompeur à la lecture. Le tableau les sépare, avec une troisième erreur qu'on leur attribue à tort.
| Situation | Erreur levée | Où chercher |
|---|---|---|
| Le module reste introuvable | ModuleNotFoundError | Installation, nom du paquet, environnement |
| Le module se charge, le nom manque | ImportError | Contenu du fichier, version, casse |
| Le nom manque après un import réussi | AttributeError | La ligne d'accès, pas la ligne d'import |
La troisième ligne est celle qui surprend. Un import requests suivi d'un appel mal orthographié ne produit aucune ImportError : l'import a réussi, c'est l'accès qui échoue vingt lignes plus bas. Le nom de l'erreur dit à quel moment Python a perdu le fil, et ce moment guide mieux que la ligne où tout s'arrête.
Les causes qui reviennent
La plus fréquente est un nom qui a bougé. Une bibliothèque se réorganise d'une version majeure à l'autre : un objet part dans un sous-module, une fonction change de place. Le code écrit pour la version précédente casse au premier lancement, sans que vous y ayez touché. Comparer la version installée à celle de la documentation ouverte sous vos yeux règle une bonne moitié des cas.
Vient ensuite la casse. Python distingue les majuscules des minuscules jusque dans un import, là où macOS et Windows voient Utils.py et utils.py comme un seul fichier. Un projet qui s'importe sans broncher sur votre portable peut donc échouer au premier déploiement sur un serveur Linux, pour une seule lettre.
Reste le fichier à vous qui prend la place du vrai. Python cherche d'abord dans le dossier depuis lequel le script est lancé : nommer un fichier json.py ou random.py suffit à ce qu'il passe devant la vraie bibliothèque, puis à ce que Python n'y trouve pas ce qu'il venait chercher.
Ce dernier cas coûte souvent une heure, parce que le message cite le nom de la bibliothèque officielle et jamais celui de votre fichier. Avant de douter d'une bibliothèque, lisez le chemin affiché dans l'erreur : s'il désigne un fichier de votre projet, il suffit de le renommer.
L'import circulaire
Un dernier cas déroute tout le monde, parce que le nom réclamé existe bel et bien là où Python prétend ne pas le trouver. L'explication tient au calendrier : deux fichiers s'importent mutuellement, et quand le premier réclame quelque chose au second, celui-ci est encore à mi-chemin de son chargement. Le nom existe dans le fichier, pas encore en mémoire.
# panier.py
from facture import editer # ce fichier demande facture.py...
# facture.py
from panier import Panier # ... qui redemande panier.py, encore en cours
# ImportError: cannot import name 'Panier'
# from partially initialized module 'panier'La mention partially initialized module est la signature du cycle : tant qu'elle apparaît, inutile de chercher une faute de frappe, il n'y en a pas.
Trois sorties existent. Déplacer l'import à l'intérieur de la fonction qui en a besoin retarde le chargement jusqu'à l'appel. Importer le module entier, puis écrire panier.Panier à l'usage, fonctionne aussi. Extraire la partie commune dans un troisième fichier traite seul la cause : un cycle signale un découpage bancal entre deux responsabilités, et il reviendra autrement si vous le contournez.
Questions fréquentes
Faut-il rattraper une ImportError ?
Un seul cas le justifie : la dépendance facultative, celle qui améliore le programme sans lui être indispensable. Un bloc try autour de l'import, avec une valeur de repli dans l'except, laisse tourner le reste. Ailleurs, rattraper masque une installation incomplète et repousse le plantage jusqu'à un message qui ne dira plus rien de la cause.
Pourquoi l'erreur n'apparaît-elle qu'en production ?
Parce que la machine n'est pas la même, et l'écart tient presque toujours à l'un de ces trois points : une version de bibliothèque différente, un système de fichiers sensible à la casse, un paquet installé un jour à la main et jamais déclaré. Figer les versions dans un fichier de dépendances, puis rejouer les imports dans un environnement virtuel neuf, révèle le problème avant les utilisateurs.
Que faire quand le nom figure pourtant bien dans le fichier ?
Lire dans le traceback le chemin du module réellement chargé, que Python affiche entre guillemets. Neuf fois sur dix, ce n'est pas le fichier ouvert dans votre éditeur mais un homonyme : un ancien paquet resté installé, un dossier de test oublié, un script du répertoire courant. Le chemin tranche en une seconde une question qui occuperait sinon la matinée. La formation Python revient longuement sur cette lecture des erreurs.