Récemment, lors du développement d'un projet Python, j'ai rencontré l'erreur suivante : ImportError: attempted relative import with no known parent package. Le scénario était le suivant : je voulais utiliser une fonction définie dans script_a.py à partir de script_b.py en utilisant une syntaxe comme from ..dossier_a.script_a import ma_fonction, mais l'exécution de script_b.py générait systématiquement cette erreur.
projet
├── dossier_a
│ └── script_a.py
├── dossier_b
│ └── script_b.py
└── lanceur_principal.py
Solutions rapides
Pour importer correctement script_a.py dans script_b.py, plusieurs approches existent :
- Modifier l'architecture du projet pour exécuter depuis
lanceur_principal.py - Ajouter le répertoire racine au chemin de recherche Python avec
sys.path.append()et utiliserfrom dossier_a.script_a import ma_fonction - Exécuter avec l'option
-m:python -m projet.dossier_b.script_b[**Solution recommandée** 👍]
Analyse technique approfondie
Une compréhension superficielle du système d'importation Python peut mener à deux erreurs fréquentes :
- ModuleNotFoundError: Aucun module nommé 'xxx' trouvé
- ImportError: Tnetative d'importation relative sans package parent connu
Il existe fondamentalement deux méthodes d'importation :
- Importation absolue :
from module import element- recherche dans les modules connus - Importation relative :
from .module import element- basée sur le chemin relatif au fichier actuel
Méthode 1 : Importation absolue et gestion du chemin
L'interpréteur Python recherche les modules dans plusieurs emplacements séquentiels :
- Le répertoire du script en cours d'exécution
- Les répertoires définis dans la variable d'environnement
PYTHONPATH - Les répertoires d'installation standard de Python
import sys
print(sys.path)
# ['/chemin/actuel', '/usr/lib/python310.zip', '/usr/lib/python3.10', ...]
Pour résoudre notre problème d'importation entre dossiers, nous pouvons ajouter manuellement le répertoire racine au chemin de recherche :
import sys
import os
# Ajout du chemin racine (approche moins élégante mais fonctionnelle)
chemin_racine = os.path.dirname(os.path.abspath(__file__))
chemin_parent = os.path.dirname(chemin_racine)
sys.path.insert(0, chemin_parent)
from dossier_a.script_a import ma_fonction
Une approche plus robuste peut utiliser cette condition :
if __package__:
from ..dossier_a import script_a
else:
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
import script_a
Méthode 2 : Importation relative dans un package
Cette méthode ne fonctionne qu'à l'intérieur d'un package reconnu par Python. Lorsqu'un script est exécuté directement avec python script.py, Python ne le considère pas comme faisant partie d'un package, même si un fichier __init__.py est présent. Vous pouvez vérifier cela avec print(__package__) - le résultat sera None pour un script exécuté directement.
Contrairement à une idée répandue, l'ajout de fichiers __init__.py ne résout pas systématiquement ce problème particulier.
Pour utiliser les imporattions relatives entre script_a.py et script_b.py, il faut exécuter le module comme un package :
python -m projet.dossier_b.script_b
Quelques points importants :
- Le projet est traité comme un package complet
- L'extension
.pyest omise - La commande
python -m dossier_b.script_bne fonctionnerait pas (limitée au top-level package)
Pour illustrer avec une structure plus complexe :
super_projet
├── projet
│ ├── dossier_a
│ │ └── script_a.py
│ ├── dossier_b
│ │ └── script_b.py
│ └── lanceur_interne.py
└── lanceur_externe.py
- Depuis
lanceur_externe.py:from projet.dossier_b import script_bpermet les imports relatifs avecfrom ..dossier_a.script_a import ma_fonction - Depuis
lanceur_interne.py:import dossier_b.script_bne permet que les imports relatifs simples (from .module import)
Notes complémentaires
Remarques importantes :
- Cet article présente des solutions testées et fonctionnelles, d'autres approches valides peuvent exister
- L'utilisation d'imports absolus comme
from dossier_a import script_aest également possible dans certains contextes - Les imports relatifs sont généralement préférés pour la maintenance du code à l'intérieur d'un package
Références techniques additionnelles :
- Documentation officielle Python sur le module search path
- Documentation officielle Python sur les packages
- Discussions sur Stack Overflow concernent les imports relatifs