Avant-propos
Je suis en train de développer un projet qui est en fait un paquetage Python qui peut être installé par l'intermédiaire de pip mais il expose également une interface en ligne de commande. Je n'ai pas de problèmes pour exécuter mon projet après l'avoir installé avec pip install .
mais bon, qui fait ça à chaque fois qu'il change quelque chose dans un des fichiers du projet ? J'avais besoin de faire passer le tout par un simple python mypackage/main.py
.
/my-project
- README.md
- setup.py
/mypackage
- __init__.py
- main.py
- common.py
Les différents visages d'un même problème
J'ai essayé d'importer quelques fonctions dans main.py
de mon common.py
module. J'ai essayé différentes configurations qui ont donné différentes erreurs, et je veux partager avec vous mes observations et laisser une note rapide pour les futurs moi aussi.
Importation relative
La première chose que j'ai essayée était une importation relative :
from .common import my_func
J'ai exécuté mon application avec simple : python mypackage/main.py
. Malheureusement, cela a donné l'erreur suivante :
ModuleNotFoundError: No module named '__main__.common'; '__main__' is not a package
La cause de ce problème est que le main.py
a été exécuté directement par python
devenant ainsi le module principal nommé __main__
. Si nous relions cette information à l'importation relative que nous avons utilisée, nous obtenons ce que nous avons dans le message d'erreur : __main__.common
. Ceci est expliqué dans le Documentation Python :
Notez que les importations relatives sont basées sur le nom du module courant. Puisque le nom du module principal est toujours __main__
Les modules destinés à être utilisés comme module principal d'une application Python doivent toujours utiliser des importations absolues.
Lorsque j'ai installé mon paquet avec pip install .
puis l'a exécuté, il a parfaitement fonctionné. J'ai également pu importer mypackage.main
dans une console Python. Il semble donc qu'il n'y ait un problème qu'en l'exécutant directement.
Importation absolue
Suivons les conseils de la documentation et changeons l'instruction d'importation en quelque chose de différent :
from common import my_func
Si nous essayons maintenant de l'exécuter comme avant : python mypackage/main.py
alors cela fonctionne comme prévu ! Mais il y a un problème lorsque, comme moi, vous développez quelque chose qui doit fonctionner en tant qu'outil autonome en ligne de commande après l'avoir installé avec la commande pip . J'ai installé mon paquet avec pip install .
et ensuite j'ai essayé de l'exécuter...
ModuleNotFoundError: No module named 'common'
Pire encore, lorsque j'ai ouvert une console Python, et que j'ai essayé d'importer le fichier main
manuellement ( import mypackage.main
), puis j'ai obtenu la même erreur que ci-dessus. La raison en est simple : common
n'est plus un import relatif, donc Python essaie de le trouver dans les paquets installés. Nous n'avons pas de tel paquet, c'est pourquoi il échoue.
La solution de l'importation absolue ne fonctionne bien que si vous créez une application Python typique qui est exécutée avec un fichier de type python
commandement.
Importation avec un nom de paquet
Il existe également une troisième possibilité d'importer le common
module :
from mypackage.common import my_func
Ce n'est pas très différent de la Importation relative l'approche, tant que nous le faisons à partir du contexte de mypackage
. Et encore une fois, j'essaie de l'exécuter avec python mypackage/main.py
finit par être similaire :
ModuleNotFoundError: No module named 'mypackage'
Comme cela peut être irritant, l'interprète a raison, vous n'avez pas installé ce paquet.
La solution
Pour les applications Python simples
Utilisez simplement les importations absolues (sans le point), et tout ira bien.
Pour les applications Python installables en cours de développement
Utilisez des importations relatives, ou des importations avec un nom de paquet au début, car vous en avez besoin comme ça lorsque votre application est installée. Lorsqu'il s'agit d'exécuter un tel module en développement, Python peut être exécuté avec la commande -m
option :
-m mod : run library module as a script (terminates option list)
Ainsi, au lieu de python mypackage/main.py
fais-le comme ça : python -m mypackage.main
.
2 votes
Le signe '.' ne fonctionne comme vous le souhaitez que lorsque le fichier dans lequel il se trouve est dans un paquet.
2 votes
@anonymoose, "proj" n'est pas un paquet ? puisqu'il possède un init .py3 ?
3 votes
Il n'est pas possible d'utiliser le "." en tête des importations et de faire en sorte que cela fonctionne comme vous le souhaitez lorsque vous exécutez le fichier directement avec Python. Vous devez importer le fichier. Si vous avez placé un autre fichier en dehors de
proj
qui avaitimport moduleA
je pense que vous obtiendrez le résultat que vous attendez.1 votes
Duplicata possible de ModuleNotFoundError : Qu'est-ce que cela signifie __main__ n'est pas un module ?