Comment corriger "tentative d'importation relative en non-paquet" même avec init.py
j'essaie de suivre PEP 328 , avec la structure de répertoire suivante:
pkg/
__init__.py
components/
core.py
__init__.py
tests/
core_test.py
__init__.py
dans core_test.py
j'ai la déclaration d'importation suivante
from ..components.core import GameLoopEvents
cependant, quand je cours, j'obtiens l'erreur suivante:
tests$ python core_test.py
Traceback (most recent call last):
File "core_test.py", line 3, in <module>
from ..components.core import GameLoopEvents
ValueError: Attempted relative import in non-package
J'ai trouvé " le chemin relatif ne fonctionne pas même avec __init__.py " et " " Importer un module à partir d'un chemin relatif " mais ils n'ont pas d'aide.
y a-t-il quelque chose que je manque ici?
12 réponses
Oui. Vous n'êtes pas à l'utiliser comme un paquet.
python -m pkg.tests.core_test
pour développer sur Ignacio Vazquez-Abrams réponse:
le mécanisme D'importation Python fonctionne relativement au __name__
du fichier courant. Lorsque vous exécutez un fichier directement, il n'a pas son nom habituel, mais a "__main__"
comme son nom au lieu. Donc les importations relatives ne marchent pas.
vous pouvez, comme Igancio l'a suggéré, l'exécuter en utilisant l'option -m
. Si vous avez une partie de votre paquet qui est destiné à être lancé comme un script, vous pouvez également utiliser l'attribut __package__
pour indiquer à ce fichier quel nom il est censé avoir dans la hiérarchie du paquet.
Voir http://www.python.org/dev/peps/pep-0366/ pour plus de détails.
Vous pouvez utiliser import components.core
directement, si vous ajoutez le répertoire courant sys.path
:
if __name__ == '__main__' and __package__ is None:
from os import sys, path
sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
Cela dépend de comment vous voulez lancer votre script.
Si vous voulez lancer votre UnitTest à partir de la ligne de commande classique", c'est:
python tests/core_test.py
puis, puisque dans ce cas 'composants' et 'tests' sont des dossiers frères et sœurs, vous pouvez importer le module relatif soit en utilisant le insérer ou le ajouter méthode du sys.chemin module. Quelque chose comme:
import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents
sinon, vous pouvez lancer votre script avec l'argument "- m" (notez que dans ce cas, nous parlons d'un paquet, et donc vous ne devez pas donner le ".PY ' extension), c'est-à-dire:
python -m pkg.tests.core_test
dans un tel cas, vous pouvez simplement utiliser l'importation relative comme vous avez été à faire:
from ..components.core import GameLoopEvents
vous pouvez enfin mélanger les deux approches, de sorte que votre script fonctionnera peu importe comment il est appelé. Par exemple:
if __name__ == '__main__':
if __package__ is None:
import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents
else:
from ..components.core import GameLoopEvents
In core_test.py, faire ce qui suit:
import sys
sys.path.append('../components')
from core import GameLoopEvents
si votre étui d'utilisation est pour exécuter des tests, et il Coume qu'il est, alors vous pouvez faire ce qui suit. Au lieu d'exécuter votre script de test comme python core_test.py
utilisez un framework de test tel que pytest
. Sur la ligne de commande, vous pouvez entrer
$$ py.test
qui exécutera les tests dans votre répertoire. Cela contourne la question de __name__
étant __main__
qui a été souligné par @BrenBarn. Ensuite, mettez un fichier __init__.py
vide dans votre répertoire de test. , cela fera du répertoire test une partie de votre paquet. Alors vous pourrez faire
from ..components.core import GameLoopEvents
cependant, si vous exécutez votre script de test en tant que programme principal, les choses vont encore échouer. Alors utilisez juste le coureur de test. Peut-être que cela fonctionne aussi avec d'autres coureurs comme nosetests
mais je n'ai pas vérifié. Espérons que cette aide.
mon quick-fix est d'ajouter le répertoire au chemin:
import sys
sys.path.insert(0, '../components/')
vieux fil. J'ai découvert que l'ajout d'un __all__= ['submodule', ...]
à la
__ _ init__.py fichier puis en utilisant le from <CURRENT_MODULE> import *
dans la cible fonctionne très bien.
si quelqu'un cherche une solution, j'en tombe sur une. Voici un peu de contexte. J'ai voulu tester une des méthodes que j'ai dans un fichier. Quand je l'exécute de l'intérieur
if __name__ == "__main__":
elle s'est toujours plainte des importations relatives. J'ai essayé d'appliquer les solutions ci-dessus, mais n'a pas réussi à travailler, car il y avait beaucoup de fichiers imbriqués, chacun avec des importations multiples.
voilà ce que j'ai fait. Je viens de créer un lanceur, un programme externe qui importez méthodes nécessaires et de les appeler. Bien que ce ne soit pas une bonne solution, ça marche.
Essayez cette
import components
from components import *
vous pouvez utiliser from pkg.components.core import GameLoopEvents
, par exemple j'utilise pycharm, ci-dessous est mon image de structure de projet, je viens d'importer à partir du paquet racine, puis il fonctionne:
Comme Paolo , a déclaré, nous avons 2 méthodes d'appel:
1) python -m tests.core_test
2) python tests/core_test.py
Une différence entre eux est sys.chaîne de chemin[0]. Depuis , l'interpréteur recherchera sys.chemin lors de l'importation de , nous pouvons faire avec tests/core_test.py
:
if __name__ == '__main__':
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from components import core
<other stuff>
et plus après cela, nous pouvons courir core_test.py avec d'autres méthodes:
cd tests
python core_test.py
python -m core_test
...
Note, py36 testé seulement.