Si un fichier Python situé dans un package lève ImportError: attempted relative import with no known parent package lorsque vous l'exécutez directement, lancez-le en tant que module depuis le répertoire situé au-dessus du package. Sur macOS, placez-vous à la racine du projet dans Terminal, puis exécutez python3 -m package.module. Cela donne à Python le contexte de package qu'un chemin de fichier direct ne fournit pas.
Pourquoi un import relatif échoue-t-il lorsque j'exécute un fichier directement ?
Un import relatif tel que from .helpers import format_result dépend du nom de package du module. Python utilise des points au début pour identifier le package actuel et ses parents. Lorsque vous exécutez un fichier avec python3 path/to/file.py, Python exécute ce fichier en tant que module de premier niveau __main__ ; il ne lui fournit pas les métadonnées de package nécessaires pour résoudre le point. L'import peut donc échouer même si helpers.py se trouve à côté du fichier. La référence de l'import Python décrit les imports relatifs et le traitement spécial de __main__.
Par exemple, supposons que votre projet soit organisé ainsi :
project/
└── myapp/
├── __init__.py
├── cli.py
└── helpers.py
Si cli.py contient from .helpers import format_result, exécuter python3 myapp/cli.py depuis project n'est pas la bonne façon d'invoquer le fichier. Le problème réside dans la manière dont on a demandé à Python d'exécuter le fichier, et non nécessairement dans le fait que l'import relatif ou l'emplacement du fichier soit incorrect.
Exécuter le module depuis la racine du projet sur macOS
Ouvrez Terminal et placez-vous dans le répertoire qui contient myapp. Remplacez le chemin d'exemple par l'emplacement réel de votre projet. Mettez le chemin entre guillemets afin que les espaces soient gérés par le shell ; le shell par défaut de Terminal sous macOS est zsh. Le guide de Terminal d'Apple documente cette valeur par défaut.
cd "/Users/yourname/Projects/project"
python3 -m myapp.cli
L'option -m demande à Python de trouver et d'exécuter le module en utilisant son nom de package avec des points. Conservez le nom du package dans la commande et omettez .py ainsi que les barres obliques du système de fichiers. Le chemin de recherche de modules de Python commence par le répertoire actuel lorsqu'il est invoqué avec -m, c'est pourquoi exécuter la commande depuis la racine du projet est primordial. La documentation de sys.path de Python explique ce chemin de départ.
Si la commande utilise la mauvaise installation de Python, activez d'abord l'environnement virtuel existant du projet, puis utilisez le même modèle python -m. Pour un .venv classique à la racine du projet :
cd "/Users/yourname/Projects/project"
source .venv/bin/activate
python -m myapp.cli
Utiliser python après l'activation sélectionne l'interpréteur de l'environnement. Si votre environnement a un nom ou un emplacement différent, ajustez le chemin d'activation ; ne supposez pas que .venv existe. Vous pouvez vérifier l'interpréteur sélectionné avec python -c 'import sys; print(sys.executable)'.
Si vous souhaitez exécuter le package lui-même
Pour une commande destinée à lancer le package, ajoutez __main__.py à l'intérieur du package et veillez à ce que le point d'entrée reste concis :
myapp/
├── __init__.py
├── __main__.py
├── cli.py
└── helpers.py
Dans myapp/__main__.py, importez et appelez la fonction d'entrée du package. Exécutez ensuite python3 -m myapp depuis la racine du projet. Python exécute le fichier __main__.py du package pour cette forme d'invocation -m, et ce fichier peut utiliser des imports relatifs au package. La documentation de __main__ de Python explique ce modèle de point d'entrée de package.
Vérifiez ces points si l'erreur persiste
- Confirmez votre répertoire de travail. Exécutez
pwddans Terminal et assurez-vous qu'il affiche la racine du projet, le répertoire contenantmyapp. Si le package est imbriqué soussrc, par exemple, utilisez le nom de package importable et assurez-vous que l'environnement configuré du projet le rende accessible ; le simple fait de vous déplacer dans le mauvais dossier parent ne résoudra pas la détection des modules. - Utilisez le vrai nom du package. Dans
python3 -m myapp.cli,myappdoit être le nom du répertoire du package (ou son nom d'importation), etclile nom du fichier de module sans.py. - Vérifiez la ligne du traceback. Si l'import relatif est résolu mais qu'un import ultérieur échoue, il peut s'agir d'un module manquant ou d'une dépendance différente. Corriger le contexte de lancement ne règle que l'échec lié au contexte de package.
- Évitez
PYTHONPATHcomme première solution. Cela modifie le chemin de recherche d'imports de Python, mais cela ne transforme pas à soi seul un fichier exécuté directement en un module de package correctement nommé. Python prévient également quePYTHONPATHpeut affecter plusieurs versions et environnements Python. La référence sur l'initialisation du chemin de Python décrit sa portée. - Si l'exécution directe du fichier est requise, envisagez de modifier le code pour utiliser un import absolu approprié et assurez-vous que le package est installé ou importable dans cet environnement. C'est un choix d'organisation différent ; ne mélangez pas un import relatif au package avec l'attente que le fichier se comporte comme un script autonome.
La règle pratique consiste à adapter la commande au style d'import : pour un import relatif au package, exécutez le module depuis le répertoire parent du package avec python -m. Pour une commande de package, fournissez __main__.py et exécutez le package avec -m.
Sources
- 5. The import system — Python Software Foundation
- The initialization of the sys.path module search path — Python Software Foundation
- main — Top-level code environment — Python Software Foundation
- Modifier le shell par défaut dans Terminal sur Mac — Apple